summaryrefslogtreecommitdiffstats
path: root/sound
diff options
context:
space:
mode:
Diffstat (limited to 'sound')
-rw-r--r--sound/core/info.c9
-rw-r--r--sound/core/seq/seq_timer.c4
-rw-r--r--sound/pci/asihpi/hpioctl.c2
-rw-r--r--sound/pci/hda/hda_intel.c7
-rw-r--r--sound/pci/hda/patch_realtek.c28
-rw-r--r--sound/pci/hda/thinkpad_helper.c3
-rw-r--r--sound/soc/atmel/Kconfig10
-rw-r--r--sound/soc/atmel/Makefile2
-rw-r--r--sound/soc/atmel/atmel_ssc_dai.c83
-rw-r--r--sound/soc/atmel/atmel_ssc_dai.h1
-rw-r--r--sound/soc/atmel/atmel_wm8904.c2
-rw-r--r--sound/soc/atmel/tse850-pcm5142.c472
-rw-r--r--sound/soc/bcm/Kconfig1
-rw-r--r--sound/soc/codecs/adau17x1.c2
-rw-r--r--sound/soc/codecs/arizona.c153
-rw-r--r--sound/soc/codecs/arizona.h109
-rw-r--r--sound/soc/codecs/cs4270.c8
-rw-r--r--sound/soc/codecs/cs47l24.c55
-rw-r--r--sound/soc/codecs/da7219-aad.c18
-rw-r--r--sound/soc/codecs/da7219.c142
-rw-r--r--sound/soc/codecs/da7219.h5
-rw-r--r--sound/soc/codecs/hdmi-codec.c7
-rw-r--r--sound/soc/codecs/rt298.c5
-rw-r--r--sound/soc/codecs/rt5663.c4
-rw-r--r--sound/soc/codecs/sti-sas.c2
-rw-r--r--sound/soc/codecs/tas571x.c37
-rw-r--r--sound/soc/codecs/tlv320aic3x.c2
-rw-r--r--sound/soc/codecs/wm5102.c59
-rw-r--r--sound/soc/codecs/wm5110.c61
-rw-r--r--sound/soc/codecs/wm8997.c39
-rw-r--r--sound/soc/codecs/wm8998.c38
-rw-r--r--sound/soc/codecs/wm9712.c2
-rw-r--r--sound/soc/codecs/wm9713.c2
-rw-r--r--sound/soc/codecs/wm_adsp.c354
-rw-r--r--sound/soc/codecs/wm_adsp.h27
-rw-r--r--sound/soc/codecs/wmfw.h4
-rw-r--r--sound/soc/intel/Kconfig3
-rw-r--r--sound/soc/intel/atom/sst-atom-controls.c2
-rw-r--r--sound/soc/intel/atom/sst-mfld-platform-pcm.c6
-rw-r--r--sound/soc/intel/atom/sst/sst.c39
-rw-r--r--sound/soc/intel/atom/sst/sst.h1
-rw-r--r--sound/soc/intel/atom/sst/sst_acpi.c3
-rw-r--r--sound/soc/intel/atom/sst/sst_ipc.c11
-rw-r--r--sound/soc/intel/atom/sst/sst_stream.c4
-rw-r--r--sound/soc/intel/baytrail/sst-baytrail-ipc.c3
-rw-r--r--sound/soc/intel/boards/broadwell.c18
-rw-r--r--sound/soc/intel/boards/bxt_da7219_max98357a.c26
-rw-r--r--sound/soc/intel/boards/bytcr_rt5640.c64
-rw-r--r--sound/soc/intel/boards/cht_bsw_rt5645.c6
-rw-r--r--sound/soc/intel/boards/cht_bsw_rt5672.c42
-rw-r--r--sound/soc/intel/boards/mfld_machine.c4
-rw-r--r--sound/soc/intel/common/sst-acpi.h17
-rw-r--r--sound/soc/intel/common/sst-ipc.c85
-rw-r--r--sound/soc/intel/common/sst-ipc.h8
-rw-r--r--sound/soc/intel/common/sst-match-acpi.c57
-rw-r--r--sound/soc/intel/haswell/sst-haswell-ipc.c3
-rw-r--r--sound/soc/intel/skylake/bxt-sst.c145
-rw-r--r--sound/soc/intel/skylake/skl-messages.c39
-rw-r--r--sound/soc/intel/skylake/skl-pcm.c28
-rw-r--r--sound/soc/intel/skylake/skl-sst-cldma.c1
-rw-r--r--sound/soc/intel/skylake/skl-sst-dsp.h12
-rw-r--r--sound/soc/intel/skylake/skl-sst-ipc.c71
-rw-r--r--sound/soc/intel/skylake/skl-sst-ipc.h37
-rw-r--r--sound/soc/intel/skylake/skl-sst-utils.c2
-rw-r--r--sound/soc/intel/skylake/skl-topology.c47
-rw-r--r--sound/soc/intel/skylake/skl-topology.h28
-rw-r--r--sound/soc/intel/skylake/skl.c67
-rw-r--r--sound/soc/intel/skylake/skl.h6
-rw-r--r--sound/soc/pxa/Kconfig2
-rw-r--r--sound/soc/qcom/lpass-cpu.c3
-rw-r--r--sound/soc/qcom/lpass-platform.c169
-rw-r--r--sound/soc/qcom/lpass.h1
-rw-r--r--sound/soc/samsung/ac97.c10
-rw-r--r--sound/soc/samsung/dmaengine.c8
-rw-r--r--sound/soc/samsung/i2s.c22
-rw-r--r--sound/soc/samsung/pcm.c19
-rw-r--r--sound/soc/samsung/s3c2412-i2s.c18
-rw-r--r--sound/soc/samsung/s3c24xx-i2s.c16
-rw-r--r--sound/soc/samsung/spdif.c19
-rw-r--r--sound/soc/sh/Kconfig3
-rw-r--r--sound/soc/sh/rcar/adg.c61
-rw-r--r--sound/soc/sh/rcar/core.c175
-rw-r--r--sound/soc/sh/rcar/dma.c295
-rw-r--r--sound/soc/sh/rcar/dvc.c2
-rw-r--r--sound/soc/sh/rcar/gen.c12
-rw-r--r--sound/soc/sh/rcar/rsnd.h156
-rw-r--r--sound/soc/sh/rcar/src.c13
-rw-r--r--sound/soc/sh/rcar/ssi.c28
-rw-r--r--sound/soc/sh/rcar/ssiu.c20
-rw-r--r--sound/soc/soc-core.c98
-rw-r--r--sound/soc/soc-dapm.c154
-rw-r--r--sound/soc/soc-generic-dmaengine-pcm.c13
-rw-r--r--sound/soc/soc-utils.c199
-rw-r--r--sound/soc/sti/uniperif_player.c6
-rw-r--r--sound/soc/sunxi/sun4i-codec.c19
-rw-r--r--sound/sparc/dbri.c27
-rw-r--r--sound/usb/card.c3
-rw-r--r--sound/usb/quirks-table.h17
98 files changed, 3276 insertions, 956 deletions
diff --git a/sound/core/info.c b/sound/core/info.c
index 895362a696c9..8ab72e0f5932 100644
--- a/sound/core/info.c
+++ b/sound/core/info.c
@@ -325,10 +325,15 @@ static ssize_t snd_info_text_entry_write(struct file *file,
size_t next;
int err = 0;
+ if (!entry->c.text.write)
+ return -EIO;
pos = *offset;
if (!valid_pos(pos, count))
return -EIO;
next = pos + count;
+ /* don't handle too large text inputs */
+ if (next > 16 * 1024)
+ return -EIO;
mutex_lock(&entry->access);
buf = data->wbuffer;
if (!buf) {
@@ -366,7 +371,9 @@ static int snd_info_seq_show(struct seq_file *seq, void *p)
struct snd_info_private_data *data = seq->private;
struct snd_info_entry *entry = data->entry;
- if (entry->c.text.read) {
+ if (!entry->c.text.read) {
+ return -EIO;
+ } else {
data->rbuffer->buffer = (char *)seq; /* XXX hack! */
entry->c.text.read(entry, data->rbuffer);
}
diff --git a/sound/core/seq/seq_timer.c b/sound/core/seq/seq_timer.c
index dcc102813aef..37d9cfbc29f9 100644
--- a/sound/core/seq/seq_timer.c
+++ b/sound/core/seq/seq_timer.c
@@ -448,8 +448,8 @@ snd_seq_real_time_t snd_seq_timer_get_cur_time(struct snd_seq_timer *tmr)
ktime_get_ts64(&tm);
tm = timespec64_sub(tm, tmr->last_update);
- cur_time.tv_nsec = tm.tv_nsec;
- cur_time.tv_sec = tm.tv_sec;
+ cur_time.tv_nsec += tm.tv_nsec;
+ cur_time.tv_sec += tm.tv_sec;
snd_seq_sanity_real_time(&cur_time);
}
spin_unlock_irqrestore(&tmr->lock, flags);
diff --git a/sound/pci/asihpi/hpioctl.c b/sound/pci/asihpi/hpioctl.c
index d17937b92331..7e3aa50b21f9 100644
--- a/sound/pci/asihpi/hpioctl.c
+++ b/sound/pci/asihpi/hpioctl.c
@@ -111,7 +111,7 @@ long asihpi_hpi_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
return -EINVAL;
hm = kmalloc(sizeof(*hm), GFP_KERNEL);
- hr = kmalloc(sizeof(*hr), GFP_KERNEL);
+ hr = kzalloc(sizeof(*hr), GFP_KERNEL);
if (!hm || !hr) {
err = -ENOMEM;
goto out;
diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c
index c3469f756ec2..c64d986009a9 100644
--- a/sound/pci/hda/hda_intel.c
+++ b/sound/pci/hda/hda_intel.c
@@ -341,8 +341,7 @@ enum {
/* quirks for Nvidia */
#define AZX_DCAPS_PRESET_NVIDIA \
- (AZX_DCAPS_NO_MSI | /*AZX_DCAPS_ALIGN_BUFSIZE |*/ \
- AZX_DCAPS_NO_64BIT | AZX_DCAPS_CORBRP_SELF_CLEAR |\
+ (AZX_DCAPS_NO_MSI | AZX_DCAPS_CORBRP_SELF_CLEAR |\
AZX_DCAPS_SNOOP_TYPE(NVIDIA))
#define AZX_DCAPS_PRESET_CTHDA \
@@ -1716,6 +1715,10 @@ static int azx_first_init(struct azx *chip)
}
}
+ /* NVidia hardware normally only supports up to 40 bits of DMA */
+ if (chip->pci->vendor == PCI_VENDOR_ID_NVIDIA)
+ dma_bits = 40;
+
/* disable 64bit DMA address on some devices */
if (chip->driver_caps & AZX_DCAPS_NO_64BIT) {
dev_dbg(card->dev, "Disabling 64bit DMA\n");
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index b58e8c76346a..ea81c08ddc7a 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -5811,8 +5811,6 @@ static const struct hda_model_fixup alc269_fixup_models[] = {
#define ALC295_STANDARD_PINS \
{0x12, 0xb7a60130}, \
{0x14, 0x90170110}, \
- {0x17, 0x21014020}, \
- {0x18, 0x21a19030}, \
{0x21, 0x04211020}
#define ALC298_STANDARD_PINS \
@@ -5859,11 +5857,19 @@ static const struct snd_hda_pin_quirk alc269_pin_fixup_tbl[] = {
{0x1b, 0x02011020},
{0x21, 0x0221101f}),
SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
+ {0x14, 0x90170110},
+ {0x1b, 0x01011020},
+ {0x21, 0x0221101f}),
+ SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
{0x14, 0x90170130},
{0x1b, 0x01014020},
{0x21, 0x0221103f}),
SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
{0x14, 0x90170130},
+ {0x1b, 0x01011020},
+ {0x21, 0x0221103f}),
+ SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
+ {0x14, 0x90170130},
{0x1b, 0x02011020},
{0x21, 0x0221103f}),
SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
@@ -6039,7 +6045,13 @@ static const struct snd_hda_pin_quirk alc269_pin_fixup_tbl[] = {
ALC292_STANDARD_PINS,
{0x13, 0x90a60140}),
SND_HDA_PIN_QUIRK(0x10ec0295, 0x1028, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE,
- ALC295_STANDARD_PINS),
+ ALC295_STANDARD_PINS,
+ {0x17, 0x21014020},
+ {0x18, 0x21a19030}),
+ SND_HDA_PIN_QUIRK(0x10ec0295, 0x1028, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE,
+ ALC295_STANDARD_PINS,
+ {0x17, 0x21014040},
+ {0x18, 0x21a19050}),
SND_HDA_PIN_QUIRK(0x10ec0298, 0x1028, "Dell", ALC298_FIXUP_DELL1_MIC_NO_PRESENCE,
ALC298_STANDARD_PINS,
{0x17, 0x90170110}),
@@ -6613,6 +6625,7 @@ enum {
ALC891_FIXUP_HEADSET_MODE,
ALC891_FIXUP_DELL_MIC_NO_PRESENCE,
ALC662_FIXUP_ACER_VERITON,
+ ALC892_FIXUP_ASROCK_MOBO,
};
static const struct hda_fixup alc662_fixups[] = {
@@ -6889,6 +6902,14 @@ static const struct hda_fixup alc662_fixups[] = {
{ }
}
},
+ [ALC892_FIXUP_ASROCK_MOBO] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x15, 0x40f000f0 }, /* disabled */
+ { 0x16, 0x40f000f0 }, /* disabled */
+ { }
+ }
+ },
};
static const struct snd_pci_quirk alc662_fixup_tbl[] = {
@@ -6926,6 +6947,7 @@ static const struct snd_pci_quirk alc662_fixup_tbl[] = {
SND_PCI_QUIRK(0x144d, 0xc051, "Samsung R720", ALC662_FIXUP_IDEAPAD),
SND_PCI_QUIRK(0x17aa, 0x38af, "Lenovo Ideapad Y550P", ALC662_FIXUP_IDEAPAD),
SND_PCI_QUIRK(0x17aa, 0x3a0d, "Lenovo Ideapad Y550", ALC662_FIXUP_IDEAPAD),
+ SND_PCI_QUIRK(0x1849, 0x5892, "ASRock B150M", ALC892_FIXUP_ASROCK_MOBO),
SND_PCI_QUIRK(0x19da, 0xa130, "Zotac Z68", ALC662_FIXUP_ZOTAC_Z68),
SND_PCI_QUIRK(0x1b0a, 0x01b8, "ACER Veriton", ALC662_FIXUP_ACER_VERITON),
SND_PCI_QUIRK(0x1b35, 0x2206, "CZC P10T", ALC662_FIXUP_CZC_P10T),
diff --git a/sound/pci/hda/thinkpad_helper.c b/sound/pci/hda/thinkpad_helper.c
index 6a23302297c9..4d9d320a7971 100644
--- a/sound/pci/hda/thinkpad_helper.c
+++ b/sound/pci/hda/thinkpad_helper.c
@@ -13,7 +13,8 @@ static void (*old_vmaster_hook)(void *, int);
static bool is_thinkpad(struct hda_codec *codec)
{
return (codec->core.subsystem_id >> 16 == 0x17aa) &&
- (acpi_dev_found("LEN0068") || acpi_dev_found("IBM0068"));
+ (acpi_dev_found("LEN0068") || acpi_dev_found("LEN0268") ||
+ acpi_dev_found("IBM0068"));
}
static void update_tpacpi_mute_led(void *private_data, int enabled)
diff --git a/sound/soc/atmel/Kconfig b/sound/soc/atmel/Kconfig
index 22aec9a1e9a4..4a56f3dfba51 100644
--- a/sound/soc/atmel/Kconfig
+++ b/sound/soc/atmel/Kconfig
@@ -78,4 +78,14 @@ config SND_ATMEL_SOC_PDMIC
help
Say Y if you want to add support for Atmel ASoC driver for boards using
PDMIC.
+
+config SND_ATMEL_SOC_TSE850_PCM5142
+ tristate "ASoC driver for the Axentia TSE-850"
+ depends on ARCH_AT91 && OF
+ depends on ATMEL_SSC && I2C
+ select SND_ATMEL_SOC_SSC_DMA
+ select SND_SOC_PCM512x_I2C
+ help
+ Say Y if you want to add support for the ASoC driver for the
+ Axentia TSE-850 with a PCM5142 codec.
endif
diff --git a/sound/soc/atmel/Makefile b/sound/soc/atmel/Makefile
index a2b127bd9c87..67e10cbd4ed7 100644
--- a/sound/soc/atmel/Makefile
+++ b/sound/soc/atmel/Makefile
@@ -13,9 +13,11 @@ snd-atmel-soc-wm8904-objs := atmel_wm8904.o
snd-soc-sam9x5-wm8731-objs := sam9x5_wm8731.o
snd-atmel-soc-classd-objs := atmel-classd.o
snd-atmel-soc-pdmic-objs := atmel-pdmic.o
+snd-atmel-soc-tse850-pcm5142-objs := tse850-pcm5142.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_ATMEL_SOC_CLASSD) += snd-atmel-soc-classd.o
obj-$(CONFIG_SND_ATMEL_SOC_PDMIC) += snd-atmel-soc-pdmic.o
+obj-$(CONFIG_SND_ATMEL_SOC_TSE850_PCM5142) += snd-atmel-soc-tse850-pcm5142.o
diff --git a/sound/soc/atmel/atmel_ssc_dai.c b/sound/soc/atmel/atmel_ssc_dai.c
index 16e459aedffe..a1e2c5682dcd 100644
--- a/sound/soc/atmel/atmel_ssc_dai.c
+++ b/sound/soc/atmel/atmel_ssc_dai.c
@@ -380,6 +380,7 @@ static void atmel_ssc_shutdown(struct snd_pcm_substream *substream,
ssc_writel(ssc_p->ssc->regs, CR, SSC_BIT(CR_SWRST));
/* Clear the SSC dividers */
ssc_p->cmr_div = ssc_p->tcmr_period = ssc_p->rcmr_period = 0;
+ ssc_p->forced_divider = 0;
}
spin_unlock_irq(&ssc_p->lock);
@@ -426,14 +427,17 @@ static int atmel_ssc_set_dai_clkdiv(struct snd_soc_dai *cpu_dai,
else
if (div != ssc_p->cmr_div)
return -EBUSY;
+ ssc_p->forced_divider |= BIT(ATMEL_SSC_CMR_DIV);
break;
case ATMEL_SSC_TCMR_PERIOD:
ssc_p->tcmr_period = div;
+ ssc_p->forced_divider |= BIT(ATMEL_SSC_TCMR_PERIOD);
break;
case ATMEL_SSC_RCMR_PERIOD:
ssc_p->rcmr_period = div;
+ ssc_p->forced_divider |= BIT(ATMEL_SSC_RCMR_PERIOD);
break;
default:
@@ -443,6 +447,28 @@ static int atmel_ssc_set_dai_clkdiv(struct snd_soc_dai *cpu_dai,
return 0;
}
+/* Is the cpu-dai master of the frame clock? */
+static int atmel_ssc_cfs(struct atmel_ssc_info *ssc_p)
+{
+ switch (ssc_p->daifmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFS:
+ case SND_SOC_DAIFMT_CBS_CFS:
+ return 1;
+ }
+ return 0;
+}
+
+/* Is the cpu-dai master of the bit clock? */
+static int atmel_ssc_cbs(struct atmel_ssc_info *ssc_p)
+{
+ switch (ssc_p->daifmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFM:
+ case SND_SOC_DAIFMT_CBS_CFS:
+ return 1;
+ }
+ return 0;
+}
+
/*
* Configure the SSC.
*/
@@ -459,6 +485,9 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream,
u32 tfmr, rfmr, tcmr, rcmr;
int ret;
int fslen, fslen_ext;
+ u32 cmr_div;
+ u32 tcmr_period;
+ u32 rcmr_period;
/*
* Currently, there is only one set of dma params for
@@ -470,6 +499,46 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream,
else
dir = 1;
+ /*
+ * If the cpu dai should provide BCLK, but noone has provided the
+ * divider needed for that to work, fall back to something sensible.
+ */
+ cmr_div = ssc_p->cmr_div;
+ if (!(ssc_p->forced_divider & BIT(ATMEL_SSC_CMR_DIV)) &&
+ atmel_ssc_cbs(ssc_p)) {
+ int bclk_rate = snd_soc_params_to_bclk(params);
+
+ if (bclk_rate < 0) {
+ dev_err(dai->dev, "unable to calculate cmr_div: %d\n",
+ bclk_rate);
+ return bclk_rate;
+ }
+
+ cmr_div = DIV_ROUND_CLOSEST(ssc_p->mck_rate, 2 * bclk_rate);
+ }
+
+ /*
+ * If the cpu dai should provide LRCLK, but noone has provided the
+ * dividers needed for that to work, fall back to something sensible.
+ */
+ tcmr_period = ssc_p->tcmr_period;
+ rcmr_period = ssc_p->rcmr_period;
+ if (atmel_ssc_cfs(ssc_p)) {
+ int frame_size = snd_soc_params_to_frame_size(params);
+
+ if (frame_size < 0) {
+ dev_err(dai->dev,
+ "unable to calculate tx/rx cmr_period: %d\n",
+ frame_size);
+ return frame_size;
+ }
+
+ if (!(ssc_p->forced_divider & BIT(ATMEL_SSC_TCMR_PERIOD)))
+ tcmr_period = frame_size / 2 - 1;
+ if (!(ssc_p->forced_divider & BIT(ATMEL_SSC_RCMR_PERIOD)))
+ rcmr_period = frame_size / 2 - 1;
+ }
+
dma_params = ssc_p->dma_params[dir];
channels = params_channels(params);
@@ -524,7 +593,7 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream,
fslen_ext = (bits - 1) / 16;
fslen = (bits - 1) % 16;
- rcmr = SSC_BF(RCMR_PERIOD, ssc_p->rcmr_period)
+ rcmr = SSC_BF(RCMR_PERIOD, rcmr_period)
| SSC_BF(RCMR_STTDLY, START_DELAY)
| SSC_BF(RCMR_START, SSC_START_FALLING_RF)
| SSC_BF(RCMR_CKI, SSC_CKI_RISING)
@@ -540,7 +609,7 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream,
| SSC_BF(RFMR_LOOP, 0)
| SSC_BF(RFMR_DATLEN, (bits - 1));
- tcmr = SSC_BF(TCMR_PERIOD, ssc_p->tcmr_period)
+ tcmr = SSC_BF(TCMR_PERIOD, tcmr_period)
| SSC_BF(TCMR_STTDLY, START_DELAY)
| SSC_BF(TCMR_START, SSC_START_FALLING_RF)
| SSC_BF(TCMR_CKI, SSC_CKI_FALLING)
@@ -606,7 +675,7 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream,
fslen_ext = (bits - 1) / 16;
fslen = (bits - 1) % 16;
- rcmr = SSC_BF(RCMR_PERIOD, ssc_p->rcmr_period)
+ rcmr = SSC_BF(RCMR_PERIOD, rcmr_period)
| SSC_BF(RCMR_STTDLY, START_DELAY)
| SSC_BF(RCMR_START, SSC_START_FALLING_RF)
| SSC_BF(RCMR_CKI, SSC_CKI_RISING)
@@ -623,7 +692,7 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream,
| SSC_BF(RFMR_LOOP, 0)
| SSC_BF(RFMR_DATLEN, (bits - 1));
- tcmr = SSC_BF(TCMR_PERIOD, ssc_p->tcmr_period)
+ tcmr = SSC_BF(TCMR_PERIOD, tcmr_period)
| SSC_BF(TCMR_STTDLY, START_DELAY)
| SSC_BF(TCMR_START, SSC_START_FALLING_RF)
| SSC_BF(TCMR_CKI, SSC_CKI_FALLING)
@@ -650,7 +719,7 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream,
* MCK divider, and the BCLK signal is output
* on the SSC TK line.
*/
- rcmr = SSC_BF(RCMR_PERIOD, ssc_p->rcmr_period)
+ rcmr = SSC_BF(RCMR_PERIOD, rcmr_period)
| SSC_BF(RCMR_STTDLY, 1)
| SSC_BF(RCMR_START, SSC_START_RISING_RF)
| SSC_BF(RCMR_CKI, SSC_CKI_RISING)
@@ -665,7 +734,7 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream,
| SSC_BF(RFMR_LOOP, 0)
| SSC_BF(RFMR_DATLEN, (bits - 1));
- tcmr = SSC_BF(TCMR_PERIOD, ssc_p->tcmr_period)
+ tcmr = SSC_BF(TCMR_PERIOD, tcmr_period)
| SSC_BF(TCMR_STTDLY, 1)
| SSC_BF(TCMR_START, SSC_START_RISING_RF)
| SSC_BF(TCMR_CKI, SSC_CKI_FALLING)
@@ -760,7 +829,7 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream,
}
/* set SSC clock mode register */
- ssc_writel(ssc_p->ssc->regs, CMR, ssc_p->cmr_div);
+ ssc_writel(ssc_p->ssc->regs, CMR, cmr_div);
/* set receive clock mode and format */
ssc_writel(ssc_p->ssc->regs, RCMR, rcmr);
diff --git a/sound/soc/atmel/atmel_ssc_dai.h b/sound/soc/atmel/atmel_ssc_dai.h
index 80b153857a88..75194f582131 100644
--- a/sound/soc/atmel/atmel_ssc_dai.h
+++ b/sound/soc/atmel/atmel_ssc_dai.h
@@ -113,6 +113,7 @@ struct atmel_ssc_info {
unsigned short cmr_div;
unsigned short tcmr_period;
unsigned short rcmr_period;
+ unsigned int forced_divider;
struct atmel_pcm_dma_params *dma_params[2];
struct atmel_ssc_state ssc_state;
unsigned long mck_rate;
diff --git a/sound/soc/atmel/atmel_wm8904.c b/sound/soc/atmel/atmel_wm8904.c
index fdd28ed3e0b9..fbc10f61eb55 100644
--- a/sound/soc/atmel/atmel_wm8904.c
+++ b/sound/soc/atmel/atmel_wm8904.c
@@ -53,7 +53,7 @@ static int atmel_asoc_wm8904_hw_params(struct snd_pcm_substream *substream,
return 0;
}
-static struct snd_soc_ops atmel_asoc_wm8904_ops = {
+static const struct snd_soc_ops atmel_asoc_wm8904_ops = {
.hw_params = atmel_asoc_wm8904_hw_params,
};
diff --git a/sound/soc/atmel/tse850-pcm5142.c b/sound/soc/atmel/tse850-pcm5142.c
new file mode 100644
index 000000000000..ac6a814c8ecf
--- /dev/null
+++ b/sound/soc/atmel/tse850-pcm5142.c
@@ -0,0 +1,472 @@
+/*
+ * TSE-850 audio - ASoC driver for the Axentia TSE-850 with a PCM5142 codec
+ *
+ * Copyright (C) 2016 Axentia Technologies AB
+ *
+ * Author: Peter Rosin <peda@axentia.se>
+ *
+ * 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.
+ */
+
+/*
+ * loop1 relays
+ * IN1 +---o +------------+ o---+ OUT1
+ * \ /
+ * + +
+ * | / |
+ * +--o +--. |
+ * | add | |
+ * | V |
+ * | .---. |
+ * DAC +----------->|Sum|---+
+ * | '---' |
+ * | |
+ * + +
+ *
+ * IN2 +---o--+------------+--o---+ OUT2
+ * loop2 relays
+ *
+ * The 'loop1' gpio pin controlls two relays, which are either in loop
+ * position, meaning that input and output are directly connected, or
+ * they are in mixer position, meaning that the signal is passed through
+ * the 'Sum' mixer. Similarly for 'loop2'.
+ *
+ * In the above, the 'loop1' relays are inactive, thus feeding IN1 to the
+ * mixer (if 'add' is active) and feeding the mixer output to OUT1. The
+ * 'loop2' relays are active, short-cutting the TSE-850 from channel 2.
+ * IN1, IN2, OUT1 and OUT2 are TSE-850 connectors and DAC is the PCB name
+ * of the (filtered) output from the PCM5142 codec.
+ */
+
+#include <linux/clk.h>
+#include <linux/gpio.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <linux/regulator/consumer.h>
+
+#include <sound/soc.h>
+#include <sound/pcm_params.h>
+
+#include "atmel_ssc_dai.h"
+
+struct tse850_priv {
+ int ssc_id;
+
+ struct gpio_desc *add;
+ struct gpio_desc *loop1;
+ struct gpio_desc *loop2;
+
+ struct regulator *ana;
+
+ int add_cache;
+ int loop1_cache;
+ int loop2_cache;
+};
+
+static int tse850_get_mux1(struct snd_kcontrol *kctrl,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kctrl);
+ struct snd_soc_card *card = dapm->card;
+ struct tse850_priv *tse850 = snd_soc_card_get_drvdata(card);
+
+ ucontrol->value.enumerated.item[0] = tse850->loop1_cache;
+
+ return 0;
+}
+
+static int tse850_put_mux1(struct snd_kcontrol *kctrl,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kctrl);
+ struct snd_soc_card *card = dapm->card;
+ struct tse850_priv *tse850 = snd_soc_card_get_drvdata(card);
+ struct soc_enum *e = (struct soc_enum *)kctrl->private_value;
+ unsigned int val = ucontrol->value.enumerated.item[0];
+
+ if (val >= e->items)
+ return -EINVAL;
+
+ gpiod_set_value_cansleep(tse850->loop1, val);
+ tse850->loop1_cache = val;
+
+ return snd_soc_dapm_put_enum_double(kctrl, ucontrol);
+}
+
+static int tse850_get_mux2(struct snd_kcontrol *kctrl,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kctrl);
+ struct snd_soc_card *card = dapm->card;
+ struct tse850_priv *tse850 = snd_soc_card_get_drvdata(card);
+
+ ucontrol->value.enumerated.item[0] = tse850->loop2_cache;
+
+ return 0;
+}
+
+static int tse850_put_mux2(struct snd_kcontrol *kctrl,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kctrl);
+ struct snd_soc_card *card = dapm->card;
+ struct tse850_priv *tse850 = snd_soc_card_get_drvdata(card);
+ struct soc_enum *e = (struct soc_enum *)kctrl->private_value;
+ unsigned int val = ucontrol->value.enumerated.item[0];
+
+ if (val >= e->items)
+ return -EINVAL;
+
+ gpiod_set_value_cansleep(tse850->loop2, val);
+ tse850->loop2_cache = val;
+
+ return snd_soc_dapm_put_enum_double(kctrl, ucontrol);
+}
+
+int tse850_get_mix(struct snd_kcontrol *kctrl,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kctrl);
+ struct snd_soc_card *card = dapm->card;
+ struct tse850_priv *tse850 = snd_soc_card_get_drvdata(card);
+
+ ucontrol->value.enumerated.item[0] = tse850->add_cache;
+
+ return 0;
+}
+
+int tse850_put_mix(struct snd_kcontrol *kctrl,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kctrl);
+ struct snd_soc_card *card = dapm->card;
+ struct tse850_priv *tse850 = snd_soc_card_get_drvdata(card);
+ int connect = !!ucontrol->value.integer.value[0];
+
+ if (tse850->add_cache == connect)
+ return 0;
+
+ /*
+ * Hmmm, this gpiod_set_value_cansleep call should probably happen
+ * inside snd_soc_dapm_mixer_update_power in the loop.
+ */
+ gpiod_set_value_cansleep(tse850->add, connect);
+ tse850->add_cache = connect;
+
+ snd_soc_dapm_mixer_update_power(dapm, kctrl, connect, NULL);
+ return 1;
+}
+
+int tse850_get_ana(struct snd_kcontrol *kctrl,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kctrl);
+ struct snd_soc_card *card = dapm->card;
+ struct tse850_priv *tse850 = snd_soc_card_get_drvdata(card);
+ int ret;
+
+ ret = regulator_get_voltage(tse850->ana);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * Map regulator output values like so:
+ * -11.5V to "Low" (enum 0)
+ * 11.5V-12.5V to "12V" (enum 1)
+ * 12.5V-13.5V to "13V" (enum 2)
+ * ...
+ * 18.5V-19.5V to "19V" (enum 8)
+ * 19.5V- to "20V" (enum 9)
+ */
+ if (ret < 11000000)
+ ret = 11000000;
+ else if (ret > 20000000)
+ ret = 20000000;
+ ret -= 11000000;
+ ret = (ret + 500000) / 1000000;
+
+ ucontrol->value.enumerated.item[0] = ret;
+
+ return 0;
+}
+
+int tse850_put_ana(struct snd_kcontrol *kctrl,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kctrl);
+ struct snd_soc_card *card = dapm->card;
+ struct tse850_priv *tse850 = snd_soc_card_get_drvdata(card);
+ struct soc_enum *e = (struct soc_enum *)kctrl->private_value;
+ unsigned int uV = ucontrol->value.enumerated.item[0];
+ int ret;
+
+ if (uV >= e->items)
+ return -EINVAL;
+
+ /*
+ * Map enum zero (Low) to 2 volts on the regulator, do this since
+ * the ana regulator is supplied by the system 12V voltage and
+ * requesting anything below the system voltage causes the system
+ * voltage to be passed through the regulator. Also, the ana
+ * regulator induces noise when requesting voltages near the
+ * system voltage. So, by mapping Low to 2V, that noise is
+ * eliminated when all that is needed is 12V (the system voltage).
+ */
+ if (uV)
+ uV = 11000000 + (1000000 * uV);
+ else
+ uV = 2000000;
+
+ ret = regulator_set_voltage(tse850->ana, uV, uV);
+ if (ret < 0)
+ return ret;
+
+ return snd_soc_dapm_put_enum_double(kctrl, ucontrol);
+}
+
+static const char * const mux_text[] = { "Mixer", "Loop" };
+
+static const struct soc_enum mux_enum =
+ SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, 2, mux_text);
+
+static const struct snd_kcontrol_new mux1 =
+ SOC_DAPM_ENUM_EXT("MUX1", mux_enum, tse850_get_mux1, tse850_put_mux1);
+
+static const struct snd_kcontrol_new mux2 =
+ SOC_DAPM_ENUM_EXT("MUX2", mux_enum, tse850_get_mux2, tse850_put_mux2);
+
+#define TSE850_DAPM_SINGLE_EXT(xname, reg, shift, max, invert, xget, xput) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+ .info = snd_soc_info_volsw, \
+ .get = xget, \
+ .put = xput, \
+ .private_value = SOC_SINGLE_VALUE(reg, shift, max, invert, 0) }
+
+static const struct snd_kcontrol_new mix[] = {
+ TSE850_DAPM_SINGLE_EXT("IN Switch", SND_SOC_NOPM, 0, 1, 0,
+ tse850_get_mix, tse850_put_mix),
+};
+
+static const char * const ana_text[] = {
+ "Low", "12V", "13V", "14V", "15V", "16V", "17V", "18V", "19V", "20V"
+};
+
+static const struct soc_enum ana_enum =
+ SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, 9, ana_text);
+
+static const struct snd_kcontrol_new out =
+ SOC_DAPM_ENUM_EXT("ANA", ana_enum, tse850_get_ana, tse850_put_ana);
+
+static const struct snd_soc_dapm_widget tse850_dapm_widgets[] = {
+ SND_SOC_DAPM_LINE("OUT1", NULL),
+ SND_SOC_DAPM_LINE("OUT2", NULL),
+ SND_SOC_DAPM_LINE("IN1", NULL),
+ SND_SOC_DAPM_LINE("IN2", NULL),
+ SND_SOC_DAPM_INPUT("DAC"),
+ SND_SOC_DAPM_AIF_IN("AIFINL", "Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("AIFINR", "Playback", 1, SND_SOC_NOPM, 0, 0),
+ SOC_MIXER_ARRAY("MIX", SND_SOC_NOPM, 0, 0, mix),
+ SND_SOC_DAPM_MUX("MUX1", SND_SOC_NOPM, 0, 0, &mux1),
+ SND_SOC_DAPM_MUX("MUX2", SND_SOC_NOPM, 0, 0, &mux2),
+ SND_SOC_DAPM_OUT_DRV("OUT", SND_SOC_NOPM, 0, 0, &out, 1),
+};
+
+/*
+ * These connections are not entirely correct, since both IN1 and IN2
+ * are always fed to MIX (if the "IN switch" is set so), i.e. without
+ * regard to the loop1 and loop2 relays that according to this only
+ * control MUX1 and MUX2 but in fact also control how the input signals
+ * are routed.
+ * But, 1) I don't know how to do it right, and 2) it doesn't seem to
+ * matter in practice since nothing is powered in those sections anyway.
+ */
+static const struct snd_soc_dapm_route tse850_intercon[] = {
+ { "OUT1", NULL, "MUX1" },
+ { "OUT2", NULL, "MUX2" },
+
+ { "MUX1", "Loop", "IN1" },
+ { "MUX1", "Mixer", "OUT" },
+
+ { "MUX2", "Loop", "IN2" },
+ { "MUX2", "Mixer", "OUT" },
+
+ { "OUT", NULL, "MIX" },
+
+ { "MIX", NULL, "DAC" },
+ { "MIX", "IN Switch", "IN1" },
+ { "MIX", "IN Switch", "IN2" },
+
+ /* connect board input to the codec left channel output pin */
+ { "DAC", NULL, "OUTL" },
+};
+
+static struct snd_soc_dai_link tse850_dailink = {
+ .name = "TSE-850",
+ .stream_name = "TSE-850-PCM",
+ .codec_dai_name = "pcm512x-hifi",
+ .dai_fmt = SND_SOC_DAIFMT_I2S
+ | SND_SOC_DAIFMT_NB_NF
+ | SND_SOC_DAIFMT_CBM_CFS,
+};
+
+static struct snd_soc_card tse850_card = {
+ .name = "TSE-850-ASoC",
+ .owner = THIS_MODULE,
+ .dai_link = &tse850_dailink,
+ .num_links = 1,
+ .dapm_widgets = tse850_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(tse850_dapm_widgets),
+ .dapm_routes = tse850_intercon,
+ .num_dapm_routes = ARRAY_SIZE(tse850_intercon),
+ .fully_routed = true,
+};
+
+static int tse850_dt_init(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct device_node *codec_np, *cpu_np;
+ struct snd_soc_card *card = &tse850_card;
+ struct snd_soc_dai_link *dailink = &tse850_dailink;
+ struct tse850_priv *tse850 = snd_soc_card_get_drvdata(card);
+
+ if (!np) {
+ dev_err(&pdev->dev, "only device tree supported\n");
+ return -EINVAL;
+ }
+
+ cpu_np = of_parse_phandle(np, "axentia,ssc-controller", 0);
+ if (!cpu_np) {
+ dev_err(&pdev->dev, "failed to get dai and pcm info\n");
+ return -EINVAL;
+ }
+ dailink->cpu_of_node = cpu_np;
+ dailink->platform_of_node = cpu_np;
+ tse850->ssc_id = of_alias_get_id(cpu_np, "ssc");
+ of_node_put(cpu_np);
+
+ codec_np = of_parse_phandle(np, "axentia,audio-codec", 0);
+ if (!codec_np) {
+ dev_err(&pdev->dev, "failed to get codec info\n");
+ return -EINVAL;
+ }
+ dailink->codec_of_node = codec_np;
+ of_node_put(codec_np);
+
+ return 0;
+}
+
+static int tse850_probe(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = &tse850_card;
+ struct device *dev = card->dev = &pdev->dev;
+ struct tse850_priv *tse850;
+ int ret;
+
+ tse850 = devm_kzalloc(dev, sizeof(*tse850), GFP_KERNEL);
+ if (!tse850)
+ return -ENOMEM;
+
+ snd_soc_card_set_drvdata(card, tse850);
+
+ ret = tse850_dt_init(pdev);
+ if (ret) {
+ dev_err(dev, "failed to init dt info\n");
+ return ret;
+ }
+
+ tse850->add = devm_gpiod_get(dev, "axentia,add", GPIOD_OUT_HIGH);
+ if (IS_ERR(tse850->add)) {
+ if (PTR_ERR(tse850->add) != -EPROBE_DEFER)
+ dev_err(dev, "failed to get 'add' gpio\n");
+ return PTR_ERR(tse850->add);
+ }
+ tse850->add_cache = 1;
+
+ tse850->loop1 = devm_gpiod_get(dev, "axentia,loop1", GPIOD_OUT_HIGH);
+ if (IS_ERR(tse850->loop1)) {
+ if (PTR_ERR(tse850->loop1) != -EPROBE_DEFER)
+ dev_err(dev, "failed to get 'loop1' gpio\n");
+ return PTR_ERR(tse850->loop1);
+ }
+ tse850->loop1_cache = 1;
+
+ tse850->loop2 = devm_gpiod_get(dev, "axentia,loop2", GPIOD_OUT_HIGH);
+ if (IS_ERR(tse850->loop2)) {
+ if (PTR_ERR(tse850->loop2) != -EPROBE_DEFER)
+ dev_err(dev, "failed to get 'loop2' gpio\n");
+ return PTR_ERR(tse850->loop2);
+ }
+ tse850->loop2_cache = 1;
+
+ tse850->ana = devm_regulator_get(dev, "axentia,ana");
+ if (IS_ERR(tse850->ana)) {
+ if (PTR_ERR(tse850->ana) != -EPROBE_DEFER)
+ dev_err(dev, "failed to get 'ana' regulator\n");
+ return PTR_ERR(tse850->ana);
+ }
+
+ ret = regulator_enable(tse850->ana);
+ if (ret < 0) {
+ dev_err(dev, "failed to enable the 'ana' regulator\n");
+ return ret;
+ }
+
+ ret = atmel_ssc_set_audio(tse850->ssc_id);
+ if (ret != 0) {
+ dev_err(dev,
+ "failed to set SSC %d for audio\n", tse850->ssc_id);
+ goto err_disable_ana;
+ }
+
+ ret = snd_soc_register_card(card);
+ if (ret) {
+ dev_err(dev, "snd_soc_register_card failed\n");
+ goto err_put_audio;
+ }
+
+ return 0;
+
+err_put_audio:
+ atmel_ssc_put_audio(tse850->ssc_id);
+err_disable_ana:
+ regulator_disable(tse850->ana);
+ return ret;
+}
+
+static int tse850_remove(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = platform_get_drvdata(pdev);
+ struct tse850_priv *tse850 = snd_soc_card_get_drvdata(card);
+
+ snd_soc_unregister_card(card);
+ atmel_ssc_put_audio(tse850->ssc_id);
+ regulator_disable(tse850->ana);
+
+ return 0;
+}
+
+static const struct of_device_id tse850_dt_ids[] = {
+ { .compatible = "axentia,tse850-pcm5142", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, tse850_dt_ids);
+
+static struct platform_driver tse850_driver = {
+ .driver = {
+ .name = "axentia-tse850-pcm5142",
+ .of_match_table = of_match_ptr(tse850_dt_ids),
+ },
+ .probe = tse850_probe,
+ .remove = tse850_remove,
+};
+
+module_platform_driver(tse850_driver);
+
+/* Module information */
+MODULE_AUTHOR("Peter Rosin <peda@axentia.se>");
+MODULE_DESCRIPTION("ALSA SoC driver for TSE-850 with PCM5142 codec");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/bcm/Kconfig b/sound/soc/bcm/Kconfig
index d528aaceaad9..edf367100ebd 100644
--- a/sound/soc/bcm/Kconfig
+++ b/sound/soc/bcm/Kconfig
@@ -11,6 +11,7 @@ config SND_BCM2835_SOC_I2S
config SND_SOC_CYGNUS
tristate "SoC platform audio for Broadcom Cygnus chips"
depends on ARCH_BCM_CYGNUS || COMPILE_TEST
+ depends on HAS_DMA
help
Say Y if you want to add support for ASoC audio on Broadcom
Cygnus chips (bcm958300, bcm958305, bcm911360)
diff --git a/sound/soc/codecs/adau17x1.c b/sound/soc/codecs/adau17x1.c
index 439aa3ff1f99..b36511d965c8 100644
--- a/sound/soc/codecs/adau17x1.c
+++ b/sound/soc/codecs/adau17x1.c
@@ -160,7 +160,7 @@ static int adau17x1_dsp_mux_enum_put(struct snd_kcontrol *kcontrol,
struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
struct adau *adau = snd_soc_codec_get_drvdata(codec);
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
- struct snd_soc_dapm_update update;
+ struct snd_soc_dapm_update update = { 0 };
unsigned int stream = e->shift_l;
unsigned int val, change;
int reg;
diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c
index 846ca079845f..0a734d910850 100644
--- a/sound/soc/codecs/arizona.c
+++ b/sound/soc/codecs/arizona.c
@@ -191,6 +191,14 @@ int arizona_init_spk(struct snd_soc_codec *codec)
break;
}
+ return 0;
+}
+EXPORT_SYMBOL_GPL(arizona_init_spk);
+
+int arizona_init_spk_irqs(struct arizona *arizona)
+{
+ int ret;
+
ret = arizona_request_irq(arizona, ARIZONA_IRQ_SPK_OVERHEAT_WARN,
"Thermal warning", arizona_thermal_warn,
arizona);
@@ -209,19 +217,16 @@ int arizona_init_spk(struct snd_soc_codec *codec)
return 0;
}
-EXPORT_SYMBOL_GPL(arizona_init_spk);
+EXPORT_SYMBOL_GPL(arizona_init_spk_irqs);
-int arizona_free_spk(struct snd_soc_codec *codec)
+int arizona_free_spk_irqs(struct arizona *arizona)
{
- struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
- struct arizona *arizona = priv->arizona;
-
arizona_free_irq(arizona, ARIZONA_IRQ_SPK_OVERHEAT_WARN, arizona);
arizona_free_irq(arizona, ARIZONA_IRQ_SPK_OVERHEAT, arizona);
return 0;
}
-EXPORT_SYMBOL_GPL(arizona_free_spk);
+EXPORT_SYMBOL_GPL(arizona_free_spk_irqs);
static const struct snd_soc_dapm_route arizona_mono_routes[] = {
{ "OUT1R", NULL, "OUT1L" },
@@ -252,6 +257,7 @@ EXPORT_SYMBOL_GPL(arizona_init_mono);
int arizona_init_gpio(struct snd_soc_codec *codec)
{
struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(dapm);
struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
struct arizona *arizona = priv->arizona;
int i;
@@ -259,21 +265,24 @@ int arizona_init_gpio(struct snd_soc_codec *codec)
switch (arizona->type) {
case WM5110:
case WM8280:
- snd_soc_dapm_disable_pin(dapm, "DRC2 Signal Activity");
+ snd_soc_component_disable_pin(component,
+ "DRC2 Signal Activity");
break;
default:
break;
}
- snd_soc_dapm_disable_pin(dapm, "DRC1 Signal Activity");
+ snd_soc_component_disable_pin(component, "DRC1 Signal Activity");
for (i = 0; i < ARRAY_SIZE(arizona->pdata.gpio_defaults); i++) {
switch (arizona->pdata.gpio_defaults[i] & ARIZONA_GPN_FN_MASK) {
case ARIZONA_GP_FN_DRC1_SIGNAL_DETECT:
- snd_soc_dapm_enable_pin(dapm, "DRC1 Signal Activity");
+ snd_soc_component_enable_pin(component,
+ "DRC1 Signal Activity");
break;
case ARIZONA_GP_FN_DRC2_SIGNAL_DETECT:
- snd_soc_dapm_enable_pin(dapm, "DRC2 Signal Activity");
+ snd_soc_component_enable_pin(component,
+ "DRC2 Signal Activity");
break;
default:
break;
@@ -1233,6 +1242,46 @@ static int arizona_set_opclk(struct snd_soc_codec *codec, unsigned int clk,
return -EINVAL;
}
+int arizona_clk_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct arizona *arizona = dev_get_drvdata(codec->dev->parent);
+ unsigned int val;
+ int clk_idx;
+ int ret;
+
+ ret = regmap_read(arizona->regmap, w->reg, &val);
+ if (ret) {
+ dev_err(codec->dev, "Failed to check clock source: %d\n", ret);
+ return ret;
+ }
+
+ val = (val & ARIZONA_SYSCLK_SRC_MASK) >> ARIZONA_SYSCLK_SRC_SHIFT;
+
+ switch (val) {
+ case ARIZONA_CLK_SRC_MCLK1:
+ clk_idx = ARIZONA_MCLK1;
+ break;
+ case ARIZONA_CLK_SRC_MCLK2:
+ clk_idx = ARIZONA_MCLK2;
+ break;
+ default:
+ return 0;
+ }
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ return clk_prepare_enable(arizona->mclk[clk_idx]);
+ case SND_SOC_DAPM_POST_PMD:
+ clk_disable_unprepare(arizona->mclk[clk_idx]);
+ return 0;
+ default:
+ return 0;
+ }
+}
+EXPORT_SYMBOL_GPL(arizona_clk_ev);
+
int arizona_set_sysclk(struct snd_soc_codec *codec, int clk_id,
int source, unsigned int freq, int dir)
{
@@ -2242,6 +2291,42 @@ static int arizona_is_enabled_fll(struct arizona_fll *fll, int base)
return reg & ARIZONA_FLL1_ENA;
}
+static int arizona_set_fll_clks(struct arizona_fll *fll, int base, bool ena)
+{
+ struct arizona *arizona = fll->arizona;
+ unsigned int val;
+ struct clk *clk;
+ int ret;
+
+ ret = regmap_read(arizona->regmap, base + 6, &val);
+ if (ret != 0) {
+ arizona_fll_err(fll, "Failed to read current source: %d\n",
+ ret);
+ return ret;
+ }
+
+ val &= ARIZONA_FLL1_CLK_REF_SRC_MASK;
+ val >>= ARIZONA_FLL1_CLK_REF_SRC_SHIFT;
+
+ switch (val) {
+ case ARIZONA_FLL_SRC_MCLK1:
+ clk = arizona->mclk[ARIZONA_MCLK1];
+ break;
+ case ARIZONA_FLL_SRC_MCLK2:
+ clk = arizona->mclk[ARIZONA_MCLK2];
+ break;
+ default:
+ return 0;
+ }
+
+ if (ena) {
+ return clk_prepare_enable(clk);
+ } else {
+ clk_disable_unprepare(clk);
+ return 0;
+ }
+}
+
static int arizona_enable_fll(struct arizona_fll *fll)
{
struct arizona *arizona = fll->arizona;
@@ -2264,6 +2349,10 @@ static int arizona_enable_fll(struct arizona_fll *fll)
udelay(32);
regmap_update_bits_async(fll->arizona->regmap, fll->base + 0x9,
ARIZONA_FLL1_GAIN_MASK, 0);
+
+ if (arizona_is_enabled_fll(fll, fll->base + 0x10) > 0)
+ arizona_set_fll_clks(fll, fll->base + 0x10, false);
+ arizona_set_fll_clks(fll, fll->base, false);
}
/*
@@ -2318,10 +2407,13 @@ static int arizona_enable_fll(struct arizona_fll *fll)
if (!already_enabled)
pm_runtime_get_sync(arizona->dev);
- if (use_sync)
+ if (use_sync) {
+ arizona_set_fll_clks(fll, fll->base + 0x10, true);
regmap_update_bits_async(arizona->regmap, fll->base + 0x11,
ARIZONA_FLL1_SYNC_ENA,
ARIZONA_FLL1_SYNC_ENA);
+ }
+ arizona_set_fll_clks(fll, fll->base, true);
regmap_update_bits_async(arizona->regmap, fll->base + 1,
ARIZONA_FLL1_ENA, ARIZONA_FLL1_ENA);
@@ -2354,19 +2446,24 @@ static int arizona_enable_fll(struct arizona_fll *fll)
static void arizona_disable_fll(struct arizona_fll *fll)
{
struct arizona *arizona = fll->arizona;
- bool change;
+ bool ref_change, sync_change;
regmap_update_bits_async(arizona->regmap, fll->base + 1,
ARIZONA_FLL1_FREERUN, ARIZONA_FLL1_FREERUN);
regmap_update_bits_check(arizona->regmap, fll->base + 1,
- ARIZONA_FLL1_ENA, 0, &change);
- regmap_update_bits(arizona->regmap, fll->base + 0x11,
- ARIZONA_FLL1_SYNC_ENA, 0);
+ ARIZONA_FLL1_ENA, 0, &ref_change);
+ regmap_update_bits_check(arizona->regmap, fll->base + 0x11,
+ ARIZONA_FLL1_SYNC_ENA, 0, &sync_change);
regmap_update_bits_async(arizona->regmap, fll->base + 1,
ARIZONA_FLL1_FREERUN, 0);
- if (change)
+ if (sync_change)
+ arizona_set_fll_clks(fll, fll->base + 0x10, false);
+
+ if (ref_change) {
+ arizona_set_fll_clks(fll, fll->base, false);
pm_runtime_put_autosuspend(arizona->dev);
+ }
}
int arizona_set_fll_refclk(struct arizona_fll *fll, int source,
@@ -2598,30 +2695,6 @@ int arizona_lhpf_coeff_put(struct snd_kcontrol *kcontrol,
}
EXPORT_SYMBOL_GPL(arizona_lhpf_coeff_put);
-int arizona_register_notifier(struct snd_soc_codec *codec,
- struct notifier_block *nb,
- int (*notify)(struct notifier_block *nb,
- unsigned long action, void *data))
-{
- struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
- struct arizona *arizona = priv->arizona;
-
- nb->notifier_call = notify;
-
- return blocking_notifier_chain_register(&arizona->notifier, nb);
-}
-EXPORT_SYMBOL_GPL(arizona_register_notifier);
-
-int arizona_unregister_notifier(struct snd_soc_codec *codec,
- struct notifier_block *nb)
-{
- struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
- struct arizona *arizona = priv->arizona;
-
- return blocking_notifier_chain_unregister(&arizona->notifier, nb);
-}
-EXPORT_SYMBOL_GPL(arizona_unregister_notifier);
-
MODULE_DESCRIPTION("ASoC Wolfson Arizona class device support");
MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/arizona.h b/sound/soc/codecs/arizona.h
index 850aa338ba29..56707860657c 100644
--- a/sound/soc/codecs/arizona.h
+++ b/sound/soc/codecs/arizona.h
@@ -14,6 +14,8 @@
#define _ASOC_ARIZONA_H
#include <linux/completion.h>
+#include <linux/notifier.h>
+#include <linux/mfd/arizona/core.h>
#include <sound/soc.h>
@@ -66,7 +68,6 @@
/* Notifier events */
#define ARIZONA_NOTIFY_VOICE_TRIGGER 0x1
-struct arizona;
struct wm_adsp;
struct arizona_dai_priv {
@@ -255,26 +256,24 @@ extern const struct soc_enum arizona_output_anc_src[];
extern const struct snd_kcontrol_new arizona_voice_trigger_switch[];
-extern int arizona_in_ev(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *kcontrol,
- int event);
-extern int arizona_out_ev(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *kcontrol,
- int event);
-extern int arizona_hp_ev(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *kcontrol,
- int event);
-extern int arizona_anc_ev(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *kcontrol,
- int event);
-
-extern int arizona_eq_coeff_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol);
-extern int arizona_lhpf_coeff_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol);
-
-extern int arizona_set_sysclk(struct snd_soc_codec *codec, int clk_id,
- int source, unsigned int freq, int dir);
+int arizona_in_ev(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol,
+ int event);
+int arizona_out_ev(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol,
+ int event);
+int arizona_hp_ev(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol,
+ int event);
+int arizona_anc_ev(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol,
+ int event);
+
+int arizona_eq_coeff_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol);
+int arizona_lhpf_coeff_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol);
+
+int arizona_clk_ev(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol,
+ int event);
+int arizona_set_sysclk(struct snd_soc_codec *codec, int clk_id, int source,
+ unsigned int freq, int dir);
extern const struct snd_soc_dai_ops arizona_dai_ops;
extern const struct snd_soc_dai_ops arizona_simple_dai_ops;
@@ -297,41 +296,57 @@ struct arizona_fll {
char clock_ok_name[ARIZONA_FLL_NAME_LEN];
};
-extern int arizona_dvfs_up(struct snd_soc_codec *codec, unsigned int flags);
-extern int arizona_dvfs_down(struct snd_soc_codec *codec, unsigned int flags);
-extern int arizona_dvfs_sysclk_ev(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *kcontrol, int event);
-extern void arizona_init_dvfs(struct arizona_priv *priv);
-
-extern int arizona_init_fll(struct arizona *arizona, int id, int base,
- int lock_irq, int ok_irq, struct arizona_fll *fll);
-extern int arizona_set_fll_refclk(struct arizona_fll *fll, int source,
- unsigned int Fref, unsigned int Fout);
-extern int arizona_set_fll(struct arizona_fll *fll, int source,
+int arizona_dvfs_up(struct snd_soc_codec *codec, unsigned int flags);
+int arizona_dvfs_down(struct snd_soc_codec *codec, unsigned int flags);
+int arizona_dvfs_sysclk_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event);
+void arizona_init_dvfs(struct arizona_priv *priv);
+
+int arizona_init_fll(struct arizona *arizona, int id, int base,
+ int lock_irq, int ok_irq, struct arizona_fll *fll);
+int arizona_set_fll_refclk(struct arizona_fll *fll, int source,
unsigned int Fref, unsigned int Fout);
+int arizona_set_fll(struct arizona_fll *fll, int source,
+ unsigned int Fref, unsigned int Fout);
-extern int arizona_init_spk(struct snd_soc_codec *codec);
-extern int arizona_init_gpio(struct snd_soc_codec *codec);
-extern int arizona_init_mono(struct snd_soc_codec *codec);
-extern int arizona_init_notifiers(struct snd_soc_codec *codec);
+int arizona_init_spk(struct snd_soc_codec *codec);
+int arizona_init_gpio(struct snd_soc_codec *codec);
+int arizona_init_mono(struct snd_soc_codec *codec);
+int arizona_init_notifiers(struct snd_soc_codec *codec);
-extern int arizona_free_spk(struct snd_soc_codec *codec);
+int arizona_init_spk_irqs(struct arizona *arizona);
+int arizona_free_spk_irqs(struct arizona *arizona);
-extern int arizona_init_dai(struct arizona_priv *priv, int dai);
+int arizona_init_dai(struct arizona_priv *priv, int dai);
int arizona_set_output_mode(struct snd_soc_codec *codec, int output,
bool diff);
-extern bool arizona_input_analog(struct snd_soc_codec *codec, int shift);
+bool arizona_input_analog(struct snd_soc_codec *codec, int shift);
+
+const char *arizona_sample_rate_val_to_name(unsigned int rate_val);
+
+static inline int arizona_register_notifier(struct snd_soc_codec *codec,
+ struct notifier_block *nb,
+ int (*notify)
+ (struct notifier_block *nb,
+ unsigned long action, void *data))
+{
+ struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+ struct arizona *arizona = priv->arizona;
+
+ nb->notifier_call = notify;
+
+ return blocking_notifier_chain_register(&arizona->notifier, nb);
+}
-extern const char *arizona_sample_rate_val_to_name(unsigned int rate_val);
+static inline int arizona_unregister_notifier(struct snd_soc_codec *codec,
+ struct notifier_block *nb)
+{
+ struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+ struct arizona *arizona = priv->arizona;
-extern int arizona_register_notifier(struct snd_soc_codec *codec,
- struct notifier_block *nb,
- int (*notify)(struct notifier_block *nb,
- unsigned long action,
- void *data));
-extern int arizona_unregister_notifier(struct snd_soc_codec *codec,
- struct notifier_block *nb);
+ return blocking_notifier_chain_unregister(&arizona->notifier, nb);
+}
#endif
diff --git a/sound/soc/codecs/cs4270.c b/sound/soc/codecs/cs4270.c
index 18baea2f7d65..84f86745c30e 100644
--- a/sound/soc/codecs/cs4270.c
+++ b/sound/soc/codecs/cs4270.c
@@ -148,11 +148,11 @@ SND_SOC_DAPM_OUTPUT("AOUTR"),
};
static const struct snd_soc_dapm_route cs4270_dapm_routes[] = {
- { "Capture", NULL, "AINA" },
- { "Capture", NULL, "AINB" },
+ { "Capture", NULL, "AINL" },
+ { "Capture", NULL, "AINR" },
- { "AOUTA", NULL, "Playback" },
- { "AOUTB", NULL, "Playback" },
+ { "AOUTL", NULL, "Playback" },
+ { "AOUTR", NULL, "Playback" },
};
/**
diff --git a/sound/soc/codecs/cs47l24.c b/sound/soc/codecs/cs47l24.c
index 5b22564f037c..73559ae864b6 100644
--- a/sound/soc/codecs/cs47l24.c
+++ b/sound/soc/codecs/cs47l24.c
@@ -335,9 +335,11 @@ static const struct snd_kcontrol_new cs47l24_aec_loopback_mux =
static const struct snd_soc_dapm_widget cs47l24_dapm_widgets[] = {
SND_SOC_DAPM_SUPPLY("SYSCLK", ARIZONA_SYSTEM_CLOCK_1,
- ARIZONA_SYSCLK_ENA_SHIFT, 0, NULL, 0),
+ ARIZONA_SYSCLK_ENA_SHIFT, 0, arizona_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_SUPPLY("ASYNCCLK", ARIZONA_ASYNC_CLOCK_1,
- ARIZONA_ASYNC_CLK_ENA_SHIFT, 0, NULL, 0),
+ ARIZONA_ASYNC_CLK_ENA_SHIFT, 0, arizona_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_SUPPLY("OPCLK", ARIZONA_OUTPUT_SYSTEM_CLOCK,
ARIZONA_OPCLK_ENA_SHIFT, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("ASYNCOPCLK", ARIZONA_OUTPUT_ASYNC_CLOCK,
@@ -1064,7 +1066,7 @@ static struct snd_soc_dai_driver cs47l24_dai[] = {
static int cs47l24_open(struct snd_compr_stream *stream)
{
struct snd_soc_pcm_runtime *rtd = stream->private_data;
- struct cs47l24_priv *priv = snd_soc_codec_get_drvdata(rtd->codec);
+ struct cs47l24_priv *priv = snd_soc_platform_get_drvdata(rtd->platform);
struct arizona *arizona = priv->core.arizona;
int n_adsp;
@@ -1113,8 +1115,8 @@ static irqreturn_t cs47l24_adsp2_irq(int irq, void *data)
static int cs47l24_codec_probe(struct snd_soc_codec *codec)
{
struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(dapm);
struct cs47l24_priv *priv = snd_soc_codec_get_drvdata(codec);
- struct arizona *arizona = priv->core.arizona;
int ret;
priv->core.arizona->dapm = dapm;
@@ -1124,14 +1126,6 @@ static int cs47l24_codec_probe(struct snd_soc_codec *codec)
arizona_init_mono(codec);
arizona_init_notifiers(codec);
- ret = arizona_request_irq(arizona, ARIZONA_IRQ_DSP_IRQ1,
- "ADSP2 Compressed IRQ", cs47l24_adsp2_irq,
- priv);
- if (ret != 0) {
- dev_err(codec->dev, "Failed to request DSP IRQ: %d\n", ret);
- return ret;
- }
-
ret = wm_adsp2_codec_probe(&priv->core.adsp[1], codec);
if (ret)
goto err_adsp2_codec_probe;
@@ -1145,7 +1139,7 @@ static int cs47l24_codec_probe(struct snd_soc_codec *codec)
if (ret)
goto err_adsp2_codec_probe;
- snd_soc_dapm_disable_pin(dapm, "HAPTICS");
+ snd_soc_component_disable_pin(component, "HAPTICS");
return 0;
@@ -1159,17 +1153,12 @@ err_adsp2_codec_probe:
static int cs47l24_codec_remove(struct snd_soc_codec *codec)
{
struct cs47l24_priv *priv = snd_soc_codec_get_drvdata(codec);
- struct arizona *arizona = priv->core.arizona;
wm_adsp2_codec_remove(&priv->core.adsp[1], codec);
wm_adsp2_codec_remove(&priv->core.adsp[2], codec);
priv->core.arizona->dapm = NULL;
- arizona_free_irq(arizona, ARIZONA_IRQ_DSP_IRQ1, priv);
-
- arizona_free_spk(codec);
-
return 0;
}
@@ -1285,25 +1274,47 @@ static int cs47l24_probe(struct platform_device *pdev)
pm_runtime_enable(&pdev->dev);
pm_runtime_idle(&pdev->dev);
+ ret = arizona_request_irq(arizona, ARIZONA_IRQ_DSP_IRQ1,
+ "ADSP2 Compressed IRQ", cs47l24_adsp2_irq,
+ cs47l24);
+ if (ret != 0) {
+ dev_err(&pdev->dev, "Failed to request DSP IRQ: %d\n", ret);
+ return ret;
+ }
+
+ ret = arizona_init_spk_irqs(arizona);
+ if (ret < 0)
+ goto err_dsp_irq;
+
ret = snd_soc_register_platform(&pdev->dev, &cs47l24_compr_platform);
if (ret < 0) {
dev_err(&pdev->dev, "Failed to register platform: %d\n", ret);
- return ret;
+ goto err_spk_irqs;
}
ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_cs47l24,
cs47l24_dai, ARRAY_SIZE(cs47l24_dai));
if (ret < 0) {
dev_err(&pdev->dev, "Failed to register codec: %d\n", ret);
- snd_soc_unregister_platform(&pdev->dev);
+ goto err_platform;
}
return ret;
+
+err_platform:
+ snd_soc_unregister_platform(&pdev->dev);
+err_spk_irqs:
+ arizona_free_spk_irqs(arizona);
+err_dsp_irq:
+ arizona_free_irq(arizona, ARIZONA_IRQ_DSP_IRQ1, cs47l24);
+
+ return ret;
}
static int cs47l24_remove(struct platform_device *pdev)
{
struct cs47l24_priv *cs47l24 = platform_get_drvdata(pdev);
+ struct arizona *arizona = cs47l24->core.arizona;
snd_soc_unregister_platform(&pdev->dev);
snd_soc_unregister_codec(&pdev->dev);
@@ -1312,6 +1323,10 @@ static int cs47l24_remove(struct platform_device *pdev)
wm_adsp2_remove(&cs47l24->core.adsp[1]);
wm_adsp2_remove(&cs47l24->core.adsp[2]);
+ arizona_free_spk_irqs(arizona);
+
+ arizona_free_irq(arizona, ARIZONA_IRQ_DSP_IRQ1, cs47l24);
+
return 0;
}
diff --git a/sound/soc/codecs/da7219-aad.c b/sound/soc/codecs/da7219-aad.c
index 2b8914dd5990..6274d79c1353 100644
--- a/sound/soc/codecs/da7219-aad.c
+++ b/sound/soc/codecs/da7219-aad.c
@@ -204,10 +204,19 @@ static void da7219_aad_hptest_work(struct work_struct *work)
snd_soc_update_bits(codec, DA7219_MIXOUT_R_CTRL,
DA7219_MIXOUT_R_AMP_EN_MASK,
DA7219_MIXOUT_R_AMP_EN_MASK);
- snd_soc_write(codec, DA7219_HP_L_CTRL,
- DA7219_HP_L_AMP_OE_MASK | DA7219_HP_L_AMP_EN_MASK);
- snd_soc_write(codec, DA7219_HP_R_CTRL,
- DA7219_HP_R_AMP_OE_MASK | DA7219_HP_R_AMP_EN_MASK);
+ snd_soc_update_bits(codec, DA7219_HP_L_CTRL,
+ DA7219_HP_L_AMP_OE_MASK | DA7219_HP_L_AMP_EN_MASK,
+ DA7219_HP_L_AMP_OE_MASK | DA7219_HP_L_AMP_EN_MASK);
+ snd_soc_update_bits(codec, DA7219_HP_R_CTRL,
+ DA7219_HP_R_AMP_OE_MASK | DA7219_HP_R_AMP_EN_MASK,
+ DA7219_HP_R_AMP_OE_MASK | DA7219_HP_R_AMP_EN_MASK);
+ msleep(DA7219_SETTLING_DELAY);
+ snd_soc_update_bits(codec, DA7219_HP_L_CTRL,
+ DA7219_HP_L_AMP_MUTE_EN_MASK |
+ DA7219_HP_L_AMP_MIN_GAIN_EN_MASK, 0);
+ snd_soc_update_bits(codec, DA7219_HP_R_CTRL,
+ DA7219_HP_R_AMP_MUTE_EN_MASK |
+ DA7219_HP_R_AMP_MIN_GAIN_EN_MASK, 0);
/*
* If we're running from the internal oscillator then give audio paths
@@ -244,6 +253,7 @@ static void da7219_aad_hptest_work(struct work_struct *work)
regcache_mark_dirty(da7219->regmap);
regcache_sync_region(da7219->regmap, DA7219_HP_L_CTRL,
DA7219_HP_R_CTRL);
+ msleep(DA7219_SETTLING_DELAY);
regcache_sync_region(da7219->regmap, DA7219_MIXOUT_L_CTRL,
DA7219_MIXOUT_R_CTRL);
regcache_sync_region(da7219->regmap, DA7219_DROUTING_ST_OUTFILT_1L,
diff --git a/sound/soc/codecs/da7219.c b/sound/soc/codecs/da7219.c
index 1152aa5e7c39..99601627f83c 100644
--- a/sound/soc/codecs/da7219.c
+++ b/sound/soc/codecs/da7219.c
@@ -823,6 +823,85 @@ static int da7219_dai_event(struct snd_soc_dapm_widget *w,
}
}
+static int da7219_settling_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ case SND_SOC_DAPM_POST_PMD:
+ msleep(DA7219_SETTLING_DELAY);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int da7219_mixout_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);
+ u8 hp_ctrl, min_gain_mask;
+
+ switch (w->reg) {
+ case DA7219_MIXOUT_L_CTRL:
+ hp_ctrl = DA7219_HP_L_CTRL;
+ min_gain_mask = DA7219_HP_L_AMP_MIN_GAIN_EN_MASK;
+ break;
+ case DA7219_MIXOUT_R_CTRL:
+ hp_ctrl = DA7219_HP_R_CTRL;
+ min_gain_mask = DA7219_HP_R_AMP_MIN_GAIN_EN_MASK;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMD:
+ /* Enable minimum gain on HP to avoid pops */
+ snd_soc_update_bits(codec, hp_ctrl, min_gain_mask,
+ min_gain_mask);
+
+ msleep(DA7219_MIN_GAIN_DELAY);
+
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ /* Remove minimum gain on HP */
+ snd_soc_update_bits(codec, hp_ctrl, min_gain_mask, 0);
+
+ break;
+ }
+
+ return 0;
+}
+
+static int da7219_gain_ramp_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ case SND_SOC_DAPM_PRE_PMD:
+ /* Ensure nominal gain ramping for DAPM sequence */
+ da7219->gain_ramp_ctrl =
+ snd_soc_read(codec, DA7219_GAIN_RAMP_CTRL);
+ snd_soc_write(codec, DA7219_GAIN_RAMP_CTRL,
+ DA7219_GAIN_RAMP_RATE_NOMINAL);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ case SND_SOC_DAPM_POST_PMD:
+ /* Restore previous gain ramp settings */
+ snd_soc_write(codec, DA7219_GAIN_RAMP_CTRL,
+ da7219->gain_ramp_ctrl);
+ break;
+ }
+
+ return 0;
+}
+
/*
* DAPM Widgets
@@ -880,7 +959,8 @@ static const struct snd_soc_dapm_widget da7219_dapm_widgets[] = {
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
/* DAI */
- SND_SOC_DAPM_AIF_OUT("DAIOUT", "Capture", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("DAIOUT", "Capture", 0, DA7219_DAI_TDM_CTRL,
+ DA7219_DAI_OE_SHIFT, DA7219_NO_INVERT),
SND_SOC_DAPM_AIF_IN("DAIIN", "Playback", 0, SND_SOC_NOPM, 0, 0),
/* Output Muxes */
@@ -906,30 +986,46 @@ static const struct snd_soc_dapm_widget da7219_dapm_widgets[] = {
ARRAY_SIZE(da7219_st_out_filtr_mix_controls)),
/* DACs */
- SND_SOC_DAPM_DAC("DACL", NULL, DA7219_DAC_L_CTRL, DA7219_DAC_L_EN_SHIFT,
- DA7219_NO_INVERT),
- SND_SOC_DAPM_DAC("DACR", NULL, DA7219_DAC_R_CTRL, DA7219_DAC_R_EN_SHIFT,
- DA7219_NO_INVERT),
+ SND_SOC_DAPM_DAC_E("DACL", NULL, DA7219_DAC_L_CTRL,
+ DA7219_DAC_L_EN_SHIFT, DA7219_NO_INVERT,
+ da7219_settling_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_DAC_E("DACR", NULL, DA7219_DAC_R_CTRL,
+ DA7219_DAC_R_EN_SHIFT, DA7219_NO_INVERT,
+ da7219_settling_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
/* Output PGAs */
- SND_SOC_DAPM_PGA("Mixout Left PGA", DA7219_MIXOUT_L_CTRL,
- DA7219_MIXOUT_L_AMP_EN_SHIFT, DA7219_NO_INVERT,
- NULL, 0),
- SND_SOC_DAPM_PGA("Mixout Right PGA", DA7219_MIXOUT_R_CTRL,
- DA7219_MIXOUT_R_AMP_EN_SHIFT, DA7219_NO_INVERT,
- NULL, 0),
- SND_SOC_DAPM_PGA("Headphone Left PGA", DA7219_HP_L_CTRL,
- DA7219_HP_L_AMP_EN_SHIFT, DA7219_NO_INVERT, NULL, 0),
- SND_SOC_DAPM_PGA("Headphone Right PGA", DA7219_HP_R_CTRL,
- DA7219_HP_R_AMP_EN_SHIFT, DA7219_NO_INVERT, NULL, 0),
+ SND_SOC_DAPM_PGA_E("Mixout Left PGA", DA7219_MIXOUT_L_CTRL,
+ DA7219_MIXOUT_L_AMP_EN_SHIFT, DA7219_NO_INVERT,
+ NULL, 0, da7219_mixout_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_PGA_E("Mixout Right PGA", DA7219_MIXOUT_R_CTRL,
+ DA7219_MIXOUT_R_AMP_EN_SHIFT, DA7219_NO_INVERT,
+ NULL, 0, da7219_mixout_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_SUPPLY_S("Headphone Left PGA", 1, DA7219_HP_L_CTRL,
+ DA7219_HP_L_AMP_EN_SHIFT, DA7219_NO_INVERT,
+ da7219_settling_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY_S("Headphone Right PGA", 1, DA7219_HP_R_CTRL,
+ DA7219_HP_R_AMP_EN_SHIFT, DA7219_NO_INVERT,
+ da7219_settling_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
/* Output Supplies */
- SND_SOC_DAPM_SUPPLY("Charge Pump", DA7219_CP_CTRL, DA7219_CP_EN_SHIFT,
- DA7219_NO_INVERT, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("Charge Pump", 0, DA7219_CP_CTRL,
+ DA7219_CP_EN_SHIFT, DA7219_NO_INVERT,
+ da7219_settling_event,
+ SND_SOC_DAPM_POST_PMU),
/* Outputs */
SND_SOC_DAPM_OUTPUT("HPL"),
SND_SOC_DAPM_OUTPUT("HPR"),
+
+ /* Pre/Post Power */
+ SND_SOC_DAPM_PRE("Pre Power Gain Ramp", da7219_gain_ramp_event),
+ SND_SOC_DAPM_POST("Post Power Gain Ramp", da7219_gain_ramp_event),
};
@@ -1002,8 +1098,8 @@ static const struct snd_soc_dapm_route da7219_audio_map[] = {
{"Mixout Left PGA", NULL, "DACL"},
{"Mixout Right PGA", NULL, "DACR"},
- {"Headphone Left PGA", NULL, "Mixout Left PGA"},
- {"Headphone Right PGA", NULL, "Mixout Right PGA"},
+ {"HPL", NULL, "Mixout Left PGA"},
+ {"HPR", NULL, "Mixout Right PGA"},
{"HPL", NULL, "Headphone Left PGA"},
{"HPR", NULL, "Headphone Right PGA"},
@@ -1711,6 +1807,14 @@ static int da7219_probe(struct snd_soc_codec *codec)
DA7219_HP_R_AMP_RAMP_EN_MASK,
DA7219_HP_R_AMP_RAMP_EN_MASK);
+ /* Default minimum gain on HP to avoid pops during DAPM sequencing */
+ snd_soc_update_bits(codec, DA7219_HP_L_CTRL,
+ DA7219_HP_L_AMP_MIN_GAIN_EN_MASK,
+ DA7219_HP_L_AMP_MIN_GAIN_EN_MASK);
+ snd_soc_update_bits(codec, DA7219_HP_R_CTRL,
+ DA7219_HP_R_AMP_MIN_GAIN_EN_MASK,
+ DA7219_HP_R_AMP_MIN_GAIN_EN_MASK);
+
/* Default infinite tone gen, start/stop by Kcontrol */
snd_soc_write(codec, DA7219_TONE_GEN_CYCLES, DA7219_BEEP_CYCLES_MASK);
diff --git a/sound/soc/codecs/da7219.h b/sound/soc/codecs/da7219.h
index 66d3bad86739..6baba7455fa1 100644
--- a/sound/soc/codecs/da7219.h
+++ b/sound/soc/codecs/da7219.h
@@ -777,6 +777,10 @@
#define DA7219_SYS_STAT_CHECK_RETRIES 6
#define DA7219_SYS_STAT_CHECK_DELAY 50
+/* Power up/down Delays */
+#define DA7219_SETTLING_DELAY 40
+#define DA7219_MIN_GAIN_DELAY 30
+
enum da7219_clk_src {
DA7219_CLKSRC_MCLK = 0,
DA7219_CLKSRC_MCLK_SQR,
@@ -814,6 +818,7 @@ struct da7219_priv {
bool master;
bool alc_en;
+ u8 gain_ramp_ctrl;
};
#endif /* __DA7219_H */
diff --git a/sound/soc/codecs/hdmi-codec.c b/sound/soc/codecs/hdmi-codec.c
index b904492d7744..90b5948e0ff3 100644
--- a/sound/soc/codecs/hdmi-codec.c
+++ b/sound/soc/codecs/hdmi-codec.c
@@ -364,7 +364,12 @@ static int hdmi_of_xlate_dai_name(struct snd_soc_component *component,
struct of_phandle_args *args,
const char **dai_name)
{
- int id = args->args[0];
+ int id;
+
+ if (args->args_count)
+ id = args->args[0];
+ else
+ id = 0;
if (id < ARRAY_SIZE(hdmi_dai_name)) {
*dai_name = hdmi_dai_name[id];
diff --git a/sound/soc/codecs/rt298.c b/sound/soc/codecs/rt298.c
index 55558643166f..2db8179047ae 100644
--- a/sound/soc/codecs/rt298.c
+++ b/sound/soc/codecs/rt298.c
@@ -249,6 +249,11 @@ static int rt298_jack_detect(struct rt298_priv *rt298, bool *hp, bool *mic)
snd_soc_dapm_force_enable_pin(dapm, "LDO1");
snd_soc_dapm_sync(dapm);
+ regmap_update_bits(rt298->regmap,
+ RT298_POWER_CTRL1, 0x1001, 0);
+ regmap_update_bits(rt298->regmap,
+ RT298_POWER_CTRL2, 0x4, 0x4);
+
regmap_write(rt298->regmap, RT298_SET_MIC1, 0x24);
msleep(50);
diff --git a/sound/soc/codecs/rt5663.c b/sound/soc/codecs/rt5663.c
index 01a18d88f1eb..00ff2788879e 100644
--- a/sound/soc/codecs/rt5663.c
+++ b/sound/soc/codecs/rt5663.c
@@ -1547,11 +1547,11 @@ static int rt5663_jack_detect(struct snd_soc_codec *codec, int jack_insert)
msleep(sleep_time[i]);
val = snd_soc_read(codec, RT5663_EM_JACK_TYPE_2) &
0x0003;
+ dev_dbg(codec->dev, "%s: MX-00e7 val=%x sleep %d\n",
+ __func__, val, sleep_time[i]);
i++;
if (val == 0x1 || val == 0x2 || val == 0x3)
break;
- dev_dbg(codec->dev, "%s: MX-00e7 val=%x sleep %d\n",
- __func__, val, sleep_time[i]);
}
dev_dbg(codec->dev, "%s val = %d\n", __func__, val);
switch (val) {
diff --git a/sound/soc/codecs/sti-sas.c b/sound/soc/codecs/sti-sas.c
index 7b31ee9b82bc..d6e00c77edcd 100644
--- a/sound/soc/codecs/sti-sas.c
+++ b/sound/soc/codecs/sti-sas.c
@@ -424,7 +424,7 @@ static const struct snd_soc_dai_ops stih407_dac_ops = {
static const struct regmap_config stih407_sas_regmap = {
.reg_bits = 32,
.val_bits = 32,
-
+ .fast_io = true,
.max_register = STIH407_AUDIO_DAC_CTRL,
.reg_defaults = stih407_sas_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(stih407_sas_reg_defaults),
diff --git a/sound/soc/codecs/tas571x.c b/sound/soc/codecs/tas571x.c
index df5e5cb33baa..810369f687d7 100644
--- a/sound/soc/codecs/tas571x.c
+++ b/sound/soc/codecs/tas571x.c
@@ -341,20 +341,9 @@ static int tas571x_set_bias_level(struct snd_soc_codec *codec,
return ret;
}
}
-
- gpiod_set_value(priv->pdn_gpio, 0);
- usleep_range(5000, 6000);
-
- regcache_cache_only(priv->regmap, false);
- ret = regcache_sync(priv->regmap);
- if (ret)
- return ret;
}
break;
case SND_SOC_BIAS_OFF:
- regcache_cache_only(priv->regmap, true);
- gpiod_set_value(priv->pdn_gpio, 1);
-
if (!IS_ERR(priv->mclk))
clk_disable_unprepare(priv->mclk);
break;
@@ -401,16 +390,6 @@ static const struct snd_kcontrol_new tas5711_controls[] = {
TAS571X_SOFT_MUTE_REG,
TAS571X_SOFT_MUTE_CH1_SHIFT, TAS571X_SOFT_MUTE_CH2_SHIFT,
1, 1),
-
- SOC_DOUBLE_R_RANGE("CH1 Mixer Volume",
- TAS5717_CH1_LEFT_CH_MIX_REG,
- TAS5717_CH1_RIGHT_CH_MIX_REG,
- 16, 0, 0x80, 0),
-
- SOC_DOUBLE_R_RANGE("CH2 Mixer Volume",
- TAS5717_CH2_LEFT_CH_MIX_REG,
- TAS5717_CH2_RIGHT_CH_MIX_REG,
- 16, 0, 0x80, 0),
};
static const struct regmap_range tas571x_readonly_regs_range[] = {
@@ -488,6 +467,16 @@ static const struct snd_kcontrol_new tas5717_controls[] = {
TAS571X_SOFT_MUTE_CH1_SHIFT, TAS571X_SOFT_MUTE_CH2_SHIFT,
1, 1),
+ SOC_DOUBLE_R_RANGE("CH1 Mixer Volume",
+ TAS5717_CH1_LEFT_CH_MIX_REG,
+ TAS5717_CH1_RIGHT_CH_MIX_REG,
+ 16, 0, 0x80, 0),
+
+ SOC_DOUBLE_R_RANGE("CH2 Mixer Volume",
+ TAS5717_CH2_LEFT_CH_MIX_REG,
+ TAS5717_CH2_RIGHT_CH_MIX_REG,
+ 16, 0, 0x80, 0),
+
/*
* The biquads are named according to the register names.
* Please note that TI's TAS57xx Graphical Development Environment
@@ -747,13 +736,14 @@ static int tas571x_i2c_probe(struct i2c_client *client,
/* pulse the active low reset line for ~100us */
usleep_range(100, 200);
gpiod_set_value(priv->reset_gpio, 0);
- usleep_range(12000, 20000);
+ usleep_range(13500, 20000);
}
ret = regmap_write(priv->regmap, TAS571X_OSC_TRIM_REG, 0);
if (ret)
return ret;
+ usleep_range(50000, 60000);
memcpy(&priv->codec_driver, &tas571x_codec, sizeof(priv->codec_driver));
priv->codec_driver.component_driver.controls = priv->chip->controls;
@@ -770,9 +760,6 @@ static int tas571x_i2c_probe(struct i2c_client *client,
return ret;
}
- regcache_cache_only(priv->regmap, true);
- gpiod_set_value(priv->pdn_gpio, 1);
-
return snd_soc_register_codec(&client->dev, &priv->codec_driver,
&tas571x_dai, 1);
}
diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c
index 5a8d96ec058c..8877b74b0510 100644
--- a/sound/soc/codecs/tlv320aic3x.c
+++ b/sound/soc/codecs/tlv320aic3x.c
@@ -157,7 +157,7 @@ static int snd_soc_dapm_put_volsw_aic3x(struct snd_kcontrol *kcontrol,
unsigned int mask = (1 << fls(max)) - 1;
unsigned int invert = mc->invert;
unsigned short val;
- struct snd_soc_dapm_update update;
+ struct snd_soc_dapm_update update = { 0 };
int connect, change;
val = (ucontrol->value.integer.value[0] & mask);
diff --git a/sound/soc/codecs/wm5102.c b/sound/soc/codecs/wm5102.c
index 93876c6d48ee..e7ab37d0dd32 100644
--- a/sound/soc/codecs/wm5102.c
+++ b/sound/soc/codecs/wm5102.c
@@ -607,6 +607,9 @@ static int wm5102_sysclk_ev(struct snd_soc_dapm_widget *w,
break;
case SND_SOC_DAPM_PRE_PMD:
break;
+ case SND_SOC_DAPM_PRE_PMU:
+ case SND_SOC_DAPM_POST_PMD:
+ return arizona_clk_ev(w, kcontrol, event);
default:
return 0;
}
@@ -1077,9 +1080,11 @@ static const struct snd_kcontrol_new wm5102_aec_loopback_mux =
static const struct snd_soc_dapm_widget wm5102_dapm_widgets[] = {
SND_SOC_DAPM_SUPPLY("SYSCLK", ARIZONA_SYSTEM_CLOCK_1, ARIZONA_SYSCLK_ENA_SHIFT,
0, wm5102_sysclk_ev,
- SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_SUPPLY("ASYNCCLK", ARIZONA_ASYNC_CLOCK_1,
- ARIZONA_ASYNC_CLK_ENA_SHIFT, 0, NULL, 0),
+ ARIZONA_ASYNC_CLK_ENA_SHIFT, 0, arizona_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_SUPPLY("OPCLK", ARIZONA_OUTPUT_SYSTEM_CLOCK,
ARIZONA_OPCLK_ENA_SHIFT, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("ASYNCOPCLK", ARIZONA_OUTPUT_ASYNC_CLOCK,
@@ -1903,7 +1908,7 @@ static struct snd_soc_dai_driver wm5102_dai[] = {
static int wm5102_open(struct snd_compr_stream *stream)
{
struct snd_soc_pcm_runtime *rtd = stream->private_data;
- struct wm5102_priv *priv = snd_soc_codec_get_drvdata(rtd->codec);
+ struct wm5102_priv *priv = snd_soc_platform_get_drvdata(rtd->platform);
return wm_adsp_compr_open(&priv->core.adsp[0], stream);
}
@@ -1926,18 +1931,10 @@ static irqreturn_t wm5102_adsp2_irq(int irq, void *data)
static int wm5102_codec_probe(struct snd_soc_codec *codec)
{
struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(dapm);
struct wm5102_priv *priv = snd_soc_codec_get_drvdata(codec);
- struct arizona *arizona = priv->core.arizona;
int ret;
- ret = arizona_request_irq(arizona, ARIZONA_IRQ_DSP_IRQ1,
- "ADSP2 Compressed IRQ", wm5102_adsp2_irq,
- priv);
- if (ret != 0) {
- dev_err(codec->dev, "Failed to request DSP IRQ: %d\n", ret);
- return ret;
- }
-
ret = wm_adsp2_codec_probe(&priv->core.adsp[0], codec);
if (ret)
return ret;
@@ -1949,8 +1946,9 @@ static int wm5102_codec_probe(struct snd_soc_codec *codec)
arizona_init_spk(codec);
arizona_init_gpio(codec);
+ arizona_init_notifiers(codec);
- snd_soc_dapm_disable_pin(dapm, "HAPTICS");
+ snd_soc_component_disable_pin(component, "HAPTICS");
priv->core.arizona->dapm = dapm;
@@ -1965,16 +1963,11 @@ err_adsp2_codec_probe:
static int wm5102_codec_remove(struct snd_soc_codec *codec)
{
struct wm5102_priv *priv = snd_soc_codec_get_drvdata(codec);
- struct arizona *arizona = priv->core.arizona;
wm_adsp2_codec_remove(&priv->core.adsp[0], codec);
priv->core.arizona->dapm = NULL;
- arizona_free_irq(arizona, ARIZONA_IRQ_DSP_IRQ1, priv);
-
- arizona_free_spk(codec);
-
return 0;
}
@@ -2092,25 +2085,47 @@ static int wm5102_probe(struct platform_device *pdev)
pm_runtime_enable(&pdev->dev);
pm_runtime_idle(&pdev->dev);
+ ret = arizona_request_irq(arizona, ARIZONA_IRQ_DSP_IRQ1,
+ "ADSP2 Compressed IRQ", wm5102_adsp2_irq,
+ wm5102);
+ if (ret != 0) {
+ dev_err(&pdev->dev, "Failed to request DSP IRQ: %d\n", ret);
+ return ret;
+ }
+
+ ret = arizona_init_spk_irqs(arizona);
+ if (ret < 0)
+ goto err_dsp_irq;
+
ret = snd_soc_register_platform(&pdev->dev, &wm5102_compr_platform);
if (ret < 0) {
dev_err(&pdev->dev, "Failed to register platform: %d\n", ret);
- return ret;
+ goto err_spk_irqs;
}
ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_wm5102,
wm5102_dai, ARRAY_SIZE(wm5102_dai));
if (ret < 0) {
dev_err(&pdev->dev, "Failed to register codec: %d\n", ret);
- snd_soc_unregister_platform(&pdev->dev);
+ goto err_platform;
}
return ret;
+
+err_platform:
+ snd_soc_unregister_platform(&pdev->dev);
+err_spk_irqs:
+ arizona_free_spk_irqs(arizona);
+err_dsp_irq:
+ arizona_free_irq(arizona, ARIZONA_IRQ_DSP_IRQ1, wm5102);
+
+ return ret;
}
static int wm5102_remove(struct platform_device *pdev)
{
struct wm5102_priv *wm5102 = platform_get_drvdata(pdev);
+ struct arizona *arizona = wm5102->core.arizona;
snd_soc_unregister_platform(&pdev->dev);
snd_soc_unregister_codec(&pdev->dev);
@@ -2118,6 +2133,10 @@ static int wm5102_remove(struct platform_device *pdev)
wm_adsp2_remove(&wm5102->core.adsp[0]);
+ arizona_free_spk_irqs(arizona);
+
+ arizona_free_irq(arizona, ARIZONA_IRQ_DSP_IRQ1, wm5102);
+
return 0;
}
diff --git a/sound/soc/codecs/wm5110.c b/sound/soc/codecs/wm5110.c
index 06bae3b23fce..585fc706c1b0 100644
--- a/sound/soc/codecs/wm5110.c
+++ b/sound/soc/codecs/wm5110.c
@@ -183,7 +183,9 @@ static int wm5110_sysclk_ev(struct snd_soc_dapm_widget *w,
regmap_write_async(regmap, patch[i].reg,
patch[i].def);
break;
-
+ case SND_SOC_DAPM_PRE_PMU:
+ case SND_SOC_DAPM_POST_PMD:
+ return arizona_clk_ev(w, kcontrol, event);
default:
break;
}
@@ -1073,9 +1075,11 @@ static const struct snd_kcontrol_new wm5110_output_anc_src[] = {
static const struct snd_soc_dapm_widget wm5110_dapm_widgets[] = {
SND_SOC_DAPM_SUPPLY("SYSCLK", ARIZONA_SYSTEM_CLOCK_1, ARIZONA_SYSCLK_ENA_SHIFT,
- 0, wm5110_sysclk_ev, SND_SOC_DAPM_POST_PMU),
+ 0, wm5110_sysclk_ev, SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_SUPPLY("ASYNCCLK", ARIZONA_ASYNC_CLOCK_1,
- ARIZONA_ASYNC_CLK_ENA_SHIFT, 0, NULL, 0),
+ ARIZONA_ASYNC_CLK_ENA_SHIFT, 0, arizona_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_SUPPLY("OPCLK", ARIZONA_OUTPUT_SYSTEM_CLOCK,
ARIZONA_OPCLK_ENA_SHIFT, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("ASYNCOPCLK", ARIZONA_OUTPUT_ASYNC_CLOCK,
@@ -2220,7 +2224,7 @@ static struct snd_soc_dai_driver wm5110_dai[] = {
static int wm5110_open(struct snd_compr_stream *stream)
{
struct snd_soc_pcm_runtime *rtd = stream->private_data;
- struct wm5110_priv *priv = snd_soc_codec_get_drvdata(rtd->codec);
+ struct wm5110_priv *priv = snd_soc_platform_get_drvdata(rtd->platform);
struct arizona *arizona = priv->core.arizona;
int n_adsp;
@@ -2269,8 +2273,8 @@ static irqreturn_t wm5110_adsp2_irq(int irq, void *data)
static int wm5110_codec_probe(struct snd_soc_codec *codec)
{
struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(dapm);
struct wm5110_priv *priv = snd_soc_codec_get_drvdata(codec);
- struct arizona *arizona = priv->core.arizona;
int i, ret;
priv->core.arizona->dapm = dapm;
@@ -2280,14 +2284,6 @@ static int wm5110_codec_probe(struct snd_soc_codec *codec)
arizona_init_mono(codec);
arizona_init_notifiers(codec);
- ret = arizona_request_irq(arizona, ARIZONA_IRQ_DSP_IRQ1,
- "ADSP2 Compressed IRQ", wm5110_adsp2_irq,
- priv);
- if (ret != 0) {
- dev_err(codec->dev, "Failed to request DSP IRQ: %d\n", ret);
- return ret;
- }
-
for (i = 0; i < WM5110_NUM_ADSP; ++i) {
ret = wm_adsp2_codec_probe(&priv->core.adsp[i], codec);
if (ret)
@@ -2300,7 +2296,7 @@ static int wm5110_codec_probe(struct snd_soc_codec *codec)
if (ret)
goto err_adsp2_codec_probe;
- snd_soc_dapm_disable_pin(dapm, "HAPTICS");
+ snd_soc_component_disable_pin(component, "HAPTICS");
return 0;
@@ -2308,15 +2304,12 @@ err_adsp2_codec_probe:
for (--i; i >= 0; --i)
wm_adsp2_codec_remove(&priv->core.adsp[i], codec);
- arizona_free_irq(arizona, ARIZONA_IRQ_DSP_IRQ1, priv);
-
return ret;
}
static int wm5110_codec_remove(struct snd_soc_codec *codec)
{
struct wm5110_priv *priv = snd_soc_codec_get_drvdata(codec);
- struct arizona *arizona = priv->core.arizona;
int i;
for (i = 0; i < WM5110_NUM_ADSP; ++i)
@@ -2324,10 +2317,6 @@ static int wm5110_codec_remove(struct snd_soc_codec *codec)
priv->core.arizona->dapm = NULL;
- arizona_free_irq(arizona, ARIZONA_IRQ_DSP_IRQ1, priv);
-
- arizona_free_spk(codec);
-
return 0;
}
@@ -2449,25 +2438,47 @@ static int wm5110_probe(struct platform_device *pdev)
pm_runtime_enable(&pdev->dev);
pm_runtime_idle(&pdev->dev);
+ ret = arizona_request_irq(arizona, ARIZONA_IRQ_DSP_IRQ1,
+ "ADSP2 Compressed IRQ", wm5110_adsp2_irq,
+ wm5110);
+ if (ret != 0) {
+ dev_err(&pdev->dev, "Failed to request DSP IRQ: %d\n", ret);
+ return ret;
+ }
+
+ ret = arizona_init_spk_irqs(arizona);
+ if (ret < 0)
+ goto err_dsp_irq;
+
ret = snd_soc_register_platform(&pdev->dev, &wm5110_compr_platform);
if (ret < 0) {
dev_err(&pdev->dev, "Failed to register platform: %d\n", ret);
- return ret;
+ goto err_spk_irqs;
}
ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_wm5110,
wm5110_dai, ARRAY_SIZE(wm5110_dai));
if (ret < 0) {
dev_err(&pdev->dev, "Failed to register codec: %d\n", ret);
- snd_soc_unregister_platform(&pdev->dev);
+ goto err_platform;
}
return ret;
+
+err_platform:
+ snd_soc_unregister_platform(&pdev->dev);
+err_spk_irqs:
+ arizona_free_spk_irqs(arizona);
+err_dsp_irq:
+ arizona_free_irq(arizona, ARIZONA_IRQ_DSP_IRQ1, wm5110);
+
+ return ret;
}
static int wm5110_remove(struct platform_device *pdev)
{
struct wm5110_priv *wm5110 = platform_get_drvdata(pdev);
+ struct arizona *arizona = wm5110->core.arizona;
int i;
snd_soc_unregister_platform(&pdev->dev);
@@ -2477,6 +2488,10 @@ static int wm5110_remove(struct platform_device *pdev)
for (i = 0; i < WM5110_NUM_ADSP; i++)
wm_adsp2_remove(&wm5110->core.adsp[i]);
+ arizona_free_spk_irqs(arizona);
+
+ arizona_free_irq(arizona, ARIZONA_IRQ_DSP_IRQ1, wm5110);
+
return 0;
}
diff --git a/sound/soc/codecs/wm8997.c b/sound/soc/codecs/wm8997.c
index 2f2821b3382f..ee0c8639c743 100644
--- a/sound/soc/codecs/wm8997.c
+++ b/sound/soc/codecs/wm8997.c
@@ -108,6 +108,9 @@ static int wm8997_sysclk_ev(struct snd_soc_dapm_widget *w,
break;
case SND_SOC_DAPM_PRE_PMD:
break;
+ case SND_SOC_DAPM_PRE_PMU:
+ case SND_SOC_DAPM_POST_PMD:
+ return arizona_clk_ev(w, kcontrol, event);
default:
return 0;
}
@@ -408,9 +411,11 @@ static const struct snd_kcontrol_new wm8997_aec_loopback_mux =
static const struct snd_soc_dapm_widget wm8997_dapm_widgets[] = {
SND_SOC_DAPM_SUPPLY("SYSCLK", ARIZONA_SYSTEM_CLOCK_1, ARIZONA_SYSCLK_ENA_SHIFT,
0, wm8997_sysclk_ev,
- SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_SUPPLY("ASYNCCLK", ARIZONA_ASYNC_CLOCK_1,
- ARIZONA_ASYNC_CLK_ENA_SHIFT, 0, NULL, 0),
+ ARIZONA_ASYNC_CLK_ENA_SHIFT, 0, arizona_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_SUPPLY("OPCLK", ARIZONA_OUTPUT_SYSTEM_CLOCK,
ARIZONA_OPCLK_ENA_SHIFT, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("ASYNCOPCLK", ARIZONA_OUTPUT_ASYNC_CLOCK,
@@ -1055,11 +1060,13 @@ static struct snd_soc_dai_driver wm8997_dai[] = {
static int wm8997_codec_probe(struct snd_soc_codec *codec)
{
struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(dapm);
struct wm8997_priv *priv = snd_soc_codec_get_drvdata(codec);
arizona_init_spk(codec);
+ arizona_init_notifiers(codec);
- snd_soc_dapm_disable_pin(dapm, "HAPTICS");
+ snd_soc_component_disable_pin(component, "HAPTICS");
priv->core.arizona->dapm = dapm;
@@ -1072,8 +1079,6 @@ static int wm8997_codec_remove(struct snd_soc_codec *codec)
priv->core.arizona->dapm = NULL;
- arizona_free_spk(codec);
-
return 0;
}
@@ -1119,7 +1124,7 @@ static int wm8997_probe(struct platform_device *pdev)
{
struct arizona *arizona = dev_get_drvdata(pdev->dev.parent);
struct wm8997_priv *wm8997;
- int i;
+ int i, ret;
wm8997 = devm_kzalloc(&pdev->dev, sizeof(struct wm8997_priv),
GFP_KERNEL);
@@ -1159,15 +1164,33 @@ static int wm8997_probe(struct platform_device *pdev)
pm_runtime_enable(&pdev->dev);
pm_runtime_idle(&pdev->dev);
- return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_wm8997,
- wm8997_dai, ARRAY_SIZE(wm8997_dai));
+ ret = arizona_init_spk_irqs(arizona);
+ if (ret < 0)
+ return ret;
+
+ ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_wm8997,
+ wm8997_dai, ARRAY_SIZE(wm8997_dai));
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Failed to register codec: %d\n", ret);
+ goto err_spk_irqs;
+ }
+
+err_spk_irqs:
+ arizona_free_spk_irqs(arizona);
+
+ return ret;
}
static int wm8997_remove(struct platform_device *pdev)
{
+ struct wm8997_priv *wm8997 = platform_get_drvdata(pdev);
+ struct arizona *arizona = wm8997->core.arizona;
+
snd_soc_unregister_codec(&pdev->dev);
pm_runtime_disable(&pdev->dev);
+ arizona_free_spk_irqs(arizona);
+
return 0;
}
diff --git a/sound/soc/codecs/wm8998.c b/sound/soc/codecs/wm8998.c
index bcc2e1060a6c..3694f5958d86 100644
--- a/sound/soc/codecs/wm8998.c
+++ b/sound/soc/codecs/wm8998.c
@@ -541,9 +541,11 @@ static const struct snd_kcontrol_new wm8998_aec_loopback_mux[] = {
static const struct snd_soc_dapm_widget wm8998_dapm_widgets[] = {
SND_SOC_DAPM_SUPPLY("SYSCLK", ARIZONA_SYSTEM_CLOCK_1,
- ARIZONA_SYSCLK_ENA_SHIFT, 0, NULL, 0),
+ ARIZONA_SYSCLK_ENA_SHIFT, 0, arizona_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_SUPPLY("ASYNCCLK", ARIZONA_ASYNC_CLOCK_1,
- ARIZONA_ASYNC_CLK_ENA_SHIFT, 0, NULL, 0),
+ ARIZONA_ASYNC_CLK_ENA_SHIFT, 0, arizona_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_SUPPLY("OPCLK", ARIZONA_OUTPUT_SYSTEM_CLOCK,
ARIZONA_OPCLK_ENA_SHIFT, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("ASYNCOPCLK", ARIZONA_OUTPUT_ASYNC_CLOCK,
@@ -1318,13 +1320,15 @@ static int wm8998_codec_probe(struct snd_soc_codec *codec)
{
struct wm8998_priv *priv = snd_soc_codec_get_drvdata(codec);
struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(dapm);
priv->core.arizona->dapm = dapm;
arizona_init_spk(codec);
arizona_init_gpio(codec);
+ arizona_init_notifiers(codec);
- snd_soc_dapm_disable_pin(dapm, "HAPTICS");
+ snd_soc_component_disable_pin(component, "HAPTICS");
return 0;
}
@@ -1335,8 +1339,6 @@ static int wm8998_codec_remove(struct snd_soc_codec *codec)
priv->core.arizona->dapm = NULL;
- arizona_free_spk(codec);
-
return 0;
}
@@ -1385,7 +1387,7 @@ static int wm8998_probe(struct platform_device *pdev)
{
struct arizona *arizona = dev_get_drvdata(pdev->dev.parent);
struct wm8998_priv *wm8998;
- int i;
+ int i, ret;
wm8998 = devm_kzalloc(&pdev->dev, sizeof(struct wm8998_priv),
GFP_KERNEL);
@@ -1417,15 +1419,35 @@ static int wm8998_probe(struct platform_device *pdev)
pm_runtime_enable(&pdev->dev);
pm_runtime_idle(&pdev->dev);
- return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_wm8998,
- wm8998_dai, ARRAY_SIZE(wm8998_dai));
+ ret = arizona_init_spk_irqs(arizona);
+ if (ret < 0)
+ return ret;
+
+ ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_wm8998,
+ wm8998_dai, ARRAY_SIZE(wm8998_dai));
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Failed to register codec: %d\n", ret);
+ goto err_spk_irqs;
+ }
+
+ return ret;
+
+err_spk_irqs:
+ arizona_free_spk_irqs(arizona);
+
+ return ret;
}
static int wm8998_remove(struct platform_device *pdev)
{
+ struct wm8998_priv *wm8998 = platform_get_drvdata(pdev);
+ struct arizona *arizona = wm8998->core.arizona;
+
snd_soc_unregister_codec(&pdev->dev);
pm_runtime_disable(&pdev->dev);
+ arizona_free_spk_irqs(arizona);
+
return 0;
}
diff --git a/sound/soc/codecs/wm9712.c b/sound/soc/codecs/wm9712.c
index 557709eac698..85f7c5bb8b82 100644
--- a/sound/soc/codecs/wm9712.c
+++ b/sound/soc/codecs/wm9712.c
@@ -187,7 +187,7 @@ static int wm9712_hp_mixer_put(struct snd_kcontrol *kcontrol,
struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
unsigned int mixer, mask, shift, old;
- struct snd_soc_dapm_update update;
+ struct snd_soc_dapm_update update = { 0 };
bool change;
mixer = mc->shift >> 8;
diff --git a/sound/soc/codecs/wm9713.c b/sound/soc/codecs/wm9713.c
index e4301ddb1b84..7e4822185feb 100644
--- a/sound/soc/codecs/wm9713.c
+++ b/sound/soc/codecs/wm9713.c
@@ -231,7 +231,7 @@ static int wm9713_hp_mixer_put(struct snd_kcontrol *kcontrol,
struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
unsigned int mixer, mask, shift, old;
- struct snd_soc_dapm_update update;
+ struct snd_soc_dapm_update update = { 0 };
bool change;
mixer = mc->shift >> 8;
diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c
index b943dde8dbe5..593b7d1aed46 100644
--- a/sound/soc/codecs/wm_adsp.c
+++ b/sound/soc/codecs/wm_adsp.c
@@ -162,6 +162,16 @@
#define ADSP_MAX_STD_CTRL_SIZE 512
+#define WM_ADSP_ACKED_CTL_TIMEOUT_MS 100
+#define WM_ADSP_ACKED_CTL_N_QUICKPOLLS 10
+#define WM_ADSP_ACKED_CTL_MIN_VALUE 0
+#define WM_ADSP_ACKED_CTL_MAX_VALUE 0xFFFFFF
+
+/*
+ * Event control messages
+ */
+#define WM_ADSP_FW_EVENT_SHUTDOWN 0x000001
+
struct wm_adsp_buf {
struct list_head list;
void *buf;
@@ -177,7 +187,7 @@ static struct wm_adsp_buf *wm_adsp_buf_alloc(const void *src, size_t len,
buf->buf = vmalloc(len);
if (!buf->buf) {
- vfree(buf);
+ kfree(buf);
return NULL;
}
memcpy(buf->buf, src, len);
@@ -441,11 +451,29 @@ struct wm_coeff_ctl {
unsigned int offset;
size_t len;
unsigned int set:1;
- struct snd_kcontrol *kcontrol;
struct soc_bytes_ext bytes_ext;
unsigned int flags;
+ unsigned int type;
};
+static const char *wm_adsp_mem_region_name(unsigned int type)
+{
+ switch (type) {
+ case WMFW_ADSP1_PM:
+ return "PM";
+ case WMFW_ADSP1_DM:
+ return "DM";
+ case WMFW_ADSP2_XM:
+ return "XM";
+ case WMFW_ADSP2_YM:
+ return "YM";
+ case WMFW_ADSP1_ZM:
+ return "ZM";
+ default:
+ return NULL;
+ }
+}
+
#ifdef CONFIG_DEBUG_FS
static void wm_adsp_debugfs_save_wmfwname(struct wm_adsp *dsp, const char *s)
{
@@ -727,6 +755,24 @@ static inline struct wm_coeff_ctl *bytes_ext_to_ctl(struct soc_bytes_ext *ext)
return container_of(ext, struct wm_coeff_ctl, bytes_ext);
}
+static int wm_coeff_base_reg(struct wm_coeff_ctl *ctl, unsigned int *reg)
+{
+ const struct wm_adsp_alg_region *alg_region = &ctl->alg_region;
+ struct wm_adsp *dsp = ctl->dsp;
+ const struct wm_adsp_region *mem;
+
+ mem = wm_adsp_find_region(dsp, alg_region->type);
+ if (!mem) {
+ adsp_err(dsp, "No base for region %x\n",
+ alg_region->type);
+ return -EINVAL;
+ }
+
+ *reg = wm_adsp_region_to_reg(mem, ctl->alg_region.base + ctl->offset);
+
+ return 0;
+}
+
static int wm_coeff_info(struct snd_kcontrol *kctl,
struct snd_ctl_elem_info *uinfo)
{
@@ -734,30 +780,94 @@ static int wm_coeff_info(struct snd_kcontrol *kctl,
(struct soc_bytes_ext *)kctl->private_value;
struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext);
- uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
- uinfo->count = ctl->len;
+ switch (ctl->type) {
+ case WMFW_CTL_TYPE_ACKED:
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->value.integer.min = WM_ADSP_ACKED_CTL_MIN_VALUE;
+ uinfo->value.integer.max = WM_ADSP_ACKED_CTL_MAX_VALUE;
+ uinfo->value.integer.step = 1;
+ uinfo->count = 1;
+ break;
+ default:
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
+ uinfo->count = ctl->len;
+ break;
+ }
+
return 0;
}
+static int wm_coeff_write_acked_control(struct wm_coeff_ctl *ctl,
+ unsigned int event_id)
+{
+ struct wm_adsp *dsp = ctl->dsp;
+ u32 val = cpu_to_be32(event_id);
+ unsigned int reg;
+ int i, ret;
+
+ ret = wm_coeff_base_reg(ctl, &reg);
+ if (ret)
+ return ret;
+
+ adsp_dbg(dsp, "Sending 0x%x to acked control alg 0x%x %s:0x%x\n",
+ event_id, ctl->alg_region.alg,
+ wm_adsp_mem_region_name(ctl->alg_region.type), ctl->offset);
+
+ ret = regmap_raw_write(dsp->regmap, reg, &val, sizeof(val));
+ if (ret) {
+ adsp_err(dsp, "Failed to write %x: %d\n", reg, ret);
+ return ret;
+ }
+
+ /*
+ * Poll for ack, we initially poll at ~1ms intervals for firmwares
+ * that respond quickly, then go to ~10ms polls. A firmware is unlikely
+ * to ack instantly so we do the first 1ms delay before reading the
+ * control to avoid a pointless bus transaction
+ */
+ for (i = 0; i < WM_ADSP_ACKED_CTL_TIMEOUT_MS;) {
+ switch (i) {
+ case 0 ... WM_ADSP_ACKED_CTL_N_QUICKPOLLS - 1:
+ usleep_range(1000, 2000);
+ i++;
+ break;
+ default:
+ usleep_range(10000, 20000);
+ i += 10;
+ break;
+ }
+
+ ret = regmap_raw_read(dsp->regmap, reg, &val, sizeof(val));
+ if (ret) {
+ adsp_err(dsp, "Failed to read %x: %d\n", reg, ret);
+ return ret;
+ }
+
+ if (val == 0) {
+ adsp_dbg(dsp, "Acked control ACKED at poll %u\n", i);
+ return 0;
+ }
+ }
+
+ adsp_warn(dsp, "Acked control @0x%x alg:0x%x %s:0x%x timed out\n",
+ reg, ctl->alg_region.alg,
+ wm_adsp_mem_region_name(ctl->alg_region.type),
+ ctl->offset);
+
+ return -ETIMEDOUT;
+}
+
static int wm_coeff_write_control(struct wm_coeff_ctl *ctl,
const void *buf, size_t len)
{
- struct wm_adsp_alg_region *alg_region = &ctl->alg_region;
- const struct wm_adsp_region *mem;
struct wm_adsp *dsp = ctl->dsp;
void *scratch;
int ret;
unsigned int reg;
- mem = wm_adsp_find_region(dsp, alg_region->type);
- if (!mem) {
- adsp_err(dsp, "No base for region %x\n",
- alg_region->type);
- return -EINVAL;
- }
-
- reg = ctl->alg_region.base + ctl->offset;
- reg = wm_adsp_region_to_reg(mem, reg);
+ ret = wm_coeff_base_reg(ctl, &reg);
+ if (ret)
+ return ret;
scratch = kmemdup(buf, len, GFP_KERNEL | GFP_DMA);
if (!scratch)
@@ -823,25 +933,41 @@ static int wm_coeff_tlv_put(struct snd_kcontrol *kctl,
return ret;
}
+static int wm_coeff_put_acked(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_bytes_ext *bytes_ext =
+ (struct soc_bytes_ext *)kctl->private_value;
+ struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext);
+ unsigned int val = ucontrol->value.integer.value[0];
+ int ret;
+
+ if (val == 0)
+ return 0; /* 0 means no event */
+
+ mutex_lock(&ctl->dsp->pwr_lock);
+
+ if (ctl->enabled)
+ ret = wm_coeff_write_acked_control(ctl, val);
+ else
+ ret = -EPERM;
+
+ mutex_unlock(&ctl->dsp->pwr_lock);
+
+ return ret;
+}
+
static int wm_coeff_read_control(struct wm_coeff_ctl *ctl,
void *buf, size_t len)
{
- struct wm_adsp_alg_region *alg_region = &ctl->alg_region;
- const struct wm_adsp_region *mem;
struct wm_adsp *dsp = ctl->dsp;
void *scratch;
int ret;
unsigned int reg;
- mem = wm_adsp_find_region(dsp, alg_region->type);
- if (!mem) {
- adsp_err(dsp, "No base for region %x\n",
- alg_region->type);
- return -EINVAL;
- }
-
- reg = ctl->alg_region.base + ctl->offset;
- reg = wm_adsp_region_to_reg(mem, reg);
+ ret = wm_coeff_base_reg(ctl, &reg);
+ if (ret)
+ return ret;
scratch = kmalloc(len, GFP_KERNEL | GFP_DMA);
if (!scratch)
@@ -918,6 +1044,21 @@ static int wm_coeff_tlv_get(struct snd_kcontrol *kctl,
return ret;
}
+static int wm_coeff_get_acked(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ /*
+ * Although it's not useful to read an acked control, we must satisfy
+ * user-side assumptions that all controls are readable and that a
+ * write of the same value should be filtered out (it's valid to send
+ * the same event number again to the firmware). We therefore return 0,
+ * meaning "no event" so valid event numbers will always be a change
+ */
+ ucontrol->value.integer.value[0] = 0;
+
+ return 0;
+}
+
struct wmfw_ctl_work {
struct wm_adsp *dsp;
struct wm_coeff_ctl *ctl;
@@ -967,30 +1108,35 @@ static int wmfw_add_ctl(struct wm_adsp *dsp, struct wm_coeff_ctl *ctl)
kcontrol = kzalloc(sizeof(*kcontrol), GFP_KERNEL);
if (!kcontrol)
return -ENOMEM;
- kcontrol->iface = SNDRV_CTL_ELEM_IFACE_MIXER;
kcontrol->name = ctl->name;
kcontrol->info = wm_coeff_info;
- kcontrol->get = wm_coeff_get;
- kcontrol->put = wm_coeff_put;
kcontrol->iface = SNDRV_CTL_ELEM_IFACE_MIXER;
kcontrol->tlv.c = snd_soc_bytes_tlv_callback;
kcontrol->private_value = (unsigned long)&ctl->bytes_ext;
+ kcontrol->access = wmfw_convert_flags(ctl->flags, ctl->len);
- ctl->bytes_ext.max = ctl->len;
- ctl->bytes_ext.get = wm_coeff_tlv_get;
- ctl->bytes_ext.put = wm_coeff_tlv_put;
+ switch (ctl->type) {
+ case WMFW_CTL_TYPE_ACKED:
+ kcontrol->get = wm_coeff_get_acked;
+ kcontrol->put = wm_coeff_put_acked;
+ break;
+ default:
+ kcontrol->get = wm_coeff_get;
+ kcontrol->put = wm_coeff_put;
- kcontrol->access = wmfw_convert_flags(ctl->flags, ctl->len);
+ ctl->bytes_ext.max = ctl->len;
+ ctl->bytes_ext.get = wm_coeff_tlv_get;
+ ctl->bytes_ext.put = wm_coeff_tlv_put;
+ break;
+ }
- ret = snd_soc_add_card_controls(dsp->card, kcontrol, 1);
+ ret = snd_soc_add_codec_controls(dsp->codec, kcontrol, 1);
if (ret < 0)
goto err_kcontrol;
kfree(kcontrol);
- ctl->kcontrol = snd_soc_card_get_kcontrol(dsp->card, ctl->name);
-
return 0;
err_kcontrol:
@@ -1035,6 +1181,27 @@ static int wm_coeff_sync_controls(struct wm_adsp *dsp)
return 0;
}
+static void wm_adsp_signal_event_controls(struct wm_adsp *dsp,
+ unsigned int event)
+{
+ struct wm_coeff_ctl *ctl;
+ int ret;
+
+ list_for_each_entry(ctl, &dsp->ctl_list, list) {
+ if (ctl->type != WMFW_CTL_TYPE_HOSTEVENT)
+ continue;
+
+ if (!ctl->enabled)
+ continue;
+
+ ret = wm_coeff_write_acked_control(ctl, event);
+ if (ret)
+ adsp_warn(dsp,
+ "Failed to send 0x%x event to alg 0x%x (%d)\n",
+ event, ctl->alg_region.alg, ret);
+ }
+}
+
static void wm_adsp_ctl_work(struct work_struct *work)
{
struct wmfw_ctl_work *ctl_work = container_of(work,
@@ -1056,34 +1223,16 @@ static int wm_adsp_create_control(struct wm_adsp *dsp,
const struct wm_adsp_alg_region *alg_region,
unsigned int offset, unsigned int len,
const char *subname, unsigned int subname_len,
- unsigned int flags)
+ unsigned int flags, unsigned int type)
{
struct wm_coeff_ctl *ctl;
struct wmfw_ctl_work *ctl_work;
char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
- char *region_name;
+ const char *region_name;
int ret;
- if (flags & WMFW_CTL_FLAG_SYS)
- return 0;
-
- switch (alg_region->type) {
- case WMFW_ADSP1_PM:
- region_name = "PM";
- break;
- case WMFW_ADSP1_DM:
- region_name = "DM";
- break;
- case WMFW_ADSP2_XM:
- region_name = "XM";
- break;
- case WMFW_ADSP2_YM:
- region_name = "YM";
- break;
- case WMFW_ADSP1_ZM:
- region_name = "ZM";
- break;
- default:
+ region_name = wm_adsp_mem_region_name(alg_region->type);
+ if (!region_name) {
adsp_err(dsp, "Unknown region type: %d\n", alg_region->type);
return -EINVAL;
}
@@ -1139,6 +1288,7 @@ static int wm_adsp_create_control(struct wm_adsp *dsp,
ctl->dsp = dsp;
ctl->flags = flags;
+ ctl->type = type;
ctl->offset = offset;
ctl->len = len;
ctl->cache = kzalloc(ctl->len, GFP_KERNEL);
@@ -1149,6 +1299,9 @@ static int wm_adsp_create_control(struct wm_adsp *dsp,
list_add(&ctl->list, &dsp->ctl_list);
+ if (flags & WMFW_CTL_FLAG_SYS)
+ return 0;
+
ctl_work = kzalloc(sizeof(*ctl_work), GFP_KERNEL);
if (!ctl_work) {
ret = -ENOMEM;
@@ -1308,6 +1461,21 @@ static inline void wm_coeff_parse_coeff(struct wm_adsp *dsp, const u8 **data,
adsp_dbg(dsp, "\tALSA control len: %#x\n", blk->len);
}
+static int wm_adsp_check_coeff_flags(struct wm_adsp *dsp,
+ const struct wm_coeff_parsed_coeff *coeff_blk,
+ unsigned int f_required,
+ unsigned int f_illegal)
+{
+ if ((coeff_blk->flags & f_illegal) ||
+ ((coeff_blk->flags & f_required) != f_required)) {
+ adsp_err(dsp, "Illegal flags 0x%x for control type 0x%x\n",
+ coeff_blk->flags, coeff_blk->ctl_type);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
static int wm_adsp_parse_coeff(struct wm_adsp *dsp,
const struct wmfw_region *region)
{
@@ -1324,6 +1492,28 @@ static int wm_adsp_parse_coeff(struct wm_adsp *dsp,
switch (coeff_blk.ctl_type) {
case SNDRV_CTL_ELEM_TYPE_BYTES:
break;
+ case WMFW_CTL_TYPE_ACKED:
+ if (coeff_blk.flags & WMFW_CTL_FLAG_SYS)
+ continue; /* ignore */
+
+ ret = wm_adsp_check_coeff_flags(dsp, &coeff_blk,
+ WMFW_CTL_FLAG_VOLATILE |
+ WMFW_CTL_FLAG_WRITEABLE |
+ WMFW_CTL_FLAG_READABLE,
+ 0);
+ if (ret)
+ return -EINVAL;
+ break;
+ case WMFW_CTL_TYPE_HOSTEVENT:
+ ret = wm_adsp_check_coeff_flags(dsp, &coeff_blk,
+ WMFW_CTL_FLAG_SYS |
+ WMFW_CTL_FLAG_VOLATILE |
+ WMFW_CTL_FLAG_WRITEABLE |
+ WMFW_CTL_FLAG_READABLE,
+ 0);
+ if (ret)
+ return -EINVAL;
+ break;
default:
adsp_err(dsp, "Unknown control type: %d\n",
coeff_blk.ctl_type);
@@ -1338,7 +1528,8 @@ static int wm_adsp_parse_coeff(struct wm_adsp *dsp,
coeff_blk.len,
coeff_blk.name,
coeff_blk.name_len,
- coeff_blk.flags);
+ coeff_blk.flags,
+ coeff_blk.ctl_type);
if (ret < 0)
adsp_err(dsp, "Failed to create control: %.*s, %d\n",
coeff_blk.name_len, coeff_blk.name, ret);
@@ -1491,23 +1682,11 @@ static int wm_adsp_load(struct wm_adsp *dsp)
reg = offset;
break;
case WMFW_ADSP1_PM:
- region_name = "PM";
- reg = wm_adsp_region_to_reg(mem, offset);
- break;
case WMFW_ADSP1_DM:
- region_name = "DM";
- reg = wm_adsp_region_to_reg(mem, offset);
- break;
case WMFW_ADSP2_XM:
- region_name = "XM";
- reg = wm_adsp_region_to_reg(mem, offset);
- break;
case WMFW_ADSP2_YM:
- region_name = "YM";
- reg = wm_adsp_region_to_reg(mem, offset);
- break;
case WMFW_ADSP1_ZM:
- region_name = "ZM";
+ region_name = wm_adsp_mem_region_name(type);
reg = wm_adsp_region_to_reg(mem, offset);
break;
default:
@@ -1750,7 +1929,8 @@ static int wm_adsp1_setup_algs(struct wm_adsp *dsp)
len -= be32_to_cpu(adsp1_alg[i].dm);
len *= 4;
wm_adsp_create_control(dsp, alg_region, 0,
- len, NULL, 0, 0);
+ len, NULL, 0, 0,
+ SNDRV_CTL_ELEM_TYPE_BYTES);
} else {
adsp_warn(dsp, "Missing length info for region DM with ID %x\n",
be32_to_cpu(adsp1_alg[i].alg.id));
@@ -1770,7 +1950,8 @@ static int wm_adsp1_setup_algs(struct wm_adsp *dsp)
len -= be32_to_cpu(adsp1_alg[i].zm);
len *= 4;
wm_adsp_create_control(dsp, alg_region, 0,
- len, NULL, 0, 0);
+ len, NULL, 0, 0,
+ SNDRV_CTL_ELEM_TYPE_BYTES);
} else {
adsp_warn(dsp, "Missing length info for region ZM with ID %x\n",
be32_to_cpu(adsp1_alg[i].alg.id));
@@ -1861,7 +2042,8 @@ static int wm_adsp2_setup_algs(struct wm_adsp *dsp)
len -= be32_to_cpu(adsp2_alg[i].xm);
len *= 4;
wm_adsp_create_control(dsp, alg_region, 0,
- len, NULL, 0, 0);
+ len, NULL, 0, 0,
+ SNDRV_CTL_ELEM_TYPE_BYTES);
} else {
adsp_warn(dsp, "Missing length info for region XM with ID %x\n",
be32_to_cpu(adsp2_alg[i].alg.id));
@@ -1881,7 +2063,8 @@ static int wm_adsp2_setup_algs(struct wm_adsp *dsp)
len -= be32_to_cpu(adsp2_alg[i].ym);
len *= 4;
wm_adsp_create_control(dsp, alg_region, 0,
- len, NULL, 0, 0);
+ len, NULL, 0, 0,
+ SNDRV_CTL_ELEM_TYPE_BYTES);
} else {
adsp_warn(dsp, "Missing length info for region YM with ID %x\n",
be32_to_cpu(adsp2_alg[i].alg.id));
@@ -1901,7 +2084,8 @@ static int wm_adsp2_setup_algs(struct wm_adsp *dsp)
len -= be32_to_cpu(adsp2_alg[i].zm);
len *= 4;
wm_adsp_create_control(dsp, alg_region, 0,
- len, NULL, 0, 0);
+ len, NULL, 0, 0,
+ SNDRV_CTL_ELEM_TYPE_BYTES);
} else {
adsp_warn(dsp, "Missing length info for region ZM with ID %x\n",
be32_to_cpu(adsp2_alg[i].alg.id));
@@ -2114,7 +2298,7 @@ int wm_adsp1_event(struct snd_soc_dapm_widget *w,
int ret;
unsigned int val;
- dsp->card = codec->component.card;
+ dsp->codec = codec;
mutex_lock(&dsp->pwr_lock);
@@ -2325,8 +2509,6 @@ int wm_adsp2_early_event(struct snd_soc_dapm_widget *w,
struct wm_adsp *dsp = &dsps[w->shift];
struct wm_coeff_ctl *ctl;
- dsp->card = codec->component.card;
-
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
wm_adsp2_set_dspclk(dsp, freq);
@@ -2393,14 +2575,22 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w,
mutex_lock(&dsp->pwr_lock);
- if (wm_adsp_fw[dsp->fw].num_caps != 0)
+ if (wm_adsp_fw[dsp->fw].num_caps != 0) {
ret = wm_adsp_buffer_init(dsp);
+ if (ret < 0) {
+ mutex_unlock(&dsp->pwr_lock);
+ goto err;
+ }
+ }
mutex_unlock(&dsp->pwr_lock);
break;
case SND_SOC_DAPM_PRE_PMD:
+ /* Tell the firmware to cleanup */
+ wm_adsp_signal_event_controls(dsp, WM_ADSP_FW_EVENT_SHUTDOWN);
+
/* Log firmware state, it can be useful for analysis */
wm_adsp2_show_fw_status(dsp);
@@ -2441,6 +2631,8 @@ EXPORT_SYMBOL_GPL(wm_adsp2_event);
int wm_adsp2_codec_probe(struct wm_adsp *dsp, struct snd_soc_codec *codec)
{
+ dsp->codec = codec;
+
wm_adsp2_init_debugfs(dsp, codec);
return snd_soc_add_codec_controls(codec,
diff --git a/sound/soc/codecs/wm_adsp.h b/sound/soc/codecs/wm_adsp.h
index 362dd7ce60d8..411d062c13f2 100644
--- a/sound/soc/codecs/wm_adsp.h
+++ b/sound/soc/codecs/wm_adsp.h
@@ -44,7 +44,7 @@ struct wm_adsp {
int type;
struct device *dev;
struct regmap *regmap;
- struct snd_soc_card *card;
+ struct snd_soc_codec *codec;
int base;
int sysclk_reg;
@@ -110,18 +110,17 @@ int wm_adsp2_early_event(struct snd_soc_dapm_widget *w,
int wm_adsp2_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event);
-extern int wm_adsp_compr_open(struct wm_adsp *dsp,
- struct snd_compr_stream *stream);
-extern int wm_adsp_compr_free(struct snd_compr_stream *stream);
-extern int wm_adsp_compr_set_params(struct snd_compr_stream *stream,
- struct snd_compr_params *params);
-extern int wm_adsp_compr_get_caps(struct snd_compr_stream *stream,
- struct snd_compr_caps *caps);
-extern int wm_adsp_compr_trigger(struct snd_compr_stream *stream, int cmd);
-extern int wm_adsp_compr_handle_irq(struct wm_adsp *dsp);
-extern int wm_adsp_compr_pointer(struct snd_compr_stream *stream,
- struct snd_compr_tstamp *tstamp);
-extern int wm_adsp_compr_copy(struct snd_compr_stream *stream,
- char __user *buf, size_t count);
+int wm_adsp_compr_open(struct wm_adsp *dsp, struct snd_compr_stream *stream);
+int wm_adsp_compr_free(struct snd_compr_stream *stream);
+int wm_adsp_compr_set_params(struct snd_compr_stream *stream,
+ struct snd_compr_params *params);
+int wm_adsp_compr_get_caps(struct snd_compr_stream *stream,
+ struct snd_compr_caps *caps);
+int wm_adsp_compr_trigger(struct snd_compr_stream *stream, int cmd);
+int wm_adsp_compr_handle_irq(struct wm_adsp *dsp);
+int wm_adsp_compr_pointer(struct snd_compr_stream *stream,
+ struct snd_compr_tstamp *tstamp);
+int wm_adsp_compr_copy(struct snd_compr_stream *stream,
+ char __user *buf, size_t count);
#endif
diff --git a/sound/soc/codecs/wmfw.h b/sound/soc/codecs/wmfw.h
index 7613d60d62ea..ec78b9da020f 100644
--- a/sound/soc/codecs/wmfw.h
+++ b/sound/soc/codecs/wmfw.h
@@ -26,6 +26,10 @@
#define WMFW_CTL_FLAG_WRITEABLE 0x0002
#define WMFW_CTL_FLAG_READABLE 0x0001
+/* Non-ALSA coefficient types start at 0x1000 */
+#define WMFW_CTL_TYPE_ACKED 0x1000 /* acked control */
+#define WMFW_CTL_TYPE_HOSTEVENT 0x1001 /* event control */
+
struct wmfw_header {
char magic[4];
__le32 len;
diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig
index 26eb5a0a5575..fd5d1e091038 100644
--- a/sound/soc/intel/Kconfig
+++ b/sound/soc/intel/Kconfig
@@ -47,6 +47,7 @@ config SND_SOC_INTEL_SST_MATCH
config SND_SOC_INTEL_HASWELL
tristate
+ select SND_SOC_INTEL_SST_FIRMWARE
config SND_SOC_INTEL_BAYTRAIL
tristate
@@ -56,7 +57,6 @@ config SND_SOC_INTEL_HASWELL_MACH
depends on X86_INTEL_LPSS && I2C && I2C_DESIGNWARE_PLATFORM
depends on DW_DMAC_CORE
select SND_SOC_INTEL_SST
- select SND_SOC_INTEL_SST_FIRMWARE
select SND_SOC_INTEL_HASWELL
select SND_SOC_RT5640
help
@@ -138,7 +138,6 @@ config SND_SOC_INTEL_BROADWELL_MACH
I2C_DESIGNWARE_PLATFORM
depends on DW_DMAC_CORE
select SND_SOC_INTEL_SST
- select SND_SOC_INTEL_SST_FIRMWARE
select SND_SOC_INTEL_HASWELL
select SND_SOC_RT286
help
diff --git a/sound/soc/intel/atom/sst-atom-controls.c b/sound/soc/intel/atom/sst-atom-controls.c
index 0838478c4c3f..c7b3cbf92faf 100644
--- a/sound/soc/intel/atom/sst-atom-controls.c
+++ b/sound/soc/intel/atom/sst-atom-controls.c
@@ -937,7 +937,7 @@ int send_ssp_cmd(struct snd_soc_dai *dai, const char *id, bool enable)
struct sst_data *drv = snd_soc_dai_get_drvdata(dai);
int ssp_id;
- dev_info(dai->dev, "Enter: enable=%d port_name=%s\n", enable, id);
+ dev_dbg(dai->dev, "Enter: enable=%d port_name=%s\n", enable, id);
if (strcmp(id, "ssp0-port") == 0)
ssp_id = SSP_MODEM;
diff --git a/sound/soc/intel/atom/sst-mfld-platform-pcm.c b/sound/soc/intel/atom/sst-mfld-platform-pcm.c
index 25c6d87c818e..f5a8050351b5 100644
--- a/sound/soc/intel/atom/sst-mfld-platform-pcm.c
+++ b/sound/soc/intel/atom/sst-mfld-platform-pcm.c
@@ -771,6 +771,9 @@ static int sst_soc_prepare(struct device *dev)
struct sst_data *drv = dev_get_drvdata(dev);
struct snd_soc_pcm_runtime *rtd;
+ if (!drv->soc_card)
+ return 0;
+
/* suspend all pcms first */
snd_soc_suspend(drv->soc_card->dev);
snd_soc_poweroff(drv->soc_card->dev);
@@ -793,6 +796,9 @@ static void sst_soc_complete(struct device *dev)
struct sst_data *drv = dev_get_drvdata(dev);
struct snd_soc_pcm_runtime *rtd;
+ if (!drv->soc_card)
+ return;
+
/* restart SSPs */
list_for_each_entry(rtd, &drv->soc_card->rtd_list, list) {
struct snd_soc_dai *dai = rtd->cpu_dai;
diff --git a/sound/soc/intel/atom/sst/sst.c b/sound/soc/intel/atom/sst/sst.c
index 9b6e27385dc9..f9ba71315e33 100644
--- a/sound/soc/intel/atom/sst/sst.c
+++ b/sound/soc/intel/atom/sst/sst.c
@@ -27,6 +27,7 @@
#include <linux/pm_qos.h>
#include <linux/async.h>
#include <linux/acpi.h>
+#include <linux/sysfs.h>
#include <sound/core.h>
#include <sound/soc.h>
#include <asm/platform_sst_audio.h>
@@ -242,6 +243,32 @@ int sst_alloc_drv_context(struct intel_sst_drv **ctx,
}
EXPORT_SYMBOL_GPL(sst_alloc_drv_context);
+static ssize_t firmware_version_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+ if (ctx->fw_version.type == 0 && ctx->fw_version.major == 0 &&
+ ctx->fw_version.minor == 0 && ctx->fw_version.build == 0)
+ return sprintf(buf, "FW not yet loaded\n");
+ else
+ return sprintf(buf, "v%02x.%02x.%02x.%02x\n",
+ ctx->fw_version.type, ctx->fw_version.major,
+ ctx->fw_version.minor, ctx->fw_version.build);
+
+}
+
+DEVICE_ATTR_RO(firmware_version);
+
+static const struct attribute *sst_fw_version_attrs[] = {
+ &dev_attr_firmware_version.attr,
+ NULL,
+};
+
+static const struct attribute_group sst_fw_version_attr_group = {
+ .attrs = (struct attribute **)sst_fw_version_attrs,
+};
+
int sst_context_init(struct intel_sst_drv *ctx)
{
int ret = 0, i;
@@ -315,8 +342,19 @@ int sst_context_init(struct intel_sst_drv *ctx)
dev_err(ctx->dev, "Firmware download failed:%d\n", ret);
goto do_free_mem;
}
+
+ ret = sysfs_create_group(&ctx->dev->kobj,
+ &sst_fw_version_attr_group);
+ if (ret) {
+ dev_err(ctx->dev,
+ "Unable to create sysfs\n");
+ goto err_sysfs;
+ }
+
sst_register(ctx->dev);
return 0;
+err_sysfs:
+ sysfs_remove_group(&ctx->dev->kobj, &sst_fw_version_attr_group);
do_free_mem:
destroy_workqueue(ctx->post_msg_wq);
@@ -330,6 +368,7 @@ void sst_context_cleanup(struct intel_sst_drv *ctx)
pm_runtime_disable(ctx->dev);
sst_unregister(ctx->dev);
sst_set_fw_state_locked(ctx, SST_SHUTDOWN);
+ sysfs_remove_group(&ctx->dev->kobj, &sst_fw_version_attr_group);
flush_scheduled_work();
destroy_workqueue(ctx->post_msg_wq);
pm_qos_remove_request(ctx->qos);
diff --git a/sound/soc/intel/atom/sst/sst.h b/sound/soc/intel/atom/sst/sst.h
index 3f493862e98d..5c9a51cc77aa 100644
--- a/sound/soc/intel/atom/sst/sst.h
+++ b/sound/soc/intel/atom/sst/sst.h
@@ -436,6 +436,7 @@ struct intel_sst_drv {
*/
char firmware_name[FW_NAME_SIZE];
+ struct snd_sst_fw_version fw_version;
struct sst_fw_save *fw_save;
};
diff --git a/sound/soc/intel/atom/sst/sst_acpi.c b/sound/soc/intel/atom/sst/sst_acpi.c
index ba5c0d71720a..f4d92bbc5373 100644
--- a/sound/soc/intel/atom/sst/sst_acpi.c
+++ b/sound/soc/intel/atom/sst/sst_acpi.c
@@ -416,6 +416,7 @@ static const struct dmi_system_id cht_table[] = {
DMI_MATCH(DMI_PRODUCT_NAME, "Surface 3"),
},
},
+ { }
};
@@ -451,6 +452,8 @@ static struct sst_acpi_mach sst_acpi_bytcr[] = {
static struct sst_acpi_mach sst_acpi_chv[] = {
{"10EC5670", "cht-bsw-rt5672", "intel/fw_sst_22a8.bin", "cht-bsw", NULL,
&chv_platform_data },
+ {"10EC5672", "cht-bsw-rt5672", "intel/fw_sst_22a8.bin", "cht-bsw", NULL,
+ &chv_platform_data },
{"10EC5645", "cht-bsw-rt5645", "intel/fw_sst_22a8.bin", "cht-bsw", NULL,
&chv_platform_data },
{"10EC5650", "cht-bsw-rt5645", "intel/fw_sst_22a8.bin", "cht-bsw", NULL,
diff --git a/sound/soc/intel/atom/sst/sst_ipc.c b/sound/soc/intel/atom/sst/sst_ipc.c
index bfc889950bb2..374bb61c596d 100644
--- a/sound/soc/intel/atom/sst/sst_ipc.c
+++ b/sound/soc/intel/atom/sst/sst_ipc.c
@@ -236,6 +236,17 @@ static void process_fw_init(struct intel_sst_drv *sst_drv_ctx,
retval = init->result;
goto ret;
}
+ dev_info(sst_drv_ctx->dev, "FW Version %02x.%02x.%02x.%02x\n",
+ init->fw_version.type, init->fw_version.major,
+ init->fw_version.minor, init->fw_version.build);
+ dev_dbg(sst_drv_ctx->dev, "Build date %s Time %s\n",
+ init->build_info.date, init->build_info.time);
+
+ /* Save FW version */
+ sst_drv_ctx->fw_version.type = init->fw_version.type;
+ sst_drv_ctx->fw_version.major = init->fw_version.major;
+ sst_drv_ctx->fw_version.minor = init->fw_version.minor;
+ sst_drv_ctx->fw_version.build = init->fw_version.build;
ret:
sst_wake_up_block(sst_drv_ctx, retval, FW_DWNL_ID, 0 , NULL, 0);
diff --git a/sound/soc/intel/atom/sst/sst_stream.c b/sound/soc/intel/atom/sst/sst_stream.c
index 4ccc80e5e8cc..51bdeeecb7c8 100644
--- a/sound/soc/intel/atom/sst/sst_stream.c
+++ b/sound/soc/intel/atom/sst/sst_stream.c
@@ -104,7 +104,7 @@ int sst_alloc_stream_mrfld(struct intel_sst_drv *sst_drv_ctx, void *params)
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",
+ dev_dbg(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),
@@ -415,7 +415,7 @@ int sst_free_stream(struct intel_sst_drv *sst_drv_ctx, int str_id)
str_info->status = STREAM_UN_INIT;
mutex_unlock(&str_info->lock);
- dev_info(sst_drv_ctx->dev, "Free for str %d pipe %#x\n",
+ dev_dbg(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,
diff --git a/sound/soc/intel/baytrail/sst-baytrail-ipc.c b/sound/soc/intel/baytrail/sst-baytrail-ipc.c
index 7ab14ce65a73..260447da32b8 100644
--- a/sound/soc/intel/baytrail/sst-baytrail-ipc.c
+++ b/sound/soc/intel/baytrail/sst-baytrail-ipc.c
@@ -23,7 +23,6 @@
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
-#include <linux/kthread.h>
#include <linux/firmware.h>
#include <linux/io.h>
#include <asm/div64.h>
@@ -338,7 +337,7 @@ static irqreturn_t sst_byt_irq_thread(int irq, void *context)
spin_unlock_irqrestore(&sst->spinlock, flags);
/* continue to send any remaining messages... */
- kthread_queue_work(&ipc->kworker, &ipc->kwork);
+ schedule_work(&ipc->kwork);
return IRQ_HANDLED;
}
diff --git a/sound/soc/intel/boards/broadwell.c b/sound/soc/intel/boards/broadwell.c
index 7486a0022fde..4d7e9decfa92 100644
--- a/sound/soc/intel/boards/broadwell.c
+++ b/sound/soc/intel/boards/broadwell.c
@@ -126,7 +126,7 @@ static int broadwell_rt286_hw_params(struct snd_pcm_substream *substream,
return ret;
}
-static struct snd_soc_ops broadwell_rt286_ops = {
+static const struct snd_soc_ops broadwell_rt286_ops = {
.hw_params = broadwell_rt286_hw_params,
};
@@ -220,10 +220,12 @@ static struct snd_soc_dai_link broadwell_rt286_dais[] = {
};
static int broadwell_suspend(struct snd_soc_card *card){
- struct snd_soc_codec *codec;
+ struct snd_soc_component *component;
+
+ list_for_each_entry(component, &card->component_dev_list, card_list) {
+ if (!strcmp(component->name, "i2c-INT343A:00")) {
+ struct snd_soc_codec *codec = snd_soc_component_to_codec(component);
- list_for_each_entry(codec, &card->codec_dev_list, card_list) {
- if (!strcmp(codec->component.name, "i2c-INT343A:00")) {
dev_dbg(codec->dev, "disabling jack detect before going to suspend.\n");
rt286_mic_detect(codec, NULL);
break;
@@ -233,10 +235,12 @@ static int broadwell_suspend(struct snd_soc_card *card){
}
static int broadwell_resume(struct snd_soc_card *card){
- struct snd_soc_codec *codec;
+ struct snd_soc_component *component;
+
+ list_for_each_entry(component, &card->component_dev_list, card_list) {
+ if (!strcmp(component->name, "i2c-INT343A:00")) {
+ struct snd_soc_codec *codec = snd_soc_component_to_codec(component);
- list_for_each_entry(codec, &card->codec_dev_list, card_list) {
- if (!strcmp(codec->component.name, "i2c-INT343A:00")) {
dev_dbg(codec->dev, "enabling jack detect for resume.\n");
rt286_mic_detect(codec, &broadwell_headset);
break;
diff --git a/sound/soc/intel/boards/bxt_da7219_max98357a.c b/sound/soc/intel/boards/bxt_da7219_max98357a.c
index 6532b8f0ab2f..3a8c24a59020 100644
--- a/sound/soc/intel/boards/bxt_da7219_max98357a.c
+++ b/sound/soc/intel/boards/bxt_da7219_max98357a.c
@@ -30,6 +30,7 @@
#define BXT_DIALOG_CODEC_DAI "da7219-hifi"
#define BXT_MAXIM_CODEC_DAI "HiFi"
#define DUAL_CHANNEL 2
+#define QUAD_CHANNEL 4
static struct snd_soc_jack broxton_headset;
@@ -130,8 +131,8 @@ static int broxton_da7219_codec_init(struct snd_soc_pcm_runtime *rtd)
*/
ret = snd_soc_card_jack_new(rtd->card, "Headset Jack",
SND_JACK_HEADSET | SND_JACK_BTN_0 | SND_JACK_BTN_1 |
- SND_JACK_BTN_2 | SND_JACK_BTN_3, &broxton_headset,
- NULL, 0);
+ SND_JACK_BTN_2 | SND_JACK_BTN_3 | SND_JACK_LINEOUT,
+ &broxton_headset, NULL, 0);
if (ret) {
dev_err(rtd->dev, "Headset Jack creation failed: %d\n", ret);
return ret;
@@ -182,6 +183,16 @@ static struct snd_pcm_hw_constraint_list constraints_channels = {
.mask = 0,
};
+static unsigned int channels_quad[] = {
+ QUAD_CHANNEL,
+};
+
+static struct snd_pcm_hw_constraint_list constraints_channels_quad = {
+ .count = ARRAY_SIZE(channels_quad),
+ .list = channels_quad,
+ .mask = 0,
+};
+
static int bxt_fe_startup(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
@@ -258,7 +269,10 @@ static int broxton_dmic_fixup(struct snd_soc_pcm_runtime *rtd,
{
struct snd_interval *channels = hw_param_interval(params,
SNDRV_PCM_HW_PARAM_CHANNELS);
- channels->min = channels->max = DUAL_CHANNEL;
+ if (params_channels(params) == 2)
+ channels->min = channels->max = 2;
+ else
+ channels->min = channels->max = 4;
return 0;
}
@@ -267,9 +281,9 @@ static int broxton_dmic_startup(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
- runtime->hw.channels_max = DUAL_CHANNEL;
+ runtime->hw.channels_min = runtime->hw.channels_max = QUAD_CHANNEL;
snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
- &constraints_channels);
+ &constraints_channels_quad);
return snd_pcm_hw_constraint_list(substream->runtime, 0,
SNDRV_PCM_HW_PARAM_RATE, &constraints_rates);
@@ -348,7 +362,7 @@ static struct snd_soc_dai_link broxton_dais[] = {
.dynamic = 1,
.ops = &broxton_refcap_ops,
},
- [BXT_DPCM_AUDIO_DMIC_CP]
+ [BXT_DPCM_AUDIO_DMIC_CP] =
{
.name = "Bxt Audio DMIC cap",
.stream_name = "dmiccap",
diff --git a/sound/soc/intel/boards/bytcr_rt5640.c b/sound/soc/intel/boards/bytcr_rt5640.c
index bff77a1f27fc..f9d73b55d2f7 100644
--- a/sound/soc/intel/boards/bytcr_rt5640.c
+++ b/sound/soc/intel/boards/bytcr_rt5640.c
@@ -57,9 +57,7 @@ struct byt_rt5640_private {
struct clk *mclk;
};
-static unsigned long byt_rt5640_quirk = BYT_RT5640_DMIC1_MAP |
- BYT_RT5640_DMIC_EN |
- BYT_RT5640_MCLK_EN;
+static unsigned long byt_rt5640_quirk = BYT_RT5640_MCLK_EN;
static void log_quirks(struct device *dev)
{
@@ -689,6 +687,10 @@ static bool is_valleyview(void)
return true;
}
+struct acpi_chan_package { /* ACPICA seems to require 64 bit integers */
+ u64 aif_value; /* 1: AIF1, 2: AIF2 */
+ u64 mclock_value; /* usually 25MHz (0x17d7940), ignored */
+};
static int snd_byt_rt5640_mc_probe(struct platform_device *pdev)
{
@@ -698,6 +700,7 @@ static int snd_byt_rt5640_mc_probe(struct platform_device *pdev)
int i;
int dai_index;
struct byt_rt5640_private *priv;
+ bool is_bytcr = false;
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_ATOMIC);
if (!priv)
@@ -734,10 +737,61 @@ static int snd_byt_rt5640_mc_probe(struct platform_device *pdev)
struct sst_platform_info *p_info = mach->pdata;
const struct sst_res_info *res_info = p_info->res_info;
- /* TODO: use CHAN package info from BIOS to detect AIF1/AIF2 */
- if (res_info->acpi_ipc_irq_index == 0) {
+ if (res_info->acpi_ipc_irq_index == 0)
+ is_bytcr = true;
+ }
+
+ if (is_bytcr) {
+ /*
+ * Baytrail CR platforms may have CHAN package in BIOS, try
+ * to find relevant routing quirk based as done on Windows
+ * platforms. We have to read the information directly from the
+ * BIOS, at this stage the card is not created and the links
+ * with the codec driver/pdata are non-existent
+ */
+
+ struct acpi_chan_package chan_package;
+
+ /* format specified: 2 64-bit integers */
+ struct acpi_buffer format = {sizeof("NN"), "NN"};
+ struct acpi_buffer state = {0, NULL};
+ struct sst_acpi_package_context pkg_ctx;
+ bool pkg_found = false;
+
+ state.length = sizeof(chan_package);
+ state.pointer = &chan_package;
+
+ pkg_ctx.name = "CHAN";
+ pkg_ctx.length = 2;
+ pkg_ctx.format = &format;
+ pkg_ctx.state = &state;
+ pkg_ctx.data_valid = false;
+
+ pkg_found = sst_acpi_find_package_from_hid(mach->id, &pkg_ctx);
+ if (pkg_found) {
+ if (chan_package.aif_value == 1) {
+ dev_info(&pdev->dev, "BIOS Routing: AIF1 connected\n");
+ byt_rt5640_quirk |= BYT_RT5640_SSP0_AIF1;
+ } else if (chan_package.aif_value == 2) {
+ dev_info(&pdev->dev, "BIOS Routing: AIF2 connected\n");
+ byt_rt5640_quirk |= BYT_RT5640_SSP0_AIF2;
+ } else {
+ dev_info(&pdev->dev, "BIOS Routing isn't valid, ignored\n");
+ pkg_found = false;
+ }
+ }
+
+ if (!pkg_found) {
+ /* no BIOS indications, assume SSP0-AIF2 connection */
byt_rt5640_quirk |= BYT_RT5640_SSP0_AIF2;
}
+
+ /* change defaults for Baytrail-CR capture */
+ byt_rt5640_quirk |= BYT_RT5640_IN1_MAP;
+ byt_rt5640_quirk |= BYT_RT5640_DIFF_MIC;
+ } else {
+ byt_rt5640_quirk |= (BYT_RT5640_DMIC1_MAP |
+ BYT_RT5640_DMIC_EN);
}
/* check quirks before creating card */
diff --git a/sound/soc/intel/boards/cht_bsw_rt5645.c b/sound/soc/intel/boards/cht_bsw_rt5645.c
index 56056ed7fcfd..16c94c45ce50 100644
--- a/sound/soc/intel/boards/cht_bsw_rt5645.c
+++ b/sound/soc/intel/boards/cht_bsw_rt5645.c
@@ -44,6 +44,7 @@ struct cht_acpi_card {
struct cht_mc_private {
struct snd_soc_jack jack;
struct cht_acpi_card *acpi_card;
+ char codec_name[16];
};
static inline struct snd_soc_dai *cht_get_codec_dai(struct snd_soc_card *card)
@@ -354,7 +355,6 @@ static int snd_cht_mc_probe(struct platform_device *pdev)
int i;
struct cht_mc_private *drv;
struct snd_soc_card *card = snd_soc_cards[0].soc_card;
- char codec_name[16];
struct sst_acpi_mach *mach;
const char *i2c_name = NULL;
int dai_index = 0;
@@ -374,12 +374,12 @@ static int snd_cht_mc_probe(struct platform_device *pdev)
}
card->dev = &pdev->dev;
mach = card->dev->platform_data;
- sprintf(codec_name, "i2c-%s:00", drv->acpi_card->codec_id);
+ sprintf(drv->codec_name, "i2c-%s:00", drv->acpi_card->codec_id);
/* set correct codec name */
for (i = 0; i < ARRAY_SIZE(cht_dailink); i++)
if (!strcmp(card->dai_link[i].codec_name, "i2c-10EC5645:00")) {
- card->dai_link[i].codec_name = kstrdup(codec_name, GFP_KERNEL);
+ card->dai_link[i].codec_name = drv->codec_name;
dai_index = i;
}
diff --git a/sound/soc/intel/boards/cht_bsw_rt5672.c b/sound/soc/intel/boards/cht_bsw_rt5672.c
index df9d254baa18..43603f1b4baa 100644
--- a/sound/soc/intel/boards/cht_bsw_rt5672.c
+++ b/sound/soc/intel/boards/cht_bsw_rt5672.c
@@ -25,12 +25,14 @@
#include <sound/jack.h>
#include "../../codecs/rt5670.h"
#include "../atom/sst-atom-controls.h"
+#include "../common/sst-acpi.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 struct snd_soc_jack cht_bsw_headset;
+static char cht_bsw_codec_name[16];
/* Headset jack detection DAPM pins */
static struct snd_soc_jack_pin cht_bsw_headset_pins[] = {
@@ -292,10 +294,12 @@ static struct snd_soc_dai_link cht_dailink[] = {
static int cht_suspend_pre(struct snd_soc_card *card)
{
- struct snd_soc_codec *codec;
+ struct snd_soc_component *component;
+
+ list_for_each_entry(component, &card->component_dev_list, card_list) {
+ if (!strcmp(component->name, "i2c-10EC5670:00")) {
+ struct snd_soc_codec *codec = snd_soc_component_to_codec(component);
- list_for_each_entry(codec, &card->codec_dev_list, card_list) {
- if (!strcmp(codec->component.name, "i2c-10EC5670:00")) {
dev_dbg(codec->dev, "disabling jack detect before going to suspend.\n");
rt5670_jack_suspend(codec);
break;
@@ -306,10 +310,12 @@ static int cht_suspend_pre(struct snd_soc_card *card)
static int cht_resume_post(struct snd_soc_card *card)
{
- struct snd_soc_codec *codec;
+ struct snd_soc_component *component;
+
+ list_for_each_entry(component, &card->component_dev_list, card_list) {
+ if (!strcmp(component->name, "i2c-10EC5670:00")) {
+ struct snd_soc_codec *codec = snd_soc_component_to_codec(component);
- list_for_each_entry(codec, &card->codec_dev_list, card_list) {
- if (!strcmp(codec->component.name, "i2c-10EC5670:00")) {
dev_dbg(codec->dev, "enabling jack detect for resume.\n");
rt5670_jack_resume(codec);
break;
@@ -335,9 +341,33 @@ static struct snd_soc_card snd_soc_card_cht = {
.resume_post = cht_resume_post,
};
+#define RT5672_I2C_DEFAULT "i2c-10EC5670:00"
+
static int snd_cht_mc_probe(struct platform_device *pdev)
{
int ret_val = 0;
+ struct sst_acpi_mach *mach = pdev->dev.platform_data;
+ const char *i2c_name;
+ int i;
+
+ strcpy(cht_bsw_codec_name, RT5672_I2C_DEFAULT);
+
+ /* fixup codec name based on HID */
+ if (mach) {
+ i2c_name = sst_acpi_find_name_from_hid(mach->id);
+ if (i2c_name) {
+ snprintf(cht_bsw_codec_name, sizeof(cht_bsw_codec_name),
+ "i2c-%s", i2c_name);
+ for (i = 0; i < ARRAY_SIZE(cht_dailink); i++) {
+ if (!strcmp(cht_dailink[i].codec_name,
+ RT5672_I2C_DEFAULT)) {
+ cht_dailink[i].codec_name =
+ cht_bsw_codec_name;
+ break;
+ }
+ }
+ }
+ }
/* register the soc card */
snd_soc_card_cht.dev = &pdev->dev;
diff --git a/sound/soc/intel/boards/mfld_machine.c b/sound/soc/intel/boards/mfld_machine.c
index 34f46c72a0e2..4e08885f37aa 100644
--- a/sound/soc/intel/boards/mfld_machine.c
+++ b/sound/soc/intel/boards/mfld_machine.c
@@ -81,9 +81,9 @@ static struct snd_soc_jack_zone mfld_zones[] = {
};
/* sound card controls */
-static const char *headset_switch_text[] = {"Earpiece", "Headset"};
+static const char * const headset_switch_text[] = {"Earpiece", "Headset"};
-static const char *lo_text[] = {"Vibra", "Headset", "IHF", "None"};
+static const char * const lo_text[] = {"Vibra", "Headset", "IHF", "None"};
static const struct soc_enum headset_enum =
SOC_ENUM_SINGLE_EXT(2, headset_switch_text);
diff --git a/sound/soc/intel/common/sst-acpi.h b/sound/soc/intel/common/sst-acpi.h
index 012742299dd5..214e000667ae 100644
--- a/sound/soc/intel/common/sst-acpi.h
+++ b/sound/soc/intel/common/sst-acpi.h
@@ -15,14 +15,29 @@
#include <linux/stddef.h>
#include <linux/acpi.h>
-/* translation fron HID to I2C name, needed for DAI codec_name */
+struct sst_acpi_package_context {
+ char *name; /* package name */
+ int length; /* number of elements */
+ struct acpi_buffer *format;
+ struct acpi_buffer *state;
+ bool data_valid;
+};
+
#if IS_ENABLED(CONFIG_ACPI)
+/* translation fron HID to I2C name, needed for DAI codec_name */
const char *sst_acpi_find_name_from_hid(const u8 hid[ACPI_ID_LEN]);
+bool sst_acpi_find_package_from_hid(const u8 hid[ACPI_ID_LEN],
+ struct sst_acpi_package_context *ctx);
#else
static inline const char *sst_acpi_find_name_from_hid(const u8 hid[ACPI_ID_LEN])
{
return NULL;
}
+static inline bool sst_acpi_find_package_from_hid(const u8 hid[ACPI_ID_LEN],
+ struct sst_acpi_package_context *ctx)
+{
+ return false;
+}
#endif
/* acpi match */
diff --git a/sound/soc/intel/common/sst-ipc.c b/sound/soc/intel/common/sst-ipc.c
index 6c672ac79cce..62f3a8e0ec87 100644
--- a/sound/soc/intel/common/sst-ipc.c
+++ b/sound/soc/intel/common/sst-ipc.c
@@ -26,7 +26,6 @@
#include <linux/sched.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
-#include <linux/kthread.h>
#include <sound/asound.h>
#include "sst-dsp.h"
@@ -109,10 +108,9 @@ static int ipc_tx_message(struct sst_generic_ipc *ipc, u64 header,
ipc->ops.tx_data_copy(msg, tx_data, tx_bytes);
list_add_tail(&msg->list, &ipc->tx_list);
+ schedule_work(&ipc->kwork);
spin_unlock_irqrestore(&ipc->dsp->spinlock, flags);
- kthread_queue_work(&ipc->kworker, &ipc->kwork);
-
if (wait)
return tx_wait_done(ipc, msg, rx_data);
else
@@ -156,42 +154,56 @@ free_mem:
return -ENOMEM;
}
-static void ipc_tx_msgs(struct kthread_work *work)
+static void ipc_tx_msgs(struct work_struct *work)
{
struct sst_generic_ipc *ipc =
container_of(work, struct sst_generic_ipc, kwork);
struct ipc_message *msg;
- unsigned long flags;
- spin_lock_irqsave(&ipc->dsp->spinlock, flags);
+ spin_lock_irq(&ipc->dsp->spinlock);
- if (list_empty(&ipc->tx_list) || ipc->pending) {
- spin_unlock_irqrestore(&ipc->dsp->spinlock, flags);
- return;
- }
-
- /* if the DSP is busy, we will TX messages after IRQ.
- * also postpone if we are in the middle of procesing completion irq*/
- if (ipc->ops.is_dsp_busy && ipc->ops.is_dsp_busy(ipc->dsp)) {
- dev_dbg(ipc->dev, "ipc_tx_msgs dsp busy\n");
- spin_unlock_irqrestore(&ipc->dsp->spinlock, flags);
- return;
- }
+ while (!list_empty(&ipc->tx_list) && !ipc->pending) {
+ /* if the DSP is busy, we will TX messages after IRQ.
+ * also postpone if we are in the middle of processing
+ * completion irq
+ */
+ if (ipc->ops.is_dsp_busy && ipc->ops.is_dsp_busy(ipc->dsp)) {
+ dev_dbg(ipc->dev, "ipc_tx_msgs dsp busy\n");
+ break;
+ }
- msg = list_first_entry(&ipc->tx_list, struct ipc_message, list);
- list_move(&msg->list, &ipc->rx_list);
+ msg = list_first_entry(&ipc->tx_list, struct ipc_message, list);
+ list_move(&msg->list, &ipc->rx_list);
- if (ipc->ops.tx_msg != NULL)
- ipc->ops.tx_msg(ipc, msg);
+ if (ipc->ops.tx_msg != NULL)
+ ipc->ops.tx_msg(ipc, msg);
+ }
- spin_unlock_irqrestore(&ipc->dsp->spinlock, flags);
+ spin_unlock_irq(&ipc->dsp->spinlock);
}
int sst_ipc_tx_message_wait(struct sst_generic_ipc *ipc, u64 header,
void *tx_data, size_t tx_bytes, void *rx_data, size_t rx_bytes)
{
- return ipc_tx_message(ipc, header, tx_data, tx_bytes,
+ int ret;
+
+ /*
+ * DSP maybe in lower power active state, so
+ * check if the DSP supports DSP lp On method
+ * if so invoke that before sending IPC
+ */
+ if (ipc->ops.check_dsp_lp_on)
+ if (ipc->ops.check_dsp_lp_on(ipc->dsp, true))
+ return -EIO;
+
+ ret = ipc_tx_message(ipc, header, tx_data, tx_bytes,
rx_data, rx_bytes, 1);
+
+ if (ipc->ops.check_dsp_lp_on)
+ if (ipc->ops.check_dsp_lp_on(ipc->dsp, false))
+ return -EIO;
+
+ return ret;
}
EXPORT_SYMBOL_GPL(sst_ipc_tx_message_wait);
@@ -203,6 +215,14 @@ int sst_ipc_tx_message_nowait(struct sst_generic_ipc *ipc, u64 header,
}
EXPORT_SYMBOL_GPL(sst_ipc_tx_message_nowait);
+int sst_ipc_tx_message_nopm(struct sst_generic_ipc *ipc, u64 header,
+ void *tx_data, size_t tx_bytes, void *rx_data, size_t rx_bytes)
+{
+ return ipc_tx_message(ipc, header, tx_data, tx_bytes,
+ rx_data, rx_bytes, 1);
+}
+EXPORT_SYMBOL_GPL(sst_ipc_tx_message_nopm);
+
struct ipc_message *sst_ipc_reply_find_msg(struct sst_generic_ipc *ipc,
u64 header)
{
@@ -280,19 +300,7 @@ int sst_ipc_init(struct sst_generic_ipc *ipc)
if (ret < 0)
return -ENOMEM;
- /* start the IPC message thread */
- kthread_init_worker(&ipc->kworker);
- ipc->tx_thread = kthread_run(kthread_worker_fn,
- &ipc->kworker, "%s",
- dev_name(ipc->dev));
- if (IS_ERR(ipc->tx_thread)) {
- dev_err(ipc->dev, "error: failed to create message TX task\n");
- ret = PTR_ERR(ipc->tx_thread);
- kfree(ipc->msg);
- return ret;
- }
-
- kthread_init_work(&ipc->kwork, ipc_tx_msgs);
+ INIT_WORK(&ipc->kwork, ipc_tx_msgs);
return 0;
}
EXPORT_SYMBOL_GPL(sst_ipc_init);
@@ -301,8 +309,7 @@ void sst_ipc_fini(struct sst_generic_ipc *ipc)
{
int i;
- if (ipc->tx_thread)
- kthread_stop(ipc->tx_thread);
+ cancel_work_sync(&ipc->kwork);
if (ipc->msg) {
for (i = 0; i < IPC_EMPTY_LIST_SIZE; i++) {
diff --git a/sound/soc/intel/common/sst-ipc.h b/sound/soc/intel/common/sst-ipc.h
index ceb7e468a3fa..7ed42a640ad6 100644
--- a/sound/soc/intel/common/sst-ipc.h
+++ b/sound/soc/intel/common/sst-ipc.h
@@ -23,7 +23,6 @@
#include <linux/list.h>
#include <linux/workqueue.h>
#include <linux/sched.h>
-#include <linux/kthread.h>
#define IPC_MAX_MAILBOX_BYTES 256
@@ -52,6 +51,7 @@ struct sst_plat_ipc_ops {
void (*tx_data_copy)(struct ipc_message *, char *, size_t);
u64 (*reply_msg_match)(u64 header, u64 *mask);
bool (*is_dsp_busy)(struct sst_dsp *dsp);
+ int (*check_dsp_lp_on)(struct sst_dsp *dsp, bool state);
};
/* SST generic IPC data */
@@ -65,8 +65,7 @@ struct sst_generic_ipc {
struct list_head empty_list;
wait_queue_head_t wait_txq;
struct task_struct *tx_thread;
- struct kthread_worker kworker;
- struct kthread_work kwork;
+ struct work_struct kwork;
bool pending;
struct ipc_message *msg;
int tx_data_max_size;
@@ -81,6 +80,9 @@ int sst_ipc_tx_message_wait(struct sst_generic_ipc *ipc, u64 header,
int sst_ipc_tx_message_nowait(struct sst_generic_ipc *ipc, u64 header,
void *tx_data, size_t tx_bytes);
+int sst_ipc_tx_message_nopm(struct sst_generic_ipc *ipc, u64 header,
+ void *tx_data, size_t tx_bytes, void *rx_data, size_t rx_bytes);
+
struct ipc_message *sst_ipc_reply_find_msg(struct sst_generic_ipc *ipc,
u64 header);
diff --git a/sound/soc/intel/common/sst-match-acpi.c b/sound/soc/intel/common/sst-match-acpi.c
index 789843307a49..1070f3ad23e5 100644
--- a/sound/soc/intel/common/sst-match-acpi.c
+++ b/sound/soc/intel/common/sst-match-acpi.c
@@ -77,5 +77,62 @@ struct sst_acpi_mach *sst_acpi_find_machine(struct sst_acpi_mach *machines)
}
EXPORT_SYMBOL_GPL(sst_acpi_find_machine);
+static acpi_status sst_acpi_find_package(acpi_handle handle, u32 level,
+ void *context, void **ret)
+{
+ struct acpi_device *adev;
+ acpi_status status = AE_OK;
+ struct sst_acpi_package_context *pkg_ctx = context;
+
+ pkg_ctx->data_valid = false;
+
+ if (acpi_bus_get_device(handle, &adev))
+ return AE_OK;
+
+ if (adev->status.present && adev->status.functional) {
+ struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};
+ union acpi_object *myobj = NULL;
+
+ status = acpi_evaluate_object_typed(handle, pkg_ctx->name,
+ NULL, &buffer,
+ ACPI_TYPE_PACKAGE);
+ if (ACPI_FAILURE(status))
+ return AE_OK;
+
+ myobj = buffer.pointer;
+ if (!myobj || myobj->package.count != pkg_ctx->length) {
+ kfree(buffer.pointer);
+ return AE_OK;
+ }
+
+ status = acpi_extract_package(myobj,
+ pkg_ctx->format, pkg_ctx->state);
+ if (ACPI_FAILURE(status)) {
+ kfree(buffer.pointer);
+ return AE_OK;
+ }
+
+ kfree(buffer.pointer);
+ pkg_ctx->data_valid = true;
+ return AE_CTRL_TERMINATE;
+ }
+
+ return AE_OK;
+}
+
+bool sst_acpi_find_package_from_hid(const u8 hid[ACPI_ID_LEN],
+ struct sst_acpi_package_context *ctx)
+{
+ acpi_status status;
+
+ status = acpi_get_devices(hid, sst_acpi_find_package, ctx, NULL);
+
+ if (ACPI_FAILURE(status) || !ctx->data_valid)
+ return false;
+
+ return true;
+}
+EXPORT_SYMBOL_GPL(sst_acpi_find_package_from_hid);
+
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("Intel Common ACPI Match module");
diff --git a/sound/soc/intel/haswell/sst-haswell-ipc.c b/sound/soc/intel/haswell/sst-haswell-ipc.c
index e432a31fd9f2..a3459d1682a6 100644
--- a/sound/soc/intel/haswell/sst-haswell-ipc.c
+++ b/sound/soc/intel/haswell/sst-haswell-ipc.c
@@ -26,7 +26,6 @@
#include <linux/delay.h>
#include <linux/sched.h>
#include <linux/platform_device.h>
-#include <linux/kthread.h>
#include <linux/firmware.h>
#include <linux/dma-mapping.h>
#include <linux/debugfs.h>
@@ -818,7 +817,7 @@ static irqreturn_t hsw_irq_thread(int irq, void *context)
spin_unlock_irqrestore(&sst->spinlock, flags);
/* continue to send any remaining messages... */
- kthread_queue_work(&ipc->kworker, &ipc->kwork);
+ schedule_work(&ipc->kwork);
return IRQ_HANDLED;
}
diff --git a/sound/soc/intel/skylake/bxt-sst.c b/sound/soc/intel/skylake/bxt-sst.c
index 1d251d59bcb9..1f9f33d34000 100644
--- a/sound/soc/intel/skylake/bxt-sst.c
+++ b/sound/soc/intel/skylake/bxt-sst.c
@@ -43,6 +43,9 @@
#define BXT_ADSP_FW_BIN_HDR_OFFSET 0x2000
+/* Delay before scheduling D0i3 entry */
+#define BXT_D0I3_DELAY 5000
+
static unsigned int bxt_get_errorcode(struct sst_dsp *ctx)
{
return sst_dsp_shim_read(ctx, BXT_ADSP_ERROR_CODE);
@@ -288,6 +291,141 @@ sst_load_base_firmware_failed:
return ret;
}
+/*
+ * Decide the D0i3 state that can be targeted based on the usecase
+ * ref counts and DSP state
+ *
+ * Decision Matrix: (X= dont care; state = target state)
+ *
+ * DSP state != SKL_DSP_RUNNING ; state = no d0i3
+ *
+ * DSP state == SKL_DSP_RUNNING , the following matrix applies
+ * non_d0i3 >0; streaming =X; non_streaming =X; state = no d0i3
+ * non_d0i3 =X; streaming =0; non_streaming =0; state = no d0i3
+ * non_d0i3 =0; streaming >0; non_streaming =X; state = streaming d0i3
+ * non_d0i3 =0; streaming =0; non_streaming =X; state = non-streaming d0i3
+ */
+static int bxt_d0i3_target_state(struct sst_dsp *ctx)
+{
+ struct skl_sst *skl = ctx->thread_context;
+ struct skl_d0i3_data *d0i3 = &skl->d0i3;
+
+ if (skl->cores.state[SKL_DSP_CORE0_ID] != SKL_DSP_RUNNING)
+ return SKL_DSP_D0I3_NONE;
+
+ if (d0i3->non_d0i3)
+ return SKL_DSP_D0I3_NONE;
+ else if (d0i3->streaming)
+ return SKL_DSP_D0I3_STREAMING;
+ else if (d0i3->non_streaming)
+ return SKL_DSP_D0I3_NON_STREAMING;
+ else
+ return SKL_DSP_D0I3_NONE;
+}
+
+static void bxt_set_dsp_D0i3(struct work_struct *work)
+{
+ int ret;
+ struct skl_ipc_d0ix_msg msg;
+ struct skl_sst *skl = container_of(work,
+ struct skl_sst, d0i3.work.work);
+ struct sst_dsp *ctx = skl->dsp;
+ struct skl_d0i3_data *d0i3 = &skl->d0i3;
+ int target_state;
+
+ dev_dbg(ctx->dev, "In %s:\n", __func__);
+
+ /* D0i3 entry allowed only if core 0 alone is running */
+ if (skl_dsp_get_enabled_cores(ctx) != SKL_DSP_CORE0_MASK) {
+ dev_warn(ctx->dev,
+ "D0i3 allowed when only core0 running:Exit\n");
+ return;
+ }
+
+ target_state = bxt_d0i3_target_state(ctx);
+ if (target_state == SKL_DSP_D0I3_NONE)
+ return;
+
+ msg.instance_id = 0;
+ msg.module_id = 0;
+ msg.wake = 1;
+ msg.streaming = 0;
+ if (target_state == SKL_DSP_D0I3_STREAMING)
+ msg.streaming = 1;
+
+ ret = skl_ipc_set_d0ix(&skl->ipc, &msg);
+
+ if (ret < 0) {
+ dev_err(ctx->dev, "Failed to set DSP to D0i3 state\n");
+ return;
+ }
+
+ /* Set Vendor specific register D0I3C.I3 to enable D0i3*/
+ if (skl->update_d0i3c)
+ skl->update_d0i3c(skl->dev, true);
+
+ d0i3->state = target_state;
+ skl->cores.state[SKL_DSP_CORE0_ID] = SKL_DSP_RUNNING_D0I3;
+}
+
+static int bxt_schedule_dsp_D0i3(struct sst_dsp *ctx)
+{
+ struct skl_sst *skl = ctx->thread_context;
+ struct skl_d0i3_data *d0i3 = &skl->d0i3;
+
+ /* Schedule D0i3 only if the usecase ref counts are appropriate */
+ if (bxt_d0i3_target_state(ctx) != SKL_DSP_D0I3_NONE) {
+
+ dev_dbg(ctx->dev, "%s: Schedule D0i3\n", __func__);
+
+ schedule_delayed_work(&d0i3->work,
+ msecs_to_jiffies(BXT_D0I3_DELAY));
+ }
+
+ return 0;
+}
+
+static int bxt_set_dsp_D0i0(struct sst_dsp *ctx)
+{
+ int ret;
+ struct skl_ipc_d0ix_msg msg;
+ struct skl_sst *skl = ctx->thread_context;
+
+ dev_dbg(ctx->dev, "In %s:\n", __func__);
+
+ /* First Cancel any pending attempt to put DSP to D0i3 */
+ cancel_delayed_work_sync(&skl->d0i3.work);
+
+ /* If DSP is currently in D0i3, bring it to D0i0 */
+ if (skl->cores.state[SKL_DSP_CORE0_ID] != SKL_DSP_RUNNING_D0I3)
+ return 0;
+
+ dev_dbg(ctx->dev, "Set DSP to D0i0\n");
+
+ msg.instance_id = 0;
+ msg.module_id = 0;
+ msg.streaming = 0;
+ msg.wake = 0;
+
+ if (skl->d0i3.state == SKL_DSP_D0I3_STREAMING)
+ msg.streaming = 1;
+
+ /* Clear Vendor specific register D0I3C.I3 to disable D0i3*/
+ if (skl->update_d0i3c)
+ skl->update_d0i3c(skl->dev, false);
+
+ ret = skl_ipc_set_d0ix(&skl->ipc, &msg);
+ if (ret < 0) {
+ dev_err(ctx->dev, "Failed to set DSP to D0i0\n");
+ return ret;
+ }
+
+ skl->cores.state[SKL_DSP_CORE0_ID] = SKL_DSP_RUNNING;
+ skl->d0i3.state = SKL_DSP_D0I3_NONE;
+
+ return 0;
+}
+
static int bxt_set_dsp_D0(struct sst_dsp *ctx, unsigned int core_id)
{
struct skl_sst *skl = ctx->thread_context;
@@ -414,6 +552,8 @@ static int bxt_set_dsp_D3(struct sst_dsp *ctx, unsigned int core_id)
static struct skl_dsp_fw_ops bxt_fw_ops = {
.set_state_D0 = bxt_set_dsp_D0,
.set_state_D3 = bxt_set_dsp_D3,
+ .set_state_D0i3 = bxt_schedule_dsp_D0i3,
+ .set_state_D0i0 = bxt_set_dsp_D0i0,
.load_fw = bxt_load_base_firmware,
.get_fw_errcode = bxt_get_errorcode,
.load_library = bxt_load_library,
@@ -470,10 +610,15 @@ int bxt_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq,
if (ret)
return ret;
+ /* set the D0i3 check */
+ skl->ipc.ops.check_dsp_lp_on = skl_ipc_check_D0i0;
+
skl->cores.count = 2;
skl->boot_complete = false;
init_waitqueue_head(&skl->boot_wait);
skl->is_first_boot = true;
+ INIT_DELAYED_WORK(&skl->d0i3.work, bxt_set_dsp_D0i3);
+ skl->d0i3.state = SKL_DSP_D0I3_NONE;
if (dsp)
*dsp = skl;
diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c
index 805b7f2173f3..e79cbcf6e462 100644
--- a/sound/soc/intel/skylake/skl-messages.c
+++ b/sound/soc/intel/skylake/skl-messages.c
@@ -294,6 +294,33 @@ int skl_free_dsp(struct skl *skl)
return 0;
}
+/*
+ * In the case of "suspend_active" i.e, the Audio IP being active
+ * during system suspend, immediately excecute any pending D0i3 work
+ * before suspending. This is needed for the IP to work in low power
+ * mode during system suspend. In the case of normal suspend, cancel
+ * any pending D0i3 work.
+ */
+int skl_suspend_late_dsp(struct skl *skl)
+{
+ struct skl_sst *ctx = skl->skl_sst;
+ struct delayed_work *dwork;
+
+ if (!ctx)
+ return 0;
+
+ dwork = &ctx->d0i3.work;
+
+ if (dwork->work.func) {
+ if (skl->supend_active)
+ flush_delayed_work(dwork);
+ else
+ cancel_delayed_work_sync(dwork);
+ }
+
+ return 0;
+}
+
int skl_suspend_dsp(struct skl *skl)
{
struct skl_sst *ctx = skl->skl_sst;
@@ -500,16 +527,14 @@ static void skl_setup_cpr_gateway_cfg(struct skl_sst *ctx,
int skl_dsp_set_dma_control(struct skl_sst *ctx, struct skl_module_cfg *mconfig)
{
struct skl_dma_control *dma_ctrl;
- struct skl_i2s_config_blob config_blob;
struct skl_ipc_large_config_msg msg = {0};
int err = 0;
/*
- * if blob size is same as capablity size, then no dma control
- * present so return
+ * if blob size zero, then return
*/
- if (mconfig->formats_config.caps_size == sizeof(config_blob))
+ if (mconfig->formats_config.caps_size == 0)
return 0;
msg.large_param_id = DMA_CONTROL_ID;
@@ -523,7 +548,7 @@ int skl_dsp_set_dma_control(struct skl_sst *ctx, struct skl_module_cfg *mconfig)
dma_ctrl->node_id = skl_get_node_id(ctx, mconfig);
/* size in dwords */
- dma_ctrl->config_length = sizeof(config_blob) / 4;
+ dma_ctrl->config_length = mconfig->formats_config.caps_size / 4;
memcpy(dma_ctrl->config_data, mconfig->formats_config.caps,
mconfig->formats_config.caps_size);
@@ -531,7 +556,6 @@ int skl_dsp_set_dma_control(struct skl_sst *ctx, struct skl_module_cfg *mconfig)
err = skl_ipc_set_large_config(&ctx->ipc, &msg, (u32 *)dma_ctrl);
kfree(dma_ctrl);
-
return err;
}
@@ -1042,7 +1066,8 @@ int skl_create_pipeline(struct skl_sst *ctx, struct skl_pipe *pipe)
dev_dbg(ctx->dev, "%s: pipe_id = %d\n", __func__, pipe->ppl_id);
ret = skl_ipc_create_pipeline(&ctx->ipc, pipe->memory_pages,
- pipe->pipe_priority, pipe->ppl_id);
+ pipe->pipe_priority, pipe->ppl_id,
+ pipe->lp_mode);
if (ret < 0) {
dev_err(ctx->dev, "Failed to create pipeline\n");
return ret;
diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c
index 58c728662600..84b5101e6ca6 100644
--- a/sound/soc/intel/skylake/skl-pcm.c
+++ b/sound/soc/intel/skylake/skl-pcm.c
@@ -144,6 +144,8 @@ static int skl_pcm_open(struct snd_pcm_substream *substream,
struct hdac_ext_stream *stream;
struct snd_pcm_runtime *runtime = substream->runtime;
struct skl_dma_params *dma_params;
+ struct skl *skl = get_skl_ctx(dai->dev);
+ struct skl_module_cfg *mconfig;
dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name);
@@ -177,6 +179,9 @@ static int skl_pcm_open(struct snd_pcm_substream *substream,
skl_set_suspend_active(substream, dai, true);
snd_pcm_set_sync(substream);
+ mconfig = skl_tplg_fe_get_cpr_module(dai, substream->stream);
+ skl_tplg_d0i3_get(skl, mconfig->d0i3_caps);
+
return 0;
}
@@ -302,6 +307,7 @@ static void skl_pcm_close(struct snd_pcm_substream *substream,
struct hdac_ext_bus *ebus = dev_get_drvdata(dai->dev);
struct skl_dma_params *dma_params = NULL;
struct skl *skl = ebus_to_skl(ebus);
+ struct skl_module_cfg *mconfig;
dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name);
@@ -325,6 +331,9 @@ static void skl_pcm_close(struct snd_pcm_substream *substream,
skl->skl_sst->miscbdcg_disabled = false;
}
+ mconfig = skl_tplg_fe_get_cpr_module(dai, substream->stream);
+ skl_tplg_d0i3_put(skl, mconfig->d0i3_caps);
+
kfree(dma_params);
}
@@ -1031,10 +1040,24 @@ static snd_pcm_uframes_t skl_platform_pcm_pointer
(struct snd_pcm_substream *substream)
{
struct hdac_ext_stream *hstream = get_hdac_ext_stream(substream);
+ struct hdac_ext_bus *ebus = get_bus_ctx(substream);
unsigned int pos;
- /* use the position buffer as default */
- pos = snd_hdac_stream_get_pos_posbuf(hdac_stream(hstream));
+ /*
+ * Use DPIB for Playback stream as the periodic DMA Position-in-
+ * Buffer Writes may be scheduled at the same time or later than
+ * the MSI and does not guarantee to reflect the Position of the
+ * last buffer that was transferred. Whereas DPIB register in
+ * HAD space reflects the actual data that is transferred.
+ * Use the position buffer for capture, as DPIB write gets
+ * completed earlier than the actual data written to the DDR.
+ */
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ pos = readl(ebus->bus.remap_addr + AZX_REG_VS_SDXDPIB_XBASE +
+ (AZX_REG_VS_SDXDPIB_XINTERVAL *
+ hdac_stream(hstream)->index));
+ else
+ pos = snd_hdac_stream_get_pos_posbuf(hdac_stream(hstream));
if (pos >= hdac_stream(hstream)->bufsize)
pos = 0;
@@ -1197,6 +1220,7 @@ static int skl_platform_soc_probe(struct snd_soc_platform *platform)
return ret;
}
skl_populate_modules(skl);
+ skl->skl_sst->update_d0i3c = skl_update_d0i3c;
}
pm_runtime_mark_last_busy(platform->dev);
pm_runtime_put_autosuspend(platform->dev);
diff --git a/sound/soc/intel/skylake/skl-sst-cldma.c b/sound/soc/intel/skylake/skl-sst-cldma.c
index efa2532114ba..c9f6d87381db 100644
--- a/sound/soc/intel/skylake/skl-sst-cldma.c
+++ b/sound/soc/intel/skylake/skl-sst-cldma.c
@@ -17,7 +17,6 @@
#include <linux/device.h>
#include <linux/mm.h>
-#include <linux/kthread.h>
#include <linux/delay.h>
#include "../common/sst-dsp.h"
#include "../common/sst-dsp-priv.h"
diff --git a/sound/soc/intel/skylake/skl-sst-dsp.h b/sound/soc/intel/skylake/skl-sst-dsp.h
index b9e71d051fb1..7c272ba0f4b5 100644
--- a/sound/soc/intel/skylake/skl-sst-dsp.h
+++ b/sound/soc/intel/skylake/skl-sst-dsp.h
@@ -126,11 +126,21 @@ struct sst_dsp_device;
#define SKL_ADSPCS_CPA_SHIFT 24
#define SKL_ADSPCS_CPA_MASK(cm) ((cm) << SKL_ADSPCS_CPA_SHIFT)
+/* DSP Core state */
enum skl_dsp_states {
SKL_DSP_RUNNING = 1,
+ /* Running in D0i3 state; can be in streaming or non-streaming D0i3 */
+ SKL_DSP_RUNNING_D0I3, /* Running in D0i3 state*/
SKL_DSP_RESET,
};
+/* D0i3 substates */
+enum skl_dsp_d0i3_states {
+ SKL_DSP_D0I3_NONE = -1, /* No D0i3 */
+ SKL_DSP_D0I3_NON_STREAMING = 0,
+ SKL_DSP_D0I3_STREAMING = 1,
+};
+
struct skl_dsp_fw_ops {
int (*load_fw)(struct sst_dsp *ctx);
/* FW module parser/loader */
@@ -139,6 +149,8 @@ struct skl_dsp_fw_ops {
int (*parse_fw)(struct sst_dsp *ctx);
int (*set_state_D0)(struct sst_dsp *ctx, unsigned int core_id);
int (*set_state_D3)(struct sst_dsp *ctx, unsigned int core_id);
+ int (*set_state_D0i3)(struct sst_dsp *ctx);
+ int (*set_state_D0i0)(struct sst_dsp *ctx);
unsigned int (*get_fw_errcode)(struct sst_dsp *ctx);
int (*load_mod)(struct sst_dsp *ctx, u16 mod_id, u8 *mod_name);
int (*unload_mod)(struct sst_dsp *ctx, u16 mod_id);
diff --git a/sound/soc/intel/skylake/skl-sst-ipc.c b/sound/soc/intel/skylake/skl-sst-ipc.c
index 797cf4053235..e1391dfbc9e9 100644
--- a/sound/soc/intel/skylake/skl-sst-ipc.c
+++ b/sound/soc/intel/skylake/skl-sst-ipc.c
@@ -81,6 +81,11 @@
#define IPC_INSTANCE_ID(x) (((x) & IPC_INSTANCE_ID_MASK) \
<< IPC_INSTANCE_ID_SHIFT)
+#define IPC_PPL_LP_MODE_SHIFT 0
+#define IPC_PPL_LP_MODE_MASK 0x1
+#define IPC_PPL_LP_MODE(x) (((x) & IPC_PPL_LP_MODE_MASK) \
+ << IPC_PPL_LP_MODE_SHIFT)
+
/* Set pipeline state message */
#define IPC_PPL_STATE_SHIFT 0
#define IPC_PPL_STATE_MASK 0x1F
@@ -172,6 +177,17 @@
<< IPC_INITIAL_BLOCK_SHIFT)
#define IPC_INITIAL_BLOCK_CLEAR ~(IPC_INITIAL_BLOCK_MASK \
<< IPC_INITIAL_BLOCK_SHIFT)
+/* Set D0ix IPC extension register */
+#define IPC_D0IX_WAKE_SHIFT 0
+#define IPC_D0IX_WAKE_MASK 0x1
+#define IPC_D0IX_WAKE(x) (((x) & IPC_D0IX_WAKE_MASK) \
+ << IPC_D0IX_WAKE_SHIFT)
+
+#define IPC_D0IX_STREAMING_SHIFT 1
+#define IPC_D0IX_STREAMING_MASK 0x1
+#define IPC_D0IX_STREAMING(x) (((x) & IPC_D0IX_STREAMING_MASK) \
+ << IPC_D0IX_STREAMING_SHIFT)
+
enum skl_ipc_msg_target {
IPC_FW_GEN_MSG = 0,
@@ -258,7 +274,8 @@ enum skl_ipc_module_msg {
IPC_MOD_LARGE_CONFIG_SET = 4,
IPC_MOD_BIND = 5,
IPC_MOD_UNBIND = 6,
- IPC_MOD_SET_DX = 7
+ IPC_MOD_SET_DX = 7,
+ IPC_MOD_SET_D0IX = 8
};
static void skl_ipc_tx_data_copy(struct ipc_message *msg, char *tx_data,
@@ -289,6 +306,23 @@ static void skl_ipc_tx_msg(struct sst_generic_ipc *ipc, struct ipc_message *msg)
header->primary | SKL_ADSP_REG_HIPCI_BUSY);
}
+int skl_ipc_check_D0i0(struct sst_dsp *dsp, bool state)
+{
+ int ret;
+
+ /* check D0i3 support */
+ if (!dsp->fw_ops.set_state_D0i0)
+ return 0;
+
+ /* Attempt D0i0 or D0i3 based on state */
+ if (state)
+ ret = dsp->fw_ops.set_state_D0i0(dsp);
+ else
+ ret = dsp->fw_ops.set_state_D0i3(dsp);
+
+ return ret;
+}
+
static struct ipc_message *skl_ipc_reply_get_msg(struct sst_generic_ipc *ipc,
u64 ipc_header)
{
@@ -464,7 +498,7 @@ irqreturn_t skl_dsp_irq_thread_handler(int irq, void *context)
skl_ipc_int_enable(dsp);
/* continue to send any remaining messages... */
- kthread_queue_work(&ipc->kworker, &ipc->kwork);
+ schedule_work(&ipc->kwork);
return IRQ_HANDLED;
}
@@ -547,7 +581,7 @@ void skl_ipc_free(struct sst_generic_ipc *ipc)
}
int skl_ipc_create_pipeline(struct sst_generic_ipc *ipc,
- u16 ppl_mem_size, u8 ppl_type, u8 instance_id)
+ u16 ppl_mem_size, u8 ppl_type, u8 instance_id, u8 lp_mode)
{
struct skl_ipc_header header = {0};
u64 *ipc_header = (u64 *)(&header);
@@ -560,6 +594,8 @@ int skl_ipc_create_pipeline(struct sst_generic_ipc *ipc,
header.primary |= IPC_PPL_TYPE(ppl_type);
header.primary |= IPC_PPL_MEM_SIZE(ppl_mem_size);
+ header.extension = IPC_PPL_LP_MODE(lp_mode);
+
dev_dbg(ipc->dev, "In %s header=%d\n", __func__, header.primary);
ret = sst_ipc_tx_message_wait(ipc, *ipc_header, NULL, 0, NULL, 0);
if (ret < 0) {
@@ -931,3 +967,32 @@ int skl_sst_ipc_load_library(struct sst_generic_ipc *ipc,
return ret;
}
EXPORT_SYMBOL_GPL(skl_sst_ipc_load_library);
+
+int skl_ipc_set_d0ix(struct sst_generic_ipc *ipc, struct skl_ipc_d0ix_msg *msg)
+{
+ struct skl_ipc_header header = {0};
+ u64 *ipc_header = (u64 *)(&header);
+ int ret;
+
+ header.primary = IPC_MSG_TARGET(IPC_MOD_MSG);
+ header.primary |= IPC_MSG_DIR(IPC_MSG_REQUEST);
+ header.primary |= IPC_GLB_TYPE(IPC_MOD_SET_D0IX);
+ header.primary |= IPC_MOD_INSTANCE_ID(msg->instance_id);
+ header.primary |= IPC_MOD_ID(msg->module_id);
+
+ header.extension = IPC_D0IX_WAKE(msg->wake);
+ header.extension |= IPC_D0IX_STREAMING(msg->streaming);
+
+ dev_dbg(ipc->dev, "In %s primary=%x ext=%x\n", __func__,
+ header.primary, header.extension);
+
+ /*
+ * Use the nopm IPC here as we dont want it checking for D0iX
+ */
+ ret = sst_ipc_tx_message_nopm(ipc, *ipc_header, NULL, 0, NULL, 0);
+ if (ret < 0)
+ dev_err(ipc->dev, "ipc: set d0ix failed, err %d\n", ret);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(skl_ipc_set_d0ix);
diff --git a/sound/soc/intel/skylake/skl-sst-ipc.h b/sound/soc/intel/skylake/skl-sst-ipc.h
index 0334ed4af031..cc40341233fa 100644
--- a/sound/soc/intel/skylake/skl-sst-ipc.h
+++ b/sound/soc/intel/skylake/skl-sst-ipc.h
@@ -16,7 +16,6 @@
#ifndef __SKL_IPC_H
#define __SKL_IPC_H
-#include <linux/kthread.h>
#include <linux/irqreturn.h>
#include "../common/sst-ipc.h"
@@ -53,6 +52,23 @@ struct skl_dsp_cores {
int usage_count[SKL_DSP_CORES_MAX];
};
+/**
+ * skl_d0i3_data: skl D0i3 counters data struct
+ *
+ * @streaming: Count of usecases that can attempt streaming D0i3
+ * @non_streaming: Count of usecases that can attempt non-streaming D0i3
+ * @non_d0i3: Count of usecases that cannot attempt D0i3
+ * @state: current state
+ * @work: D0i3 worker thread
+ */
+struct skl_d0i3_data {
+ int streaming;
+ int non_streaming;
+ int non_d0i3;
+ enum skl_dsp_d0i3_states state;
+ struct delayed_work work;
+};
+
struct skl_sst {
struct device *dev;
struct sst_dsp *dsp;
@@ -83,6 +99,11 @@ struct skl_sst {
/* tplg manifest */
struct skl_dfw_manifest manifest;
+
+ /* Callback to update D0i3C register */
+ void (*update_d0i3c)(struct device *dev, bool enable);
+
+ struct skl_d0i3_data d0i3;
};
struct skl_ipc_init_instance_msg {
@@ -111,6 +132,13 @@ struct skl_ipc_large_config_msg {
u32 param_data_size;
};
+struct skl_ipc_d0ix_msg {
+ u32 module_id;
+ u32 instance_id;
+ u8 streaming;
+ u8 wake;
+};
+
#define SKL_IPC_BOOT_MSECS 3000
#define SKL_IPC_D3_MASK 0
@@ -119,7 +147,7 @@ struct skl_ipc_large_config_msg {
irqreturn_t skl_dsp_irq_thread_handler(int irq, void *context);
int skl_ipc_create_pipeline(struct sst_generic_ipc *sst_ipc,
- u16 ppl_mem_size, u8 ppl_type, u8 instance_id);
+ u16 ppl_mem_size, u8 ppl_type, u8 instance_id, u8 lp_mode);
int skl_ipc_delete_pipeline(struct sst_generic_ipc *sst_ipc, u8 instance_id);
@@ -155,6 +183,11 @@ int skl_ipc_get_large_config(struct sst_generic_ipc *ipc,
int skl_sst_ipc_load_library(struct sst_generic_ipc *ipc,
u8 dma_id, u8 table_id);
+int skl_ipc_set_d0ix(struct sst_generic_ipc *ipc,
+ struct skl_ipc_d0ix_msg *msg);
+
+int skl_ipc_check_D0i0(struct sst_dsp *dsp, bool state);
+
void skl_ipc_int_enable(struct sst_dsp *dsp);
void skl_ipc_op_int_enable(struct sst_dsp *ctx);
void skl_ipc_op_int_disable(struct sst_dsp *ctx);
diff --git a/sound/soc/intel/skylake/skl-sst-utils.c b/sound/soc/intel/skylake/skl-sst-utils.c
index 8dc03039b311..ea162fbf68e5 100644
--- a/sound/soc/intel/skylake/skl-sst-utils.c
+++ b/sound/soc/intel/skylake/skl-sst-utils.c
@@ -179,7 +179,7 @@ static inline int skl_getid_32(struct uuid_module *module, u64 *val,
index = ffz(mask_val);
pvt_id = index + word1_mask + word2_mask;
if (pvt_id <= (max_inst - 1)) {
- *val |= 1 << (index + word1_mask);
+ *val |= 1ULL << (index + word1_mask);
return pvt_id;
}
}
diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c
index b5b1934d8550..bd313c907b20 100644
--- a/sound/soc/intel/skylake/skl-topology.c
+++ b/sound/soc/intel/skylake/skl-topology.c
@@ -36,6 +36,44 @@
#define SKL_IN_DIR_BIT_MASK BIT(0)
#define SKL_PIN_COUNT_MASK GENMASK(7, 4)
+void skl_tplg_d0i3_get(struct skl *skl, enum d0i3_capability caps)
+{
+ struct skl_d0i3_data *d0i3 = &skl->skl_sst->d0i3;
+
+ switch (caps) {
+ case SKL_D0I3_NONE:
+ d0i3->non_d0i3++;
+ break;
+
+ case SKL_D0I3_STREAMING:
+ d0i3->streaming++;
+ break;
+
+ case SKL_D0I3_NON_STREAMING:
+ d0i3->non_streaming++;
+ break;
+ }
+}
+
+void skl_tplg_d0i3_put(struct skl *skl, enum d0i3_capability caps)
+{
+ struct skl_d0i3_data *d0i3 = &skl->skl_sst->d0i3;
+
+ switch (caps) {
+ case SKL_D0I3_NONE:
+ d0i3->non_d0i3--;
+ break;
+
+ case SKL_D0I3_STREAMING:
+ d0i3->streaming--;
+ break;
+
+ case SKL_D0I3_NON_STREAMING:
+ d0i3->non_streaming--;
+ break;
+ }
+}
+
/*
* SKL DSP driver modelling uses only few DAPM widgets so for rest we will
* ignore. This helpers checks if the SKL driver handles this widget type
@@ -1519,6 +1557,10 @@ static int skl_tplg_fill_pipe_tkn(struct device *dev,
pipe->memory_pages = tkn_val;
break;
+ case SKL_TKN_U32_PMODE:
+ pipe->lp_mode = tkn_val;
+ break;
+
default:
dev_err(dev, "Token not handled %d\n", tkn);
return -EINVAL;
@@ -1826,6 +1868,10 @@ static int skl_tplg_get_token(struct device *dev,
mconfig->converter = tkn_elem->value;
break;
+ case SKL_TKL_U32_D0I3_CAPS:
+ mconfig->d0i3_caps = tkn_elem->value;
+ break;
+
case SKL_TKN_U32_PIPE_ID:
ret = skl_tplg_add_pipe(dev,
mconfig, skl, tkn_elem);
@@ -1841,6 +1887,7 @@ static int skl_tplg_get_token(struct device *dev,
case SKL_TKN_U32_PIPE_CONN_TYPE:
case SKL_TKN_U32_PIPE_PRIORITY:
case SKL_TKN_U32_PIPE_MEM_PGS:
+ case SKL_TKN_U32_PMODE:
if (is_pipe_exists) {
ret = skl_tplg_fill_pipe_tkn(dev, mconfig->pipe,
tkn_elem->token, tkn_elem->value);
diff --git a/sound/soc/intel/skylake/skl-topology.h b/sound/soc/intel/skylake/skl-topology.h
index a519360f42a6..08d39280b07b 100644
--- a/sound/soc/intel/skylake/skl-topology.h
+++ b/sound/soc/intel/skylake/skl-topology.h
@@ -113,23 +113,6 @@ struct skl_cpr_gtw_cfg {
u32 config_data[1];
} __packed;
-struct skl_i2s_config_blob {
- u32 gateway_attrib;
- u32 tdm_ts_group[8];
- u32 ssc0;
- u32 ssc1;
- u32 sscto;
- u32 sspsp;
- u32 sstsa;
- u32 ssrsa;
- u32 ssc2;
- u32 sspsp2;
- u32 ssc3;
- u32 ssioc;
- u32 mdivc;
- u32 mdivr;
-} __packed;
-
struct skl_dma_control {
u32 node_id;
u32 config_length;
@@ -279,6 +262,7 @@ struct skl_pipe {
u8 pipe_priority;
u16 conn_type;
u32 memory_pages;
+ u8 lp_mode;
struct skl_pipe_params *p_params;
enum skl_pipe_state state;
struct list_head w_list;
@@ -293,6 +277,12 @@ enum skl_module_state {
SKL_MODULE_UNLOADED = 4,
};
+enum d0i3_capability {
+ SKL_D0I3_NONE = 0,
+ SKL_D0I3_STREAMING = 1,
+ SKL_D0I3_NON_STREAMING = 2,
+};
+
struct skl_module_cfg {
u8 guid[16];
struct skl_module_inst_id id;
@@ -319,6 +309,7 @@ struct skl_module_cfg {
u32 converter;
u32 vbus_id;
u32 mem_pages;
+ enum d0i3_capability d0i3_caps;
struct skl_module_pin *m_in_pin;
struct skl_module_pin *m_out_pin;
enum skl_module_type m_type;
@@ -361,6 +352,9 @@ struct skl_module_cfg *skl_tplg_fe_get_cpr_module(
int skl_tplg_update_pipe_params(struct device *dev,
struct skl_module_cfg *mconfig, struct skl_pipe_params *params);
+void skl_tplg_d0i3_get(struct skl *skl, enum d0i3_capability caps);
+void skl_tplg_d0i3_put(struct skl *skl, enum d0i3_capability caps);
+
int skl_create_pipeline(struct skl_sst *ctx, struct skl_pipe *pipe);
int skl_run_pipe(struct skl_sst *ctx, struct skl_pipe *pipe);
diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c
index 2989c164dafe..da5db5098274 100644
--- a/sound/soc/intel/skylake/skl.c
+++ b/sound/soc/intel/skylake/skl.c
@@ -26,6 +26,7 @@
#include <linux/pm_runtime.h>
#include <linux/platform_device.h>
#include <linux/firmware.h>
+#include <linux/delay.h>
#include <sound/pcm.h>
#include "../common/sst-acpi.h"
#include <sound/hda_register.h>
@@ -109,6 +110,52 @@ static int skl_init_chip(struct hdac_bus *bus, bool full_reset)
return ret;
}
+void skl_update_d0i3c(struct device *dev, bool enable)
+{
+ struct pci_dev *pci = to_pci_dev(dev);
+ struct hdac_ext_bus *ebus = pci_get_drvdata(pci);
+ struct hdac_bus *bus = ebus_to_hbus(ebus);
+ u8 reg;
+ int timeout = 50;
+
+ reg = snd_hdac_chip_readb(bus, VS_D0I3C);
+ /* Do not write to D0I3C until command in progress bit is cleared */
+ while ((reg & AZX_REG_VS_D0I3C_CIP) && --timeout) {
+ udelay(10);
+ reg = snd_hdac_chip_readb(bus, VS_D0I3C);
+ }
+
+ /* Highly unlikely. But if it happens, flag error explicitly */
+ if (!timeout) {
+ dev_err(bus->dev, "Before D0I3C update: D0I3C CIP timeout\n");
+ return;
+ }
+
+ if (enable)
+ reg = reg | AZX_REG_VS_D0I3C_I3;
+ else
+ reg = reg & (~AZX_REG_VS_D0I3C_I3);
+
+ snd_hdac_chip_writeb(bus, VS_D0I3C, reg);
+
+ timeout = 50;
+ /* Wait for cmd in progress to be cleared before exiting the function */
+ reg = snd_hdac_chip_readb(bus, VS_D0I3C);
+ while ((reg & AZX_REG_VS_D0I3C_CIP) && --timeout) {
+ udelay(10);
+ reg = snd_hdac_chip_readb(bus, VS_D0I3C);
+ }
+
+ /* Highly unlikely. But if it happens, flag error explicitly */
+ if (!timeout) {
+ dev_err(bus->dev, "After D0I3C update: D0I3C CIP timeout\n");
+ return;
+ }
+
+ dev_dbg(bus->dev, "D0I3C register = 0x%x\n",
+ snd_hdac_chip_readb(bus, VS_D0I3C));
+}
+
/* called from IRQ */
static void skl_stream_update(struct hdac_bus *bus, struct hdac_stream *hstr)
{
@@ -181,6 +228,15 @@ static int skl_acquire_irq(struct hdac_ext_bus *ebus, int do_disconnect)
return 0;
}
+static int skl_suspend_late(struct device *dev)
+{
+ struct pci_dev *pci = to_pci_dev(dev);
+ struct hdac_ext_bus *ebus = pci_get_drvdata(pci);
+ struct skl *skl = ebus_to_skl(ebus);
+
+ return skl_suspend_late_dsp(skl);
+}
+
#ifdef CONFIG_PM
static int _skl_suspend(struct hdac_ext_bus *ebus)
{
@@ -243,7 +299,6 @@ static int skl_suspend(struct device *dev)
enable_irq_wake(bus->irq);
pci_save_state(pci);
- pci_disable_device(pci);
} else {
ret = _skl_suspend(ebus);
if (ret < 0)
@@ -286,7 +341,6 @@ static int skl_resume(struct device *dev)
*/
if (skl->supend_active) {
pci_restore_state(pci);
- ret = pci_enable_device(pci);
snd_hdac_ext_bus_link_power_up_all(ebus);
disable_irq_wake(bus->irq);
/*
@@ -345,6 +399,7 @@ static int skl_runtime_resume(struct device *dev)
static const struct dev_pm_ops skl_pm = {
SET_SYSTEM_SLEEP_PM_OPS(skl_suspend, skl_resume)
SET_RUNTIME_PM_OPS(skl_runtime_suspend, skl_runtime_resume, NULL)
+ .suspend_late = skl_suspend_late,
};
/*
@@ -674,7 +729,7 @@ static int skl_probe(struct pci_dev *pci,
if (skl->nhlt == NULL) {
err = -ENODEV;
- goto out_free;
+ goto out_display_power_off;
}
skl_nhlt_update_topology_bin(skl);
@@ -746,6 +801,9 @@ out_mach_free:
skl_machine_device_unregister(skl);
out_nhlt_free:
skl_nhlt_free(skl->nhlt);
+out_display_power_off:
+ if (IS_ENABLED(CONFIG_SND_SOC_HDAC_HDMI))
+ snd_hdac_display_power(bus, false);
out_free:
skl->init_failed = 1;
skl_free(ebus);
@@ -785,8 +843,7 @@ static void skl_remove(struct pci_dev *pci)
release_firmware(skl->tplg);
- if (pci_dev_run_wake(pci))
- pm_runtime_get_noresume(&pci->dev);
+ pm_runtime_get_noresume(&pci->dev);
/* codec removal, invoke bus_device_remove */
snd_hdac_ext_bus_device_remove(ebus);
diff --git a/sound/soc/intel/skylake/skl.h b/sound/soc/intel/skylake/skl.h
index 5d4fbb094c48..4986e3929dd3 100644
--- a/sound/soc/intel/skylake/skl.h
+++ b/sound/soc/intel/skylake/skl.h
@@ -52,6 +52,9 @@
#define AZX_PGCTL_LSRMD_MASK (1 << 4)
#define AZX_PCIREG_CGCTL 0x48
#define AZX_CGCTL_MISCBDCGE_MASK (1 << 6)
+/* D0I3C Register fields */
+#define AZX_REG_VS_D0I3C_CIP 0x1 /* Command in progress */
+#define AZX_REG_VS_D0I3C_I3 0x4 /* D0i3 enable */
struct skl_dsp_resource {
u32 max_mcps;
@@ -121,8 +124,11 @@ int skl_get_dmic_geo(struct skl *skl);
int skl_nhlt_update_topology_bin(struct skl *skl);
int skl_init_dsp(struct skl *skl);
int skl_free_dsp(struct skl *skl);
+int skl_suspend_late_dsp(struct skl *skl);
int skl_suspend_dsp(struct skl *skl);
int skl_resume_dsp(struct skl *skl);
void skl_cleanup_resources(struct skl *skl);
const struct skl_dsp_ops *skl_get_dsp_ops(int pci_id);
+void skl_update_d0i3c(struct device *dev, bool enable);
+
#endif /* __SOUND_SOC_SKL_H */
diff --git a/sound/soc/pxa/Kconfig b/sound/soc/pxa/Kconfig
index f2bf8661dd21..823b5a236d8d 100644
--- a/sound/soc/pxa/Kconfig
+++ b/sound/soc/pxa/Kconfig
@@ -208,7 +208,7 @@ config SND_PXA2XX_SOC_IMOTE2
config SND_MMP_SOC_BROWNSTONE
tristate "SoC Audio support for Marvell Brownstone"
- depends on SND_MMP_SOC && MACH_BROWNSTONE
+ depends on SND_MMP_SOC && MACH_BROWNSTONE && I2C
select SND_MMP_SOC_SSPA
select MFD_WM8994
select SND_SOC_WM8994
diff --git a/sound/soc/qcom/lpass-cpu.c b/sound/soc/qcom/lpass-cpu.c
index 3cde9fb977fa..eff3f9a8b685 100644
--- a/sound/soc/qcom/lpass-cpu.c
+++ b/sound/soc/qcom/lpass-cpu.c
@@ -586,3 +586,6 @@ int asoc_qcom_lpass_cpu_platform_remove(struct platform_device *pdev)
return 0;
}
EXPORT_SYMBOL_GPL(asoc_qcom_lpass_cpu_platform_remove);
+
+MODULE_DESCRIPTION("QTi LPASS CPU Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/qcom/lpass-platform.c b/sound/soc/qcom/lpass-platform.c
index e2ff538a8aa5..420d200f9a05 100644
--- a/sound/soc/qcom/lpass-platform.c
+++ b/sound/soc/qcom/lpass-platform.c
@@ -61,7 +61,44 @@ static int lpass_platform_pcmops_open(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
- int ret;
+ struct snd_soc_dai *cpu_dai = soc_runtime->cpu_dai;
+ struct lpass_data *drvdata =
+ snd_soc_platform_get_drvdata(soc_runtime->platform);
+ struct lpass_variant *v = drvdata->variant;
+ int ret, dma_ch, dir = substream->stream;
+ struct lpass_pcm_data *data;
+
+ data = devm_kzalloc(soc_runtime->dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->i2s_port = cpu_dai->driver->id;
+ runtime->private_data = data;
+
+ dma_ch = 0;
+ if (v->alloc_dma_channel)
+ dma_ch = v->alloc_dma_channel(drvdata, dir);
+ else
+ dma_ch = 0;
+
+ if (dma_ch < 0)
+ return dma_ch;
+
+ drvdata->substream[dma_ch] = substream;
+
+ ret = regmap_write(drvdata->lpaif_map,
+ LPAIF_DMACTL_REG(v, dma_ch, dir), 0);
+ if (ret) {
+ dev_err(soc_runtime->dev,
+ "%s() error writing to rdmactl reg: %d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ if (dir == SNDRV_PCM_STREAM_PLAYBACK)
+ data->rdma_ch = dma_ch;
+ else
+ data->wrdma_ch = dma_ch;
snd_soc_set_runtime_hwparams(substream, &lpass_platform_pcm_hardware);
@@ -80,13 +117,40 @@ static int lpass_platform_pcmops_open(struct snd_pcm_substream *substream)
return 0;
}
+static int lpass_platform_pcmops_close(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
+ struct lpass_data *drvdata =
+ snd_soc_platform_get_drvdata(soc_runtime->platform);
+ struct lpass_variant *v = drvdata->variant;
+ struct lpass_pcm_data *data;
+ int dma_ch, dir = substream->stream;
+
+ data = runtime->private_data;
+ v = drvdata->variant;
+
+ if (dir == SNDRV_PCM_STREAM_PLAYBACK)
+ dma_ch = data->rdma_ch;
+ else
+ dma_ch = data->wrdma_ch;
+
+ drvdata->substream[dma_ch] = NULL;
+
+ if (v->free_dma_channel)
+ v->free_dma_channel(drvdata, dma_ch);
+
+ return 0;
+}
+
static int lpass_platform_pcmops_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
struct lpass_data *drvdata =
snd_soc_platform_get_drvdata(soc_runtime->platform);
- struct lpass_pcm_data *pcm_data = drvdata->private_data;
+ struct snd_pcm_runtime *rt = substream->runtime;
+ struct lpass_pcm_data *pcm_data = rt->private_data;
struct lpass_variant *v = drvdata->variant;
snd_pcm_format_t format = params_format(params);
unsigned int channels = params_channels(params);
@@ -179,7 +243,8 @@ static int lpass_platform_pcmops_hw_free(struct snd_pcm_substream *substream)
struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
struct lpass_data *drvdata =
snd_soc_platform_get_drvdata(soc_runtime->platform);
- struct lpass_pcm_data *pcm_data = drvdata->private_data;
+ struct snd_pcm_runtime *rt = substream->runtime;
+ struct lpass_pcm_data *pcm_data = rt->private_data;
struct lpass_variant *v = drvdata->variant;
unsigned int reg;
int ret;
@@ -203,7 +268,8 @@ static int lpass_platform_pcmops_prepare(struct snd_pcm_substream *substream)
struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
struct lpass_data *drvdata =
snd_soc_platform_get_drvdata(soc_runtime->platform);
- struct lpass_pcm_data *pcm_data = drvdata->private_data;
+ struct snd_pcm_runtime *rt = substream->runtime;
+ struct lpass_pcm_data *pcm_data = rt->private_data;
struct lpass_variant *v = drvdata->variant;
int ret, ch, dir = substream->stream;
@@ -257,7 +323,8 @@ static int lpass_platform_pcmops_trigger(struct snd_pcm_substream *substream,
struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
struct lpass_data *drvdata =
snd_soc_platform_get_drvdata(soc_runtime->platform);
- struct lpass_pcm_data *pcm_data = drvdata->private_data;
+ struct snd_pcm_runtime *rt = substream->runtime;
+ struct lpass_pcm_data *pcm_data = rt->private_data;
struct lpass_variant *v = drvdata->variant;
int ret, ch, dir = substream->stream;
@@ -333,7 +400,8 @@ static snd_pcm_uframes_t lpass_platform_pcmops_pointer(
struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
struct lpass_data *drvdata =
snd_soc_platform_get_drvdata(soc_runtime->platform);
- struct lpass_pcm_data *pcm_data = drvdata->private_data;
+ struct snd_pcm_runtime *rt = substream->runtime;
+ struct lpass_pcm_data *pcm_data = rt->private_data;
struct lpass_variant *v = drvdata->variant;
unsigned int base_addr, curr_addr;
int ret, ch, dir = substream->stream;
@@ -374,6 +442,7 @@ static int lpass_platform_pcmops_mmap(struct snd_pcm_substream *substream,
static const struct snd_pcm_ops lpass_platform_pcm_ops = {
.open = lpass_platform_pcmops_open,
+ .close = lpass_platform_pcmops_close,
.ioctl = snd_pcm_lib_ioctl,
.hw_params = lpass_platform_pcmops_hw_params,
.hw_free = lpass_platform_pcmops_hw_free,
@@ -470,117 +539,45 @@ static int lpass_platform_pcm_new(struct snd_soc_pcm_runtime *soc_runtime)
{
struct snd_pcm *pcm = soc_runtime->pcm;
struct snd_pcm_substream *psubstream, *csubstream;
- struct snd_soc_dai *cpu_dai = soc_runtime->cpu_dai;
- struct lpass_data *drvdata =
- snd_soc_platform_get_drvdata(soc_runtime->platform);
- struct lpass_variant *v = drvdata->variant;
int ret = -EINVAL;
- struct lpass_pcm_data *data;
size_t size = lpass_platform_pcm_hardware.buffer_bytes_max;
- data = devm_kzalloc(soc_runtime->dev, sizeof(*data), GFP_KERNEL);
- if (!data)
- return -ENOMEM;
-
- data->i2s_port = cpu_dai->driver->id;
- drvdata->private_data = data;
-
psubstream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
if (psubstream) {
- if (v->alloc_dma_channel)
- data->rdma_ch = v->alloc_dma_channel(drvdata,
- SNDRV_PCM_STREAM_PLAYBACK);
-
- if (data->rdma_ch < 0)
- return data->rdma_ch;
-
- drvdata->substream[data->rdma_ch] = psubstream;
-
ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV,
soc_runtime->platform->dev,
size, &psubstream->dma_buffer);
- if (ret)
- goto playback_alloc_err;
-
- ret = regmap_write(drvdata->lpaif_map,
- LPAIF_RDMACTL_REG(v, data->rdma_ch), 0);
if (ret) {
- dev_err(soc_runtime->dev,
- "%s() error writing to rdmactl reg: %d\n",
- __func__, ret);
- goto capture_alloc_err;
+ dev_err(soc_runtime->dev, "Cannot allocate buffer(s)\n");
+ return ret;
}
}
csubstream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream;
if (csubstream) {
- if (v->alloc_dma_channel)
- data->wrdma_ch = v->alloc_dma_channel(drvdata,
- SNDRV_PCM_STREAM_CAPTURE);
-
- if (data->wrdma_ch < 0) {
- ret = data->wrdma_ch;
- goto capture_alloc_err;
- }
-
- drvdata->substream[data->wrdma_ch] = csubstream;
-
ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV,
soc_runtime->platform->dev,
size, &csubstream->dma_buffer);
- if (ret)
- goto capture_alloc_err;
-
- ret = regmap_write(drvdata->lpaif_map,
- LPAIF_WRDMACTL_REG(v, data->wrdma_ch), 0);
if (ret) {
- dev_err(soc_runtime->dev,
- "%s() error writing to wrdmactl reg: %d\n",
- __func__, ret);
- goto capture_reg_err;
+ dev_err(soc_runtime->dev, "Cannot allocate buffer(s)\n");
+ if (psubstream)
+ snd_dma_free_pages(&psubstream->dma_buffer);
+ return ret;
}
+
}
return 0;
-
-capture_reg_err:
- if (csubstream)
- snd_dma_free_pages(&csubstream->dma_buffer);
-
-capture_alloc_err:
- if (psubstream)
- snd_dma_free_pages(&psubstream->dma_buffer);
-
- playback_alloc_err:
- dev_err(soc_runtime->dev, "Cannot allocate buffer(s)\n");
-
- return ret;
}
static void lpass_platform_pcm_free(struct snd_pcm *pcm)
{
- struct snd_soc_pcm_runtime *rt;
- struct lpass_data *drvdata;
- struct lpass_pcm_data *data;
- struct lpass_variant *v;
struct snd_pcm_substream *substream;
- int ch, i;
+ int i;
for (i = 0; i < ARRAY_SIZE(pcm->streams); i++) {
substream = pcm->streams[i].substream;
if (substream) {
- rt = substream->private_data;
- drvdata = snd_soc_platform_get_drvdata(rt->platform);
- data = drvdata->private_data;
-
- ch = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- ? data->rdma_ch
- : data->wrdma_ch;
- v = drvdata->variant;
- drvdata->substream[ch] = NULL;
- if (v->free_dma_channel)
- v->free_dma_channel(drvdata, ch);
-
snd_dma_free_pages(&substream->dma_buffer);
substream->dma_buffer.area = NULL;
substream->dma_buffer.addr = 0;
diff --git a/sound/soc/qcom/lpass.h b/sound/soc/qcom/lpass.h
index 35b3cea8207d..924971b6ded5 100644
--- a/sound/soc/qcom/lpass.h
+++ b/sound/soc/qcom/lpass.h
@@ -59,7 +59,6 @@ struct lpass_data {
struct clk *pcnoc_mport_clk;
struct clk *pcnoc_sway_clk;
- void *private_data;
};
/* Vairant data per each SOC */
diff --git a/sound/soc/samsung/ac97.c b/sound/soc/samsung/ac97.c
index 97d6700b1009..cbc0023c2bc8 100644
--- a/sound/soc/samsung/ac97.c
+++ b/sound/soc/samsung/ac97.c
@@ -383,11 +383,6 @@ static int s3c_ac97_probe(struct platform_device *pdev)
goto err4;
}
- ret = devm_snd_soc_register_component(&pdev->dev, &s3c_ac97_component,
- s3c_ac97_dai, ARRAY_SIZE(s3c_ac97_dai));
- if (ret)
- goto err5;
-
ret = samsung_asoc_dma_platform_register(&pdev->dev,
ac97_pdata->dma_filter,
NULL, NULL);
@@ -396,6 +391,11 @@ static int s3c_ac97_probe(struct platform_device *pdev)
goto err5;
}
+ ret = devm_snd_soc_register_component(&pdev->dev, &s3c_ac97_component,
+ s3c_ac97_dai, ARRAY_SIZE(s3c_ac97_dai));
+ if (ret)
+ goto err5;
+
return 0;
err5:
free_irq(irq_res->start, NULL);
diff --git a/sound/soc/samsung/dmaengine.c b/sound/soc/samsung/dmaengine.c
index 9104c98deeb7..cda656e4afc6 100644
--- a/sound/soc/samsung/dmaengine.c
+++ b/sound/soc/samsung/dmaengine.c
@@ -37,12 +37,8 @@ int samsung_asoc_dma_platform_register(struct device *dev, dma_filter_fn filter,
pcm_conf->prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config;
pcm_conf->compat_filter_fn = filter;
- if (dev->of_node) {
- pcm_conf->chan_names[SNDRV_PCM_STREAM_PLAYBACK] = tx;
- pcm_conf->chan_names[SNDRV_PCM_STREAM_CAPTURE] = rx;
- } else {
- flags |= SND_DMAENGINE_PCM_FLAG_CUSTOM_CHANNEL_NAME;
- }
+ pcm_conf->chan_names[SNDRV_PCM_STREAM_PLAYBACK] = tx;
+ pcm_conf->chan_names[SNDRV_PCM_STREAM_CAPTURE] = rx;
return devm_snd_dmaengine_pcm_register(dev, pcm_conf, flags);
}
diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c
index 7e32cf4581f8..8766ebb0dc9b 100644
--- a/sound/soc/samsung/i2s.c
+++ b/sound/soc/samsung/i2s.c
@@ -1237,14 +1237,14 @@ static int samsung_i2s_probe(struct platform_device *pdev)
dev_err(&pdev->dev, "Unable to get drvdata\n");
return -EFAULT;
}
- ret = devm_snd_soc_register_component(&sec_dai->pdev->dev,
- &samsung_i2s_component,
- &sec_dai->i2s_dai_drv, 1);
+ ret = samsung_asoc_dma_platform_register(&pdev->dev,
+ sec_dai->filter, "tx-sec", NULL);
if (ret != 0)
return ret;
- return samsung_asoc_dma_platform_register(&pdev->dev,
- sec_dai->filter, "tx-sec", NULL);
+ return devm_snd_soc_register_component(&sec_dai->pdev->dev,
+ &samsung_i2s_component,
+ &sec_dai->i2s_dai_drv, 1);
}
pri_dai = i2s_alloc_dai(pdev, false);
@@ -1304,8 +1304,6 @@ static int samsung_i2s_probe(struct platform_device *pdev)
}
pri_dai->dma_playback.addr = regs_base + I2STXD;
pri_dai->dma_capture.addr = regs_base + I2SRXD;
- pri_dai->dma_playback.chan_name = "tx";
- pri_dai->dma_capture.chan_name = "rx";
pri_dai->dma_playback.addr_width = 4;
pri_dai->dma_capture.addr_width = 4;
pri_dai->quirks = quirks;
@@ -1314,6 +1312,11 @@ static int samsung_i2s_probe(struct platform_device *pdev)
if (quirks & QUIRK_PRI_6CHAN)
pri_dai->i2s_dai_drv.playback.channels_max = 6;
+ ret = samsung_asoc_dma_platform_register(&pdev->dev, pri_dai->filter,
+ NULL, NULL);
+ if (ret < 0)
+ goto err_disable_clk;
+
if (quirks & QUIRK_SEC_DAI) {
sec_dai = i2s_alloc_dai(pdev, true);
if (!sec_dai) {
@@ -1325,7 +1328,6 @@ static int samsung_i2s_probe(struct platform_device *pdev)
sec_dai->lock = &pri_dai->spinlock;
sec_dai->variant_regs = pri_dai->variant_regs;
sec_dai->dma_playback.addr = regs_base + I2STXDS;
- sec_dai->dma_playback.chan_name = "tx-sec";
if (!np) {
sec_dai->dma_playback.filter_data = i2s_pdata->dma_play_sec;
@@ -1353,10 +1355,6 @@ static int samsung_i2s_probe(struct platform_device *pdev)
if (ret < 0)
goto err_free_dai;
- ret = samsung_asoc_dma_platform_register(&pdev->dev, pri_dai->filter,
- NULL, NULL);
- if (ret < 0)
- goto err_free_dai;
pm_runtime_enable(&pdev->dev);
diff --git a/sound/soc/samsung/pcm.c b/sound/soc/samsung/pcm.c
index 43e367a9acc3..c484985812ed 100644
--- a/sound/soc/samsung/pcm.c
+++ b/sound/soc/samsung/pcm.c
@@ -565,24 +565,25 @@ static int s3c_pcm_dev_probe(struct platform_device *pdev)
pcm->dma_capture = &s3c_pcm_stereo_in[pdev->id];
pcm->dma_playback = &s3c_pcm_stereo_out[pdev->id];
+ ret = samsung_asoc_dma_platform_register(&pdev->dev, filter,
+ NULL, NULL);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to get register DMA: %d\n", ret);
+ goto err5;
+ }
+
pm_runtime_enable(&pdev->dev);
ret = devm_snd_soc_register_component(&pdev->dev, &s3c_pcm_component,
&s3c_pcm_dai[pdev->id], 1);
if (ret != 0) {
dev_err(&pdev->dev, "failed to get register DAI: %d\n", ret);
- goto err5;
- }
-
- ret = samsung_asoc_dma_platform_register(&pdev->dev, filter,
- NULL, NULL);
- if (ret) {
- dev_err(&pdev->dev, "failed to get register DMA: %d\n", ret);
- goto err5;
+ goto err6;
}
return 0;
-
+err6:
+ pm_runtime_disable(&pdev->dev);
err5:
clk_disable_unprepare(pcm->pclk);
err4:
diff --git a/sound/soc/samsung/s3c2412-i2s.c b/sound/soc/samsung/s3c2412-i2s.c
index 3e89fbc0c51d..6d0b8897fa6c 100644
--- a/sound/soc/samsung/s3c2412-i2s.c
+++ b/sound/soc/samsung/s3c2412-i2s.c
@@ -35,12 +35,10 @@
#include <linux/platform_data/asoc-s3c.h>
static struct snd_dmaengine_dai_dma_data s3c2412_i2s_pcm_stereo_out = {
- .chan_name = "tx",
.addr_width = 4,
};
static struct snd_dmaengine_dai_dma_data s3c2412_i2s_pcm_stereo_in = {
- .chan_name = "rx",
.addr_width = 4,
};
@@ -168,19 +166,19 @@ static int s3c2412_iis_dev_probe(struct platform_device *pdev)
s3c2412_i2s_pcm_stereo_in.addr = res->start + S3C2412_IISRXD;
s3c2412_i2s_pcm_stereo_in.filter_data = pdata->dma_capture;
- ret = s3c_i2sv2_register_component(&pdev->dev, -1,
- &s3c2412_i2s_component,
- &s3c2412_i2s_dai);
+ ret = samsung_asoc_dma_platform_register(&pdev->dev,
+ pdata->dma_filter,
+ NULL, NULL);
if (ret) {
- pr_err("failed to register the dai\n");
+ pr_err("failed to register the DMA: %d\n", ret);
return ret;
}
- ret = samsung_asoc_dma_platform_register(&pdev->dev,
- pdata->dma_filter,
- NULL, NULL);
+ ret = s3c_i2sv2_register_component(&pdev->dev, -1,
+ &s3c2412_i2s_component,
+ &s3c2412_i2s_dai);
if (ret)
- pr_err("failed to register the DMA: %d\n", ret);
+ pr_err("failed to register the dai\n");
return ret;
}
diff --git a/sound/soc/samsung/s3c24xx-i2s.c b/sound/soc/samsung/s3c24xx-i2s.c
index c78a936a3099..189827760229 100644
--- a/sound/soc/samsung/s3c24xx-i2s.c
+++ b/sound/soc/samsung/s3c24xx-i2s.c
@@ -33,12 +33,10 @@
#include <linux/platform_data/asoc-s3c.h>
static struct snd_dmaengine_dai_dma_data s3c24xx_i2s_pcm_stereo_out = {
- .chan_name = "tx",
.addr_width = 2,
};
static struct snd_dmaengine_dai_dma_data s3c24xx_i2s_pcm_stereo_in = {
- .chan_name = "rx",
.addr_width = 2,
};
@@ -474,18 +472,18 @@ static int s3c24xx_iis_dev_probe(struct platform_device *pdev)
s3c24xx_i2s_pcm_stereo_in.addr = res->start + S3C2410_IISFIFO;
s3c24xx_i2s_pcm_stereo_in.filter_data = pdata->dma_capture;
- ret = devm_snd_soc_register_component(&pdev->dev,
- &s3c24xx_i2s_component, &s3c24xx_i2s_dai, 1);
+ ret = samsung_asoc_dma_platform_register(&pdev->dev,
+ pdata->dma_filter,
+ NULL, NULL);
if (ret) {
- pr_err("failed to register the dai\n");
+ pr_err("failed to register the dma: %d\n", ret);
return ret;
}
- ret = samsung_asoc_dma_platform_register(&pdev->dev,
- pdata->dma_filter,
- NULL, NULL);
+ ret = devm_snd_soc_register_component(&pdev->dev,
+ &s3c24xx_i2s_component, &s3c24xx_i2s_dai, 1);
if (ret)
- pr_err("failed to register the dma: %d\n", ret);
+ pr_err("failed to register the dai\n");
return ret;
}
diff --git a/sound/soc/samsung/spdif.c b/sound/soc/samsung/spdif.c
index 26c1fbed4d35..779504f54bc0 100644
--- a/sound/soc/samsung/spdif.c
+++ b/sound/soc/samsung/spdif.c
@@ -416,15 +416,6 @@ static int spdif_probe(struct platform_device *pdev)
goto err3;
}
- dev_set_drvdata(&pdev->dev, spdif);
-
- ret = devm_snd_soc_register_component(&pdev->dev,
- &samsung_spdif_component, &samsung_spdif_dai, 1);
- if (ret != 0) {
- dev_err(&pdev->dev, "fail to register dai\n");
- goto err4;
- }
-
spdif_stereo_out.addr_width = 2;
spdif_stereo_out.addr = mem_res->start + DATA_OUTBUF;
filter = NULL;
@@ -432,7 +423,6 @@ static int spdif_probe(struct platform_device *pdev)
spdif_stereo_out.filter_data = spdif_pdata->dma_playback;
filter = spdif_pdata->dma_filter;
}
-
spdif->dma_playback = &spdif_stereo_out;
ret = samsung_asoc_dma_platform_register(&pdev->dev, filter,
@@ -442,6 +432,15 @@ static int spdif_probe(struct platform_device *pdev)
goto err4;
}
+ dev_set_drvdata(&pdev->dev, spdif);
+
+ ret = devm_snd_soc_register_component(&pdev->dev,
+ &samsung_spdif_component, &samsung_spdif_dai, 1);
+ if (ret != 0) {
+ dev_err(&pdev->dev, "fail to register dai\n");
+ goto err4;
+ }
+
return 0;
err4:
iounmap(spdif->regs);
diff --git a/sound/soc/sh/Kconfig b/sound/soc/sh/Kconfig
index 6db6405d952f..147ebecfed94 100644
--- a/sound/soc/sh/Kconfig
+++ b/sound/soc/sh/Kconfig
@@ -1,5 +1,5 @@
menu "SoC Audio support for SuperH"
- depends on SUPERH || ARCH_SHMOBILE
+ depends on SUPERH || ARCH_SHMOBILE || COMPILE_TEST
config SND_SOC_PCM_SH7760
tristate "SoC Audio support for Renesas SH7760"
@@ -37,6 +37,7 @@ config SND_SOC_SH4_SIU
config SND_SOC_RCAR
tristate "R-Car series SRU/SCU/SSIU/SSI support"
depends on COMMON_CLK
+ depends on OF || COMPILE_TEST
select SND_SIMPLE_CARD
select REGMAP_MMIO
help
diff --git a/sound/soc/sh/rcar/adg.c b/sound/soc/sh/rcar/adg.c
index 2145957d0229..85a33ac0a5c4 100644
--- a/sound/soc/sh/rcar/adg.c
+++ b/sound/soc/sh/rcar/adg.c
@@ -34,6 +34,9 @@ struct rsnd_adg {
struct clk_onecell_data onecell;
struct rsnd_mod mod;
u32 flags;
+ u32 ckr;
+ u32 rbga;
+ u32 rbgb;
int rbga_rate_for_441khz; /* RBGA */
int rbgb_rate_for_48khz; /* RBGB */
@@ -316,9 +319,11 @@ int rsnd_adg_ssi_clk_try_start(struct rsnd_mod *ssi_mod, unsigned int rate)
struct rsnd_priv *priv = rsnd_mod_to_priv(ssi_mod);
struct rsnd_adg *adg = rsnd_priv_to_adg(priv);
struct device *dev = rsnd_priv_to_dev(priv);
+ struct rsnd_mod *adg_mod = rsnd_mod_get(adg);
struct clk *clk;
int i;
u32 data;
+ u32 ckr = 0;
int sel_table[] = {
[CLKA] = 0x1,
[CLKB] = 0x2,
@@ -360,15 +365,14 @@ found_clock:
rsnd_adg_set_ssi_clk(ssi_mod, data);
if (!(adg_mode_flags(adg) & LRCLK_ASYNC)) {
- struct rsnd_mod *adg_mod = rsnd_mod_get(adg);
- u32 ckr = 0;
-
if (0 == (rate % 8000))
ckr = 0x80000000;
-
- rsnd_mod_bset(adg_mod, SSICKR, 0x80000000, ckr);
}
+ rsnd_mod_bset(adg_mod, BRGCKR, 0x80FF0000, adg->ckr | ckr);
+ rsnd_mod_write(adg_mod, BRRA, adg->rbga);
+ rsnd_mod_write(adg_mod, BRRB, adg->rbgb);
+
dev_dbg(dev, "ADG: %s[%d] selects 0x%x for %d\n",
rsnd_mod_name(ssi_mod), rsnd_mod_id(ssi_mod),
data, rate);
@@ -376,6 +380,25 @@ found_clock:
return 0;
}
+void rsnd_adg_clk_control(struct rsnd_priv *priv, int enable)
+{
+ struct rsnd_adg *adg = rsnd_priv_to_adg(priv);
+ struct device *dev = rsnd_priv_to_dev(priv);
+ struct clk *clk;
+ int i, ret;
+
+ for_each_rsnd_clk(clk, adg, i) {
+ ret = 0;
+ if (enable)
+ ret = clk_prepare_enable(clk);
+ else
+ clk_disable_unprepare(clk);
+
+ if (ret < 0)
+ dev_warn(dev, "can't use clk %d\n", i);
+ }
+}
+
static void rsnd_adg_get_clkin(struct rsnd_priv *priv,
struct rsnd_adg *adg)
{
@@ -387,27 +410,21 @@ static void rsnd_adg_get_clkin(struct rsnd_priv *priv,
[CLKC] = "clk_c",
[CLKI] = "clk_i",
};
- int i, ret;
+ int i;
for (i = 0; i < CLKMAX; i++) {
clk = devm_clk_get(dev, clk_name[i]);
adg->clk[i] = IS_ERR(clk) ? NULL : clk;
}
- for_each_rsnd_clk(clk, adg, i) {
- ret = clk_prepare_enable(clk);
- if (ret < 0)
- dev_warn(dev, "can't use clk %d\n", i);
-
+ for_each_rsnd_clk(clk, adg, i)
dev_dbg(dev, "clk %d : %p : %ld\n", i, clk, clk_get_rate(clk));
- }
}
static void rsnd_adg_get_clkout(struct rsnd_priv *priv,
struct rsnd_adg *adg)
{
struct clk *clk;
- struct rsnd_mod *adg_mod = rsnd_mod_get(adg);
struct device *dev = rsnd_priv_to_dev(priv);
struct device_node *np = dev->of_node;
u32 ckr, rbgx, rbga, rbgb;
@@ -532,13 +549,13 @@ static void rsnd_adg_get_clkout(struct rsnd_priv *priv,
}
}
- rsnd_mod_bset(adg_mod, SSICKR, 0x80FF0000, ckr);
- rsnd_mod_write(adg_mod, BRRA, rbga);
- rsnd_mod_write(adg_mod, BRRB, rbgb);
+ adg->ckr = ckr;
+ adg->rbga = rbga;
+ adg->rbgb = rbgb;
for_each_rsnd_clkout(clk, adg, i)
dev_dbg(dev, "clkout %d : %p : %ld\n", i, clk, clk_get_rate(clk));
- dev_dbg(dev, "SSICKR = 0x%08x, BRRA/BRRB = 0x%x/0x%x\n",
+ dev_dbg(dev, "BRGCKR = 0x%08x, BRRA/BRRB = 0x%x/0x%x\n",
ckr, rbga, rbgb);
}
@@ -565,16 +582,12 @@ int rsnd_adg_probe(struct rsnd_priv *priv)
priv->adg = adg;
+ rsnd_adg_clk_enable(priv);
+
return 0;
}
void rsnd_adg_remove(struct rsnd_priv *priv)
{
- struct rsnd_adg *adg = rsnd_priv_to_adg(priv);
- struct clk *clk;
- int i;
-
- for_each_rsnd_clk(clk, adg, i) {
- clk_disable_unprepare(clk);
- }
+ rsnd_adg_clk_disable(priv);
}
diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c
index f18141098b50..4bd68de76130 100644
--- a/sound/soc/sh/rcar/core.c
+++ b/sound/soc/sh/rcar/core.c
@@ -306,7 +306,7 @@ u32 rsnd_get_adinr_bit(struct rsnd_mod *mod, struct rsnd_dai_stream *io)
*/
u32 rsnd_get_dalign(struct rsnd_mod *mod, struct rsnd_dai_stream *io)
{
- struct rsnd_mod *ssi = rsnd_io_to_mod_ssi(io);
+ struct rsnd_mod *ssiu = rsnd_io_to_mod_ssiu(io);
struct rsnd_mod *target;
struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
u32 val = 0x76543210;
@@ -315,11 +315,11 @@ u32 rsnd_get_dalign(struct rsnd_mod *mod, struct rsnd_dai_stream *io)
if (rsnd_io_is_play(io)) {
struct rsnd_mod *src = rsnd_io_to_mod_src(io);
- target = src ? src : ssi;
+ target = src ? src : ssiu;
} else {
struct rsnd_mod *cmd = rsnd_io_to_mod_cmd(io);
- target = cmd ? cmd : ssi;
+ target = cmd ? cmd : ssiu;
}
mask <<= runtime->channels * 4;
@@ -348,32 +348,28 @@ u32 rsnd_get_dalign(struct rsnd_mod *mod, struct rsnd_dai_stream *io)
/*
* rsnd_dai functions
*/
-#define rsnd_mod_call(idx, io, func, param...) \
-({ \
- struct rsnd_priv *priv = rsnd_mod_to_priv(mod); \
- struct rsnd_mod *mod = (io)->mod[idx]; \
- struct device *dev = rsnd_priv_to_dev(priv); \
- u32 *status = mod->get_status(io, mod, idx); \
- u32 mask = 0xF << __rsnd_mod_shift_##func; \
- u8 val = (*status >> __rsnd_mod_shift_##func) & 0xF; \
- u8 add = ((val + __rsnd_mod_add_##func) & 0xF); \
- int ret = 0; \
- int call = (val == __rsnd_mod_call_##func) && (mod)->ops->func; \
- if (add == 0xF) \
- call = 0; \
- else \
- *status = (*status & ~mask) + \
- (add << __rsnd_mod_shift_##func); \
- dev_dbg(dev, "%s[%d]\t0x%08x %s\n", \
- rsnd_mod_name(mod), rsnd_mod_id(mod), \
- *status, call ? #func : ""); \
- if (call) \
- ret = (mod)->ops->func(mod, io, param); \
- if (ret) \
- dev_dbg(dev, "%s[%d] : rsnd_mod_call error %d\n", \
- rsnd_mod_name(mod), rsnd_mod_id(mod), ret); \
- ret; \
-})
+struct rsnd_mod *rsnd_mod_next(int *iterator,
+ struct rsnd_dai_stream *io,
+ enum rsnd_mod_type *array,
+ int array_size)
+{
+ struct rsnd_mod *mod;
+ enum rsnd_mod_type type;
+ int max = array ? array_size : RSND_MOD_MAX;
+
+ for (; *iterator < max; (*iterator)++) {
+ type = (array) ? array[*iterator] : *iterator;
+ mod = io->mod[type];
+ if (!mod)
+ continue;
+
+ (*iterator)++;
+
+ return mod;
+ }
+
+ return NULL;
+}
static enum rsnd_mod_type rsnd_mod_sequence[][RSND_MOD_MAX] = {
{
@@ -409,19 +405,49 @@ static enum rsnd_mod_type rsnd_mod_sequence[][RSND_MOD_MAX] = {
},
};
-#define rsnd_dai_call(fn, io, param...) \
-({ \
- struct rsnd_mod *mod; \
- int type, is_play = rsnd_io_is_play(io); \
- int ret = 0, i; \
- for (i = 0; i < RSND_MOD_MAX; i++) { \
- type = rsnd_mod_sequence[is_play][i]; \
- mod = (io)->mod[type]; \
- if (!mod) \
- continue; \
- ret |= rsnd_mod_call(type, io, fn, param); \
- } \
- ret; \
+static int rsnd_status_update(u32 *status,
+ int shift, int add, int timing)
+{
+ u32 mask = 0xF << shift;
+ u8 val = (*status >> shift) & 0xF;
+ u8 next_val = (val + add) & 0xF;
+ int func_call = (val == timing);
+
+ if (next_val == 0xF) /* underflow case */
+ func_call = 0;
+ else
+ *status = (*status & ~mask) + (next_val << shift);
+
+ return func_call;
+}
+
+#define rsnd_dai_call(fn, io, param...) \
+({ \
+ struct rsnd_priv *priv = rsnd_io_to_priv(io); \
+ struct device *dev = rsnd_priv_to_dev(priv); \
+ struct rsnd_mod *mod; \
+ int is_play = rsnd_io_is_play(io); \
+ int ret = 0, i; \
+ enum rsnd_mod_type *types = rsnd_mod_sequence[is_play]; \
+ for_each_rsnd_mod_arrays(i, mod, io, types, RSND_MOD_MAX) { \
+ int tmp = 0; \
+ u32 *status = mod->get_status(io, mod, types[i]); \
+ int func_call = rsnd_status_update(status, \
+ __rsnd_mod_shift_##fn, \
+ __rsnd_mod_add_##fn, \
+ __rsnd_mod_call_##fn); \
+ dev_dbg(dev, "%s[%d]\t0x%08x %s\n", \
+ rsnd_mod_name(mod), rsnd_mod_id(mod), *status, \
+ (func_call && (mod)->ops->fn) ? #fn : ""); \
+ if (func_call && (mod)->ops->fn) \
+ tmp = (mod)->ops->fn(mod, io, param); \
+ if (tmp) \
+ dev_err(dev, "%s[%d] : %s error %d\n", \
+ rsnd_mod_name(mod), rsnd_mod_id(mod), \
+ #fn, tmp); \
+ ret |= tmp; \
+ } \
+ ret; \
})
int rsnd_dai_connect(struct rsnd_mod *mod,
@@ -690,7 +716,33 @@ static int rsnd_soc_set_dai_tdm_slot(struct snd_soc_dai *dai,
return 0;
}
+static int rsnd_soc_dai_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai);
+ struct rsnd_dai_stream *io = rsnd_rdai_to_io(rdai, substream);
+
+ /*
+ * call rsnd_dai_call without spinlock
+ */
+ return rsnd_dai_call(nolock_start, io, priv);
+}
+
+static void rsnd_soc_dai_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai);
+ struct rsnd_dai_stream *io = rsnd_rdai_to_io(rdai, substream);
+
+ /*
+ * call rsnd_dai_call without spinlock
+ */
+ rsnd_dai_call(nolock_stop, io, priv);
+}
+
static const struct snd_soc_dai_ops rsnd_soc_dai_ops = {
+ .startup = rsnd_soc_dai_startup,
+ .shutdown = rsnd_soc_dai_shutdown,
.trigger = rsnd_soc_dai_trigger,
.set_fmt = rsnd_soc_dai_set_fmt,
.set_tdm_slot = rsnd_soc_set_dai_tdm_slot,
@@ -993,7 +1045,11 @@ static int __rsnd_kctrl_new(struct rsnd_mod *mod,
void _rsnd_kctrl_remove(struct rsnd_kctrl_cfg *cfg)
{
- snd_ctl_remove(cfg->card, cfg->kctrl);
+ if (cfg->card && cfg->kctrl)
+ snd_ctl_remove(cfg->card, cfg->kctrl);
+
+ cfg->card = NULL;
+ cfg->kctrl = NULL;
}
int rsnd_kctrl_new_m(struct rsnd_mod *mod,
@@ -1070,8 +1126,8 @@ static int rsnd_pcm_new(struct snd_soc_pcm_runtime *rtd)
return snd_pcm_lib_preallocate_pages_for_all(
rtd->pcm,
- SNDRV_DMA_TYPE_DEV,
- rtd->card->snd_card->dev,
+ SNDRV_DMA_TYPE_CONTINUOUS,
+ snd_dma_continuous_data(GFP_KERNEL),
PREALLOC_BUFFER, PREALLOC_BUFFER_MAX);
}
@@ -1092,6 +1148,7 @@ static int rsnd_rdai_continuance_probe(struct rsnd_priv *priv,
ret = rsnd_dai_call(probe, io, priv);
if (ret == -EAGAIN) {
struct rsnd_mod *ssi_mod = rsnd_io_to_mod_ssi(io);
+ struct rsnd_mod *mod;
int i;
/*
@@ -1111,8 +1168,8 @@ static int rsnd_rdai_continuance_probe(struct rsnd_priv *priv,
* remove all mod from io
* and, re connect ssi
*/
- for (i = 0; i < RSND_MOD_MAX; i++)
- rsnd_dai_disconnect((io)->mod[i], io, i);
+ for_each_rsnd_mod(i, mod, io)
+ rsnd_dai_disconnect(mod, io, i);
rsnd_dai_connect(ssi_mod, io, RSND_MOD_SSI);
/*
@@ -1251,9 +1308,33 @@ static int rsnd_remove(struct platform_device *pdev)
return ret;
}
+static int rsnd_suspend(struct device *dev)
+{
+ struct rsnd_priv *priv = dev_get_drvdata(dev);
+
+ rsnd_adg_clk_disable(priv);
+
+ return 0;
+}
+
+static int rsnd_resume(struct device *dev)
+{
+ struct rsnd_priv *priv = dev_get_drvdata(dev);
+
+ rsnd_adg_clk_enable(priv);
+
+ return 0;
+}
+
+static struct dev_pm_ops rsnd_pm_ops = {
+ .suspend = rsnd_suspend,
+ .resume = rsnd_resume,
+};
+
static struct platform_driver rsnd_driver = {
.driver = {
.name = "rcar_sound",
+ .pm = &rsnd_pm_ops,
.of_match_table = rsnd_of_match,
},
.probe = rsnd_probe,
diff --git a/sound/soc/sh/rcar/dma.c b/sound/soc/sh/rcar/dma.c
index 6bc93cbb3049..1f405c833867 100644
--- a/sound/soc/sh/rcar/dma.c
+++ b/sound/soc/sh/rcar/dma.c
@@ -25,6 +25,10 @@
struct rsnd_dmaen {
struct dma_chan *chan;
+ dma_addr_t dma_buf;
+ unsigned int dma_len;
+ unsigned int dma_period;
+ unsigned int dma_cnt;
};
struct rsnd_dmapp {
@@ -34,6 +38,8 @@ struct rsnd_dmapp {
struct rsnd_dma {
struct rsnd_mod mod;
+ struct rsnd_mod *mod_from;
+ struct rsnd_mod *mod_to;
dma_addr_t src_addr;
dma_addr_t dst_addr;
union {
@@ -56,10 +62,38 @@ struct rsnd_dma_ctrl {
/*
* Audio DMAC
*/
+#define rsnd_dmaen_sync(dmaen, io, i) __rsnd_dmaen_sync(dmaen, io, i, 1)
+#define rsnd_dmaen_unsync(dmaen, io, i) __rsnd_dmaen_sync(dmaen, io, i, 0)
+static void __rsnd_dmaen_sync(struct rsnd_dmaen *dmaen, struct rsnd_dai_stream *io,
+ int i, int sync)
+{
+ struct device *dev = dmaen->chan->device->dev;
+ enum dma_data_direction dir;
+ int is_play = rsnd_io_is_play(io);
+ dma_addr_t buf;
+ int len, max;
+ size_t period;
+
+ len = dmaen->dma_len;
+ period = dmaen->dma_period;
+ max = len / period;
+ i = i % max;
+ buf = dmaen->dma_buf + (period * i);
+
+ dir = is_play ? DMA_TO_DEVICE : DMA_FROM_DEVICE;
+
+ if (sync)
+ dma_sync_single_for_device(dev, buf, period, dir);
+ else
+ dma_sync_single_for_cpu(dev, buf, period, dir);
+}
+
static void __rsnd_dmaen_complete(struct rsnd_mod *mod,
struct rsnd_dai_stream *io)
{
struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+ struct rsnd_dma *dma = rsnd_mod_to_dma(mod);
+ struct rsnd_dmaen *dmaen = rsnd_dma_to_dmaen(dma);
bool elapsed = false;
unsigned long flags;
@@ -76,9 +110,22 @@ static void __rsnd_dmaen_complete(struct rsnd_mod *mod,
*/
spin_lock_irqsave(&priv->lock, flags);
- if (rsnd_io_is_working(io))
+ if (rsnd_io_is_working(io)) {
+ rsnd_dmaen_unsync(dmaen, io, dmaen->dma_cnt);
+
+ /*
+ * Next period is already started.
+ * Let's sync Next Next period
+ * see
+ * rsnd_dmaen_start()
+ */
+ rsnd_dmaen_sync(dmaen, io, dmaen->dma_cnt + 2);
+
elapsed = rsnd_dai_pointer_update(io, io->byte_per_period);
+ dmaen->dma_cnt++;
+ }
+
spin_unlock_irqrestore(&priv->lock, flags);
if (elapsed)
@@ -92,6 +139,20 @@ static void rsnd_dmaen_complete(void *data)
rsnd_mod_interrupt(mod, __rsnd_dmaen_complete);
}
+static struct dma_chan *rsnd_dmaen_request_channel(struct rsnd_dai_stream *io,
+ struct rsnd_mod *mod_from,
+ struct rsnd_mod *mod_to)
+{
+ if ((!mod_from && !mod_to) ||
+ (mod_from && mod_to))
+ return NULL;
+
+ if (mod_from)
+ return rsnd_mod_dma_req(io, mod_from);
+ else
+ return rsnd_mod_dma_req(io, mod_to);
+}
+
static int rsnd_dmaen_stop(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct rsnd_priv *priv)
@@ -99,7 +160,66 @@ static int rsnd_dmaen_stop(struct rsnd_mod *mod,
struct rsnd_dma *dma = rsnd_mod_to_dma(mod);
struct rsnd_dmaen *dmaen = rsnd_dma_to_dmaen(dma);
- dmaengine_terminate_all(dmaen->chan);
+ if (dmaen->chan) {
+ int is_play = rsnd_io_is_play(io);
+
+ dmaengine_terminate_all(dmaen->chan);
+ dma_unmap_single(dmaen->chan->device->dev,
+ dmaen->dma_buf, dmaen->dma_len,
+ is_play ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
+ }
+
+ return 0;
+}
+
+static int rsnd_dmaen_nolock_stop(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
+ struct rsnd_priv *priv)
+{
+ struct rsnd_dma *dma = rsnd_mod_to_dma(mod);
+ struct rsnd_dmaen *dmaen = rsnd_dma_to_dmaen(dma);
+
+ /*
+ * DMAEngine release uses mutex lock.
+ * Thus, it shouldn't be called under spinlock.
+ * Let's call it under nolock_start
+ */
+ if (dmaen->chan)
+ dma_release_channel(dmaen->chan);
+
+ dmaen->chan = NULL;
+
+ return 0;
+}
+
+static int rsnd_dmaen_nolock_start(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
+ struct rsnd_priv *priv)
+{
+ struct rsnd_dma *dma = rsnd_mod_to_dma(mod);
+ struct rsnd_dmaen *dmaen = rsnd_dma_to_dmaen(dma);
+ struct device *dev = rsnd_priv_to_dev(priv);
+
+ if (dmaen->chan) {
+ dev_err(dev, "it already has dma channel\n");
+ return -EIO;
+ }
+
+ /*
+ * DMAEngine request uses mutex lock.
+ * Thus, it shouldn't be called under spinlock.
+ * Let's call it under nolock_start
+ */
+ dmaen->chan = rsnd_dmaen_request_channel(io,
+ dma->mod_from,
+ dma->mod_to);
+ if (IS_ERR_OR_NULL(dmaen->chan)) {
+ int ret = PTR_ERR(dmaen->chan);
+
+ dmaen->chan = NULL;
+ dev_err(dev, "can't get dma channel\n");
+ return ret;
+ }
return 0;
}
@@ -113,12 +233,41 @@ static int rsnd_dmaen_start(struct rsnd_mod *mod,
struct snd_pcm_substream *substream = io->substream;
struct device *dev = rsnd_priv_to_dev(priv);
struct dma_async_tx_descriptor *desc;
+ struct dma_slave_config cfg = {};
+ dma_addr_t buf;
+ size_t len;
+ size_t period;
int is_play = rsnd_io_is_play(io);
+ int i;
+ int ret;
+
+ cfg.direction = is_play ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM;
+ cfg.src_addr = dma->src_addr;
+ cfg.dst_addr = dma->dst_addr;
+ cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+ cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+
+ dev_dbg(dev, "%s[%d] %pad -> %pad\n",
+ rsnd_mod_name(mod), rsnd_mod_id(mod),
+ &cfg.src_addr, &cfg.dst_addr);
+
+ ret = dmaengine_slave_config(dmaen->chan, &cfg);
+ if (ret < 0)
+ return ret;
+
+ len = snd_pcm_lib_buffer_bytes(substream);
+ period = snd_pcm_lib_period_bytes(substream);
+ buf = dma_map_single(dmaen->chan->device->dev,
+ substream->runtime->dma_area,
+ len,
+ is_play ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
+ if (dma_mapping_error(dmaen->chan->device->dev, buf)) {
+ dev_err(dev, "dma map failed\n");
+ return -EIO;
+ }
desc = dmaengine_prep_dma_cyclic(dmaen->chan,
- substream->runtime->dma_addr,
- snd_pcm_lib_buffer_bytes(substream),
- snd_pcm_lib_period_bytes(substream),
+ buf, len, period,
is_play ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM,
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
@@ -130,6 +279,19 @@ static int rsnd_dmaen_start(struct rsnd_mod *mod,
desc->callback = rsnd_dmaen_complete;
desc->callback_param = rsnd_mod_get(dma);
+ dmaen->dma_buf = buf;
+ dmaen->dma_len = len;
+ dmaen->dma_period = period;
+ dmaen->dma_cnt = 0;
+
+ /*
+ * synchronize this and next period
+ * see
+ * __rsnd_dmaen_complete()
+ */
+ for (i = 0; i < 2; i++)
+ rsnd_dmaen_sync(dmaen, io, i);
+
if (dmaengine_submit(desc) < 0) {
dev_err(dev, "dmaengine_submit() fail\n");
return -EIO;
@@ -143,124 +305,55 @@ static int rsnd_dmaen_start(struct rsnd_mod *mod,
struct dma_chan *rsnd_dma_request_channel(struct device_node *of_node,
struct rsnd_mod *mod, char *name)
{
- struct dma_chan *chan;
+ struct dma_chan *chan = NULL;
struct device_node *np;
int i = 0;
for_each_child_of_node(of_node, np) {
- if (i == rsnd_mod_id(mod))
- break;
+ if (i == rsnd_mod_id(mod) && (!chan))
+ chan = of_dma_request_slave_channel(np, name);
i++;
}
- chan = of_dma_request_slave_channel(np, name);
-
- of_node_put(np);
+ /* It should call of_node_put(), since, it is rsnd_xxx_of_node() */
of_node_put(of_node);
return chan;
}
-static struct dma_chan *rsnd_dmaen_request_channel(struct rsnd_dai_stream *io,
- struct rsnd_mod *mod_from,
- struct rsnd_mod *mod_to)
-{
- if ((!mod_from && !mod_to) ||
- (mod_from && mod_to))
- return NULL;
-
- if (mod_from)
- return rsnd_mod_dma_req(io, mod_from);
- else
- return rsnd_mod_dma_req(io, mod_to);
-}
-
-static int rsnd_dmaen_remove(struct rsnd_mod *mod,
- struct rsnd_dai_stream *io,
- struct rsnd_priv *priv)
-{
- struct rsnd_dma *dma = rsnd_mod_to_dma(mod);
- struct rsnd_dmaen *dmaen = rsnd_dma_to_dmaen(dma);
-
- if (dmaen->chan)
- dma_release_channel(dmaen->chan);
-
- dmaen->chan = NULL;
-
- return 0;
-}
-
static int rsnd_dmaen_attach(struct rsnd_dai_stream *io,
- struct rsnd_dma *dma, int id,
+ struct rsnd_dma *dma,
struct rsnd_mod *mod_from, struct rsnd_mod *mod_to)
{
- struct rsnd_mod *mod = rsnd_mod_get(dma);
- struct rsnd_dmaen *dmaen = rsnd_dma_to_dmaen(dma);
struct rsnd_priv *priv = rsnd_io_to_priv(io);
struct rsnd_dma_ctrl *dmac = rsnd_priv_to_dmac(priv);
- struct device *dev = rsnd_priv_to_dev(priv);
- struct dma_slave_config cfg = {};
- int is_play = rsnd_io_is_play(io);
- int ret;
-
- if (dmaen->chan) {
- dev_err(dev, "it already has dma channel\n");
- return -EIO;
- }
-
- if (dev->of_node) {
- dmaen->chan = rsnd_dmaen_request_channel(io, mod_from, mod_to);
- } else {
- dma_cap_mask_t mask;
-
- dma_cap_zero(mask);
- dma_cap_set(DMA_SLAVE, mask);
+ struct dma_chan *chan;
- dmaen->chan = dma_request_channel(mask, shdma_chan_filter,
- (void *)(uintptr_t)id);
- }
- if (IS_ERR_OR_NULL(dmaen->chan)) {
- dmaen->chan = NULL;
- dev_err(dev, "can't get dma channel\n");
- goto rsnd_dma_channel_err;
+ /* try to get DMAEngine channel */
+ chan = rsnd_dmaen_request_channel(io, mod_from, mod_to);
+ if (IS_ERR_OR_NULL(chan)) {
+ /*
+ * DMA failed. try to PIO mode
+ * see
+ * rsnd_ssi_fallback()
+ * rsnd_rdai_continuance_probe()
+ */
+ return -EAGAIN;
}
- cfg.direction = is_play ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM;
- cfg.src_addr = dma->src_addr;
- cfg.dst_addr = dma->dst_addr;
- cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
- cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
-
- dev_dbg(dev, "%s[%d] %pad -> %pad\n",
- rsnd_mod_name(mod), rsnd_mod_id(mod),
- &cfg.src_addr, &cfg.dst_addr);
-
- ret = dmaengine_slave_config(dmaen->chan, &cfg);
- if (ret < 0)
- goto rsnd_dma_attach_err;
+ dma_release_channel(chan);
dmac->dmaen_num++;
return 0;
-
-rsnd_dma_attach_err:
- rsnd_dmaen_remove(mod, io, priv);
-rsnd_dma_channel_err:
-
- /*
- * DMA failed. try to PIO mode
- * see
- * rsnd_ssi_fallback()
- * rsnd_rdai_continuance_probe()
- */
- return -EAGAIN;
}
static struct rsnd_mod_ops rsnd_dmaen_ops = {
.name = "audmac",
+ .nolock_start = rsnd_dmaen_nolock_start,
+ .nolock_stop = rsnd_dmaen_nolock_stop,
.start = rsnd_dmaen_start,
.stop = rsnd_dmaen_stop,
- .remove = rsnd_dmaen_remove,
};
/*
@@ -394,7 +487,7 @@ static int rsnd_dmapp_start(struct rsnd_mod *mod,
}
static int rsnd_dmapp_attach(struct rsnd_dai_stream *io,
- struct rsnd_dma *dma, int id,
+ struct rsnd_dma *dma,
struct rsnd_mod *mod_from, struct rsnd_mod *mod_to)
{
struct rsnd_dmapp *dmapp = rsnd_dma_to_dmapp(dma);
@@ -627,7 +720,7 @@ static void rsnd_dma_of_path(struct rsnd_mod *this,
}
int rsnd_dma_attach(struct rsnd_dai_stream *io, struct rsnd_mod *mod,
- struct rsnd_mod **dma_mod, int id)
+ struct rsnd_mod **dma_mod)
{
struct rsnd_mod *mod_from = NULL;
struct rsnd_mod *mod_to = NULL;
@@ -636,7 +729,7 @@ int rsnd_dma_attach(struct rsnd_dai_stream *io, struct rsnd_mod *mod,
struct device *dev = rsnd_priv_to_dev(priv);
struct rsnd_mod_ops *ops;
enum rsnd_mod_type type;
- int (*attach)(struct rsnd_dai_stream *io, struct rsnd_dma *dma, int id,
+ int (*attach)(struct rsnd_dai_stream *io, struct rsnd_dma *dma,
struct rsnd_mod *mod_from, struct rsnd_mod *mod_to);
int is_play = rsnd_io_is_play(io);
int ret, dma_id;
@@ -682,9 +775,6 @@ int rsnd_dma_attach(struct rsnd_dai_stream *io, struct rsnd_mod *mod,
*dma_mod = rsnd_mod_get(dma);
- dma->src_addr = rsnd_dma_addr(io, mod_from, is_play, 1);
- dma->dst_addr = rsnd_dma_addr(io, mod_to, is_play, 0);
-
ret = rsnd_mod_init(priv, *dma_mod, ops, NULL,
rsnd_mod_get_status, type, dma_id);
if (ret < 0)
@@ -695,9 +785,14 @@ int rsnd_dma_attach(struct rsnd_dai_stream *io, struct rsnd_mod *mod,
rsnd_mod_name(mod_from), rsnd_mod_id(mod_from),
rsnd_mod_name(mod_to), rsnd_mod_id(mod_to));
- ret = attach(io, dma, id, mod_from, mod_to);
+ ret = attach(io, dma, mod_from, mod_to);
if (ret < 0)
return ret;
+
+ dma->src_addr = rsnd_dma_addr(io, mod_from, is_play, 1);
+ dma->dst_addr = rsnd_dma_addr(io, mod_to, is_play, 0);
+ dma->mod_from = mod_from;
+ dma->mod_to = mod_to;
}
ret = rsnd_dai_connect(*dma_mod, io, type);
diff --git a/sound/soc/sh/rcar/dvc.c b/sound/soc/sh/rcar/dvc.c
index 02d971f69eff..cf8f59cdd8d7 100644
--- a/sound/soc/sh/rcar/dvc.c
+++ b/sound/soc/sh/rcar/dvc.c
@@ -48,8 +48,6 @@ struct rsnd_dvc {
#define rsnd_dvc_get(priv, id) ((struct rsnd_dvc *)(priv->dvc) + id)
#define rsnd_dvc_nr(priv) ((priv)->dvc_nr)
-#define rsnd_dvc_of_node(priv) \
- of_get_child_by_name(rsnd_priv_to_dev(priv)->of_node, "rcar_sound,dvc")
#define rsnd_mod_to_dvc(_mod) \
container_of((_mod), struct rsnd_dvc, mod)
diff --git a/sound/soc/sh/rcar/gen.c b/sound/soc/sh/rcar/gen.c
index 7d2fdf8dd188..63b6d3c28021 100644
--- a/sound/soc/sh/rcar/gen.c
+++ b/sound/soc/sh/rcar/gen.c
@@ -211,6 +211,14 @@ static int rsnd_gen2_probe(struct rsnd_priv *priv)
RSND_GEN_S_REG(SSI_MODE1, 0x804),
RSND_GEN_S_REG(SSI_MODE2, 0x808),
RSND_GEN_S_REG(SSI_CONTROL, 0x810),
+ RSND_GEN_S_REG(SSI_SYS_STATUS0, 0x840),
+ RSND_GEN_S_REG(SSI_SYS_STATUS1, 0x844),
+ RSND_GEN_S_REG(SSI_SYS_STATUS2, 0x848),
+ RSND_GEN_S_REG(SSI_SYS_STATUS3, 0x84c),
+ RSND_GEN_S_REG(SSI_SYS_STATUS4, 0x880),
+ RSND_GEN_S_REG(SSI_SYS_STATUS5, 0x884),
+ RSND_GEN_S_REG(SSI_SYS_STATUS6, 0x888),
+ RSND_GEN_S_REG(SSI_SYS_STATUS7, 0x88c),
/* FIXME: it needs SSI_MODE2/3 in the future */
RSND_GEN_M_REG(SSI_BUSIF_MODE, 0x0, 0x80),
@@ -311,7 +319,7 @@ static int rsnd_gen2_probe(struct rsnd_priv *priv)
static const struct rsnd_regmap_field_conf conf_adg[] = {
RSND_GEN_S_REG(BRRA, 0x00),
RSND_GEN_S_REG(BRRB, 0x04),
- RSND_GEN_S_REG(SSICKR, 0x08),
+ RSND_GEN_S_REG(BRGCKR, 0x08),
RSND_GEN_S_REG(AUDIO_CLK_SEL0, 0x0c),
RSND_GEN_S_REG(AUDIO_CLK_SEL1, 0x10),
RSND_GEN_S_REG(AUDIO_CLK_SEL2, 0x14),
@@ -362,7 +370,7 @@ static int rsnd_gen1_probe(struct rsnd_priv *priv)
static const struct rsnd_regmap_field_conf conf_adg[] = {
RSND_GEN_S_REG(BRRA, 0x00),
RSND_GEN_S_REG(BRRB, 0x04),
- RSND_GEN_S_REG(SSICKR, 0x08),
+ RSND_GEN_S_REG(BRGCKR, 0x08),
RSND_GEN_S_REG(AUDIO_CLK_SEL0, 0x0c),
RSND_GEN_S_REG(AUDIO_CLK_SEL1, 0x10),
};
diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h
index a8f61d79333b..b90df77662df 100644
--- a/sound/soc/sh/rcar/rsnd.h
+++ b/sound/soc/sh/rcar/rsnd.h
@@ -43,17 +43,7 @@
* see gen1/gen2 for detail
*/
enum rsnd_reg {
- /* SCU (SRC/SSIU/MIX/CTU/DVC) */
- RSND_REG_SSI_MODE, /* Gen2 only */
- RSND_REG_SSI_MODE0,
- RSND_REG_SSI_MODE1,
- RSND_REG_SSI_MODE2,
- RSND_REG_SSI_CONTROL,
- RSND_REG_SSI_CTRL, /* Gen2 only */
- RSND_REG_SSI_BUSIF_MODE, /* Gen2 only */
- RSND_REG_SSI_BUSIF_ADINR, /* Gen2 only */
- RSND_REG_SSI_BUSIF_DALIGN, /* Gen2 only */
- RSND_REG_SSI_INT_ENABLE, /* Gen2 only */
+ /* SCU (MIX/CTU/DVC) */
RSND_REG_SRC_I_BUSIF_MODE,
RSND_REG_SRC_O_BUSIF_MODE,
RSND_REG_SRC_ROUTE_MODE0,
@@ -63,29 +53,29 @@ enum rsnd_reg {
RSND_REG_SRC_IFSCR,
RSND_REG_SRC_IFSVR,
RSND_REG_SRC_SRCCR,
- RSND_REG_SRC_CTRL, /* Gen2 only */
- RSND_REG_SRC_BSDSR, /* Gen2 only */
- RSND_REG_SRC_BSISR, /* Gen2 only */
- RSND_REG_SRC_INT_ENABLE0, /* Gen2 only */
- RSND_REG_SRC_BUSIF_DALIGN, /* Gen2 only */
- RSND_REG_SRCIN_TIMSEL0, /* Gen2 only */
- RSND_REG_SRCIN_TIMSEL1, /* Gen2 only */
- RSND_REG_SRCIN_TIMSEL2, /* Gen2 only */
- RSND_REG_SRCIN_TIMSEL3, /* Gen2 only */
- RSND_REG_SRCIN_TIMSEL4, /* Gen2 only */
- RSND_REG_SRCOUT_TIMSEL0, /* Gen2 only */
- RSND_REG_SRCOUT_TIMSEL1, /* Gen2 only */
- RSND_REG_SRCOUT_TIMSEL2, /* Gen2 only */
- RSND_REG_SRCOUT_TIMSEL3, /* Gen2 only */
- RSND_REG_SRCOUT_TIMSEL4, /* Gen2 only */
+ RSND_REG_SRC_CTRL,
+ RSND_REG_SRC_BSDSR,
+ RSND_REG_SRC_BSISR,
+ RSND_REG_SRC_INT_ENABLE0,
+ RSND_REG_SRC_BUSIF_DALIGN,
+ RSND_REG_SRCIN_TIMSEL0,
+ RSND_REG_SRCIN_TIMSEL1,
+ RSND_REG_SRCIN_TIMSEL2,
+ RSND_REG_SRCIN_TIMSEL3,
+ RSND_REG_SRCIN_TIMSEL4,
+ RSND_REG_SRCOUT_TIMSEL0,
+ RSND_REG_SRCOUT_TIMSEL1,
+ RSND_REG_SRCOUT_TIMSEL2,
+ RSND_REG_SRCOUT_TIMSEL3,
+ RSND_REG_SRCOUT_TIMSEL4,
RSND_REG_SCU_SYS_STATUS0,
- RSND_REG_SCU_SYS_STATUS1, /* Gen2 only */
+ RSND_REG_SCU_SYS_STATUS1,
RSND_REG_SCU_SYS_INT_EN0,
- RSND_REG_SCU_SYS_INT_EN1, /* Gen2 only */
- RSND_REG_CMD_CTRL, /* Gen2 only */
- RSND_REG_CMD_BUSIF_DALIGN, /* Gen2 only */
+ RSND_REG_SCU_SYS_INT_EN1,
+ RSND_REG_CMD_CTRL,
+ RSND_REG_CMD_BUSIF_DALIGN,
RSND_REG_CMD_ROUTE_SLCT,
- RSND_REG_CMDOUT_TIMSEL, /* Gen2 only */
+ RSND_REG_CMDOUT_TIMSEL,
RSND_REG_CTU_SWRSR,
RSND_REG_CTU_CTUIR,
RSND_REG_CTU_ADINR,
@@ -147,18 +137,38 @@ enum rsnd_reg {
RSND_REG_DVC_VOL6R,
RSND_REG_DVC_VOL7R,
RSND_REG_DVC_DVUER,
- RSND_REG_DVC_VRCTR, /* Gen2 only */
- RSND_REG_DVC_VRPDR, /* Gen2 only */
- RSND_REG_DVC_VRDBR, /* Gen2 only */
+ RSND_REG_DVC_VRCTR,
+ RSND_REG_DVC_VRPDR,
+ RSND_REG_DVC_VRDBR,
/* ADG */
RSND_REG_BRRA,
RSND_REG_BRRB,
- RSND_REG_SSICKR,
- RSND_REG_DIV_EN, /* Gen2 only */
+ RSND_REG_BRGCKR,
+ RSND_REG_DIV_EN,
RSND_REG_AUDIO_CLK_SEL0,
RSND_REG_AUDIO_CLK_SEL1,
- RSND_REG_AUDIO_CLK_SEL2, /* Gen2 only */
+ RSND_REG_AUDIO_CLK_SEL2,
+
+ /* SSIU */
+ RSND_REG_SSI_MODE,
+ RSND_REG_SSI_MODE0,
+ RSND_REG_SSI_MODE1,
+ RSND_REG_SSI_MODE2,
+ RSND_REG_SSI_CONTROL,
+ RSND_REG_SSI_CTRL,
+ RSND_REG_SSI_BUSIF_MODE,
+ RSND_REG_SSI_BUSIF_ADINR,
+ RSND_REG_SSI_BUSIF_DALIGN,
+ RSND_REG_SSI_INT_ENABLE,
+ RSND_REG_SSI_SYS_STATUS0,
+ RSND_REG_SSI_SYS_STATUS1,
+ RSND_REG_SSI_SYS_STATUS2,
+ RSND_REG_SSI_SYS_STATUS3,
+ RSND_REG_SSI_SYS_STATUS4,
+ RSND_REG_SSI_SYS_STATUS5,
+ RSND_REG_SSI_SYS_STATUS6,
+ RSND_REG_SSI_SYS_STATUS7,
/* SSI */
RSND_REG_SSICR,
@@ -199,7 +209,7 @@ u32 rsnd_get_dalign(struct rsnd_mod *mod, struct rsnd_dai_stream *io);
* R-Car DMA
*/
int rsnd_dma_attach(struct rsnd_dai_stream *io,
- struct rsnd_mod *mod, struct rsnd_mod **dma_mod, int id);
+ struct rsnd_mod *mod, struct rsnd_mod **dma_mod);
int rsnd_dma_probe(struct rsnd_priv *priv);
struct dma_chan *rsnd_dma_request_channel(struct device_node *of_node,
struct rsnd_mod *mod, char *name);
@@ -259,6 +269,12 @@ struct rsnd_mod_ops {
int (*fallback)(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct rsnd_priv *priv);
+ int (*nolock_start)(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
+ struct rsnd_priv *priv);
+ int (*nolock_stop)(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
+ struct rsnd_priv *priv);
};
struct rsnd_dai_stream;
@@ -278,7 +294,7 @@ struct rsnd_mod {
*
* 0xH0000CBA
*
- * A 0: probe 1: remove
+ * A 0: nolock_start 1: nolock_stop
* B 0: init 1: quit
* C 0: start 1: stop
*
@@ -288,19 +304,23 @@ struct rsnd_mod {
* H 0: fallback
* H 0: hw_params
*/
-#define __rsnd_mod_shift_probe 0
-#define __rsnd_mod_shift_remove 0
+#define __rsnd_mod_shift_nolock_start 0
+#define __rsnd_mod_shift_nolock_stop 0
#define __rsnd_mod_shift_init 4
#define __rsnd_mod_shift_quit 4
#define __rsnd_mod_shift_start 8
#define __rsnd_mod_shift_stop 8
+#define __rsnd_mod_shift_probe 28 /* always called */
+#define __rsnd_mod_shift_remove 28 /* always called */
#define __rsnd_mod_shift_irq 28 /* always called */
#define __rsnd_mod_shift_pcm_new 28 /* always called */
#define __rsnd_mod_shift_fallback 28 /* always called */
#define __rsnd_mod_shift_hw_params 28 /* always called */
-#define __rsnd_mod_add_probe 1
-#define __rsnd_mod_add_remove -1
+#define __rsnd_mod_add_probe 0
+#define __rsnd_mod_add_remove 0
+#define __rsnd_mod_add_nolock_start 1
+#define __rsnd_mod_add_nolock_stop -1
#define __rsnd_mod_add_init 1
#define __rsnd_mod_add_quit -1
#define __rsnd_mod_add_start 1
@@ -311,7 +331,7 @@ struct rsnd_mod {
#define __rsnd_mod_add_hw_params 0
#define __rsnd_mod_call_probe 0
-#define __rsnd_mod_call_remove 1
+#define __rsnd_mod_call_remove 0
#define __rsnd_mod_call_init 0
#define __rsnd_mod_call_quit 1
#define __rsnd_mod_call_start 0
@@ -320,6 +340,8 @@ struct rsnd_mod {
#define __rsnd_mod_call_pcm_new 0
#define __rsnd_mod_call_fallback 0
#define __rsnd_mod_call_hw_params 0
+#define __rsnd_mod_call_nolock_start 0
+#define __rsnd_mod_call_nolock_stop 1
#define rsnd_mod_to_priv(mod) ((mod)->priv)
#define rsnd_mod_id(mod) ((mod) ? (mod)->id : -1)
@@ -346,6 +368,18 @@ void rsnd_mod_interrupt(struct rsnd_mod *mod,
u32 *rsnd_mod_get_status(struct rsnd_dai_stream *io,
struct rsnd_mod *mod,
enum rsnd_mod_type type);
+struct rsnd_mod *rsnd_mod_next(int *iterator,
+ struct rsnd_dai_stream *io,
+ enum rsnd_mod_type *array,
+ int array_size);
+#define for_each_rsnd_mod(iterator, pos, io) \
+ for (iterator = 0; \
+ (pos = rsnd_mod_next(&iterator, io, NULL, 0));)
+#define for_each_rsnd_mod_arrays(iterator, pos, io, array, size) \
+ for (iterator = 0; \
+ (pos = rsnd_mod_next(&iterator, io, array, size));)
+#define for_each_rsnd_mod_array(iterator, pos, io, array) \
+ for_each_rsnd_mod_arrays(iterator, pos, io, array, ARRAY_SIZE(array))
void rsnd_parse_connect_common(struct rsnd_dai *rdai,
struct rsnd_mod* (*mod_get)(struct rsnd_priv *priv, int id),
@@ -365,6 +399,18 @@ int rsnd_runtime_is_ssi_multi(struct rsnd_dai_stream *io);
int rsnd_runtime_is_ssi_tdm(struct rsnd_dai_stream *io);
/*
+ * DT
+ */
+#define rsnd_parse_of_node(priv, node) \
+ of_get_child_by_name(rsnd_priv_to_dev(priv)->of_node, node)
+#define RSND_NODE_DAI "rcar_sound,dai"
+#define RSND_NODE_SSI "rcar_sound,ssi"
+#define RSND_NODE_SRC "rcar_sound,src"
+#define RSND_NODE_CTU "rcar_sound,ctu"
+#define RSND_NODE_MIX "rcar_sound,mix"
+#define RSND_NODE_DVC "rcar_sound,dvc"
+
+/*
* R-Car sound DAI
*/
#define RSND_DAI_NAME_SIZE 16
@@ -382,6 +428,7 @@ struct rsnd_dai_stream {
};
#define rsnd_io_to_mod(io, i) ((i) < RSND_MOD_MAX ? (io)->mod[(i)] : NULL)
#define rsnd_io_to_mod_ssi(io) rsnd_io_to_mod((io), RSND_MOD_SSI)
+#define rsnd_io_to_mod_ssiu(io) rsnd_io_to_mod((io), RSND_MOD_SSIU)
#define rsnd_io_to_mod_ssip(io) rsnd_io_to_mod((io), RSND_MOD_SSIP)
#define rsnd_io_to_mod_src(io) rsnd_io_to_mod((io), RSND_MOD_SRC)
#define rsnd_io_to_mod_ctu(io) rsnd_io_to_mod((io), RSND_MOD_CTU)
@@ -428,8 +475,7 @@ int rsnd_dai_pointer_offset(struct rsnd_dai_stream *io, int additional);
int rsnd_dai_connect(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
enum rsnd_mod_type type);
-#define rsnd_dai_of_node(priv) \
- of_get_child_by_name(rsnd_priv_to_dev(priv)->of_node, "rcar_sound,dai")
+#define rsnd_dai_of_node(priv) rsnd_parse_of_node(priv, RSND_NODE_DAI)
/*
* R-Car Gen1/Gen2
@@ -453,6 +499,9 @@ int rsnd_adg_set_src_timesel_gen2(struct rsnd_mod *src_mod,
unsigned int out_rate);
int rsnd_adg_set_cmd_timsel_gen2(struct rsnd_mod *mod,
struct rsnd_dai_stream *io);
+#define rsnd_adg_clk_enable(priv) rsnd_adg_clk_control(priv, 1)
+#define rsnd_adg_clk_disable(priv) rsnd_adg_clk_control(priv, 0)
+void rsnd_adg_clk_control(struct rsnd_priv *priv, int enable);
/*
* R-Car sound priv
@@ -606,8 +655,7 @@ u32 rsnd_ssi_multi_slaves_runtime(struct rsnd_dai_stream *io);
__rsnd_ssi_is_pin_sharing(rsnd_io_to_mod_ssi(io))
int __rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod);
-#define rsnd_ssi_of_node(priv) \
- of_get_child_by_name(rsnd_priv_to_dev(priv)->of_node, "rcar_sound,ssi")
+#define rsnd_ssi_of_node(priv) rsnd_parse_of_node(priv, RSND_NODE_SSI)
void rsnd_parse_connect_ssi(struct rsnd_dai *rdai,
struct device_node *playback,
struct device_node *capture);
@@ -633,8 +681,7 @@ unsigned int rsnd_src_get_rate(struct rsnd_priv *priv,
struct rsnd_dai_stream *io,
int is_in);
-#define rsnd_src_of_node(priv) \
- of_get_child_by_name(rsnd_priv_to_dev(priv)->of_node, "rcar_sound,src")
+#define rsnd_src_of_node(priv) rsnd_parse_of_node(priv, RSND_NODE_SRC)
#define rsnd_parse_connect_src(rdai, playback, capture) \
rsnd_parse_connect_common(rdai, rsnd_src_mod_get, \
rsnd_src_of_node(rsnd_rdai_to_priv(rdai)), \
@@ -647,8 +694,7 @@ int rsnd_ctu_probe(struct rsnd_priv *priv);
void rsnd_ctu_remove(struct rsnd_priv *priv);
int rsnd_ctu_converted_channel(struct rsnd_mod *mod);
struct rsnd_mod *rsnd_ctu_mod_get(struct rsnd_priv *priv, int id);
-#define rsnd_ctu_of_node(priv) \
- of_get_child_by_name(rsnd_priv_to_dev(priv)->of_node, "rcar_sound,ctu")
+#define rsnd_ctu_of_node(priv) rsnd_parse_of_node(priv, RSND_NODE_CTU)
#define rsnd_parse_connect_ctu(rdai, playback, capture) \
rsnd_parse_connect_common(rdai, rsnd_ctu_mod_get, \
rsnd_ctu_of_node(rsnd_rdai_to_priv(rdai)), \
@@ -660,8 +706,7 @@ struct rsnd_mod *rsnd_ctu_mod_get(struct rsnd_priv *priv, int id);
int rsnd_mix_probe(struct rsnd_priv *priv);
void rsnd_mix_remove(struct rsnd_priv *priv);
struct rsnd_mod *rsnd_mix_mod_get(struct rsnd_priv *priv, int id);
-#define rsnd_mix_of_node(priv) \
- of_get_child_by_name(rsnd_priv_to_dev(priv)->of_node, "rcar_sound,mix")
+#define rsnd_mix_of_node(priv) rsnd_parse_of_node(priv, RSND_NODE_MIX)
#define rsnd_parse_connect_mix(rdai, playback, capture) \
rsnd_parse_connect_common(rdai, rsnd_mix_mod_get, \
rsnd_mix_of_node(rsnd_rdai_to_priv(rdai)), \
@@ -673,8 +718,7 @@ struct rsnd_mod *rsnd_mix_mod_get(struct rsnd_priv *priv, int id);
int rsnd_dvc_probe(struct rsnd_priv *priv);
void rsnd_dvc_remove(struct rsnd_priv *priv);
struct rsnd_mod *rsnd_dvc_mod_get(struct rsnd_priv *priv, int id);
-#define rsnd_dvc_of_node(priv) \
- of_get_child_by_name(rsnd_priv_to_dev(priv)->of_node, "rcar_sound,dvc")
+#define rsnd_dvc_of_node(priv) rsnd_parse_of_node(priv, RSND_NODE_DVC)
#define rsnd_parse_connect_dvc(rdai, playback, capture) \
rsnd_parse_connect_common(rdai, rsnd_dvc_mod_get, \
rsnd_dvc_of_node(rsnd_rdai_to_priv(rdai)), \
diff --git a/sound/soc/sh/rcar/src.c b/sound/soc/sh/rcar/src.c
index 969a5169de25..3a8f65bd1bf9 100644
--- a/sound/soc/sh/rcar/src.c
+++ b/sound/soc/sh/rcar/src.c
@@ -189,6 +189,7 @@ static void rsnd_src_set_convert_rate(struct rsnd_dai_stream *io,
struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
struct device *dev = rsnd_priv_to_dev(priv);
struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
+ int use_src = 0;
u32 fin, fout;
u32 ifscr, fsrate, adinr;
u32 cr, route;
@@ -214,6 +215,8 @@ static void rsnd_src_set_convert_rate(struct rsnd_dai_stream *io,
return;
}
+ use_src = (fin != fout) | rsnd_src_sync_is_enabled(mod);
+
/*
* SRC_ADINR
*/
@@ -225,7 +228,7 @@ static void rsnd_src_set_convert_rate(struct rsnd_dai_stream *io,
*/
ifscr = 0;
fsrate = 0;
- if (fin != fout) {
+ if (use_src) {
u64 n;
ifscr = 1;
@@ -239,7 +242,7 @@ static void rsnd_src_set_convert_rate(struct rsnd_dai_stream *io,
*/
cr = 0x00011110;
route = 0x0;
- if (fin != fout) {
+ if (use_src) {
route = 0x1;
if (rsnd_src_sync_is_enabled(mod)) {
@@ -327,8 +330,8 @@ static void rsnd_src_status_clear(struct rsnd_mod *mod)
{
u32 val = OUF_SRC(rsnd_mod_id(mod));
- rsnd_mod_bset(mod, SCU_SYS_STATUS0, val, val);
- rsnd_mod_bset(mod, SCU_SYS_STATUS1, val, val);
+ rsnd_mod_write(mod, SCU_SYS_STATUS0, val);
+ rsnd_mod_write(mod, SCU_SYS_STATUS1, val);
}
static bool rsnd_src_error_occurred(struct rsnd_mod *mod)
@@ -475,7 +478,7 @@ static int rsnd_src_probe_(struct rsnd_mod *mod,
return ret;
}
- ret = rsnd_dma_attach(io, mod, &src->dma, 0);
+ ret = rsnd_dma_attach(io, mod, &src->dma);
return ret;
}
diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c
index 6cb6db005fc4..411bda2387ad 100644
--- a/sound/soc/sh/rcar/ssi.c
+++ b/sound/soc/sh/rcar/ssi.c
@@ -417,11 +417,14 @@ static int rsnd_ssi_hw_params(struct rsnd_mod *mod,
int chan = params_channels(params);
/*
- * Already working.
- * It will happen if SSI has parent/child connection.
+ * snd_pcm_ops::hw_params will be called *before*
+ * snd_soc_dai_ops::trigger. Thus, ssi->usrcnt is 0
+ * in 1st call.
*/
- if (ssi->usrcnt > 1) {
+ if (ssi->usrcnt) {
/*
+ * Already working.
+ * It will happen if SSI has parent/child connection.
* it is error if child <-> parent SSI uses
* different channels.
*/
@@ -644,10 +647,14 @@ static int rsnd_ssi_common_probe(struct rsnd_mod *mod,
if (ret < 0)
return ret;
- ret = devm_request_irq(dev, ssi->irq,
- rsnd_ssi_interrupt,
- IRQF_SHARED,
- dev_name(dev), mod);
+ /*
+ * SSI might be called again as PIO fallback
+ * It is easy to manual handling for IRQ request/free
+ */
+ ret = request_irq(ssi->irq,
+ rsnd_ssi_interrupt,
+ IRQF_SHARED,
+ dev_name(dev), mod);
return ret;
}
@@ -669,7 +676,6 @@ static int rsnd_ssi_dma_probe(struct rsnd_mod *mod,
struct rsnd_priv *priv)
{
struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
- int dma_id = 0; /* not needed */
int ret;
/*
@@ -684,7 +690,7 @@ static int rsnd_ssi_dma_probe(struct rsnd_mod *mod,
return ret;
/* SSI probe might be called many times in MUX multi path */
- ret = rsnd_dma_attach(io, mod, &ssi->dma, dma_id);
+ ret = rsnd_dma_attach(io, mod, &ssi->dma);
return ret;
}
@@ -694,11 +700,9 @@ static int rsnd_ssi_dma_remove(struct rsnd_mod *mod,
struct rsnd_priv *priv)
{
struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
- struct device *dev = rsnd_priv_to_dev(priv);
- int irq = ssi->irq;
/* PIO will request IRQ again */
- devm_free_irq(dev, irq, mod);
+ free_irq(ssi->irq, mod);
return 0;
}
diff --git a/sound/soc/sh/rcar/ssiu.c b/sound/soc/sh/rcar/ssiu.c
index 6f9b388ec5a8..4e817c8a18c0 100644
--- a/sound/soc/sh/rcar/ssiu.c
+++ b/sound/soc/sh/rcar/ssiu.c
@@ -33,6 +33,26 @@ static int rsnd_ssiu_init(struct rsnd_mod *mod,
u32 mask1, val1;
u32 mask2, val2;
+ /* clear status */
+ switch (id) {
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ rsnd_mod_write(mod, SSI_SYS_STATUS0, 0xf << (id * 4));
+ rsnd_mod_write(mod, SSI_SYS_STATUS2, 0xf << (id * 4));
+ rsnd_mod_write(mod, SSI_SYS_STATUS4, 0xf << (id * 4));
+ rsnd_mod_write(mod, SSI_SYS_STATUS6, 0xf << (id * 4));
+ break;
+ case 9:
+ rsnd_mod_write(mod, SSI_SYS_STATUS1, 0xf << 4);
+ rsnd_mod_write(mod, SSI_SYS_STATUS3, 0xf << 4);
+ rsnd_mod_write(mod, SSI_SYS_STATUS5, 0xf << 4);
+ rsnd_mod_write(mod, SSI_SYS_STATUS7, 0xf << 4);
+ break;
+ }
+
/*
* SSI_MODE0
*/
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c
index c0bbcd903261..7c3bf160a1a1 100644
--- a/sound/soc/soc-core.c
+++ b/sound/soc/soc-core.c
@@ -626,7 +626,7 @@ static void codec2codec_close_delayed_work(struct work_struct *work)
int snd_soc_suspend(struct device *dev)
{
struct snd_soc_card *card = dev_get_drvdata(dev);
- struct snd_soc_codec *codec;
+ struct snd_soc_component *component;
struct snd_soc_pcm_runtime *rtd;
int i;
@@ -702,39 +702,39 @@ int snd_soc_suspend(struct device *dev)
dapm_mark_endpoints_dirty(card);
snd_soc_dapm_sync(&card->dapm);
- /* suspend all CODECs */
- list_for_each_entry(codec, &card->codec_dev_list, card_list) {
- struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+ /* suspend all COMPONENTs */
+ list_for_each_entry(component, &card->component_dev_list, card_list) {
+ struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
- /* If there are paths active then the CODEC will be held with
+ /* If there are paths active then the COMPONENT will be held with
* bias _ON and should not be suspended. */
- if (!codec->suspended) {
+ if (!component->suspended) {
switch (snd_soc_dapm_get_bias_level(dapm)) {
case SND_SOC_BIAS_STANDBY:
/*
- * If the CODEC is capable of idle
+ * If the COMPONENT is capable of idle
* bias off then being in STANDBY
* means it's doing something,
* otherwise fall through.
*/
if (dapm->idle_bias_off) {
- dev_dbg(codec->dev,
+ dev_dbg(component->dev,
"ASoC: idle_bias_off CODEC on over suspend\n");
break;
}
case SND_SOC_BIAS_OFF:
- if (codec->driver->suspend)
- codec->driver->suspend(codec);
- codec->suspended = 1;
- if (codec->component.regmap)
- regcache_mark_dirty(codec->component.regmap);
+ if (component->suspend)
+ component->suspend(component);
+ component->suspended = 1;
+ if (component->regmap)
+ regcache_mark_dirty(component->regmap);
/* deactivate pins to sleep state */
- pinctrl_pm_select_sleep_state(codec->dev);
+ pinctrl_pm_select_sleep_state(component->dev);
break;
default:
- dev_dbg(codec->dev,
- "ASoC: CODEC is on over suspend\n");
+ dev_dbg(component->dev,
+ "ASoC: COMPONENT is on over suspend\n");
break;
}
}
@@ -768,7 +768,7 @@ static void soc_resume_deferred(struct work_struct *work)
struct snd_soc_card *card =
container_of(work, struct snd_soc_card, deferred_resume_work);
struct snd_soc_pcm_runtime *rtd;
- struct snd_soc_codec *codec;
+ struct snd_soc_component *component;
int i;
/* our power state is still SNDRV_CTL_POWER_D3hot from suspend time,
@@ -794,11 +794,11 @@ static void soc_resume_deferred(struct work_struct *work)
cpu_dai->driver->resume(cpu_dai);
}
- list_for_each_entry(codec, &card->codec_dev_list, card_list) {
- if (codec->suspended) {
- if (codec->driver->resume)
- codec->driver->resume(codec);
- codec->suspended = 0;
+ list_for_each_entry(component, &card->component_dev_list, card_list) {
+ if (component->suspended) {
+ if (component->resume)
+ component->resume(component);
+ component->suspended = 0;
}
}
@@ -993,6 +993,7 @@ static int soc_bind_dai_link(struct snd_soc_card *card,
struct snd_soc_dai_link_component cpu_dai_component;
struct snd_soc_dai **codec_dais;
struct snd_soc_platform *platform;
+ struct device_node *platform_of_node;
const char *platform_name;
int i;
@@ -1042,9 +1043,12 @@ static int soc_bind_dai_link(struct snd_soc_card *card,
/* find one from the set of registered platforms */
list_for_each_entry(platform, &platform_list, list) {
+ platform_of_node = platform->dev->of_node;
+ if (!platform_of_node && platform->dev->parent->of_node)
+ platform_of_node = platform->dev->parent->of_node;
+
if (dai_link->platform_of_node) {
- if (platform->dev->of_node !=
- dai_link->platform_of_node)
+ if (platform_of_node != dai_link->platform_of_node)
continue;
} else {
if (strcmp(platform->component.name, platform_name))
@@ -1072,9 +1076,7 @@ static void soc_remove_component(struct snd_soc_component *component)
if (!component->card)
return;
- /* This is a HACK and will be removed soon */
- if (component->codec)
- list_del(&component->codec->card_list);
+ list_del(&component->card_list);
if (component->remove)
component->remove(component);
@@ -1443,10 +1445,7 @@ static int soc_probe_component(struct snd_soc_card *card,
component->num_dapm_routes);
list_add(&dapm->list, &card->dapm_list);
-
- /* This is a HACK and will be removed soon */
- if (component->codec)
- list_add(&component->codec->card_list, &card->codec_dev_list);
+ list_add(&component->card_list, &card->component_dev_list);
return 0;
@@ -1706,7 +1705,8 @@ static int soc_bind_aux_dev(struct snd_soc_card *card, int num)
}
component->init = aux_dev->init;
- list_add(&component->list_aux, &card->aux_comp_list);
+ component->auxiliary = 1;
+
return 0;
err_defer:
@@ -1722,7 +1722,10 @@ static int soc_probe_aux_devices(struct snd_soc_card *card)
for (order = SND_SOC_COMP_ORDER_FIRST; order <= SND_SOC_COMP_ORDER_LAST;
order++) {
- list_for_each_entry(comp, &card->aux_comp_list, list_aux) {
+ list_for_each_entry(comp, &card->component_dev_list, card_list) {
+ if (!comp->auxiliary)
+ continue;
+
if (comp->driver->probe_order == order) {
ret = soc_probe_component(card, comp);
if (ret < 0) {
@@ -1746,11 +1749,14 @@ static void soc_remove_aux_devices(struct snd_soc_card *card)
for (order = SND_SOC_COMP_ORDER_FIRST; order <= SND_SOC_COMP_ORDER_LAST;
order++) {
list_for_each_entry_safe(comp, _comp,
- &card->aux_comp_list, list_aux) {
+ &card->component_dev_list, card_list) {
+
+ if (!comp->auxiliary)
+ continue;
+
if (comp->driver->remove_order == order) {
soc_remove_component(comp);
- /* remove it from the card's aux_comp_list */
- list_del(&comp->list_aux);
+ comp->auxiliary = 0;
}
}
}
@@ -2926,6 +2932,8 @@ static int snd_soc_component_initialize(struct snd_soc_component *component,
component->driver = driver;
component->probe = component->driver->probe;
component->remove = component->driver->remove;
+ component->suspend = component->driver->suspend;
+ component->resume = component->driver->resume;
dapm = &component->dapm;
dapm->dev = dev;
@@ -3275,6 +3283,20 @@ static void snd_soc_codec_drv_remove(struct snd_soc_component *component)
codec->driver->remove(codec);
}
+static int snd_soc_codec_drv_suspend(struct snd_soc_component *component)
+{
+ struct snd_soc_codec *codec = snd_soc_component_to_codec(component);
+
+ return codec->driver->suspend(codec);
+}
+
+static int snd_soc_codec_drv_resume(struct snd_soc_component *component)
+{
+ struct snd_soc_codec *codec = snd_soc_component_to_codec(component);
+
+ return codec->driver->resume(codec);
+}
+
static int snd_soc_codec_drv_write(struct snd_soc_component *component,
unsigned int reg, unsigned int val)
{
@@ -3336,6 +3358,10 @@ int snd_soc_register_codec(struct device *dev,
codec->component.probe = snd_soc_codec_drv_probe;
if (codec_drv->remove)
codec->component.remove = snd_soc_codec_drv_remove;
+ if (codec_drv->suspend)
+ codec->component.suspend = snd_soc_codec_drv_suspend;
+ if (codec_drv->resume)
+ codec->component.resume = snd_soc_codec_drv_resume;
if (codec_drv->write)
codec->component.write = snd_soc_codec_drv_write;
if (codec_drv->read)
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c
index 3bbe32ee4630..27dd02e57b31 100644
--- a/sound/soc/soc-dapm.c
+++ b/sound/soc/soc-dapm.c
@@ -330,6 +330,11 @@ static int dapm_kcontrol_data_alloc(struct snd_soc_dapm_widget *widget,
case snd_soc_dapm_mixer_named_ctl:
mc = (struct soc_mixer_control *)kcontrol->private_value;
+ if (mc->autodisable && snd_soc_volsw_is_stereo(mc))
+ dev_warn(widget->dapm->dev,
+ "ASoC: Unsupported stereo autodisable control '%s'\n",
+ ctrl_name);
+
if (mc->autodisable) {
struct snd_soc_dapm_widget template;
@@ -723,7 +728,8 @@ 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_path *p, int i)
+static void dapm_set_mixer_path_status(struct snd_soc_dapm_path *p, int i,
+ int nth_path)
{
struct soc_mixer_control *mc = (struct soc_mixer_control *)
p->sink->kcontrol_news[i].private_value;
@@ -736,7 +742,25 @@ static void dapm_set_mixer_path_status(struct snd_soc_dapm_path *p, int i)
if (reg != SND_SOC_NOPM) {
soc_dapm_read(p->sink->dapm, reg, &val);
- val = (val >> shift) & mask;
+ /*
+ * The nth_path argument allows this function to know
+ * which path of a kcontrol it is setting the initial
+ * status for. Ideally this would support any number
+ * of paths and channels. But since kcontrols only come
+ * in mono and stereo variants, we are limited to 2
+ * channels.
+ *
+ * The following code assumes for stereo controls the
+ * first path is the left channel, and all remaining
+ * paths are the right channel.
+ */
+ if (snd_soc_volsw_is_stereo(mc) && nth_path > 0) {
+ if (reg != mc->rreg)
+ soc_dapm_read(p->sink->dapm, mc->rreg, &val);
+ val = (val >> mc->rshift) & mask;
+ } else {
+ val = (val >> shift) & mask;
+ }
if (invert)
val = max - val;
p->connect = !!val;
@@ -749,13 +773,13 @@ static void dapm_set_mixer_path_status(struct snd_soc_dapm_path *p, int i)
static int dapm_connect_mixer(struct snd_soc_dapm_context *dapm,
struct snd_soc_dapm_path *path, const char *control_name)
{
- int i;
+ int i, nth_path = 0;
/* search for mixer kcontrol */
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);
+ dapm_set_mixer_path_status(path, i, nth_path++);
return 0;
}
}
@@ -1626,6 +1650,15 @@ static void dapm_widget_update(struct snd_soc_card *card)
dev_err(w->dapm->dev, "ASoC: %s DAPM update failed: %d\n",
w->name, ret);
+ if (update->has_second_set) {
+ ret = soc_dapm_update_bits(w->dapm, update->reg2,
+ update->mask2, update->val2);
+ if (ret < 0)
+ dev_err(w->dapm->dev,
+ "ASoC: %s DAPM update failed: %d\n",
+ w->name, ret);
+ }
+
for (wi = 0; wi < wlist->num_widgets; wi++) {
w = wlist->widgets[wi];
@@ -2177,7 +2210,8 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_mux_update_power);
/* test and update the power status of a mixer or switch widget */
static int soc_dapm_mixer_update_power(struct snd_soc_card *card,
- struct snd_kcontrol *kcontrol, int connect)
+ struct snd_kcontrol *kcontrol,
+ int connect, int rconnect)
{
struct snd_soc_dapm_path *path;
int found = 0;
@@ -2186,8 +2220,33 @@ 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) {
+ /*
+ * Ideally this function should support any number of
+ * paths and channels. But since kcontrols only come
+ * in mono and stereo variants, we are limited to 2
+ * channels.
+ *
+ * The following code assumes for stereo controls the
+ * first path (when 'found == 0') is the left channel,
+ * and all remaining paths (when 'found == 1') are the
+ * right channel.
+ *
+ * A stereo control is signified by a valid 'rconnect'
+ * value, either 0 for unconnected, or >= 0 for connected.
+ * This is chosen instead of using snd_soc_volsw_is_stereo,
+ * so that the behavior of snd_soc_dapm_mixer_update_power
+ * doesn't change even when the kcontrol passed in is
+ * stereo.
+ *
+ * It passes 'connect' as the path connect status for
+ * the left channel, and 'rconnect' for the right
+ * channel.
+ */
+ if (found && rconnect >= 0)
+ soc_dapm_connect_path(path, rconnect, "mixer update");
+ else
+ soc_dapm_connect_path(path, connect, "mixer update");
found = 1;
- soc_dapm_connect_path(path, connect, "mixer update");
}
if (found)
@@ -2205,7 +2264,7 @@ int snd_soc_dapm_mixer_update_power(struct snd_soc_dapm_context *dapm,
mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
card->update = update;
- ret = soc_dapm_mixer_update_power(card, kcontrol, connect);
+ ret = soc_dapm_mixer_update_power(card, kcontrol, connect, -1);
card->update = NULL;
mutex_unlock(&card->dapm_mutex);
if (ret > 0)
@@ -3030,22 +3089,28 @@ int snd_soc_dapm_get_volsw(struct snd_kcontrol *kcontrol,
int reg = mc->reg;
unsigned int shift = mc->shift;
int max = mc->max;
+ unsigned int width = fls(max);
unsigned int mask = (1 << fls(max)) - 1;
unsigned int invert = mc->invert;
- unsigned int val;
+ unsigned int reg_val, val, rval = 0;
int ret = 0;
- if (snd_soc_volsw_is_stereo(mc))
- dev_warn(dapm->dev,
- "ASoC: Control '%s' is stereo, which is not supported\n",
- kcontrol->id.name);
-
mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
if (dapm_kcontrol_is_powered(kcontrol) && reg != SND_SOC_NOPM) {
- ret = soc_dapm_read(dapm, reg, &val);
- val = (val >> shift) & mask;
+ ret = soc_dapm_read(dapm, reg, &reg_val);
+ val = (reg_val >> shift) & mask;
+
+ if (ret == 0 && reg != mc->rreg)
+ ret = soc_dapm_read(dapm, mc->rreg, &reg_val);
+
+ if (snd_soc_volsw_is_stereo(mc))
+ rval = (reg_val >> mc->rshift) & mask;
} else {
- val = dapm_kcontrol_get_value(kcontrol);
+ reg_val = dapm_kcontrol_get_value(kcontrol);
+ val = reg_val & mask;
+
+ if (snd_soc_volsw_is_stereo(mc))
+ rval = (reg_val >> width) & mask;
}
mutex_unlock(&card->dapm_mutex);
@@ -3057,6 +3122,13 @@ int snd_soc_dapm_get_volsw(struct snd_kcontrol *kcontrol,
else
ucontrol->value.integer.value[0] = val;
+ if (snd_soc_volsw_is_stereo(mc)) {
+ if (invert)
+ ucontrol->value.integer.value[1] = max - rval;
+ else
+ ucontrol->value.integer.value[1] = rval;
+ }
+
return ret;
}
EXPORT_SYMBOL_GPL(snd_soc_dapm_get_volsw);
@@ -3080,46 +3152,66 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol,
int reg = mc->reg;
unsigned int shift = mc->shift;
int max = mc->max;
- unsigned int mask = (1 << fls(max)) - 1;
+ unsigned int width = fls(max);
+ unsigned int mask = (1 << width) - 1;
unsigned int invert = mc->invert;
- unsigned int val;
- int connect, change, reg_change = 0;
- struct snd_soc_dapm_update update;
+ unsigned int val, rval = 0;
+ int connect, rconnect = -1, change, reg_change = 0;
+ struct snd_soc_dapm_update update = { NULL };
int ret = 0;
- if (snd_soc_volsw_is_stereo(mc))
- dev_warn(dapm->dev,
- "ASoC: Control '%s' is stereo, which is not supported\n",
- kcontrol->id.name);
-
val = (ucontrol->value.integer.value[0] & mask);
connect = !!val;
if (invert)
val = max - val;
+ if (snd_soc_volsw_is_stereo(mc)) {
+ rval = (ucontrol->value.integer.value[1] & mask);
+ rconnect = !!rval;
+ if (invert)
+ rval = max - rval;
+ }
+
mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
- change = dapm_kcontrol_set_value(kcontrol, val);
+ /* This assumes field width < (bits in unsigned int / 2) */
+ if (width > sizeof(unsigned int) * 8 / 2)
+ dev_warn(dapm->dev,
+ "ASoC: control %s field width limit exceeded\n",
+ kcontrol->id.name);
+ change = dapm_kcontrol_set_value(kcontrol, val | (rval << width));
if (reg != SND_SOC_NOPM) {
- mask = mask << shift;
val = val << shift;
+ rval = rval << mc->rshift;
+
+ reg_change = soc_dapm_test_bits(dapm, reg, mask << shift, val);
- reg_change = soc_dapm_test_bits(dapm, reg, mask, val);
+ if (snd_soc_volsw_is_stereo(mc))
+ reg_change |= soc_dapm_test_bits(dapm, mc->rreg,
+ mask << mc->rshift,
+ rval);
}
if (change || reg_change) {
if (reg_change) {
+ if (snd_soc_volsw_is_stereo(mc)) {
+ update.has_second_set = true;
+ update.reg2 = mc->rreg;
+ update.mask2 = mask << mc->rshift;
+ update.val2 = rval;
+ }
update.kcontrol = kcontrol;
update.reg = reg;
- update.mask = mask;
+ update.mask = mask << shift;
update.val = val;
card->update = &update;
}
change |= reg_change;
- ret = soc_dapm_mixer_update_power(card, kcontrol, connect);
+ ret = soc_dapm_mixer_update_power(card, kcontrol, connect,
+ rconnect);
card->update = NULL;
}
@@ -3192,7 +3284,7 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol,
unsigned int *item = ucontrol->value.enumerated.item;
unsigned int val, change, reg_change = 0;
unsigned int mask;
- struct snd_soc_dapm_update update;
+ struct snd_soc_dapm_update update = { NULL };
int ret = 0;
if (item[0] >= e->items)
diff --git a/sound/soc/soc-generic-dmaengine-pcm.c b/sound/soc/soc-generic-dmaengine-pcm.c
index 6cef3977507a..17eb14935577 100644
--- a/sound/soc/soc-generic-dmaengine-pcm.c
+++ b/sound/soc/soc-generic-dmaengine-pcm.c
@@ -263,7 +263,6 @@ static int dmaengine_pcm_new(struct snd_soc_pcm_runtime *rtd)
struct dmaengine_pcm *pcm = soc_platform_to_pcm(rtd->platform);
const struct snd_dmaengine_pcm_config *config = pcm->config;
struct device *dev = rtd->platform->dev;
- struct snd_dmaengine_dai_dma_data *dma_data;
struct snd_pcm_substream *substream;
size_t prealloc_buffer_size;
size_t max_buffer_size;
@@ -278,19 +277,11 @@ static int dmaengine_pcm_new(struct snd_soc_pcm_runtime *rtd)
max_buffer_size = SIZE_MAX;
}
-
for (i = SNDRV_PCM_STREAM_PLAYBACK; i <= SNDRV_PCM_STREAM_CAPTURE; i++) {
substream = rtd->pcm->streams[i].substream;
if (!substream)
continue;
- dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
-
- if (!pcm->chan[i] &&
- (pcm->flags & SND_DMAENGINE_PCM_FLAG_CUSTOM_CHANNEL_NAME))
- pcm->chan[i] = dma_request_slave_channel(dev,
- dma_data->chan_name);
-
if (!pcm->chan[i] && (pcm->flags & SND_DMAENGINE_PCM_FLAG_COMPAT)) {
pcm->chan[i] = dmaengine_pcm_compat_request_channel(rtd,
substream);
@@ -359,9 +350,7 @@ static int dmaengine_pcm_request_chan_of(struct dmaengine_pcm *pcm,
const char *name;
struct dma_chan *chan;
- if ((pcm->flags & (SND_DMAENGINE_PCM_FLAG_NO_DT |
- SND_DMAENGINE_PCM_FLAG_CUSTOM_CHANNEL_NAME)) ||
- !dev->of_node)
+ if ((pcm->flags & SND_DMAENGINE_PCM_FLAG_NO_DT) || !dev->of_node)
return 0;
if (config && config->dma_dev) {
diff --git a/sound/soc/soc-utils.c b/sound/soc/soc-utils.c
index 393e8f0fe2cc..644d9a9ebfbc 100644
--- a/sound/soc/soc-utils.c
+++ b/sound/soc/soc-utils.c
@@ -58,6 +58,205 @@ int snd_soc_params_to_bclk(struct snd_pcm_hw_params *params)
}
EXPORT_SYMBOL_GPL(snd_soc_params_to_bclk);
+int snd_soc_component_enable_pin(struct snd_soc_component *component,
+ const char *pin)
+{
+ struct snd_soc_dapm_context *dapm =
+ snd_soc_component_get_dapm(component);
+ char *full_name;
+ int ret;
+
+ if (!component->name_prefix)
+ return snd_soc_dapm_enable_pin(dapm, pin);
+
+ full_name = kasprintf(GFP_KERNEL, "%s %s", component->name_prefix, pin);
+ if (!full_name)
+ return -ENOMEM;
+
+ ret = snd_soc_dapm_enable_pin(dapm, full_name);
+ kfree(full_name);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(snd_soc_component_enable_pin);
+
+int snd_soc_component_enable_pin_unlocked(struct snd_soc_component *component,
+ const char *pin)
+{
+ struct snd_soc_dapm_context *dapm =
+ snd_soc_component_get_dapm(component);
+ char *full_name;
+ int ret;
+
+ if (!component->name_prefix)
+ return snd_soc_dapm_enable_pin_unlocked(dapm, pin);
+
+ full_name = kasprintf(GFP_KERNEL, "%s %s", component->name_prefix, pin);
+ if (!full_name)
+ return -ENOMEM;
+
+ ret = snd_soc_dapm_enable_pin_unlocked(dapm, full_name);
+ kfree(full_name);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(snd_soc_component_enable_pin_unlocked);
+
+int snd_soc_component_disable_pin(struct snd_soc_component *component,
+ const char *pin)
+{
+ struct snd_soc_dapm_context *dapm =
+ snd_soc_component_get_dapm(component);
+ char *full_name;
+ int ret;
+
+ if (!component->name_prefix)
+ return snd_soc_dapm_disable_pin(dapm, pin);
+
+ full_name = kasprintf(GFP_KERNEL, "%s %s", component->name_prefix, pin);
+ if (!full_name)
+ return -ENOMEM;
+
+ ret = snd_soc_dapm_disable_pin(dapm, full_name);
+ kfree(full_name);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(snd_soc_component_disable_pin);
+
+int snd_soc_component_disable_pin_unlocked(struct snd_soc_component *component,
+ const char *pin)
+{
+ struct snd_soc_dapm_context *dapm =
+ snd_soc_component_get_dapm(component);
+ char *full_name;
+ int ret;
+
+ if (!component->name_prefix)
+ return snd_soc_dapm_disable_pin_unlocked(dapm, pin);
+
+ full_name = kasprintf(GFP_KERNEL, "%s %s", component->name_prefix, pin);
+ if (!full_name)
+ return -ENOMEM;
+
+ ret = snd_soc_dapm_disable_pin_unlocked(dapm, full_name);
+ kfree(full_name);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(snd_soc_component_disable_pin_unlocked);
+
+int snd_soc_component_nc_pin(struct snd_soc_component *component,
+ const char *pin)
+{
+ struct snd_soc_dapm_context *dapm =
+ snd_soc_component_get_dapm(component);
+ char *full_name;
+ int ret;
+
+ if (!component->name_prefix)
+ return snd_soc_dapm_nc_pin(dapm, pin);
+
+ full_name = kasprintf(GFP_KERNEL, "%s %s", component->name_prefix, pin);
+ if (!full_name)
+ return -ENOMEM;
+
+ ret = snd_soc_dapm_nc_pin(dapm, full_name);
+ kfree(full_name);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(snd_soc_component_nc_pin);
+
+int snd_soc_component_nc_pin_unlocked(struct snd_soc_component *component,
+ const char *pin)
+{
+ struct snd_soc_dapm_context *dapm =
+ snd_soc_component_get_dapm(component);
+ char *full_name;
+ int ret;
+
+ if (!component->name_prefix)
+ return snd_soc_dapm_nc_pin_unlocked(dapm, pin);
+
+ full_name = kasprintf(GFP_KERNEL, "%s %s", component->name_prefix, pin);
+ if (!full_name)
+ return -ENOMEM;
+
+ ret = snd_soc_dapm_nc_pin_unlocked(dapm, full_name);
+ kfree(full_name);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(snd_soc_component_nc_pin_unlocked);
+
+int snd_soc_component_get_pin_status(struct snd_soc_component *component,
+ const char *pin)
+{
+ struct snd_soc_dapm_context *dapm =
+ snd_soc_component_get_dapm(component);
+ char *full_name;
+ int ret;
+
+ if (!component->name_prefix)
+ return snd_soc_dapm_get_pin_status(dapm, pin);
+
+ full_name = kasprintf(GFP_KERNEL, "%s %s", component->name_prefix, pin);
+ if (!full_name)
+ return -ENOMEM;
+
+ ret = snd_soc_dapm_get_pin_status(dapm, full_name);
+ kfree(full_name);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(snd_soc_component_get_pin_status);
+
+int snd_soc_component_force_enable_pin(struct snd_soc_component *component,
+ const char *pin)
+{
+ struct snd_soc_dapm_context *dapm =
+ snd_soc_component_get_dapm(component);
+ char *full_name;
+ int ret;
+
+ if (!component->name_prefix)
+ return snd_soc_dapm_force_enable_pin(dapm, pin);
+
+ full_name = kasprintf(GFP_KERNEL, "%s %s", component->name_prefix, pin);
+ if (!full_name)
+ return -ENOMEM;
+
+ ret = snd_soc_dapm_force_enable_pin(dapm, full_name);
+ kfree(full_name);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(snd_soc_component_force_enable_pin);
+
+int snd_soc_component_force_enable_pin_unlocked(
+ struct snd_soc_component *component,
+ const char *pin)
+{
+ struct snd_soc_dapm_context *dapm =
+ snd_soc_component_get_dapm(component);
+ char *full_name;
+ int ret;
+
+ if (!component->name_prefix)
+ return snd_soc_dapm_force_enable_pin_unlocked(dapm, pin);
+
+ full_name = kasprintf(GFP_KERNEL, "%s %s", component->name_prefix, pin);
+ if (!full_name)
+ return -ENOMEM;
+
+ ret = snd_soc_dapm_force_enable_pin_unlocked(dapm, full_name);
+ kfree(full_name);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(snd_soc_component_force_enable_pin_unlocked);
+
static const struct snd_pcm_hardware dummy_dma_hardware = {
/* Random values to keep userspace happy when checking constraints */
.info = SNDRV_PCM_INFO_INTERLEAVED |
diff --git a/sound/soc/sti/uniperif_player.c b/sound/soc/sti/uniperif_player.c
index 1bc8ebc2528e..ad54d4cf58ad 100644
--- a/sound/soc/sti/uniperif_player.c
+++ b/sound/soc/sti/uniperif_player.c
@@ -614,7 +614,11 @@ static int uni_player_ctl_iec958_put(struct snd_kcontrol *kcontrol,
iec958->status[3] = ucontrol->value.iec958.status[3];
mutex_unlock(&player->ctrl_lock);
- uni_player_set_channel_status(player, NULL);
+ if (player->substream && player->substream->runtime)
+ uni_player_set_channel_status(player,
+ player->substream->runtime);
+ else
+ uni_player_set_channel_status(player, NULL);
return 0;
}
diff --git a/sound/soc/sunxi/sun4i-codec.c b/sound/soc/sunxi/sun4i-codec.c
index e047ec06d538..56ed9472e89f 100644
--- a/sound/soc/sunxi/sun4i-codec.c
+++ b/sound/soc/sunxi/sun4i-codec.c
@@ -765,11 +765,11 @@ static struct snd_soc_card *sun4i_codec_create_card(struct device *dev)
card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
if (!card)
- return NULL;
+ return ERR_PTR(-ENOMEM);
card->dai_link = sun4i_codec_create_link(dev, &card->num_links);
if (!card->dai_link)
- return NULL;
+ return ERR_PTR(-ENOMEM);
card->dev = dev;
card->name = "sun4i-codec";
@@ -829,12 +829,6 @@ static int sun4i_codec_probe(struct platform_device *pdev)
return PTR_ERR(scodec->clk_module);
}
- /* Enable the bus clock */
- if (clk_prepare_enable(scodec->clk_apb)) {
- dev_err(&pdev->dev, "Failed to enable the APB clock\n");
- return -EINVAL;
- }
-
scodec->gpio_pa = devm_gpiod_get_optional(&pdev->dev, "allwinner,pa",
GPIOD_OUT_LOW);
if (IS_ERR(scodec->gpio_pa)) {
@@ -844,6 +838,12 @@ static int sun4i_codec_probe(struct platform_device *pdev)
return ret;
}
+ /* Enable the bus clock */
+ if (clk_prepare_enable(scodec->clk_apb)) {
+ dev_err(&pdev->dev, "Failed to enable the APB clock\n");
+ return -EINVAL;
+ }
+
/* DMA configuration for TX FIFO */
scodec->playback_dma_data.addr = res->start + SUN4I_CODEC_DAC_TXDATA;
scodec->playback_dma_data.maxburst = 4;
@@ -876,7 +876,8 @@ static int sun4i_codec_probe(struct platform_device *pdev)
}
card = sun4i_codec_create_card(&pdev->dev);
- if (!card) {
+ if (IS_ERR(card)) {
+ ret = PTR_ERR(card);
dev_err(&pdev->dev, "Failed to create our card\n");
goto err_unregister_codec;
}
diff --git a/sound/sparc/dbri.c b/sound/sparc/dbri.c
index 0190cb6332f2..52063b262667 100644
--- a/sound/sparc/dbri.c
+++ b/sound/sparc/dbri.c
@@ -304,7 +304,7 @@ struct snd_dbri {
spinlock_t lock;
struct dbri_dma *dma; /* Pointer to our DMA block */
- u32 dma_dvma; /* DBRI visible DMA address */
+ dma_addr_t dma_dvma; /* DBRI visible DMA address */
void __iomem *regs; /* dbri HW regs */
int dbri_irqp; /* intr queue pointer */
@@ -657,12 +657,14 @@ static void dbri_cmdwait(struct snd_dbri *dbri)
*/
static s32 *dbri_cmdlock(struct snd_dbri *dbri, int len)
{
+ u32 dvma_addr = (u32)dbri->dma_dvma;
+
/* Space for 2 WAIT cmds (replaced later by 1 JUMP cmd) */
len += 2;
spin_lock(&dbri->cmdlock);
if (dbri->cmdptr - dbri->dma->cmd + len < DBRI_NO_CMDS - 2)
return dbri->cmdptr + 2;
- else if (len < sbus_readl(dbri->regs + REG8) - dbri->dma_dvma)
+ else if (len < sbus_readl(dbri->regs + REG8) - dvma_addr)
return dbri->dma->cmd;
else
printk(KERN_ERR "DBRI: no space for commands.");
@@ -680,6 +682,7 @@ static s32 *dbri_cmdlock(struct snd_dbri *dbri, int len)
*/
static void dbri_cmdsend(struct snd_dbri *dbri, s32 *cmd, int len)
{
+ u32 dvma_addr = (u32)dbri->dma_dvma;
s32 tmp, addr;
static int wait_id = 0;
@@ -689,7 +692,7 @@ static void dbri_cmdsend(struct snd_dbri *dbri, s32 *cmd, int len)
*(cmd+1) = DBRI_CMD(D_WAIT, 1, wait_id);
/* Replace the last command with JUMP */
- addr = dbri->dma_dvma + (cmd - len - dbri->dma->cmd) * sizeof(s32);
+ addr = dvma_addr + (cmd - len - dbri->dma->cmd) * sizeof(s32);
*(dbri->cmdptr+1) = addr;
*(dbri->cmdptr) = DBRI_CMD(D_JUMP, 0, 0);
@@ -747,6 +750,7 @@ static void dbri_reset(struct snd_dbri *dbri)
/* Lock must not be held before calling this */
static void dbri_initialize(struct snd_dbri *dbri)
{
+ u32 dvma_addr = (u32)dbri->dma_dvma;
s32 *cmd;
u32 dma_addr;
unsigned long flags;
@@ -764,7 +768,7 @@ static void dbri_initialize(struct snd_dbri *dbri)
/*
* Initialize the interrupt ring buffer.
*/
- dma_addr = dbri->dma_dvma + dbri_dma_off(intr, 0);
+ dma_addr = dvma_addr + dbri_dma_off(intr, 0);
dbri->dma->intr[0] = dma_addr;
dbri->dbri_irqp = 1;
/*
@@ -778,7 +782,7 @@ static void dbri_initialize(struct snd_dbri *dbri)
dbri->cmdptr = cmd;
*(cmd++) = DBRI_CMD(D_WAIT, 1, 0);
*(cmd++) = DBRI_CMD(D_WAIT, 1, 0);
- dma_addr = dbri->dma_dvma + dbri_dma_off(cmd, 0);
+ dma_addr = dvma_addr + dbri_dma_off(cmd, 0);
sbus_writel(dma_addr, dbri->regs + REG8);
spin_unlock(&dbri->cmdlock);
@@ -1077,6 +1081,7 @@ static void recv_fixed(struct snd_dbri *dbri, int pipe, volatile __u32 *ptr)
static int setup_descs(struct snd_dbri *dbri, int streamno, unsigned int period)
{
struct dbri_streaminfo *info = &dbri->stream_info[streamno];
+ u32 dvma_addr = (u32)dbri->dma_dvma;
__u32 dvma_buffer;
int desc;
int len;
@@ -1177,7 +1182,7 @@ static int setup_descs(struct snd_dbri *dbri, int streamno, unsigned int period)
else {
dbri->next_desc[last_desc] = desc;
dbri->dma->desc[last_desc].nda =
- dbri->dma_dvma + dbri_dma_off(desc, desc);
+ dvma_addr + dbri_dma_off(desc, desc);
}
last_desc = desc;
@@ -1192,7 +1197,7 @@ static int setup_descs(struct snd_dbri *dbri, int streamno, unsigned int period)
}
dbri->dma->desc[last_desc].nda =
- dbri->dma_dvma + dbri_dma_off(desc, first_desc);
+ dvma_addr + dbri_dma_off(desc, first_desc);
dbri->next_desc[last_desc] = first_desc;
dbri->pipes[info->pipe].first_desc = first_desc;
dbri->pipes[info->pipe].desc = first_desc;
@@ -1697,6 +1702,7 @@ interrupts are disabled.
static void xmit_descs(struct snd_dbri *dbri)
{
struct dbri_streaminfo *info;
+ u32 dvma_addr;
s32 *cmd;
unsigned long flags;
int first_td;
@@ -1704,6 +1710,7 @@ static void xmit_descs(struct snd_dbri *dbri)
if (dbri == NULL)
return; /* Disabled */
+ dvma_addr = (u32)dbri->dma_dvma;
info = &dbri->stream_info[DBRI_REC];
spin_lock_irqsave(&dbri->lock, flags);
@@ -1718,7 +1725,7 @@ static void xmit_descs(struct snd_dbri *dbri)
*(cmd++) = DBRI_CMD(D_SDP, 0,
dbri->pipes[info->pipe].sdp
| D_SDP_P | D_SDP_EVERY | D_SDP_C);
- *(cmd++) = dbri->dma_dvma +
+ *(cmd++) = dvma_addr +
dbri_dma_off(desc, first_td);
dbri_cmdsend(dbri, cmd, 2);
@@ -1740,7 +1747,7 @@ static void xmit_descs(struct snd_dbri *dbri)
*(cmd++) = DBRI_CMD(D_SDP, 0,
dbri->pipes[info->pipe].sdp
| D_SDP_P | D_SDP_EVERY | D_SDP_C);
- *(cmd++) = dbri->dma_dvma +
+ *(cmd++) = dvma_addr +
dbri_dma_off(desc, first_td);
dbri_cmdsend(dbri, cmd, 2);
@@ -2539,7 +2546,7 @@ static int snd_dbri_create(struct snd_card *card,
if (!dbri->dma)
return -ENOMEM;
- dprintk(D_GEN, "DMA Cmd Block 0x%p (0x%08x)\n",
+ dprintk(D_GEN, "DMA Cmd Block 0x%p (%pad)\n",
dbri->dma, dbri->dma_dvma);
/* Map the registers into memory. */
diff --git a/sound/usb/card.c b/sound/usb/card.c
index 9e5276d6dda0..2ddc034673a8 100644
--- a/sound/usb/card.c
+++ b/sound/usb/card.c
@@ -315,7 +315,8 @@ static int snd_usb_audio_free(struct snd_usb_audio *chip)
snd_usb_endpoint_free(ep);
mutex_destroy(&chip->mutex);
- dev_set_drvdata(&chip->dev->dev, NULL);
+ if (!atomic_read(&chip->shutdown))
+ dev_set_drvdata(&chip->dev->dev, NULL);
kfree(chip);
return 0;
}
diff --git a/sound/usb/quirks-table.h b/sound/usb/quirks-table.h
index c60a776e815d..8a59d4782a0f 100644
--- a/sound/usb/quirks-table.h
+++ b/sound/usb/quirks-table.h
@@ -2907,6 +2907,23 @@ AU0828_DEVICE(0x2040, 0x7260, "Hauppauge", "HVR-950Q"),
AU0828_DEVICE(0x2040, 0x7213, "Hauppauge", "HVR-950Q"),
AU0828_DEVICE(0x2040, 0x7270, "Hauppauge", "HVR-950Q"),
+/* Syntek STK1160 */
+{
+ .match_flags = USB_DEVICE_ID_MATCH_DEVICE |
+ USB_DEVICE_ID_MATCH_INT_CLASS |
+ USB_DEVICE_ID_MATCH_INT_SUBCLASS,
+ .idVendor = 0x05e1,
+ .idProduct = 0x0408,
+ .bInterfaceClass = USB_CLASS_AUDIO,
+ .bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL,
+ .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) {
+ .vendor_name = "Syntek",
+ .product_name = "STK1160",
+ .ifnum = QUIRK_ANY_INTERFACE,
+ .type = QUIRK_AUDIO_ALIGN_TRANSFER
+ }
+},
+
/* Digidesign Mbox */
{
/* Thanks to Clemens Ladisch <clemens@ladisch.de> */