summaryrefslogtreecommitdiff
path: root/sound
diff options
context:
space:
mode:
Diffstat (limited to 'sound')
-rw-r--r--sound/core/control.c24
-rw-r--r--sound/core/control_led.c5
-rw-r--r--sound/core/memalloc.c91
-rw-r--r--sound/firewire/motu/motu-hwdep.c4
-rw-r--r--sound/hda/ext/hdac_ext_stream.c41
-rw-r--r--sound/hda/hdac_controller.c4
-rw-r--r--sound/hda/hdac_stream.c54
-rw-r--r--sound/i2c/other/ak4117.c2
-rw-r--r--sound/pci/azt3328.c9
-rw-r--r--sound/pci/hda/cs35l41_hda.c20
-rw-r--r--sound/pci/hda/hda_bind.c2
-rw-r--r--sound/pci/hda/hda_codec.c4
-rw-r--r--sound/pci/hda/patch_hdmi.c148
-rw-r--r--sound/pci/hda/patch_realtek.c97
-rw-r--r--sound/pci/hda/patch_via.c3
-rw-r--r--sound/soc/amd/acp-es8336.c6
-rw-r--r--sound/soc/amd/yc/acp6x-mach.c35
-rw-r--r--sound/soc/codecs/cs42l56.c6
-rw-r--r--[-rwxr-xr-x]sound/soc/codecs/es8326.c0
-rw-r--r--[-rwxr-xr-x]sound/soc/codecs/es8326.h0
-rw-r--r--sound/soc/codecs/lochnagar-sc.c2
-rw-r--r--sound/soc/codecs/rt5670.c2
-rw-r--r--sound/soc/codecs/rt9120.c12
-rw-r--r--sound/soc/codecs/wcd-clsh-v2.c6
-rw-r--r--sound/soc/codecs/wm8904.c7
-rw-r--r--sound/soc/codecs/wm8994.c5
-rw-r--r--sound/soc/codecs/wsa883x.c4
-rw-r--r--sound/soc/fsl/fsl-asoc-card.c8
-rw-r--r--sound/soc/fsl/fsl_micfil.c16
-rw-r--r--sound/soc/fsl/fsl_ssi.c4
-rw-r--r--sound/soc/fsl/imx-audmux.c3
-rw-r--r--sound/soc/generic/audio-graph-card.c4
-rw-r--r--sound/soc/intel/Kconfig1
-rw-r--r--sound/soc/intel/avs/Makefile4
-rw-r--r--sound/soc/intel/avs/apl.c24
-rw-r--r--sound/soc/intel/avs/avs.h80
-rw-r--r--sound/soc/intel/avs/board_selection.c33
-rw-r--r--sound/soc/intel/avs/boards/Kconfig8
-rw-r--r--sound/soc/intel/avs/boards/Makefile2
-rw-r--r--sound/soc/intel/avs/boards/probe.c64
-rw-r--r--sound/soc/intel/avs/core.c26
-rw-r--r--sound/soc/intel/avs/debugfs.c436
-rw-r--r--sound/soc/intel/avs/ipc.c2
-rw-r--r--sound/soc/intel/avs/messages.c104
-rw-r--r--sound/soc/intel/avs/messages.h53
-rw-r--r--sound/soc/intel/avs/pcm.c6
-rw-r--r--sound/soc/intel/avs/probes.c313
-rw-r--r--sound/soc/intel/avs/registers.h3
-rw-r--r--sound/soc/intel/avs/skl.c17
-rw-r--r--sound/soc/intel/avs/utils.c22
-rw-r--r--sound/soc/intel/boards/Kconfig2
-rw-r--r--sound/soc/intel/boards/Makefile3
-rw-r--r--sound/soc/intel/boards/bytcht_es8316.c20
-rw-r--r--sound/soc/intel/boards/bytcr_rt5640.c37
-rw-r--r--sound/soc/intel/boards/bytcr_rt5651.c2
-rw-r--r--sound/soc/intel/boards/bytcr_wm5102.c2
-rw-r--r--sound/soc/intel/boards/sof_cs42l42.c3
-rw-r--r--sound/soc/intel/boards/sof_es8336.c16
-rw-r--r--sound/soc/intel/boards/sof_nau8825.c36
-rw-r--r--sound/soc/intel/boards/sof_realtek_common.c3
-rw-r--r--sound/soc/intel/boards/sof_rt5682.c11
-rw-r--r--sound/soc/intel/boards/sof_sdw.c10
-rw-r--r--sound/soc/intel/boards/sof_sdw_common.h20
-rw-r--r--sound/soc/intel/boards/sof_sdw_rt1316.c239
-rw-r--r--sound/soc/intel/boards/sof_sdw_rt1318.c120
-rw-r--r--sound/soc/intel/boards/sof_sdw_rt_amp.c (renamed from sound/soc/intel/boards/sof_sdw_rt1308.c)139
-rw-r--r--sound/soc/intel/boards/sof_ssp_amp.c5
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-adl-match.c20
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-jsl-match.c5
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-rpl-match.c52
-rw-r--r--sound/soc/intel/skylake/skl-sst-cldma.c27
-rw-r--r--sound/soc/intel/skylake/skl-topology.c73
-rw-r--r--sound/soc/intel/skylake/skl-topology.h1
-rw-r--r--sound/soc/intel/skylake/skl.c5
-rw-r--r--sound/soc/mediatek/Kconfig4
-rw-r--r--sound/soc/mediatek/mt8173/mt8173-rt5650-rt5514.c7
-rw-r--r--sound/soc/mediatek/mt8183/mt8183-mt6358-ts3a227-max98357.c14
-rw-r--r--sound/soc/mediatek/mt8186/mt8186-mt6366-rt1019-rt5682s.c22
-rw-r--r--sound/soc/mediatek/mt8195/mt8195-mt6359.c30
-rw-r--r--sound/soc/qcom/Kconfig21
-rw-r--r--sound/soc/qcom/Makefile2
-rw-r--r--sound/soc/qcom/common.c114
-rw-r--r--sound/soc/qcom/common.h10
-rw-r--r--sound/soc/qcom/lpass-cpu.c5
-rw-r--r--sound/soc/qcom/sc8280xp.c1
-rw-r--r--sound/soc/qcom/sdw.c123
-rw-r--r--sound/soc/qcom/sdw.h18
-rw-r--r--sound/soc/qcom/sm8250.c1
-rw-r--r--sound/soc/rockchip/rockchip_pdm.c1
-rw-r--r--sound/soc/rockchip/rockchip_spdif.c1
-rw-r--r--sound/soc/soc-pcm.c20
-rw-r--r--sound/soc/sof/core.c9
-rw-r--r--sound/soc/sof/debug.c4
-rw-r--r--sound/soc/sof/intel/hda-dsp.c72
-rw-r--r--sound/soc/sof/intel/hda.c2
-rw-r--r--sound/soc/sof/intel/hda.h1
-rw-r--r--sound/soc/sof/intel/tgl.c2
-rw-r--r--sound/soc/sof/ipc4-mtrace.c7
-rw-r--r--sound/soc/sof/mediatek/mtk-adsp-common.c2
-rw-r--r--sound/soc/sof/pm.c9
-rw-r--r--sound/soc/sof/sof-audio.c16
-rw-r--r--sound/sound_core.c2
-rw-r--r--sound/synth/emux/emux.c2
-rw-r--r--sound/usb/card.h1
-rw-r--r--sound/usb/endpoint.c16
-rw-r--r--sound/usb/endpoint.h3
-rw-r--r--sound/usb/implicit.c9
-rw-r--r--sound/usb/implicit.h2
-rw-r--r--sound/usb/line6/driver.c3
-rw-r--r--sound/usb/line6/midi.c6
-rw-r--r--sound/usb/line6/midibuf.c25
-rw-r--r--sound/usb/line6/midibuf.h5
-rw-r--r--sound/usb/line6/pod.c3
-rw-r--r--sound/usb/pcm.c256
-rw-r--r--sound/usb/pcm.h2
-rw-r--r--sound/usb/quirks-table.h2
-rw-r--r--sound/usb/quirks.c4
-rw-r--r--sound/usb/stream.c6
-rw-r--r--sound/usb/usbaudio.h4
-rw-r--r--sound/xen/xen_snd_front.c3
120 files changed, 2533 insertions, 1016 deletions
diff --git a/sound/core/control.c b/sound/core/control.c
index 50e7ba66f187..82aa1af1d1d8 100644
--- a/sound/core/control.c
+++ b/sound/core/control.c
@@ -1203,14 +1203,19 @@ static int snd_ctl_elem_read(struct snd_card *card,
const u32 pattern = 0xdeadbeef;
int ret;
+ down_read(&card->controls_rwsem);
kctl = snd_ctl_find_id(card, &control->id);
- if (kctl == NULL)
- return -ENOENT;
+ if (kctl == NULL) {
+ ret = -ENOENT;
+ goto unlock;
+ }
index_offset = snd_ctl_get_ioff(kctl, &control->id);
vd = &kctl->vd[index_offset];
- if (!(vd->access & SNDRV_CTL_ELEM_ACCESS_READ) || kctl->get == NULL)
- return -EPERM;
+ if (!(vd->access & SNDRV_CTL_ELEM_ACCESS_READ) || kctl->get == NULL) {
+ ret = -EPERM;
+ goto unlock;
+ }
snd_ctl_build_ioff(&control->id, kctl, index_offset);
@@ -1220,7 +1225,7 @@ static int snd_ctl_elem_read(struct snd_card *card,
info.id = control->id;
ret = __snd_ctl_elem_info(card, kctl, &info, NULL);
if (ret < 0)
- return ret;
+ goto unlock;
#endif
if (!snd_ctl_skip_validation(&info))
@@ -1230,7 +1235,7 @@ static int snd_ctl_elem_read(struct snd_card *card,
ret = kctl->get(kctl, control);
snd_power_unref(card);
if (ret < 0)
- return ret;
+ goto unlock;
if (!snd_ctl_skip_validation(&info) &&
sanity_check_elem_value(card, control, &info, pattern) < 0) {
dev_err(card->dev,
@@ -1238,8 +1243,11 @@ static int snd_ctl_elem_read(struct snd_card *card,
control->id.iface, control->id.device,
control->id.subdevice, control->id.name,
control->id.index);
- return -EINVAL;
+ ret = -EINVAL;
+ goto unlock;
}
+unlock:
+ up_read(&card->controls_rwsem);
return ret;
}
@@ -1253,9 +1261,7 @@ static int snd_ctl_elem_read_user(struct snd_card *card,
if (IS_ERR(control))
return PTR_ERR(control);
- down_read(&card->controls_rwsem);
result = snd_ctl_elem_read(card, control);
- up_read(&card->controls_rwsem);
if (result < 0)
goto error;
diff --git a/sound/core/control_led.c b/sound/core/control_led.c
index f975cc85772b..3cadd40100f3 100644
--- a/sound/core/control_led.c
+++ b/sound/core/control_led.c
@@ -530,12 +530,11 @@ static ssize_t set_led_id(struct snd_ctl_led_card *led_card, const char *buf, si
bool attach)
{
char buf2[256], *s, *os;
- size_t len = max(sizeof(s) - 1, count);
struct snd_ctl_elem_id id;
int err;
- strncpy(buf2, buf, len);
- buf2[len] = '\0';
+ if (strscpy(buf2, buf, sizeof(buf2)) < 0)
+ return -E2BIG;
memset(&id, 0, sizeof(id));
id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
s = buf2;
diff --git a/sound/core/memalloc.c b/sound/core/memalloc.c
index 3cf5a87d69ea..f901504b5afc 100644
--- a/sound/core/memalloc.c
+++ b/sound/core/memalloc.c
@@ -541,16 +541,15 @@ static void *snd_dma_noncontig_alloc(struct snd_dma_buffer *dmab, size_t size)
struct sg_table *sgt;
void *p;
+#ifdef CONFIG_SND_DMA_SGBUF
+ if (cpu_feature_enabled(X86_FEATURE_XENPV))
+ return snd_dma_sg_fallback_alloc(dmab, size);
+#endif
sgt = dma_alloc_noncontiguous(dmab->dev.dev, size, dmab->dev.dir,
- DEFAULT_GFP | __GFP_COMP, 0);
+ DEFAULT_GFP, 0);
#ifdef CONFIG_SND_DMA_SGBUF
- if (!sgt && !get_dma_ops(dmab->dev.dev)) {
- if (dmab->dev.type == SNDRV_DMA_TYPE_DEV_WC_SG)
- dmab->dev.type = SNDRV_DMA_TYPE_DEV_WC_SG_FALLBACK;
- else
- dmab->dev.type = SNDRV_DMA_TYPE_DEV_SG_FALLBACK;
+ if (!sgt && !get_dma_ops(dmab->dev.dev))
return snd_dma_sg_fallback_alloc(dmab, size);
- }
#endif
if (!sgt)
return NULL;
@@ -717,19 +716,38 @@ static const struct snd_malloc_ops snd_dma_sg_wc_ops = {
/* Fallback SG-buffer allocations for x86 */
struct snd_dma_sg_fallback {
+ bool use_dma_alloc_coherent;
size_t count;
struct page **pages;
+ /* DMA address array; the first page contains #pages in ~PAGE_MASK */
+ dma_addr_t *addrs;
};
static void __snd_dma_sg_fallback_free(struct snd_dma_buffer *dmab,
struct snd_dma_sg_fallback *sgbuf)
{
- bool wc = dmab->dev.type == SNDRV_DMA_TYPE_DEV_WC_SG_FALLBACK;
- size_t i;
-
- for (i = 0; i < sgbuf->count && sgbuf->pages[i]; i++)
- do_free_pages(page_address(sgbuf->pages[i]), PAGE_SIZE, wc);
+ size_t i, size;
+
+ if (sgbuf->pages && sgbuf->addrs) {
+ i = 0;
+ while (i < sgbuf->count) {
+ if (!sgbuf->pages[i] || !sgbuf->addrs[i])
+ break;
+ size = sgbuf->addrs[i] & ~PAGE_MASK;
+ if (WARN_ON(!size))
+ break;
+ if (sgbuf->use_dma_alloc_coherent)
+ dma_free_coherent(dmab->dev.dev, size << PAGE_SHIFT,
+ page_address(sgbuf->pages[i]),
+ sgbuf->addrs[i] & PAGE_MASK);
+ else
+ do_free_pages(page_address(sgbuf->pages[i]),
+ size << PAGE_SHIFT, false);
+ i += size;
+ }
+ }
kvfree(sgbuf->pages);
+ kvfree(sgbuf->addrs);
kfree(sgbuf);
}
@@ -738,24 +756,36 @@ static void *snd_dma_sg_fallback_alloc(struct snd_dma_buffer *dmab, size_t size)
struct snd_dma_sg_fallback *sgbuf;
struct page **pagep, *curp;
size_t chunk, npages;
+ dma_addr_t *addrp;
dma_addr_t addr;
void *p;
- bool wc = dmab->dev.type == SNDRV_DMA_TYPE_DEV_WC_SG_FALLBACK;
+
+ /* correct the type */
+ if (dmab->dev.type == SNDRV_DMA_TYPE_DEV_SG)
+ dmab->dev.type = SNDRV_DMA_TYPE_DEV_SG_FALLBACK;
+ else if (dmab->dev.type == SNDRV_DMA_TYPE_DEV_WC_SG)
+ dmab->dev.type = SNDRV_DMA_TYPE_DEV_WC_SG_FALLBACK;
sgbuf = kzalloc(sizeof(*sgbuf), GFP_KERNEL);
if (!sgbuf)
return NULL;
+ sgbuf->use_dma_alloc_coherent = cpu_feature_enabled(X86_FEATURE_XENPV);
size = PAGE_ALIGN(size);
sgbuf->count = size >> PAGE_SHIFT;
sgbuf->pages = kvcalloc(sgbuf->count, sizeof(*sgbuf->pages), GFP_KERNEL);
- if (!sgbuf->pages)
+ sgbuf->addrs = kvcalloc(sgbuf->count, sizeof(*sgbuf->addrs), GFP_KERNEL);
+ if (!sgbuf->pages || !sgbuf->addrs)
goto error;
pagep = sgbuf->pages;
- chunk = size;
+ addrp = sgbuf->addrs;
+ chunk = (PAGE_SIZE - 1) << PAGE_SHIFT; /* to fit in low bits in addrs */
while (size > 0) {
chunk = min(size, chunk);
- p = do_alloc_pages(dmab->dev.dev, chunk, &addr, wc);
+ if (sgbuf->use_dma_alloc_coherent)
+ p = dma_alloc_coherent(dmab->dev.dev, chunk, &addr, DEFAULT_GFP);
+ else
+ p = do_alloc_pages(dmab->dev.dev, chunk, &addr, false);
if (!p) {
if (chunk <= PAGE_SIZE)
goto error;
@@ -767,17 +797,25 @@ static void *snd_dma_sg_fallback_alloc(struct snd_dma_buffer *dmab, size_t size)
size -= chunk;
/* fill pages */
npages = chunk >> PAGE_SHIFT;
+ *addrp = npages; /* store in lower bits */
curp = virt_to_page(p);
- while (npages--)
+ while (npages--) {
*pagep++ = curp++;
+ *addrp++ |= addr;
+ addr += PAGE_SIZE;
+ }
}
p = vmap(sgbuf->pages, sgbuf->count, VM_MAP, PAGE_KERNEL);
if (!p)
goto error;
+
+ if (dmab->dev.type == SNDRV_DMA_TYPE_DEV_WC_SG_FALLBACK)
+ set_pages_array_wc(sgbuf->pages, sgbuf->count);
+
dmab->private_data = sgbuf;
/* store the first page address for convenience */
- dmab->addr = snd_sgbuf_get_addr(dmab, 0);
+ dmab->addr = sgbuf->addrs[0] & PAGE_MASK;
return p;
error:
@@ -787,10 +825,23 @@ static void *snd_dma_sg_fallback_alloc(struct snd_dma_buffer *dmab, size_t size)
static void snd_dma_sg_fallback_free(struct snd_dma_buffer *dmab)
{
+ struct snd_dma_sg_fallback *sgbuf = dmab->private_data;
+
+ if (dmab->dev.type == SNDRV_DMA_TYPE_DEV_WC_SG_FALLBACK)
+ set_pages_array_wb(sgbuf->pages, sgbuf->count);
vunmap(dmab->area);
__snd_dma_sg_fallback_free(dmab, dmab->private_data);
}
+static dma_addr_t snd_dma_sg_fallback_get_addr(struct snd_dma_buffer *dmab,
+ size_t offset)
+{
+ struct snd_dma_sg_fallback *sgbuf = dmab->private_data;
+ size_t index = offset >> PAGE_SHIFT;
+
+ return (sgbuf->addrs[index] & PAGE_MASK) | (offset & ~PAGE_MASK);
+}
+
static int snd_dma_sg_fallback_mmap(struct snd_dma_buffer *dmab,
struct vm_area_struct *area)
{
@@ -805,8 +856,8 @@ static const struct snd_malloc_ops snd_dma_sg_fallback_ops = {
.alloc = snd_dma_sg_fallback_alloc,
.free = snd_dma_sg_fallback_free,
.mmap = snd_dma_sg_fallback_mmap,
+ .get_addr = snd_dma_sg_fallback_get_addr,
/* reuse vmalloc helpers */
- .get_addr = snd_dma_vmalloc_get_addr,
.get_page = snd_dma_vmalloc_get_page,
.get_chunk_size = snd_dma_vmalloc_get_chunk_size,
};
@@ -820,7 +871,7 @@ static void *snd_dma_noncoherent_alloc(struct snd_dma_buffer *dmab, size_t size)
void *p;
p = dma_alloc_noncoherent(dmab->dev.dev, size, &dmab->addr,
- dmab->dev.dir, DEFAULT_GFP | __GFP_COMP);
+ dmab->dev.dir, DEFAULT_GFP);
if (p)
dmab->dev.need_sync = dma_need_sync(dmab->dev.dev, dmab->addr);
return p;
diff --git a/sound/firewire/motu/motu-hwdep.c b/sound/firewire/motu/motu-hwdep.c
index a900fc0e7644..88d1f4b56e4b 100644
--- a/sound/firewire/motu/motu-hwdep.c
+++ b/sound/firewire/motu/motu-hwdep.c
@@ -87,6 +87,10 @@ static long hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count,
return -EFAULT;
count = consumed;
+ } else {
+ spin_unlock_irq(&motu->lock);
+
+ count = 0;
}
return count;
diff --git a/sound/hda/ext/hdac_ext_stream.c b/sound/hda/ext/hdac_ext_stream.c
index 2a071a09224d..11b7119cc47e 100644
--- a/sound/hda/ext/hdac_ext_stream.c
+++ b/sound/hda/ext/hdac_ext_stream.c
@@ -14,6 +14,7 @@
#include <sound/pcm.h>
#include <sound/hda_register.h>
#include <sound/hdaudio_ext.h>
+#include <sound/compress_driver.h>
/**
* snd_hdac_ext_stream_init - initialize each stream (aka device)
@@ -367,3 +368,43 @@ void snd_hdac_ext_stream_release(struct hdac_ext_stream *hext_stream, int type)
}
EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_release);
+
+/**
+ * snd_hdac_ext_cstream_assign - assign a host stream for compress
+ * @bus: HD-audio core bus
+ * @cstream: Compress stream to assign
+ *
+ * Assign an unused host stream for the given compress stream.
+ * If no stream is free, NULL is returned. Stream is decoupled
+ * before assignment.
+ */
+struct hdac_ext_stream *snd_hdac_ext_cstream_assign(struct hdac_bus *bus,
+ struct snd_compr_stream *cstream)
+{
+ struct hdac_ext_stream *res = NULL;
+ struct hdac_stream *hstream;
+
+ spin_lock_irq(&bus->reg_lock);
+ list_for_each_entry(hstream, &bus->stream_list, list) {
+ struct hdac_ext_stream *hext_stream = stream_to_hdac_ext_stream(hstream);
+
+ if (hstream->direction != cstream->direction)
+ continue;
+
+ if (!hstream->opened) {
+ res = hext_stream;
+ break;
+ }
+ }
+
+ if (res) {
+ snd_hdac_ext_stream_decouple_locked(bus, res, true);
+ res->hstream.opened = 1;
+ res->hstream.running = 0;
+ res->hstream.cstream = cstream;
+ }
+ spin_unlock_irq(&bus->reg_lock);
+
+ return res;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_ext_cstream_assign);
diff --git a/sound/hda/hdac_controller.c b/sound/hda/hdac_controller.c
index 9a60bfdb39ba..3c7af6558249 100644
--- a/sound/hda/hdac_controller.c
+++ b/sound/hda/hdac_controller.c
@@ -578,8 +578,8 @@ int snd_hdac_bus_handle_stream_irq(struct hdac_bus *bus, unsigned int status,
sd_status = snd_hdac_stream_readb(azx_dev, SD_STS);
snd_hdac_stream_writeb(azx_dev, SD_STS, SD_INT_MASK);
handled |= 1 << azx_dev->index;
- if (!azx_dev->substream || !azx_dev->running ||
- !(sd_status & SD_INT_COMPLETE))
+ if ((!azx_dev->substream && !azx_dev->cstream) ||
+ !azx_dev->running || !(sd_status & SD_INT_COMPLETE))
continue;
if (ack)
ack(bus, azx_dev);
diff --git a/sound/hda/hdac_stream.c b/sound/hda/hdac_stream.c
index 3b250ee7f6a7..547adbc22590 100644
--- a/sound/hda/hdac_stream.c
+++ b/sound/hda/hdac_stream.c
@@ -7,6 +7,7 @@
#include <linux/delay.h>
#include <linux/export.h>
#include <linux/clocksource.h>
+#include <sound/compress_driver.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/hdaudio.h>
@@ -487,11 +488,23 @@ int snd_hdac_stream_setup_periods(struct hdac_stream *azx_dev)
{
struct hdac_bus *bus = azx_dev->bus;
struct snd_pcm_substream *substream = azx_dev->substream;
- struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_compr_stream *cstream = azx_dev->cstream;
+ struct snd_pcm_runtime *runtime = NULL;
+ struct snd_dma_buffer *dmab;
__le32 *bdl;
int i, ofs, periods, period_bytes;
int pos_adj, pos_align;
+ if (substream) {
+ runtime = substream->runtime;
+ dmab = snd_pcm_get_dma_buf(substream);
+ } else if (cstream) {
+ dmab = snd_pcm_get_dma_buf(cstream);
+ } else {
+ WARN(1, "No substream or cstream assigned\n");
+ return -EINVAL;
+ }
+
/* reset BDL address */
snd_hdac_stream_writel(azx_dev, SD_BDLPL, 0);
snd_hdac_stream_writel(azx_dev, SD_BDLPU, 0);
@@ -505,7 +518,7 @@ int snd_hdac_stream_setup_periods(struct hdac_stream *azx_dev)
azx_dev->frags = 0;
pos_adj = bus->bdl_pos_adj;
- if (!azx_dev->no_period_wakeup && pos_adj > 0) {
+ if (runtime && !azx_dev->no_period_wakeup && pos_adj > 0) {
pos_align = pos_adj;
pos_adj = DIV_ROUND_UP(pos_adj * runtime->rate, 48000);
if (!pos_adj)
@@ -518,8 +531,7 @@ int snd_hdac_stream_setup_periods(struct hdac_stream *azx_dev)
pos_adj);
pos_adj = 0;
} else {
- ofs = setup_bdle(bus, snd_pcm_get_dma_buf(substream),
- azx_dev,
+ ofs = setup_bdle(bus, dmab, azx_dev,
&bdl, ofs, pos_adj, true);
if (ofs < 0)
goto error;
@@ -529,13 +541,11 @@ int snd_hdac_stream_setup_periods(struct hdac_stream *azx_dev)
for (i = 0; i < periods; i++) {
if (i == periods - 1 && pos_adj)
- ofs = setup_bdle(bus, snd_pcm_get_dma_buf(substream),
- azx_dev, &bdl, ofs,
- period_bytes - pos_adj, 0);
+ ofs = setup_bdle(bus, dmab, azx_dev,
+ &bdl, ofs, period_bytes - pos_adj, 0);
else
- ofs = setup_bdle(bus, snd_pcm_get_dma_buf(substream),
- azx_dev, &bdl, ofs,
- period_bytes,
+ ofs = setup_bdle(bus, dmab, azx_dev,
+ &bdl, ofs, period_bytes,
!azx_dev->no_period_wakeup);
if (ofs < 0)
goto error;
@@ -560,26 +570,32 @@ EXPORT_SYMBOL_GPL(snd_hdac_stream_setup_periods);
int snd_hdac_stream_set_params(struct hdac_stream *azx_dev,
unsigned int format_val)
{
-
- unsigned int bufsize, period_bytes;
struct snd_pcm_substream *substream = azx_dev->substream;
- struct snd_pcm_runtime *runtime;
+ struct snd_compr_stream *cstream = azx_dev->cstream;
+ unsigned int bufsize, period_bytes;
+ unsigned int no_period_wakeup;
int err;
- if (!substream)
+ if (substream) {
+ bufsize = snd_pcm_lib_buffer_bytes(substream);
+ period_bytes = snd_pcm_lib_period_bytes(substream);
+ no_period_wakeup = substream->runtime->no_period_wakeup;
+ } else if (cstream) {
+ bufsize = cstream->runtime->buffer_size;
+ period_bytes = cstream->runtime->fragment_size;
+ no_period_wakeup = 0;
+ } else {
return -EINVAL;
- runtime = substream->runtime;
- bufsize = snd_pcm_lib_buffer_bytes(substream);
- period_bytes = snd_pcm_lib_period_bytes(substream);
+ }
if (bufsize != azx_dev->bufsize ||
period_bytes != azx_dev->period_bytes ||
format_val != azx_dev->format_val ||
- runtime->no_period_wakeup != azx_dev->no_period_wakeup) {
+ no_period_wakeup != azx_dev->no_period_wakeup) {
azx_dev->bufsize = bufsize;
azx_dev->period_bytes = period_bytes;
azx_dev->format_val = format_val;
- azx_dev->no_period_wakeup = runtime->no_period_wakeup;
+ azx_dev->no_period_wakeup = no_period_wakeup;
err = snd_hdac_stream_setup_periods(azx_dev);
if (err < 0)
return err;
diff --git a/sound/i2c/other/ak4117.c b/sound/i2c/other/ak4117.c
index 1bc43e927d82..640501bb3ca6 100644
--- a/sound/i2c/other/ak4117.c
+++ b/sound/i2c/other/ak4117.c
@@ -47,7 +47,7 @@ static void reg_dump(struct ak4117 *ak4117)
static void snd_ak4117_free(struct ak4117 *chip)
{
- del_timer_sync(&chip->timer);
+ timer_shutdown_sync(&chip->timer);
kfree(chip);
}
diff --git a/sound/pci/azt3328.c b/sound/pci/azt3328.c
index 7f329dfc5404..0c6754bf9455 100644
--- a/sound/pci/azt3328.c
+++ b/sound/pci/azt3328.c
@@ -364,15 +364,6 @@ snd_azf3328_codec_inw(const struct snd_azf3328_codec_data *codec, unsigned reg)
}
static inline void
-snd_azf3328_codec_outl(const struct snd_azf3328_codec_data *codec,
- unsigned reg,
- u32 value
-)
-{
- outl(value, codec->io_base + reg);
-}
-
-static inline void
snd_azf3328_codec_outl_multi(const struct snd_azf3328_codec_data *codec,
unsigned reg, const void *buffer, int count
)
diff --git a/sound/pci/hda/cs35l41_hda.c b/sound/pci/hda/cs35l41_hda.c
index 91842c0c8c74..f7815ee24f83 100644
--- a/sound/pci/hda/cs35l41_hda.c
+++ b/sound/pci/hda/cs35l41_hda.c
@@ -598,8 +598,8 @@ static int cs35l41_system_suspend(struct device *dev)
dev_dbg(cs35l41->dev, "System Suspend\n");
if (cs35l41->hw_cfg.bst_type == CS35L41_EXT_BOOST_NO_VSPK_SWITCH) {
- dev_err(cs35l41->dev, "System Suspend not supported\n");
- return -EINVAL;
+ dev_err_once(cs35l41->dev, "System Suspend not supported\n");
+ return 0; /* don't block the whole system suspend */
}
ret = pm_runtime_force_suspend(dev);
@@ -624,8 +624,8 @@ static int cs35l41_system_resume(struct device *dev)
dev_dbg(cs35l41->dev, "System Resume\n");
if (cs35l41->hw_cfg.bst_type == CS35L41_EXT_BOOST_NO_VSPK_SWITCH) {
- dev_err(cs35l41->dev, "System Resume not supported\n");
- return -EINVAL;
+ dev_err_once(cs35l41->dev, "System Resume not supported\n");
+ return 0; /* don't block the whole system resume */
}
if (cs35l41->reset_gpio) {
@@ -647,6 +647,15 @@ static int cs35l41_system_resume(struct device *dev)
return ret;
}
+static int cs35l41_runtime_idle(struct device *dev)
+{
+ struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev);
+
+ if (cs35l41->hw_cfg.bst_type == CS35L41_EXT_BOOST_NO_VSPK_SWITCH)
+ return -EBUSY; /* suspend not supported yet on this model */
+ return 0;
+}
+
static int cs35l41_runtime_suspend(struct device *dev)
{
struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev);
@@ -1536,7 +1545,8 @@ void cs35l41_hda_remove(struct device *dev)
EXPORT_SYMBOL_NS_GPL(cs35l41_hda_remove, SND_HDA_SCODEC_CS35L41);
const struct dev_pm_ops cs35l41_hda_pm_ops = {
- RUNTIME_PM_OPS(cs35l41_runtime_suspend, cs35l41_runtime_resume, NULL)
+ RUNTIME_PM_OPS(cs35l41_runtime_suspend, cs35l41_runtime_resume,
+ cs35l41_runtime_idle)
SYSTEM_SLEEP_PM_OPS(cs35l41_system_suspend, cs35l41_system_resume)
};
EXPORT_SYMBOL_NS_GPL(cs35l41_hda_pm_ops, SND_HDA_SCODEC_CS35L41);
diff --git a/sound/pci/hda/hda_bind.c b/sound/pci/hda/hda_bind.c
index 1a868dd9dc4b..890c2f7c33fc 100644
--- a/sound/pci/hda/hda_bind.c
+++ b/sound/pci/hda/hda_bind.c
@@ -144,6 +144,7 @@ static int hda_codec_driver_probe(struct device *dev)
error:
snd_hda_codec_cleanup_for_unbind(codec);
+ codec->preset = NULL;
return err;
}
@@ -166,6 +167,7 @@ static int hda_codec_driver_remove(struct device *dev)
if (codec->patch_ops.free)
codec->patch_ops.free(codec);
snd_hda_codec_cleanup_for_unbind(codec);
+ codec->preset = NULL;
module_put(dev->driver->owner);
return 0;
}
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c
index b4d1e658c556..ac1cc7c5290e 100644
--- a/sound/pci/hda/hda_codec.c
+++ b/sound/pci/hda/hda_codec.c
@@ -795,7 +795,6 @@ void snd_hda_codec_cleanup_for_unbind(struct hda_codec *codec)
snd_array_free(&codec->cvt_setups);
snd_array_free(&codec->spdif_out);
snd_array_free(&codec->verbs);
- codec->preset = NULL;
codec->follower_dig_outs = NULL;
codec->spdif_status_reset = 0;
snd_array_free(&codec->mixers);
@@ -2886,7 +2885,8 @@ static unsigned int hda_call_codec_suspend(struct hda_codec *codec)
snd_hdac_enter_pm(&codec->core);
if (codec->patch_ops.suspend)
codec->patch_ops.suspend(codec);
- hda_cleanup_all_streams(codec);
+ if (!codec->no_stream_clean_at_suspend)
+ hda_cleanup_all_streams(codec);
state = hda_set_power_state(codec, AC_PWRST_D3);
update_power_acct(codec, true);
snd_hdac_leave_pm(&codec->core);
diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c
index 21edf7a619f0..9ea633fe9339 100644
--- a/sound/pci/hda/patch_hdmi.c
+++ b/sound/pci/hda/patch_hdmi.c
@@ -167,6 +167,7 @@ struct hdmi_spec {
struct hdmi_ops ops;
bool dyn_pin_out;
+ bool static_pcm_mapping;
/* hdmi interrupt trigger control flag for Nvidia codec */
bool hdmi_intr_trig_ctrl;
bool nv_dp_workaround; /* workaround DP audio infoframe for Nvidia */
@@ -1525,13 +1526,16 @@ static void update_eld(struct hda_codec *codec,
*/
pcm_jack = pin_idx_to_pcm_jack(codec, per_pin);
- if (eld->eld_valid) {
- hdmi_attach_hda_pcm(spec, per_pin);
- hdmi_pcm_setup_pin(spec, per_pin);
- } else {
- hdmi_pcm_reset_pin(spec, per_pin);
- hdmi_detach_hda_pcm(spec, per_pin);
+ if (!spec->static_pcm_mapping) {
+ if (eld->eld_valid) {
+ hdmi_attach_hda_pcm(spec, per_pin);
+ hdmi_pcm_setup_pin(spec, per_pin);
+ } else {
+ hdmi_pcm_reset_pin(spec, per_pin);
+ hdmi_detach_hda_pcm(spec, per_pin);
+ }
}
+
/* if pcm_idx == -1, it means this is in monitor connection event
* we can get the correct pcm_idx now.
*/
@@ -1738,6 +1742,7 @@ static void silent_stream_enable(struct hda_codec *codec,
switch (spec->silent_stream_type) {
case SILENT_STREAM_KAE:
+ silent_stream_enable_i915(codec, per_pin);
silent_stream_set_kae(codec, per_pin, true);
break;
case SILENT_STREAM_I915:
@@ -1975,6 +1980,8 @@ static int hdmi_add_cvt(struct hda_codec *codec, hda_nid_t cvt_nid)
static const struct snd_pci_quirk force_connect_list[] = {
SND_PCI_QUIRK(0x103c, 0x870f, "HP", 1),
SND_PCI_QUIRK(0x103c, 0x871a, "HP", 1),
+ SND_PCI_QUIRK(0x103c, 0x8711, "HP", 1),
+ SND_PCI_QUIRK(0x103c, 0x8715, "HP", 1),
SND_PCI_QUIRK(0x1462, 0xec94, "MS-7C94", 1),
SND_PCI_QUIRK(0x8086, 0x2081, "Intel NUC 10", 1),
{}
@@ -2279,8 +2286,8 @@ static int generic_hdmi_build_pcms(struct hda_codec *codec)
struct hdmi_spec *spec = codec->spec;
int idx, pcm_num;
- /* limit the PCM devices to the codec converters */
- pcm_num = spec->num_cvts;
+ /* limit the PCM devices to the codec converters or available PINs */
+ pcm_num = min(spec->num_cvts, spec->num_pins);
codec_dbg(codec, "hdmi: pcm_num set to %d\n", pcm_num);
for (idx = 0; idx < pcm_num; idx++) {
@@ -2377,6 +2384,11 @@ static int generic_hdmi_build_controls(struct hda_codec *codec)
struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
struct hdmi_eld *pin_eld = &per_pin->sink_eld;
+ if (spec->static_pcm_mapping) {
+ hdmi_attach_hda_pcm(spec, per_pin);
+ hdmi_pcm_setup_pin(spec, per_pin);
+ }
+
pin_eld->eld_valid = false;
hdmi_present_sense(per_pin, 0);
}
@@ -2878,9 +2890,33 @@ static int i915_hsw_setup_stream(struct hda_codec *codec, hda_nid_t cvt_nid,
hda_nid_t pin_nid, int dev_id, u32 stream_tag,
int format)
{
+ struct hdmi_spec *spec = codec->spec;
+ int pin_idx = pin_id_to_pin_index(codec, pin_nid, dev_id);
+ struct hdmi_spec_per_pin *per_pin;
+ int res;
+
+ if (pin_idx < 0)
+ per_pin = NULL;
+ else
+ per_pin = get_pin(spec, pin_idx);
+
haswell_verify_D0(codec, cvt_nid, pin_nid);
- return hdmi_setup_stream(codec, cvt_nid, pin_nid, dev_id,
- stream_tag, format);
+
+ if (spec->silent_stream_type == SILENT_STREAM_KAE && per_pin && per_pin->silent_stream) {
+ silent_stream_set_kae(codec, per_pin, false);
+ /* wait for pending transfers in codec to clear */
+ usleep_range(100, 200);
+ }
+
+ res = hdmi_setup_stream(codec, cvt_nid, pin_nid, dev_id,
+ stream_tag, format);
+
+ if (spec->silent_stream_type == SILENT_STREAM_KAE && per_pin && per_pin->silent_stream) {
+ usleep_range(100, 200);
+ silent_stream_set_kae(codec, per_pin, true);
+ }
+
+ return res;
}
/* pin_cvt_fixup ops override for HSW+ and VLV+ */
@@ -2900,6 +2936,88 @@ static void i915_pin_cvt_fixup(struct hda_codec *codec,
}
}
+#ifdef CONFIG_PM
+static int i915_adlp_hdmi_suspend(struct hda_codec *codec)
+{
+ struct hdmi_spec *spec = codec->spec;
+ bool silent_streams = false;
+ int pin_idx, res;
+
+ res = generic_hdmi_suspend(codec);
+
+ for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
+ struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
+
+ if (per_pin->silent_stream) {
+ silent_streams = true;
+ break;
+ }
+ }
+
+ if (silent_streams && spec->silent_stream_type == SILENT_STREAM_KAE) {
+ /*
+ * stream-id should remain programmed when codec goes
+ * to runtime suspend
+ */
+ codec->no_stream_clean_at_suspend = 1;
+
+ /*
+ * the system might go to S3, in which case keep-alive
+ * must be reprogrammed upon resume
+ */
+ codec->forced_resume = 1;
+
+ codec_dbg(codec, "HDMI: KAE active at suspend\n");
+ } else {
+ codec->no_stream_clean_at_suspend = 0;
+ codec->forced_resume = 0;
+ }
+
+ return res;
+}
+
+static int i915_adlp_hdmi_resume(struct hda_codec *codec)
+{
+ struct hdmi_spec *spec = codec->spec;
+ int pin_idx, res;
+
+ res = generic_hdmi_resume(codec);
+
+ /* KAE not programmed at suspend, nothing to do here */
+ if (!codec->no_stream_clean_at_suspend)
+ return res;
+
+ for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
+ struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
+
+ /*
+ * If system was in suspend with monitor connected,
+ * the codec setting may have been lost. Re-enable
+ * keep-alive.
+ */
+ if (per_pin->silent_stream) {
+ unsigned int param;
+
+ param = snd_hda_codec_read(codec, per_pin->cvt_nid, 0,
+ AC_VERB_GET_CONV, 0);
+ if (!param) {
+ codec_dbg(codec, "HDMI: KAE: restore stream id\n");
+ silent_stream_enable_i915(codec, per_pin);
+ }
+
+ param = snd_hda_codec_read(codec, per_pin->cvt_nid, 0,
+ AC_VERB_GET_DIGI_CONVERT_1, 0);
+ if (!(param & (AC_DIG3_KAE << 16))) {
+ codec_dbg(codec, "HDMI: KAE: restore DIG3_KAE\n");
+ silent_stream_set_kae(codec, per_pin, true);
+ }
+ }
+ }
+
+ return res;
+}
+#endif
+
/* precondition and allocation for Intel codecs */
static int alloc_intel_hdmi(struct hda_codec *codec)
{
@@ -3030,8 +3148,14 @@ static int patch_i915_adlp_hdmi(struct hda_codec *codec)
if (!res) {
spec = codec->spec;
- if (spec->silent_stream_type)
+ if (spec->silent_stream_type) {
spec->silent_stream_type = SILENT_STREAM_KAE;
+
+#ifdef CONFIG_PM
+ codec->patch_ops.resume = i915_adlp_hdmi_resume;
+ codec->patch_ops.suspend = i915_adlp_hdmi_suspend;
+#endif
+ }
}
return res;
@@ -4305,6 +4429,8 @@ static int patch_atihdmi(struct hda_codec *codec)
spec = codec->spec;
+ spec->static_pcm_mapping = true;
+
spec->ops.pin_get_eld = atihdmi_pin_get_eld;
spec->ops.pin_setup_infoframe = atihdmi_pin_setup_infoframe;
spec->ops.pin_hbr_setup = atihdmi_pin_hbr_setup;
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index 5f51f8fc7fdc..db9518de9343 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -3564,6 +3564,15 @@ static void alc256_init(struct hda_codec *codec)
hda_nid_t hp_pin = alc_get_hp_pin(spec);
bool hp_pin_sense;
+ if (spec->ultra_low_power) {
+ alc_update_coef_idx(codec, 0x03, 1<<1, 1<<1);
+ alc_update_coef_idx(codec, 0x08, 3<<2, 3<<2);
+ alc_update_coef_idx(codec, 0x08, 7<<4, 0);
+ alc_update_coef_idx(codec, 0x3b, 1<<15, 0);
+ alc_update_coef_idx(codec, 0x0e, 7<<6, 7<<6);
+ msleep(30);
+ }
+
if (!hp_pin)
hp_pin = 0x21;
@@ -3575,14 +3584,6 @@ static void alc256_init(struct hda_codec *codec)
msleep(2);
alc_update_coefex_idx(codec, 0x57, 0x04, 0x0007, 0x1); /* Low power */
- if (spec->ultra_low_power) {
- alc_update_coef_idx(codec, 0x03, 1<<1, 1<<1);
- alc_update_coef_idx(codec, 0x08, 3<<2, 3<<2);
- alc_update_coef_idx(codec, 0x08, 7<<4, 0);
- alc_update_coef_idx(codec, 0x3b, 1<<15, 0);
- alc_update_coef_idx(codec, 0x0e, 7<<6, 7<<6);
- msleep(30);
- }
snd_hda_codec_write(codec, hp_pin, 0,
AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);
@@ -3713,6 +3714,13 @@ static void alc225_init(struct hda_codec *codec)
hda_nid_t hp_pin = alc_get_hp_pin(spec);
bool hp1_pin_sense, hp2_pin_sense;
+ if (spec->ultra_low_power) {
+ alc_update_coef_idx(codec, 0x08, 0x0f << 2, 3<<2);
+ alc_update_coef_idx(codec, 0x0e, 7<<6, 7<<6);
+ alc_update_coef_idx(codec, 0x33, 1<<11, 0);
+ msleep(30);
+ }
+
if (spec->codec_variant != ALC269_TYPE_ALC287 &&
spec->codec_variant != ALC269_TYPE_ALC245)
/* required only at boot or S3 and S4 resume time */
@@ -3734,12 +3742,6 @@ static void alc225_init(struct hda_codec *codec)
msleep(2);
alc_update_coefex_idx(codec, 0x57, 0x04, 0x0007, 0x1); /* Low power */
- if (spec->ultra_low_power) {
- alc_update_coef_idx(codec, 0x08, 0x0f << 2, 3<<2);
- alc_update_coef_idx(codec, 0x0e, 7<<6, 7<<6);
- alc_update_coef_idx(codec, 0x33, 1<<11, 0);
- msleep(30);
- }
if (hp1_pin_sense || spec->ultra_low_power)
snd_hda_codec_write(codec, hp_pin, 0,
@@ -4644,6 +4646,16 @@ static void alc285_fixup_hp_coef_micmute_led(struct hda_codec *codec,
}
}
+static void alc285_fixup_hp_gpio_micmute_led(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct alc_spec *spec = codec->spec;
+
+ if (action == HDA_FIXUP_ACT_PRE_PROBE)
+ spec->micmute_led_polarity = 1;
+ alc_fixup_hp_gpio_led(codec, action, 0, 0x04);
+}
+
static void alc236_fixup_hp_coef_micmute_led(struct hda_codec *codec,
const struct hda_fixup *fix, int action)
{
@@ -4665,6 +4677,13 @@ static void alc285_fixup_hp_mute_led(struct hda_codec *codec,
alc285_fixup_hp_coef_micmute_led(codec, fix, action);
}
+static void alc285_fixup_hp_spectre_x360_mute_led(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ alc285_fixup_hp_mute_led_coefbit(codec, fix, action);
+ alc285_fixup_hp_gpio_micmute_led(codec, fix, action);
+}
+
static void alc236_fixup_hp_mute_led(struct hda_codec *codec,
const struct hda_fixup *fix, int action)
{
@@ -7106,6 +7125,7 @@ enum {
ALC285_FIXUP_ASUS_G533Z_PINS,
ALC285_FIXUP_HP_GPIO_LED,
ALC285_FIXUP_HP_MUTE_LED,
+ ALC285_FIXUP_HP_SPECTRE_X360_MUTE_LED,
ALC236_FIXUP_HP_GPIO_LED,
ALC236_FIXUP_HP_MUTE_LED,
ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF,
@@ -7175,6 +7195,7 @@ enum {
ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK,
ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN,
ALC295_FIXUP_DELL_INSPIRON_TOP_SPEAKERS,
+ ALC236_FIXUP_DELL_DUAL_CODECS,
};
/* A special fixup for Lenovo C940 and Yoga Duet 7;
@@ -8485,6 +8506,10 @@ static const struct hda_fixup alc269_fixups[] = {
.type = HDA_FIXUP_FUNC,
.v.func = alc285_fixup_hp_mute_led,
},
+ [ALC285_FIXUP_HP_SPECTRE_X360_MUTE_LED] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc285_fixup_hp_spectre_x360_mute_led,
+ },
[ALC236_FIXUP_HP_GPIO_LED] = {
.type = HDA_FIXUP_FUNC,
.v.func = alc236_fixup_hp_gpio_led,
@@ -9130,6 +9155,12 @@ static const struct hda_fixup alc269_fixups[] = {
.chained = true,
.chain_id = ALC269_FIXUP_DELL4_MIC_NO_PRESENCE,
},
+ [ALC236_FIXUP_DELL_DUAL_CODECS] = {
+ .type = HDA_FIXUP_PINS,
+ .v.func = alc1220_fixup_gb_dual_codecs,
+ .chained = true,
+ .chain_id = ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
+ },
};
static const struct snd_pci_quirk alc269_fixup_tbl[] = {
@@ -9171,6 +9202,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x1025, 0x142b, "Acer Swift SF314-42", ALC255_FIXUP_ACER_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1025, 0x1430, "Acer TravelMate B311R-31", ALC256_FIXUP_ACER_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1025, 0x1466, "Acer Aspire A515-56", ALC255_FIXUP_ACER_HEADPHONE_AND_MIC),
+ SND_PCI_QUIRK(0x1025, 0x1534, "Acer Predator PH315-54", ALC255_FIXUP_ACER_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1028, 0x0470, "Dell M101z", ALC269_FIXUP_DELL_M101Z),
SND_PCI_QUIRK(0x1028, 0x053c, "Dell Latitude E5430", ALC292_FIXUP_DELL_E7X),
SND_PCI_QUIRK(0x1028, 0x054b, "Dell XPS one 2710", ALC275_FIXUP_DELL_XPS),
@@ -9232,6 +9264,13 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x1028, 0x0b1a, "Dell Precision 5570", ALC289_FIXUP_DUAL_SPK),
SND_PCI_QUIRK(0x1028, 0x0b37, "Dell Inspiron 16 Plus 7620 2-in-1", ALC295_FIXUP_DELL_INSPIRON_TOP_SPEAKERS),
SND_PCI_QUIRK(0x1028, 0x0b71, "Dell Inspiron 16 Plus 7620", ALC295_FIXUP_DELL_INSPIRON_TOP_SPEAKERS),
+ SND_PCI_QUIRK(0x1028, 0x0c03, "Dell Precision 5340", ALC269_FIXUP_DELL4_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x0c19, "Dell Precision 3340", ALC236_FIXUP_DELL_DUAL_CODECS),
+ SND_PCI_QUIRK(0x1028, 0x0c1a, "Dell Precision 3340", ALC236_FIXUP_DELL_DUAL_CODECS),
+ SND_PCI_QUIRK(0x1028, 0x0c1b, "Dell Precision 3440", ALC236_FIXUP_DELL_DUAL_CODECS),
+ SND_PCI_QUIRK(0x1028, 0x0c1c, "Dell Precision 3540", ALC236_FIXUP_DELL_DUAL_CODECS),
+ SND_PCI_QUIRK(0x1028, 0x0c1d, "Dell Precision 3440", ALC236_FIXUP_DELL_DUAL_CODECS),
+ SND_PCI_QUIRK(0x1028, 0x0c1e, "Dell Precision 3540", ALC236_FIXUP_DELL_DUAL_CODECS),
SND_PCI_QUIRK(0x1028, 0x164a, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1028, 0x164b, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x103c, 0x1586, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC2),
@@ -9314,6 +9353,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x103c, 0x86c7, "HP Envy AiO 32", ALC274_FIXUP_HP_ENVY_GPIO),
SND_PCI_QUIRK(0x103c, 0x86e7, "HP Spectre x360 15-eb0xxx", ALC285_FIXUP_HP_SPECTRE_X360_EB1),
SND_PCI_QUIRK(0x103c, 0x86e8, "HP Spectre x360 15-eb0xxx", ALC285_FIXUP_HP_SPECTRE_X360_EB1),
+ SND_PCI_QUIRK(0x103c, 0x86f9, "HP Spectre x360 13-aw0xxx", ALC285_FIXUP_HP_SPECTRE_X360_MUTE_LED),
SND_PCI_QUIRK(0x103c, 0x8716, "HP Elite Dragonfly G2 Notebook PC", ALC285_FIXUP_HP_GPIO_AMP_INIT),
SND_PCI_QUIRK(0x103c, 0x8720, "HP EliteBook x360 1040 G8 Notebook PC", ALC285_FIXUP_HP_GPIO_AMP_INIT),
SND_PCI_QUIRK(0x103c, 0x8724, "HP EliteBook 850 G7", ALC285_FIXUP_HP_GPIO_LED),
@@ -9393,6 +9433,8 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x103c, 0x8ad2, "HP EliteBook 860 16 inch G9 Notebook PC", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED),
SND_PCI_QUIRK(0x103c, 0x8b5d, "HP", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF),
SND_PCI_QUIRK(0x103c, 0x8b5e, "HP", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF),
+ SND_PCI_QUIRK(0x103c, 0x8b92, "HP", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8bf0, "HP", ALC236_FIXUP_HP_GPIO_LED),
SND_PCI_QUIRK(0x1043, 0x103e, "ASUS X540SA", ALC256_FIXUP_ASUS_MIC),
SND_PCI_QUIRK(0x1043, 0x103f, "ASUS TX300", ALC282_FIXUP_ASUS_TX300),
SND_PCI_QUIRK(0x1043, 0x106d, "Asus K53BE", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
@@ -10999,6 +11041,17 @@ static void alc897_fixup_lenovo_headset_mic(struct hda_codec *codec,
}
}
+static void alc897_fixup_lenovo_headset_mode(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct alc_spec *spec = codec->spec;
+
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ spec->parse_flags |= HDA_PINCFG_HEADSET_MIC;
+ spec->gen.hp_automute_hook = alc897_hp_automute_hook;
+ }
+}
+
static const struct coef_fw alc668_coefs[] = {
WRITE_COEF(0x01, 0xbebe), WRITE_COEF(0x02, 0xaaaa), WRITE_COEF(0x03, 0x0),
WRITE_COEF(0x04, 0x0180), WRITE_COEF(0x06, 0x0), WRITE_COEF(0x07, 0x0f80),
@@ -11082,6 +11135,8 @@ enum {
ALC897_FIXUP_LENOVO_HEADSET_MIC,
ALC897_FIXUP_HEADSET_MIC_PIN,
ALC897_FIXUP_HP_HSMIC_VERB,
+ ALC897_FIXUP_LENOVO_HEADSET_MODE,
+ ALC897_FIXUP_HEADSET_MIC_PIN2,
};
static const struct hda_fixup alc662_fixups[] = {
@@ -11508,6 +11563,19 @@ static const struct hda_fixup alc662_fixups[] = {
{ }
},
},
+ [ALC897_FIXUP_LENOVO_HEADSET_MODE] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc897_fixup_lenovo_headset_mode,
+ },
+ [ALC897_FIXUP_HEADSET_MIC_PIN2] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x1a, 0x01a11140 }, /* use as headset mic, without its own jack detect */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC897_FIXUP_LENOVO_HEADSET_MODE
+ },
};
static const struct snd_pci_quirk alc662_fixup_tbl[] = {
@@ -11560,6 +11628,7 @@ static const struct snd_pci_quirk alc662_fixup_tbl[] = {
SND_PCI_QUIRK(0x17aa, 0x32cb, "Lenovo ThinkCentre M70", ALC897_FIXUP_HEADSET_MIC_PIN),
SND_PCI_QUIRK(0x17aa, 0x32cf, "Lenovo ThinkCentre M950", ALC897_FIXUP_HEADSET_MIC_PIN),
SND_PCI_QUIRK(0x17aa, 0x32f7, "Lenovo ThinkCentre M90", ALC897_FIXUP_HEADSET_MIC_PIN),
+ SND_PCI_QUIRK(0x17aa, 0x3742, "Lenovo TianYi510Pro-14IOB", ALC897_FIXUP_HEADSET_MIC_PIN2),
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),
diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c
index aea7fae2ca4b..2994f85bc1b9 100644
--- a/sound/pci/hda/patch_via.c
+++ b/sound/pci/hda/patch_via.c
@@ -819,6 +819,9 @@ static int add_secret_dac_path(struct hda_codec *codec)
return 0;
nums = snd_hda_get_connections(codec, spec->gen.mixer_nid, conn,
ARRAY_SIZE(conn) - 1);
+ if (nums < 0)
+ return nums;
+
for (i = 0; i < nums; i++) {
if (get_wcaps_type(get_wcaps(codec, conn[i])) == AC_WID_AUD_OUT)
return 0;
diff --git a/sound/soc/amd/acp-es8336.c b/sound/soc/amd/acp-es8336.c
index 2fe8df86053a..89499542c803 100644
--- a/sound/soc/amd/acp-es8336.c
+++ b/sound/soc/amd/acp-es8336.c
@@ -198,9 +198,11 @@ static int st_es8336_late_probe(struct snd_soc_card *card)
int ret;
adev = acpi_dev_get_first_match_dev("ESSX8336", NULL, -1);
- if (adev)
- put_device(&adev->dev);
+ if (!adev)
+ return -ENODEV;
+
codec_dev = acpi_get_first_physical_node(adev);
+ acpi_dev_put(adev);
if (!codec_dev)
dev_err(card->dev, "can not find codec dev\n");
diff --git a/sound/soc/amd/yc/acp6x-mach.c b/sound/soc/amd/yc/acp6x-mach.c
index 1f0b5527c594..36314753923b 100644
--- a/sound/soc/amd/yc/acp6x-mach.c
+++ b/sound/soc/amd/yc/acp6x-mach.c
@@ -209,6 +209,13 @@ static const struct dmi_system_id yc_acp_quirk_table[] = {
{
.driver_data = &acp6x_card,
.matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "M5402RA"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "Alienware"),
DMI_MATCH(DMI_PRODUCT_NAME, "Alienware m17 R5 AMD"),
}
@@ -220,6 +227,34 @@ static const struct dmi_system_id yc_acp_quirk_table[] = {
DMI_MATCH(DMI_PRODUCT_NAME, "Redmi Book Pro 14 2022"),
}
},
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "TIMI"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Redmi Book Pro 15 2022"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "Razer"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Blade 14 (2022) - RZ09-0427"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "RB"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Swift SFA16-41"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "IRBIS"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "15NBC1011"),
+ }
+ },
{}
};
diff --git a/sound/soc/codecs/cs42l56.c b/sound/soc/codecs/cs42l56.c
index 26066682c983..3b0e715549c9 100644
--- a/sound/soc/codecs/cs42l56.c
+++ b/sound/soc/codecs/cs42l56.c
@@ -1191,18 +1191,12 @@ static int cs42l56_i2c_probe(struct i2c_client *i2c_client)
if (pdata) {
cs42l56->pdata = *pdata;
} else {
- pdata = devm_kzalloc(&i2c_client->dev, sizeof(*pdata),
- GFP_KERNEL);
- if (!pdata)
- return -ENOMEM;
-
if (i2c_client->dev.of_node) {
ret = cs42l56_handle_of_data(i2c_client,
&cs42l56->pdata);
if (ret != 0)
return ret;
}
- cs42l56->pdata = *pdata;
}
if (cs42l56->pdata.gpio_nreset) {
diff --git a/sound/soc/codecs/es8326.c b/sound/soc/codecs/es8326.c
index 9ddf6a35e91c..9ddf6a35e91c 100755..100644
--- a/sound/soc/codecs/es8326.c
+++ b/sound/soc/codecs/es8326.c
diff --git a/sound/soc/codecs/es8326.h b/sound/soc/codecs/es8326.h
index 8e5ffe5ee10d..8e5ffe5ee10d 100755..100644
--- a/sound/soc/codecs/es8326.h
+++ b/sound/soc/codecs/es8326.h
diff --git a/sound/soc/codecs/lochnagar-sc.c b/sound/soc/codecs/lochnagar-sc.c
index 13fbd8830b09..5e0bd0d24ed3 100644
--- a/sound/soc/codecs/lochnagar-sc.c
+++ b/sound/soc/codecs/lochnagar-sc.c
@@ -253,7 +253,7 @@ MODULE_DEVICE_TABLE(of, lochnagar_of_match);
static struct platform_driver lochnagar_sc_codec_driver = {
.driver = {
.name = "lochnagar-soundcard",
- .of_match_table = of_match_ptr(lochnagar_of_match),
+ .of_match_table = lochnagar_of_match,
},
.probe = lochnagar_sc_probe,
diff --git a/sound/soc/codecs/rt5670.c b/sound/soc/codecs/rt5670.c
index ebac6caeb40a..a230f441559a 100644
--- a/sound/soc/codecs/rt5670.c
+++ b/sound/soc/codecs/rt5670.c
@@ -3311,8 +3311,6 @@ static int rt5670_i2c_probe(struct i2c_client *i2c)
if (ret < 0)
goto err;
- pm_runtime_put(&i2c->dev);
-
return 0;
err:
pm_runtime_disable(&i2c->dev);
diff --git a/sound/soc/codecs/rt9120.c b/sound/soc/codecs/rt9120.c
index 644300e88b4c..fcf4fbaed3c7 100644
--- a/sound/soc/codecs/rt9120.c
+++ b/sound/soc/codecs/rt9120.c
@@ -177,8 +177,20 @@ static int rt9120_codec_probe(struct snd_soc_component *comp)
return 0;
}
+static int rt9120_codec_suspend(struct snd_soc_component *comp)
+{
+ return pm_runtime_force_suspend(comp->dev);
+}
+
+static int rt9120_codec_resume(struct snd_soc_component *comp)
+{
+ return pm_runtime_force_resume(comp->dev);
+}
+
static const struct snd_soc_component_driver rt9120_component_driver = {
.probe = rt9120_codec_probe,
+ .suspend = rt9120_codec_suspend,
+ .resume = rt9120_codec_resume,
.controls = rt9120_snd_controls,
.num_controls = ARRAY_SIZE(rt9120_snd_controls),
.dapm_widgets = rt9120_dapm_widgets,
diff --git a/sound/soc/codecs/wcd-clsh-v2.c b/sound/soc/codecs/wcd-clsh-v2.c
index 4c7ebc7fb400..a75db27e5205 100644
--- a/sound/soc/codecs/wcd-clsh-v2.c
+++ b/sound/soc/codecs/wcd-clsh-v2.c
@@ -130,12 +130,6 @@ static inline void wcd_enable_clsh_block(struct wcd_clsh_ctrl *ctrl,
ctrl->clsh_users = 0;
}
-static inline bool wcd_clsh_enable_status(struct snd_soc_component *comp)
-{
- return snd_soc_component_read(comp, WCD9XXX_A_CDC_CLSH_CRC) &
- WCD9XXX_A_CDC_CLSH_CRC_CLK_EN_MASK;
-}
-
static inline void wcd_clsh_set_buck_mode(struct snd_soc_component *comp,
int mode)
{
diff --git a/sound/soc/codecs/wm8904.c b/sound/soc/codecs/wm8904.c
index ca6a01a230af..791d8738d1c0 100644
--- a/sound/soc/codecs/wm8904.c
+++ b/sound/soc/codecs/wm8904.c
@@ -697,6 +697,7 @@ static int out_pga_event(struct snd_soc_dapm_widget *w,
int dcs_mask;
int dcs_l, dcs_r;
int dcs_l_reg, dcs_r_reg;
+ int an_out_reg;
int timeout;
int pwr_reg;
@@ -712,6 +713,7 @@ static int out_pga_event(struct snd_soc_dapm_widget *w,
dcs_mask = WM8904_DCS_ENA_CHAN_0 | WM8904_DCS_ENA_CHAN_1;
dcs_r_reg = WM8904_DC_SERVO_8;
dcs_l_reg = WM8904_DC_SERVO_9;
+ an_out_reg = WM8904_ANALOGUE_OUT1_LEFT;
dcs_l = 0;
dcs_r = 1;
break;
@@ -720,6 +722,7 @@ static int out_pga_event(struct snd_soc_dapm_widget *w,
dcs_mask = WM8904_DCS_ENA_CHAN_2 | WM8904_DCS_ENA_CHAN_3;
dcs_r_reg = WM8904_DC_SERVO_6;
dcs_l_reg = WM8904_DC_SERVO_7;
+ an_out_reg = WM8904_ANALOGUE_OUT2_LEFT;
dcs_l = 2;
dcs_r = 3;
break;
@@ -792,6 +795,10 @@ static int out_pga_event(struct snd_soc_dapm_widget *w,
snd_soc_component_update_bits(component, reg,
WM8904_HPL_ENA_OUTP | WM8904_HPR_ENA_OUTP,
WM8904_HPL_ENA_OUTP | WM8904_HPR_ENA_OUTP);
+
+ /* Update volume, requires PGA to be powered */
+ val = snd_soc_component_read(component, an_out_reg);
+ snd_soc_component_write(component, an_out_reg, val);
break;
case SND_SOC_DAPM_POST_PMU:
diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c
index d3cfd3788f2a..8fe9a75d1235 100644
--- a/sound/soc/codecs/wm8994.c
+++ b/sound/soc/codecs/wm8994.c
@@ -3853,7 +3853,12 @@ static irqreturn_t wm1811_jackdet_irq(int irq, void *data)
} else {
dev_dbg(component->dev, "Jack not detected\n");
+ /* Release wm8994->accdet_lock to avoid deadlock:
+ * cancel_delayed_work_sync() takes wm8994->mic_work internal
+ * lock and wm1811_mic_work takes wm8994->accdet_lock */
+ mutex_unlock(&wm8994->accdet_lock);
cancel_delayed_work_sync(&wm8994->mic_work);
+ mutex_lock(&wm8994->accdet_lock);
snd_soc_component_update_bits(component, WM8958_MICBIAS2,
WM8958_MICB2_DISCH, WM8958_MICB2_DISCH);
diff --git a/sound/soc/codecs/wsa883x.c b/sound/soc/codecs/wsa883x.c
index 966ba4909204..58fdb4e9fd97 100644
--- a/sound/soc/codecs/wsa883x.c
+++ b/sound/soc/codecs/wsa883x.c
@@ -1359,8 +1359,8 @@ static struct snd_soc_dai_driver wsa883x_dais[] = {
.stream_name = "SPKR Playback",
.rates = WSA883X_RATES | WSA883X_FRAC_RATES,
.formats = WSA883X_FORMATS,
- .rate_max = 8000,
- .rate_min = 352800,
+ .rate_min = 8000,
+ .rate_max = 352800,
.channels_min = 1,
.channels_max = 1,
},
diff --git a/sound/soc/fsl/fsl-asoc-card.c b/sound/soc/fsl/fsl-asoc-card.c
index c836848ef0a6..8d14b5593658 100644
--- a/sound/soc/fsl/fsl-asoc-card.c
+++ b/sound/soc/fsl/fsl-asoc-card.c
@@ -121,11 +121,11 @@ static const struct snd_soc_dapm_route audio_map[] = {
static const struct snd_soc_dapm_route audio_map_ac97[] = {
/* 1st half -- Normal DAPM routes */
- {"Playback", NULL, "AC97 Playback"},
- {"AC97 Capture", NULL, "Capture"},
+ {"AC97 Playback", NULL, "CPU AC97 Playback"},
+ {"CPU AC97 Capture", NULL, "AC97 Capture"},
/* 2nd half -- ASRC DAPM routes */
- {"AC97 Playback", NULL, "ASRC-Playback"},
- {"ASRC-Capture", NULL, "AC97 Capture"},
+ {"CPU AC97 Playback", NULL, "ASRC-Playback"},
+ {"ASRC-Capture", NULL, "CPU AC97 Capture"},
};
static const struct snd_soc_dapm_route audio_map_tx[] = {
diff --git a/sound/soc/fsl/fsl_micfil.c b/sound/soc/fsl/fsl_micfil.c
index 7b17f152bbf3..94341e4352b3 100644
--- a/sound/soc/fsl/fsl_micfil.c
+++ b/sound/soc/fsl/fsl_micfil.c
@@ -315,21 +315,21 @@ static int hwvad_detected(struct snd_kcontrol *kcontrol,
static const struct snd_kcontrol_new fsl_micfil_snd_controls[] = {
SOC_SINGLE_SX_TLV("CH0 Volume", REG_MICFIL_OUT_CTRL,
- MICFIL_OUTGAIN_CHX_SHIFT(0), 0xF, 0x7, gain_tlv),
+ MICFIL_OUTGAIN_CHX_SHIFT(0), 0x8, 0xF, gain_tlv),
SOC_SINGLE_SX_TLV("CH1 Volume", REG_MICFIL_OUT_CTRL,
- MICFIL_OUTGAIN_CHX_SHIFT(1), 0xF, 0x7, gain_tlv),
+ MICFIL_OUTGAIN_CHX_SHIFT(1), 0x8, 0xF, gain_tlv),
SOC_SINGLE_SX_TLV("CH2 Volume", REG_MICFIL_OUT_CTRL,
- MICFIL_OUTGAIN_CHX_SHIFT(2), 0xF, 0x7, gain_tlv),
+ MICFIL_OUTGAIN_CHX_SHIFT(2), 0x8, 0xF, gain_tlv),
SOC_SINGLE_SX_TLV("CH3 Volume", REG_MICFIL_OUT_CTRL,
- MICFIL_OUTGAIN_CHX_SHIFT(3), 0xF, 0x7, gain_tlv),
+ MICFIL_OUTGAIN_CHX_SHIFT(3), 0x8, 0xF, gain_tlv),
SOC_SINGLE_SX_TLV("CH4 Volume", REG_MICFIL_OUT_CTRL,
- MICFIL_OUTGAIN_CHX_SHIFT(4), 0xF, 0x7, gain_tlv),
+ MICFIL_OUTGAIN_CHX_SHIFT(4), 0x8, 0xF, gain_tlv),
SOC_SINGLE_SX_TLV("CH5 Volume", REG_MICFIL_OUT_CTRL,
- MICFIL_OUTGAIN_CHX_SHIFT(5), 0xF, 0x7, gain_tlv),
+ MICFIL_OUTGAIN_CHX_SHIFT(5), 0x8, 0xF, gain_tlv),
SOC_SINGLE_SX_TLV("CH6 Volume", REG_MICFIL_OUT_CTRL,
- MICFIL_OUTGAIN_CHX_SHIFT(6), 0xF, 0x7, gain_tlv),
+ MICFIL_OUTGAIN_CHX_SHIFT(6), 0x8, 0xF, gain_tlv),
SOC_SINGLE_SX_TLV("CH7 Volume", REG_MICFIL_OUT_CTRL,
- MICFIL_OUTGAIN_CHX_SHIFT(7), 0xF, 0x7, gain_tlv),
+ MICFIL_OUTGAIN_CHX_SHIFT(7), 0x8, 0xF, gain_tlv),
SOC_ENUM_EXT("MICFIL Quality Select",
fsl_micfil_quality_enum,
micfil_quality_get, micfil_quality_set),
diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c
index c9e0e31d5b34..46a53551b955 100644
--- a/sound/soc/fsl/fsl_ssi.c
+++ b/sound/soc/fsl/fsl_ssi.c
@@ -1189,14 +1189,14 @@ static struct snd_soc_dai_driver fsl_ssi_ac97_dai = {
.symmetric_channels = 1,
.probe = fsl_ssi_dai_probe,
.playback = {
- .stream_name = "AC97 Playback",
+ .stream_name = "CPU AC97 Playback",
.channels_min = 2,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_8000_48000,
.formats = SNDRV_PCM_FMTBIT_S16 | SNDRV_PCM_FMTBIT_S20,
},
.capture = {
- .stream_name = "AC97 Capture",
+ .stream_name = "CPU AC97 Capture",
.channels_min = 2,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_48000,
diff --git a/sound/soc/fsl/imx-audmux.c b/sound/soc/fsl/imx-audmux.c
index 50b71e5d4589..582f1e2431ee 100644
--- a/sound/soc/fsl/imx-audmux.c
+++ b/sound/soc/fsl/imx-audmux.c
@@ -75,8 +75,7 @@ static ssize_t audmux_read_file(struct file *file, char __user *user_buf,
if (!buf)
return -ENOMEM;
- ret = scnprintf(buf, PAGE_SIZE, "PDCR: %08x\nPTCR: %08x\n",
- pdcr, ptcr);
+ ret = sysfs_emit(buf, "PDCR: %08x\nPTCR: %08x\n", pdcr, ptcr);
if (ptcr & IMX_AUDMUX_V2_PTCR_TFSDIR)
ret += scnprintf(buf + ret, PAGE_SIZE - ret,
diff --git a/sound/soc/generic/audio-graph-card.c b/sound/soc/generic/audio-graph-card.c
index fe7cf972d44c..5daa824a4ffc 100644
--- a/sound/soc/generic/audio-graph-card.c
+++ b/sound/soc/generic/audio-graph-card.c
@@ -485,8 +485,10 @@ static int __graph_for_each_link(struct asoc_simple_priv *priv,
of_node_put(codec_ep);
of_node_put(codec_port);
- if (ret < 0)
+ if (ret < 0) {
+ of_node_put(cpu_ep);
return ret;
+ }
codec_port_old = codec_port;
}
diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig
index ac799de4f7fd..4b9e498e3303 100644
--- a/sound/soc/intel/Kconfig
+++ b/sound/soc/intel/Kconfig
@@ -217,6 +217,7 @@ config SND_SOC_INTEL_AVS
select SND_SOC_ACPI if ACPI
select SND_SOC_TOPOLOGY
select SND_SOC_HDA
+ select SND_SOC_COMPRESS if DEBUG_FS
select SND_HDA_EXT_CORE
select SND_HDA_DSP_LOADER
select SND_INTEL_DSP_CONFIG
diff --git a/sound/soc/intel/avs/Makefile b/sound/soc/intel/avs/Makefile
index 919212825f21..1c6924a1ebca 100644
--- a/sound/soc/intel/avs/Makefile
+++ b/sound/soc/intel/avs/Makefile
@@ -9,6 +9,10 @@ snd-soc-avs-objs += trace.o
# tell define_trace.h where to find the trace header
CFLAGS_trace.o := -I$(src)
+ifneq ($(CONFIG_DEBUG_FS),)
+snd-soc-avs-objs += probes.o debugfs.o
+endif
+
obj-$(CONFIG_SND_SOC_INTEL_AVS) += snd-soc-avs.o
# Machine support
diff --git a/sound/soc/intel/avs/apl.c b/sound/soc/intel/avs/apl.c
index 7c8ce98eda9d..02683dce277a 100644
--- a/sound/soc/intel/avs/apl.c
+++ b/sound/soc/intel/avs/apl.c
@@ -13,8 +13,9 @@
#include "path.h"
#include "topology.h"
-static int apl_enable_logs(struct avs_dev *adev, enum avs_log_enable enable, u32 aging_period,
- u32 fifo_full_period, unsigned long resource_mask, u32 *priorities)
+static int __maybe_unused
+apl_enable_logs(struct avs_dev *adev, enum avs_log_enable enable, u32 aging_period,
+ u32 fifo_full_period, unsigned long resource_mask, u32 *priorities)
{
struct apl_log_state_info *info;
u32 size, num_cores = adev->hw_cfg.dsp_cores;
@@ -50,7 +51,6 @@ static int apl_enable_logs(struct avs_dev *adev, enum avs_log_enable enable, u32
static int apl_log_buffer_status(struct avs_dev *adev, union avs_notify_msg *msg)
{
struct apl_log_buffer_layout layout;
- unsigned long flags;
void __iomem *addr, *buf;
addr = avs_log_buffer_addr(adev, msg->log.core);
@@ -59,26 +59,20 @@ static int apl_log_buffer_status(struct avs_dev *adev, union avs_notify_msg *msg
memcpy_fromio(&layout, addr, sizeof(layout));
- spin_lock_irqsave(&adev->dbg.trace_lock, flags);
- if (!kfifo_initialized(&adev->dbg.trace_fifo))
+ if (!avs_logging_fw(adev))
/* consume the logs regardless of consumer presence */
goto update_read_ptr;
buf = apl_log_payload_addr(addr);
if (layout.read_ptr > layout.write_ptr) {
- __kfifo_fromio_locked(&adev->dbg.trace_fifo, buf + layout.read_ptr,
- apl_log_payload_size(adev) - layout.read_ptr,
- &adev->dbg.fifo_lock);
+ avs_dump_fw_log(adev, buf + layout.read_ptr,
+ apl_log_payload_size(adev) - layout.read_ptr);
layout.read_ptr = 0;
}
- __kfifo_fromio_locked(&adev->dbg.trace_fifo, buf + layout.read_ptr,
- layout.write_ptr - layout.read_ptr, &adev->dbg.fifo_lock);
-
- wake_up(&adev->dbg.trace_waitq);
+ avs_dump_fw_log_wakeup(adev, buf + layout.read_ptr, layout.write_ptr - layout.read_ptr);
update_read_ptr:
- spin_unlock_irqrestore(&adev->dbg.trace_lock, flags);
writel(layout.write_ptr, addr);
return 0;
}
@@ -140,7 +134,7 @@ static int apl_coredump(struct avs_dev *adev, union avs_notify_msg *msg)
* gathered before dumping stack
*/
lbs_msg.log.core = msg->ext.coredump.core_id;
- avs_dsp_op(adev, log_buffer_status, &lbs_msg);
+ avs_log_buffer_status_locked(adev, &lbs_msg);
}
pos = dump + AVS_FW_REGS_SIZE;
@@ -243,10 +237,10 @@ const struct avs_dsp_ops apl_dsp_ops = {
.load_basefw = avs_hda_load_basefw,
.load_lib = avs_hda_load_library,
.transfer_mods = avs_hda_transfer_modules,
- .enable_logs = apl_enable_logs,
.log_buffer_offset = skl_log_buffer_offset,
.log_buffer_status = apl_log_buffer_status,
.coredump = apl_coredump,
.d0ix_toggle = apl_d0ix_toggle,
.set_d0ix = apl_set_d0ix,
+ AVS_SET_ENABLE_LOGS_OP(apl)
};
diff --git a/sound/soc/intel/avs/avs.h b/sound/soc/intel/avs/avs.h
index 8d05b27608fe..d7fccdcb9c16 100644
--- a/sound/soc/intel/avs/avs.h
+++ b/sound/soc/intel/avs/avs.h
@@ -9,6 +9,7 @@
#ifndef __SOUND_SOC_INTEL_AVS_H
#define __SOUND_SOC_INTEL_AVS_H
+#include <linux/debugfs.h>
#include <linux/device.h>
#include <linux/firmware.h>
#include <linux/kfifo.h>
@@ -93,16 +94,6 @@ struct avs_fw_entry {
struct list_head node;
};
-struct avs_debug {
- struct kfifo trace_fifo;
- spinlock_t fifo_lock; /* serialize I/O for trace_fifo */
- spinlock_t trace_lock; /* serialize debug window I/O between each LOG_BUFFER_STATUS */
- wait_queue_head_t trace_waitq;
- u32 aging_timer_period;
- u32 fifo_full_timer_period;
- u32 logged_resources; /* context dependent: core or library */
-};
-
/*
* struct avs_dev - Intel HD-Audio driver data
*
@@ -146,7 +137,18 @@ struct avs_dev {
spinlock_t path_list_lock;
struct mutex path_mutex;
- struct avs_debug dbg;
+ spinlock_t trace_lock; /* serialize debug window I/O between each LOG_BUFFER_STATUS */
+#ifdef CONFIG_DEBUG_FS
+ struct kfifo trace_fifo;
+ wait_queue_head_t trace_waitq;
+ u32 aging_timer_period;
+ u32 fifo_full_timer_period;
+ u32 logged_resources; /* context dependent: core or library */
+ struct dentry *debugfs_root;
+ /* probes */
+ struct hdac_ext_stream *extractor;
+ unsigned int num_probe_streams;
+#endif
};
/* from hda_bus to avs_dev */
@@ -321,6 +323,9 @@ struct avs_soc_component {
extern const struct snd_soc_dai_ops avs_dai_fe_ops;
+int avs_soc_component_register(struct device *dev, const char *name,
+ const struct snd_soc_component_driver *drv,
+ struct snd_soc_dai_driver *cpu_dais, int num_cpu_dais);
int avs_dmic_platform_register(struct avs_dev *adev, const char *name);
int avs_i2s_platform_register(struct avs_dev *adev, const char *name, unsigned long port_mask,
unsigned long *tdms);
@@ -331,9 +336,6 @@ void avs_unregister_all_boards(struct avs_dev *adev);
/* Firmware tracing helpers */
-unsigned int __kfifo_fromio_locked(struct kfifo *fifo, const void __iomem *src, unsigned int len,
- spinlock_t *lock);
-
#define avs_log_buffer_size(adev) \
((adev)->fw_cfg.trace_log_bytes / (adev)->hw_cfg.dsp_cores)
@@ -344,6 +346,18 @@ unsigned int __kfifo_fromio_locked(struct kfifo *fifo, const void __iomem *src,
(avs_sram_addr(adev, AVS_DEBUG_WINDOW) + __offset); \
})
+static inline int avs_log_buffer_status_locked(struct avs_dev *adev, union avs_notify_msg *msg)
+{
+ unsigned long flags;
+ int ret;
+
+ spin_lock_irqsave(&adev->trace_lock, flags);
+ ret = avs_dsp_op(adev, log_buffer_status, msg);
+ spin_unlock_irqrestore(&adev->trace_lock, flags);
+
+ return ret;
+}
+
struct apl_log_buffer_layout {
u32 read_ptr;
u32 write_ptr;
@@ -356,4 +370,42 @@ struct apl_log_buffer_layout {
#define apl_log_payload_addr(addr) \
(addr + sizeof(struct apl_log_buffer_layout))
+#ifdef CONFIG_DEBUG_FS
+#define AVS_SET_ENABLE_LOGS_OP(name) \
+ .enable_logs = name##_enable_logs
+
+bool avs_logging_fw(struct avs_dev *adev);
+void avs_dump_fw_log(struct avs_dev *adev, const void __iomem *src, unsigned int len);
+void avs_dump_fw_log_wakeup(struct avs_dev *adev, const void __iomem *src, unsigned int len);
+
+int avs_probe_platform_register(struct avs_dev *adev, const char *name);
+
+void avs_debugfs_init(struct avs_dev *adev);
+void avs_debugfs_exit(struct avs_dev *adev);
+#else
+#define AVS_SET_ENABLE_LOGS_OP(name)
+
+static inline bool avs_logging_fw(struct avs_dev *adev)
+{
+ return false;
+}
+
+static inline void avs_dump_fw_log(struct avs_dev *adev, const void __iomem *src, unsigned int len)
+{
+}
+
+static inline void
+avs_dump_fw_log_wakeup(struct avs_dev *adev, const void __iomem *src, unsigned int len)
+{
+}
+
+static inline int avs_probe_platform_register(struct avs_dev *adev, const char *name)
+{
+ return 0;
+}
+
+static inline void avs_debugfs_init(struct avs_dev *adev) { }
+static inline void avs_debugfs_exit(struct avs_dev *adev) { }
+#endif
+
#endif /* __SOUND_SOC_INTEL_AVS_H */
diff --git a/sound/soc/intel/avs/board_selection.c b/sound/soc/intel/avs/board_selection.c
index 02cc1ce8f5f5..b2823c2107f7 100644
--- a/sound/soc/intel/avs/board_selection.c
+++ b/sound/soc/intel/avs/board_selection.c
@@ -291,6 +291,33 @@ static void board_pdev_unregister(void *data)
platform_device_unregister(data);
}
+static int __maybe_unused avs_register_probe_board(struct avs_dev *adev)
+{
+ struct platform_device *board;
+ struct snd_soc_acpi_mach mach = {{0}};
+ int ret;
+
+ ret = avs_probe_platform_register(adev, "probe-platform");
+ if (ret < 0)
+ return ret;
+
+ mach.mach_params.platform = "probe-platform";
+
+ board = platform_device_register_data(NULL, "avs_probe_mb", PLATFORM_DEVID_NONE,
+ (const void *)&mach, sizeof(mach));
+ if (IS_ERR(board)) {
+ dev_err(adev->dev, "probe board register failed\n");
+ return PTR_ERR(board);
+ }
+
+ ret = devm_add_action(adev->dev, board_pdev_unregister, board);
+ if (ret < 0) {
+ platform_device_unregister(board);
+ return ret;
+ }
+ return 0;
+}
+
static int avs_register_dmic_board(struct avs_dev *adev)
{
struct platform_device *codec, *board;
@@ -500,6 +527,12 @@ int avs_register_all_boards(struct avs_dev *adev)
{
int ret;
+#ifdef CONFIG_DEBUG_FS
+ ret = avs_register_probe_board(adev);
+ if (ret < 0)
+ dev_warn(adev->dev, "enumerate PROBE endpoints failed: %d\n", ret);
+#endif
+
ret = avs_register_dmic_board(adev);
if (ret < 0)
dev_warn(adev->dev, "enumerate DMIC endpoints failed: %d\n",
diff --git a/sound/soc/intel/avs/boards/Kconfig b/sound/soc/intel/avs/boards/Kconfig
index 9bd40fdd9028..e4c230efe8d7 100644
--- a/sound/soc/intel/avs/boards/Kconfig
+++ b/sound/soc/intel/avs/boards/Kconfig
@@ -77,6 +77,14 @@ config SND_SOC_INTEL_AVS_MACH_NAU8825
Say Y or m if you have such a device. This is a recommended option.
If unsure select "N".
+config SND_SOC_INTEL_AVS_MACH_PROBE
+ tristate "Probing (data) board"
+ depends on DEBUG_FS
+ select SND_HWDEP
+ help
+ This adds support for data probing board which can be used to
+ gather data from runtime stream over compress operations.
+
config SND_SOC_INTEL_AVS_MACH_RT274
tristate "rt274 in I2S mode"
depends on I2C
diff --git a/sound/soc/intel/avs/boards/Makefile b/sound/soc/intel/avs/boards/Makefile
index 4d70b8d09ce5..b81343420370 100644
--- a/sound/soc/intel/avs/boards/Makefile
+++ b/sound/soc/intel/avs/boards/Makefile
@@ -8,6 +8,7 @@ snd-soc-avs-max98927-objs := max98927.o
snd-soc-avs-max98357a-objs := max98357a.o
snd-soc-avs-max98373-objs := max98373.o
snd-soc-avs-nau8825-objs := nau8825.o
+snd-soc-avs-probe-objs := probe.o
snd-soc-avs-rt274-objs := rt274.o
snd-soc-avs-rt286-objs := rt286.o
snd-soc-avs-rt298-objs := rt298.o
@@ -22,6 +23,7 @@ obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_MAX98927) += snd-soc-avs-max98927.o
obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_MAX98357A) += snd-soc-avs-max98357a.o
obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_MAX98373) += snd-soc-avs-max98373.o
obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_NAU8825) += snd-soc-avs-nau8825.o
+obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_PROBE) += snd-soc-avs-probe.o
obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_RT274) += snd-soc-avs-rt274.o
obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_RT286) += snd-soc-avs-rt286.o
obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_RT298) += snd-soc-avs-rt298.o
diff --git a/sound/soc/intel/avs/boards/probe.c b/sound/soc/intel/avs/boards/probe.c
new file mode 100644
index 000000000000..411acaee74f9
--- /dev/null
+++ b/sound/soc/intel/avs/boards/probe.c
@@ -0,0 +1,64 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Copyright(c) 2021-2022 Intel Corporation. All rights reserved.
+//
+// Authors: Cezary Rojewski <cezary.rojewski@intel.com>
+// Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
+//
+
+#include <linux/device.h>
+#include <linux/module.h>
+#include <sound/soc.h>
+#include <sound/soc-acpi.h>
+
+SND_SOC_DAILINK_DEF(dummy, DAILINK_COMP_ARRAY(COMP_DUMMY()));
+SND_SOC_DAILINK_DEF(probe_cp, DAILINK_COMP_ARRAY(COMP_CPU("Probe Extraction CPU DAI")));
+SND_SOC_DAILINK_DEF(platform, DAILINK_COMP_ARRAY(COMP_PLATFORM("probe-platform")));
+
+static struct snd_soc_dai_link probe_mb_dai_links[] = {
+ {
+ .name = "Compress Probe Capture",
+ .nonatomic = 1,
+ SND_SOC_DAILINK_REG(probe_cp, dummy, platform),
+ },
+};
+
+static int avs_probe_mb_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct snd_soc_acpi_mach *mach;
+ struct snd_soc_card *card;
+ int ret;
+
+ mach = dev_get_platdata(dev);
+
+ card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
+ if (!card)
+ return -ENOMEM;
+
+ card->name = "avs_probe_mb";
+ card->dev = dev;
+ card->owner = THIS_MODULE;
+ card->dai_link = probe_mb_dai_links;
+ card->num_links = ARRAY_SIZE(probe_mb_dai_links);
+ card->fully_routed = true;
+
+ ret = snd_soc_fixup_dai_links_platform_name(card, mach->mach_params.platform);
+ if (ret)
+ return ret;
+
+ return devm_snd_soc_register_card(dev, card);
+}
+
+static struct platform_driver avs_probe_mb_driver = {
+ .probe = avs_probe_mb_probe,
+ .driver = {
+ .name = "avs_probe_mb",
+ .pm = &snd_soc_pm_ops,
+ },
+};
+
+module_platform_driver(avs_probe_mb_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:avs_probe_mb");
diff --git a/sound/soc/intel/avs/core.c b/sound/soc/intel/avs/core.c
index f7bc06404dbc..637501850728 100644
--- a/sound/soc/intel/avs/core.c
+++ b/sound/soc/intel/avs/core.c
@@ -214,6 +214,7 @@ static void avs_hda_probe_work(struct work_struct *work)
adev->nhlt = intel_nhlt_init(adev->dev);
if (!adev->nhlt)
dev_info(bus->dev, "platform has no NHLT\n");
+ avs_debugfs_init(adev);
avs_register_all_boards(adev);
@@ -480,6 +481,29 @@ err_remap_bar0:
return ret;
}
+static void avs_pci_shutdown(struct pci_dev *pci)
+{
+ struct hdac_bus *bus = pci_get_drvdata(pci);
+ struct avs_dev *adev = hdac_to_avs(bus);
+
+ cancel_work_sync(&adev->probe_work);
+ avs_ipc_block(adev->ipc);
+
+ snd_hdac_stop_streams(bus);
+ avs_dsp_op(adev, int_control, false);
+ snd_hdac_ext_bus_ppcap_int_enable(bus, false);
+ snd_hdac_ext_bus_link_power_down_all(bus);
+
+ snd_hdac_bus_stop_chip(bus);
+ snd_hdac_display_power(bus, HDA_CODEC_IDX_CONTROLLER, false);
+
+ if (avs_platattr_test(adev, CLDMA))
+ pci_free_irq(pci, 0, &code_loader);
+ pci_free_irq(pci, 0, adev);
+ pci_free_irq(pci, 0, bus);
+ pci_free_irq_vectors(pci);
+}
+
static void avs_pci_remove(struct pci_dev *pci)
{
struct hdac_device *hdev, *save;
@@ -491,6 +515,7 @@ static void avs_pci_remove(struct pci_dev *pci)
avs_unregister_all_boards(adev);
+ avs_debugfs_exit(adev);
if (adev->nhlt)
intel_nhlt_free(adev->nhlt);
@@ -737,6 +762,7 @@ static struct pci_driver avs_pci_driver = {
.id_table = avs_ids,
.probe = avs_pci_probe,
.remove = avs_pci_remove,
+ .shutdown = avs_pci_shutdown,
.driver = {
.pm = &avs_dev_pm,
},
diff --git a/sound/soc/intel/avs/debugfs.c b/sound/soc/intel/avs/debugfs.c
new file mode 100644
index 000000000000..bdd388ec01ea
--- /dev/null
+++ b/sound/soc/intel/avs/debugfs.c
@@ -0,0 +1,436 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Copyright(c) 2021-2022 Intel Corporation. All rights reserved.
+//
+// Authors: Cezary Rojewski <cezary.rojewski@intel.com>
+// Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
+//
+
+#include <linux/debugfs.h>
+#include <linux/kfifo.h>
+#include <linux/wait.h>
+#include <linux/sched/signal.h>
+#include <sound/soc.h>
+#include "avs.h"
+#include "messages.h"
+
+static unsigned int __kfifo_fromio(struct kfifo *fifo, const void __iomem *src, unsigned int len)
+{
+ struct __kfifo *__fifo = &fifo->kfifo;
+ unsigned int l, off;
+
+ len = min(len, kfifo_avail(fifo));
+ off = __fifo->in & __fifo->mask;
+ l = min(len, kfifo_size(fifo) - off);
+
+ memcpy_fromio(__fifo->data + off, src, l);
+ memcpy_fromio(__fifo->data, src + l, len - l);
+ /* Make sure data copied from SRAM is visible to all CPUs. */
+ smp_mb();
+ __fifo->in += len;
+
+ return len;
+}
+
+bool avs_logging_fw(struct avs_dev *adev)
+{
+ return kfifo_initialized(&adev->trace_fifo);
+}
+
+void avs_dump_fw_log(struct avs_dev *adev, const void __iomem *src, unsigned int len)
+{
+ __kfifo_fromio(&adev->trace_fifo, src, len);
+}
+
+void avs_dump_fw_log_wakeup(struct avs_dev *adev, const void __iomem *src, unsigned int len)
+{
+ avs_dump_fw_log(adev, src, len);
+ wake_up(&adev->trace_waitq);
+}
+
+static ssize_t fw_regs_read(struct file *file, char __user *to, size_t count, loff_t *ppos)
+{
+ struct avs_dev *adev = file->private_data;
+ char *buf;
+ int ret;
+
+ buf = kzalloc(AVS_FW_REGS_SIZE, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ memcpy_fromio(buf, avs_sram_addr(adev, AVS_FW_REGS_WINDOW), AVS_FW_REGS_SIZE);
+
+ ret = simple_read_from_buffer(to, count, ppos, buf, AVS_FW_REGS_SIZE);
+ kfree(buf);
+ return ret;
+}
+
+static const struct file_operations fw_regs_fops = {
+ .open = simple_open,
+ .read = fw_regs_read,
+ .llseek = no_llseek,
+};
+
+static ssize_t debug_window_read(struct file *file, char __user *to, size_t count, loff_t *ppos)
+{
+ struct avs_dev *adev = file->private_data;
+ size_t size;
+ char *buf;
+ int ret;
+
+ size = adev->hw_cfg.dsp_cores * AVS_WINDOW_CHUNK_SIZE;
+ buf = kzalloc(size, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ memcpy_fromio(buf, avs_sram_addr(adev, AVS_DEBUG_WINDOW), size);
+
+ ret = simple_read_from_buffer(to, count, ppos, buf, size);
+ kfree(buf);
+ return ret;
+}
+
+static const struct file_operations debug_window_fops = {
+ .open = simple_open,
+ .read = debug_window_read,
+ .llseek = no_llseek,
+};
+
+static ssize_t probe_points_read(struct file *file, char __user *to, size_t count, loff_t *ppos)
+{
+ struct avs_dev *adev = file->private_data;
+ struct avs_probe_point_desc *desc;
+ size_t num_desc, len = 0;
+ char *buf;
+ int i, ret;
+
+ /* Prevent chaining, send and dump IPC value just once. */
+ if (*ppos)
+ return 0;
+
+ buf = kzalloc(PAGE_SIZE, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ ret = avs_ipc_probe_get_points(adev, &desc, &num_desc);
+ if (ret) {
+ ret = AVS_IPC_RET(ret);
+ goto exit;
+ }
+
+ for (i = 0; i < num_desc; i++) {
+ ret = snprintf(buf + len, PAGE_SIZE - len,
+ "Id: %#010x Purpose: %d Node id: %#x\n",
+ desc[i].id.value, desc[i].purpose, desc[i].node_id.val);
+ if (ret < 0)
+ goto free_desc;
+ len += ret;
+ }
+
+ ret = simple_read_from_buffer(to, count, ppos, buf, len);
+free_desc:
+ kfree(desc);
+exit:
+ kfree(buf);
+ return ret;
+}
+
+static ssize_t probe_points_write(struct file *file, const char __user *from, size_t count,
+ loff_t *ppos)
+{
+ struct avs_dev *adev = file->private_data;
+ struct avs_probe_point_desc *desc;
+ u32 *array, num_elems;
+ size_t bytes;
+ int ret;
+
+ ret = parse_int_array_user(from, count, (int **)&array);
+ if (ret < 0)
+ return ret;
+
+ num_elems = *array;
+ bytes = sizeof(*array) * num_elems;
+ if (bytes % sizeof(*desc)) {
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ desc = (struct avs_probe_point_desc *)&array[1];
+ ret = avs_ipc_probe_connect_points(adev, desc, bytes / sizeof(*desc));
+ if (ret)
+ ret = AVS_IPC_RET(ret);
+ else
+ ret = count;
+exit:
+ kfree(array);
+ return ret;
+}
+
+static const struct file_operations probe_points_fops = {
+ .open = simple_open,
+ .read = probe_points_read,
+ .write = probe_points_write,
+ .llseek = no_llseek,
+};
+
+static ssize_t probe_points_disconnect_write(struct file *file, const char __user *from,
+ size_t count, loff_t *ppos)
+{
+ struct avs_dev *adev = file->private_data;
+ union avs_probe_point_id *id;
+ u32 *array, num_elems;
+ size_t bytes;
+ int ret;
+
+ ret = parse_int_array_user(from, count, (int **)&array);
+ if (ret < 0)
+ return ret;
+
+ num_elems = *array;
+ bytes = sizeof(*array) * num_elems;
+ if (bytes % sizeof(*id)) {
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ id = (union avs_probe_point_id *)&array[1];
+ ret = avs_ipc_probe_disconnect_points(adev, id, bytes / sizeof(*id));
+ if (ret)
+ ret = AVS_IPC_RET(ret);
+ else
+ ret = count;
+exit:
+ kfree(array);
+ return ret;
+}
+
+static const struct file_operations probe_points_disconnect_fops = {
+ .open = simple_open,
+ .write = probe_points_disconnect_write,
+ .llseek = default_llseek,
+};
+
+static ssize_t strace_read(struct file *file, char __user *to, size_t count, loff_t *ppos)
+{
+ struct avs_dev *adev = file->private_data;
+ struct kfifo *fifo = &adev->trace_fifo;
+ unsigned int copied;
+
+ if (kfifo_is_empty(fifo)) {
+ DEFINE_WAIT(wait);
+
+ prepare_to_wait(&adev->trace_waitq, &wait, TASK_INTERRUPTIBLE);
+ if (!signal_pending(current))
+ schedule();
+ finish_wait(&adev->trace_waitq, &wait);
+ }
+
+ if (kfifo_to_user(fifo, to, count, &copied))
+ return -EFAULT;
+ *ppos += copied;
+ return copied;
+}
+
+static int strace_open(struct inode *inode, struct file *file)
+{
+ struct avs_dev *adev = inode->i_private;
+ int ret;
+
+ if (kfifo_initialized(&adev->trace_fifo))
+ return -EBUSY;
+
+ ret = kfifo_alloc(&adev->trace_fifo, PAGE_SIZE, GFP_KERNEL);
+ if (ret < 0)
+ return ret;
+
+ file->private_data = adev;
+ return 0;
+}
+
+static int strace_release(struct inode *inode, struct file *file)
+{
+ union avs_notify_msg msg = AVS_NOTIFICATION(LOG_BUFFER_STATUS);
+ struct avs_dev *adev = file->private_data;
+ unsigned long resource_mask;
+ unsigned long flags, i;
+ u32 num_cores;
+
+ resource_mask = adev->logged_resources;
+ num_cores = adev->hw_cfg.dsp_cores;
+
+ spin_lock_irqsave(&adev->trace_lock, flags);
+
+ /* Gather any remaining logs. */
+ for_each_set_bit(i, &resource_mask, num_cores) {
+ msg.log.core = i;
+ avs_dsp_op(adev, log_buffer_status, &msg);
+ }
+
+ kfifo_free(&adev->trace_fifo);
+
+ spin_unlock_irqrestore(&adev->trace_lock, flags);
+
+ return 0;
+}
+
+static const struct file_operations strace_fops = {
+ .llseek = default_llseek,
+ .read = strace_read,
+ .open = strace_open,
+ .release = strace_release,
+};
+
+#define DISABLE_TIMERS UINT_MAX
+
+static int enable_logs(struct avs_dev *adev, u32 resource_mask, u32 *priorities)
+{
+ int ret;
+
+ /* Logging demands D0i0 state from DSP. */
+ if (!adev->logged_resources) {
+ pm_runtime_get_sync(adev->dev);
+
+ ret = avs_dsp_disable_d0ix(adev);
+ if (ret)
+ goto err_d0ix;
+ }
+
+ ret = avs_ipc_set_system_time(adev);
+ if (ret && ret != AVS_IPC_NOT_SUPPORTED) {
+ ret = AVS_IPC_RET(ret);
+ goto err_ipc;
+ }
+
+ ret = avs_dsp_op(adev, enable_logs, AVS_LOG_ENABLE, adev->aging_timer_period,
+ adev->fifo_full_timer_period, resource_mask, priorities);
+ if (ret)
+ goto err_ipc;
+
+ adev->logged_resources |= resource_mask;
+ return 0;
+
+err_ipc:
+ if (!adev->logged_resources) {
+ avs_dsp_enable_d0ix(adev);
+err_d0ix:
+ pm_runtime_mark_last_busy(adev->dev);
+ pm_runtime_put_autosuspend(adev->dev);
+ }
+
+ return ret;
+}
+
+static int disable_logs(struct avs_dev *adev, u32 resource_mask)
+{
+ int ret;
+
+ /* Check if there's anything to do. */
+ if (!adev->logged_resources)
+ return 0;
+
+ ret = avs_dsp_op(adev, enable_logs, AVS_LOG_DISABLE, DISABLE_TIMERS, DISABLE_TIMERS,
+ resource_mask, NULL);
+
+ /*
+ * If IPC fails causing recovery, logged_resources is already zero
+ * so unsetting bits is still safe.
+ */
+ adev->logged_resources &= ~resource_mask;
+
+ /* If that's the last resource, allow for D3. */
+ if (!adev->logged_resources) {
+ avs_dsp_enable_d0ix(adev);
+ pm_runtime_mark_last_busy(adev->dev);
+ pm_runtime_put_autosuspend(adev->dev);
+ }
+
+ return ret;
+}
+
+static ssize_t trace_control_read(struct file *file, char __user *to, size_t count, loff_t *ppos)
+{
+ struct avs_dev *adev = file->private_data;
+ char buf[64];
+ int len;
+
+ len = snprintf(buf, sizeof(buf), "0x%08x\n", adev->logged_resources);
+
+ return simple_read_from_buffer(to, count, ppos, buf, len);
+}
+
+static ssize_t trace_control_write(struct file *file, const char __user *from, size_t count,
+ loff_t *ppos)
+{
+ struct avs_dev *adev = file->private_data;
+ u32 *array, num_elems;
+ u32 resource_mask;
+ int ret;
+
+ ret = parse_int_array_user(from, count, (int **)&array);
+ if (ret < 0)
+ return ret;
+
+ num_elems = *array;
+ resource_mask = array[1];
+
+ /*
+ * Disable if just resource mask is provided - no log priority flags.
+ *
+ * Enable input format: mask, prio1, .., prioN
+ * Where 'N' equals number of bits set in the 'mask'.
+ */
+ if (num_elems == 1) {
+ ret = disable_logs(adev, resource_mask);
+ } else {
+ if (num_elems != (hweight_long(resource_mask) + 1)) {
+ ret = -EINVAL;
+ goto free_array;
+ }
+
+ ret = enable_logs(adev, resource_mask, &array[2]);
+ }
+
+ if (!ret)
+ ret = count;
+free_array:
+ kfree(array);
+ return ret;
+}
+
+static const struct file_operations trace_control_fops = {
+ .llseek = default_llseek,
+ .read = trace_control_read,
+ .write = trace_control_write,
+ .open = simple_open,
+};
+
+void avs_debugfs_init(struct avs_dev *adev)
+{
+ init_waitqueue_head(&adev->trace_waitq);
+ spin_lock_init(&adev->trace_lock);
+
+ adev->debugfs_root = debugfs_create_dir("avs", snd_soc_debugfs_root);
+
+ /* Initialize timer periods with recommended defaults. */
+ adev->aging_timer_period = 10;
+ adev->fifo_full_timer_period = 10;
+
+ debugfs_create_file("strace", 0444, adev->debugfs_root, adev, &strace_fops);
+ debugfs_create_file("trace_control", 0644, adev->debugfs_root, adev, &trace_control_fops);
+ debugfs_create_file("fw_regs", 0444, adev->debugfs_root, adev, &fw_regs_fops);
+ debugfs_create_file("debug_window", 0444, adev->debugfs_root, adev, &debug_window_fops);
+
+ debugfs_create_u32("trace_aging_period", 0644, adev->debugfs_root,
+ &adev->aging_timer_period);
+ debugfs_create_u32("trace_fifo_full_period", 0644, adev->debugfs_root,
+ &adev->fifo_full_timer_period);
+
+ debugfs_create_file("probe_points", 0644, adev->debugfs_root, adev, &probe_points_fops);
+ debugfs_create_file("probe_points_disconnect", 0200, adev->debugfs_root, adev,
+ &probe_points_disconnect_fops);
+}
+
+void avs_debugfs_exit(struct avs_dev *adev)
+{
+ debugfs_remove_recursive(adev->debugfs_root);
+}
diff --git a/sound/soc/intel/avs/ipc.c b/sound/soc/intel/avs/ipc.c
index af8a260093f4..bdf013c3dd12 100644
--- a/sound/soc/intel/avs/ipc.c
+++ b/sound/soc/intel/avs/ipc.c
@@ -266,7 +266,7 @@ static void avs_dsp_process_notification(struct avs_dev *adev, u64 header)
break;
case AVS_NOTIFY_LOG_BUFFER_STATUS:
- avs_dsp_op(adev, log_buffer_status, &msg);
+ avs_log_buffer_status_locked(adev, &msg);
break;
case AVS_NOTIFY_EXCEPTION_CAUGHT:
diff --git a/sound/soc/intel/avs/messages.c b/sound/soc/intel/avs/messages.c
index 6b0fecbf07c3..e11ae4246416 100644
--- a/sound/soc/intel/avs/messages.c
+++ b/sound/soc/intel/avs/messages.c
@@ -685,6 +685,24 @@ int avs_ipc_get_modules_info(struct avs_dev *adev, struct avs_mods_info **info)
return 0;
}
+int avs_ipc_copier_set_sink_format(struct avs_dev *adev, u16 module_id,
+ u8 instance_id, u32 sink_id,
+ const struct avs_audio_format *src_fmt,
+ const struct avs_audio_format *sink_fmt)
+{
+ struct avs_copier_sink_format cpr_fmt;
+
+ cpr_fmt.sink_id = sink_id;
+ /* Firmware expects driver to resend copier's input format. */
+ cpr_fmt.src_fmt = *src_fmt;
+ cpr_fmt.sink_fmt = *sink_fmt;
+
+ return avs_ipc_set_large_config(adev, module_id, instance_id,
+ AVS_COPIER_SET_SINK_FORMAT,
+ (u8 *)&cpr_fmt, sizeof(cpr_fmt));
+}
+
+#ifdef CONFIG_DEBUG_FS
int avs_ipc_set_enable_logs(struct avs_dev *adev, u8 *log_info, size_t size)
{
return avs_ipc_set_large_config(adev, AVS_BASEFW_MOD_ID, AVS_BASEFW_INST_ID,
@@ -705,19 +723,81 @@ int avs_ipc_set_system_time(struct avs_dev *adev)
AVS_BASEFW_SYSTEM_TIME, (u8 *)&sys_time, sizeof(sys_time));
}
-int avs_ipc_copier_set_sink_format(struct avs_dev *adev, u16 module_id,
- u8 instance_id, u32 sink_id,
- const struct avs_audio_format *src_fmt,
- const struct avs_audio_format *sink_fmt)
+int avs_ipc_probe_get_dma(struct avs_dev *adev, struct avs_probe_dma **dmas, size_t *num_dmas)
{
- struct avs_copier_sink_format cpr_fmt;
+ size_t payload_size;
+ u32 module_id;
+ u8 *payload;
+ int ret;
- cpr_fmt.sink_id = sink_id;
- /* Firmware expects driver to resend copier's input format. */
- cpr_fmt.src_fmt = *src_fmt;
- cpr_fmt.sink_fmt = *sink_fmt;
+ module_id = avs_get_module_id(adev, &AVS_PROBE_MOD_UUID);
- return avs_ipc_set_large_config(adev, module_id, instance_id,
- AVS_COPIER_SET_SINK_FORMAT,
- (u8 *)&cpr_fmt, sizeof(cpr_fmt));
+ ret = avs_ipc_get_large_config(adev, module_id, AVS_PROBE_INST_ID, AVS_PROBE_INJECTION_DMA,
+ NULL, 0, &payload, &payload_size);
+ if (ret)
+ return ret;
+
+ *dmas = (struct avs_probe_dma *)payload;
+ *num_dmas = payload_size / sizeof(**dmas);
+
+ return 0;
+}
+
+int avs_ipc_probe_attach_dma(struct avs_dev *adev, struct avs_probe_dma *dmas, size_t num_dmas)
+{
+ u32 module_id = avs_get_module_id(adev, &AVS_PROBE_MOD_UUID);
+
+ return avs_ipc_set_large_config(adev, module_id, AVS_PROBE_INST_ID, AVS_PROBE_INJECTION_DMA,
+ (u8 *)dmas, array_size(sizeof(*dmas), num_dmas));
+}
+
+int avs_ipc_probe_detach_dma(struct avs_dev *adev, union avs_connector_node_id *node_ids,
+ size_t num_node_ids)
+{
+ u32 module_id = avs_get_module_id(adev, &AVS_PROBE_MOD_UUID);
+
+ return avs_ipc_set_large_config(adev, module_id, AVS_PROBE_INST_ID,
+ AVS_PROBE_INJECTION_DMA_DETACH, (u8 *)node_ids,
+ array_size(sizeof(*node_ids), num_node_ids));
+}
+
+int avs_ipc_probe_get_points(struct avs_dev *adev, struct avs_probe_point_desc **descs,
+ size_t *num_descs)
+{
+ size_t payload_size;
+ u32 module_id;
+ u8 *payload;
+ int ret;
+
+ module_id = avs_get_module_id(adev, &AVS_PROBE_MOD_UUID);
+
+ ret = avs_ipc_get_large_config(adev, module_id, AVS_PROBE_INST_ID, AVS_PROBE_POINTS, NULL,
+ 0, &payload, &payload_size);
+ if (ret)
+ return ret;
+
+ *descs = (struct avs_probe_point_desc *)payload;
+ *num_descs = payload_size / sizeof(**descs);
+
+ return 0;
+}
+
+int avs_ipc_probe_connect_points(struct avs_dev *adev, struct avs_probe_point_desc *descs,
+ size_t num_descs)
+{
+ u32 module_id = avs_get_module_id(adev, &AVS_PROBE_MOD_UUID);
+
+ return avs_ipc_set_large_config(adev, module_id, AVS_PROBE_INST_ID, AVS_PROBE_POINTS,
+ (u8 *)descs, array_size(sizeof(*descs), num_descs));
+}
+
+int avs_ipc_probe_disconnect_points(struct avs_dev *adev, union avs_probe_point_id *ids,
+ size_t num_ids)
+{
+ u32 module_id = avs_get_module_id(adev, &AVS_PROBE_MOD_UUID);
+
+ return avs_ipc_set_large_config(adev, module_id, AVS_PROBE_INST_ID,
+ AVS_PROBE_POINTS_DISCONNECT, (u8 *)ids,
+ array_size(sizeof(*ids), num_ids));
}
+#endif
diff --git a/sound/soc/intel/avs/messages.h b/sound/soc/intel/avs/messages.h
index 02b3b7a74783..9dd835527e02 100644
--- a/sound/soc/intel/avs/messages.h
+++ b/sound/soc/intel/avs/messages.h
@@ -802,4 +802,57 @@ int avs_ipc_copier_set_sink_format(struct avs_dev *adev, u16 module_id,
const struct avs_audio_format *src_fmt,
const struct avs_audio_format *sink_fmt);
+#define AVS_PROBE_INST_ID 0
+
+enum avs_probe_runtime_param {
+ AVS_PROBE_INJECTION_DMA = 1,
+ AVS_PROBE_INJECTION_DMA_DETACH,
+ AVS_PROBE_POINTS,
+ AVS_PROBE_POINTS_DISCONNECT,
+};
+
+struct avs_probe_dma {
+ union avs_connector_node_id node_id;
+ u32 dma_buffer_size;
+} __packed;
+
+enum avs_probe_type {
+ AVS_PROBE_TYPE_INPUT = 0,
+ AVS_PROBE_TYPE_OUTPUT,
+ AVS_PROBE_TYPE_INTERNAL
+};
+
+union avs_probe_point_id {
+ u32 value;
+ struct {
+ u32 module_id:16;
+ u32 instance_id:8;
+ u32 type:2;
+ u32 index:6;
+ } id;
+} __packed;
+
+enum avs_connection_purpose {
+ AVS_CONNECTION_PURPOSE_EXTRACT = 0,
+ AVS_CONNECTION_PURPOSE_INJECT,
+ AVS_CONNECTION_PURPOSE_INJECT_REEXTRACT,
+};
+
+struct avs_probe_point_desc {
+ union avs_probe_point_id id;
+ u32 purpose;
+ union avs_connector_node_id node_id;
+} __packed;
+
+int avs_ipc_probe_get_dma(struct avs_dev *adev, struct avs_probe_dma **dmas, size_t *num_dmas);
+int avs_ipc_probe_attach_dma(struct avs_dev *adev, struct avs_probe_dma *dmas, size_t num_dmas);
+int avs_ipc_probe_detach_dma(struct avs_dev *adev, union avs_connector_node_id *node_ids,
+ size_t num_node_ids);
+int avs_ipc_probe_get_points(struct avs_dev *adev, struct avs_probe_point_desc **descs,
+ size_t *num_descs);
+int avs_ipc_probe_connect_points(struct avs_dev *adev, struct avs_probe_point_desc *descs,
+ size_t num_descs);
+int avs_ipc_probe_disconnect_points(struct avs_dev *adev, union avs_probe_point_id *ids,
+ size_t num_ids);
+
#endif /* __SOUND_SOC_INTEL_AVS_MSGS_H */
diff --git a/sound/soc/intel/avs/pcm.c b/sound/soc/intel/avs/pcm.c
index 70d687fa9923..f930c5e86a84 100644
--- a/sound/soc/intel/avs/pcm.c
+++ b/sound/soc/intel/avs/pcm.c
@@ -1126,9 +1126,9 @@ static const struct snd_soc_component_driver avs_component_driver = {
.topology_name_prefix = "intel/avs",
};
-static int avs_soc_component_register(struct device *dev, const char *name,
- const struct snd_soc_component_driver *drv,
- struct snd_soc_dai_driver *cpu_dais, int num_cpu_dais)
+int avs_soc_component_register(struct device *dev, const char *name,
+ const struct snd_soc_component_driver *drv,
+ struct snd_soc_dai_driver *cpu_dais, int num_cpu_dais)
{
struct avs_soc_component *acomp;
int ret;
diff --git a/sound/soc/intel/avs/probes.c b/sound/soc/intel/avs/probes.c
new file mode 100644
index 000000000000..29d63f2a9616
--- /dev/null
+++ b/sound/soc/intel/avs/probes.c
@@ -0,0 +1,313 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Copyright(c) 2021-2022 Intel Corporation. All rights reserved.
+//
+// Authors: Cezary Rojewski <cezary.rojewski@intel.com>
+// Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
+//
+
+#include <sound/compress_driver.h>
+#include <sound/hdaudio_ext.h>
+#include <sound/hdaudio.h>
+#include <sound/soc.h>
+#include "avs.h"
+#include "messages.h"
+
+static int avs_dsp_init_probe(struct avs_dev *adev, union avs_connector_node_id node_id,
+ size_t buffer_size)
+{
+ struct avs_probe_cfg cfg = {{0}};
+ struct avs_module_entry mentry;
+ u16 dummy;
+
+ avs_get_module_entry(adev, &AVS_PROBE_MOD_UUID, &mentry);
+
+ /*
+ * Probe module uses no cycles, audio data format and input and output
+ * frame sizes are unused. It is also not owned by any pipeline.
+ */
+ cfg.base.ibs = 1;
+ /* BSS module descriptor is always segment of index=2. */
+ cfg.base.is_pages = mentry.segments[2].flags.length;
+ cfg.gtw_cfg.node_id = node_id;
+ cfg.gtw_cfg.dma_buffer_size = buffer_size;
+
+ return avs_dsp_init_module(adev, mentry.module_id, INVALID_PIPELINE_ID, 0, 0, &cfg,
+ sizeof(cfg), &dummy);
+}
+
+static void avs_dsp_delete_probe(struct avs_dev *adev)
+{
+ struct avs_module_entry mentry;
+
+ avs_get_module_entry(adev, &AVS_PROBE_MOD_UUID, &mentry);
+
+ /* There is only ever one probe module instance. */
+ avs_dsp_delete_module(adev, mentry.module_id, 0, INVALID_PIPELINE_ID, 0);
+}
+
+static inline struct hdac_ext_stream *avs_compr_get_host_stream(struct snd_compr_stream *cstream)
+{
+ return cstream->runtime->private_data;
+}
+
+static int avs_probe_compr_open(struct snd_compr_stream *cstream, struct snd_soc_dai *dai)
+{
+ struct avs_dev *adev = to_avs_dev(dai->dev);
+ struct hdac_bus *bus = &adev->base.core;
+ struct hdac_ext_stream *host_stream;
+
+ if (adev->extractor) {
+ dev_err(dai->dev, "Cannot open more than one extractor stream\n");
+ return -EEXIST;
+ }
+
+ host_stream = snd_hdac_ext_cstream_assign(bus, cstream);
+ if (!host_stream) {
+ dev_err(dai->dev, "Failed to assign HDAudio stream for extraction\n");
+ return -EBUSY;
+ }
+
+ adev->extractor = host_stream;
+ hdac_stream(host_stream)->curr_pos = 0;
+ cstream->runtime->private_data = host_stream;
+
+ return 0;
+}
+
+static int avs_probe_compr_free(struct snd_compr_stream *cstream, struct snd_soc_dai *dai)
+{
+ struct hdac_ext_stream *host_stream = avs_compr_get_host_stream(cstream);
+ struct avs_dev *adev = to_avs_dev(dai->dev);
+ struct avs_probe_point_desc *desc;
+ /* Extractor node identifier. */
+ unsigned int vindex = INVALID_NODE_ID.vindex;
+ size_t num_desc;
+ int i, ret;
+
+ /* Disconnect all probe points. */
+ ret = avs_ipc_probe_get_points(adev, &desc, &num_desc);
+ if (ret) {
+ dev_err(dai->dev, "get probe points failed: %d\n", ret);
+ ret = AVS_IPC_RET(ret);
+ goto exit;
+ }
+
+ for (i = 0; i < num_desc; i++)
+ if (desc[i].node_id.vindex == vindex)
+ avs_ipc_probe_disconnect_points(adev, &desc[i].id, 1);
+ kfree(desc);
+
+exit:
+ if (adev->num_probe_streams) {
+ adev->num_probe_streams--;
+ if (!adev->num_probe_streams) {
+ avs_dsp_delete_probe(adev);
+ avs_dsp_enable_d0ix(adev);
+ }
+ }
+
+ snd_hdac_stream_cleanup(hdac_stream(host_stream));
+ hdac_stream(host_stream)->prepared = 0;
+ snd_hdac_ext_stream_release(host_stream, HDAC_EXT_STREAM_TYPE_HOST);
+
+ snd_compr_free_pages(cstream);
+ adev->extractor = NULL;
+
+ return ret;
+}
+
+static int avs_probe_compr_set_params(struct snd_compr_stream *cstream,
+ struct snd_compr_params *params, struct snd_soc_dai *dai)
+{
+ struct hdac_ext_stream *host_stream = avs_compr_get_host_stream(cstream);
+ struct snd_compr_runtime *rtd = cstream->runtime;
+ struct avs_dev *adev = to_avs_dev(dai->dev);
+ /* compr params do not store bit depth, default to S32_LE. */
+ snd_pcm_format_t format = SNDRV_PCM_FORMAT_S32_LE;
+ unsigned int format_val;
+ int bps, ret;
+
+ hdac_stream(host_stream)->bufsize = 0;
+ hdac_stream(host_stream)->period_bytes = 0;
+ hdac_stream(host_stream)->format_val = 0;
+ cstream->dma_buffer.dev.type = SNDRV_DMA_TYPE_DEV_SG;
+ cstream->dma_buffer.dev.dev = adev->dev;
+
+ ret = snd_compr_malloc_pages(cstream, rtd->buffer_size);
+ if (ret < 0)
+ return ret;
+ bps = snd_pcm_format_physical_width(format);
+ if (bps < 0)
+ return bps;
+ format_val = snd_hdac_calc_stream_format(params->codec.sample_rate, params->codec.ch_out,
+ format, bps, 0);
+ ret = snd_hdac_stream_set_params(hdac_stream(host_stream), format_val);
+ if (ret < 0)
+ return ret;
+ ret = snd_hdac_stream_setup(hdac_stream(host_stream));
+ if (ret < 0)
+ return ret;
+
+ hdac_stream(host_stream)->prepared = 1;
+
+ if (!adev->num_probe_streams) {
+ union avs_connector_node_id node_id;
+
+ /* D0ix not allowed during probing. */
+ ret = avs_dsp_disable_d0ix(adev);
+ if (ret)
+ return ret;
+
+ node_id.vindex = hdac_stream(host_stream)->stream_tag - 1;
+ node_id.dma_type = AVS_DMA_HDA_HOST_INPUT;
+
+ ret = avs_dsp_init_probe(adev, node_id, rtd->dma_bytes);
+ if (ret < 0) {
+ dev_err(dai->dev, "probe init failed: %d\n", ret);
+ avs_dsp_enable_d0ix(adev);
+ return ret;
+ }
+ }
+
+ adev->num_probe_streams++;
+ return 0;
+}
+
+static int avs_probe_compr_trigger(struct snd_compr_stream *cstream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct hdac_ext_stream *host_stream = avs_compr_get_host_stream(cstream);
+ struct avs_dev *adev = to_avs_dev(dai->dev);
+ struct hdac_bus *bus = &adev->base.core;
+ unsigned long cookie;
+
+ if (!hdac_stream(host_stream)->prepared)
+ return -EPIPE;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ spin_lock_irqsave(&bus->reg_lock, cookie);
+ snd_hdac_stream_start(hdac_stream(host_stream), true);
+ spin_unlock_irqrestore(&bus->reg_lock, cookie);
+ break;
+
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_STOP:
+ spin_lock_irqsave(&bus->reg_lock, cookie);
+ snd_hdac_stream_stop(hdac_stream(host_stream));
+ spin_unlock_irqrestore(&bus->reg_lock, cookie);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int avs_probe_compr_pointer(struct snd_compr_stream *cstream,
+ struct snd_compr_tstamp *tstamp, struct snd_soc_dai *dai)
+{
+ struct hdac_ext_stream *host_stream = avs_compr_get_host_stream(cstream);
+ struct snd_soc_pcm_stream *pstream;
+
+ pstream = &dai->driver->capture;
+ tstamp->copied_total = hdac_stream(host_stream)->curr_pos;
+ tstamp->sampling_rate = snd_pcm_rate_bit_to_rate(pstream->rates);
+
+ return 0;
+}
+
+static int avs_probe_compr_copy(struct snd_soc_component *comp, struct snd_compr_stream *cstream,
+ char __user *buf, size_t count)
+{
+ struct snd_compr_runtime *rtd = cstream->runtime;
+ unsigned int offset, n;
+ void *ptr;
+ int ret;
+
+ if (count > rtd->buffer_size)
+ count = rtd->buffer_size;
+
+ div_u64_rem(rtd->total_bytes_transferred, rtd->buffer_size, &offset);
+ ptr = rtd->dma_area + offset;
+ n = rtd->buffer_size - offset;
+
+ if (count < n) {
+ ret = copy_to_user(buf, ptr, count);
+ } else {
+ ret = copy_to_user(buf, ptr, n);
+ ret += copy_to_user(buf + n, rtd->dma_area, count - n);
+ }
+
+ if (ret)
+ return count - ret;
+ return count;
+}
+
+static const struct snd_soc_cdai_ops avs_probe_dai_ops = {
+ .startup = avs_probe_compr_open,
+ .shutdown = avs_probe_compr_free,
+ .set_params = avs_probe_compr_set_params,
+ .trigger = avs_probe_compr_trigger,
+ .pointer = avs_probe_compr_pointer,
+};
+
+static const struct snd_compress_ops avs_probe_compress_ops = {
+ .copy = avs_probe_compr_copy,
+};
+
+static struct snd_soc_dai_driver probe_cpu_dais[] = {
+{
+ .name = "Probe Extraction CPU DAI",
+ .compress_new = snd_soc_new_compress,
+ .cops = &avs_probe_dai_ops,
+ .capture = {
+ .stream_name = "Probe Extraction",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = SNDRV_PCM_RATE_48000,
+ .rate_min = 48000,
+ .rate_max = 48000,
+ },
+},
+};
+
+static int avs_probe_component_probe(struct snd_soc_component *component)
+{
+ struct avs_soc_component *acomp = to_avs_soc_component(component);
+ struct avs_dev *adev = to_avs_dev(component->dev);
+
+ mutex_lock(&adev->comp_list_mutex);
+ list_add_tail(&acomp->node, &adev->comp_list);
+ mutex_unlock(&adev->comp_list_mutex);
+ return 0;
+}
+
+static void avs_probe_component_remove(struct snd_soc_component *component)
+{
+ struct avs_soc_component *acomp = to_avs_soc_component(component);
+ struct avs_dev *adev = to_avs_dev(component->dev);
+
+ mutex_lock(&adev->comp_list_mutex);
+ list_del(&acomp->node);
+ mutex_unlock(&adev->comp_list_mutex);
+}
+
+static const struct snd_soc_component_driver avs_probe_component_driver = {
+ .name = "avs-probe-compr",
+ .probe = avs_probe_component_probe,
+ .remove = avs_probe_component_remove,
+ .compress_ops = &avs_probe_compress_ops,
+ .module_get_upon_open = 1, /* increment refcount when a stream is opened */
+};
+
+int avs_probe_platform_register(struct avs_dev *adev, const char *name)
+{
+ return avs_soc_component_register(adev->dev, name, &avs_probe_component_driver,
+ probe_cpu_dais, ARRAY_SIZE(probe_cpu_dais));
+}
diff --git a/sound/soc/intel/avs/registers.h b/sound/soc/intel/avs/registers.h
index 95be86148cf3..2b464e466ed5 100644
--- a/sound/soc/intel/avs/registers.h
+++ b/sound/soc/intel/avs/registers.h
@@ -59,7 +59,8 @@
#define AVS_FW_REG_STATUS(adev) (AVS_FW_REG_BASE(adev) + 0x0)
#define AVS_FW_REG_ERROR_CODE(adev) (AVS_FW_REG_BASE(adev) + 0x4)
-#define AVS_FW_REGS_SIZE PAGE_SIZE
+#define AVS_WINDOW_CHUNK_SIZE PAGE_SIZE
+#define AVS_FW_REGS_SIZE AVS_WINDOW_CHUNK_SIZE
#define AVS_FW_REGS_WINDOW 0
/* DSP -> HOST communication window */
#define AVS_UPLINK_WINDOW AVS_FW_REGS_WINDOW
diff --git a/sound/soc/intel/avs/skl.c b/sound/soc/intel/avs/skl.c
index dc98b5cf900f..6bb8bbc70442 100644
--- a/sound/soc/intel/avs/skl.c
+++ b/sound/soc/intel/avs/skl.c
@@ -12,8 +12,9 @@
#include "avs.h"
#include "messages.h"
-static int skl_enable_logs(struct avs_dev *adev, enum avs_log_enable enable, u32 aging_period,
- u32 fifo_full_period, unsigned long resource_mask, u32 *priorities)
+static int __maybe_unused
+skl_enable_logs(struct avs_dev *adev, enum avs_log_enable enable, u32 aging_period,
+ u32 fifo_full_period, unsigned long resource_mask, u32 *priorities)
{
struct skl_log_state_info *info;
u32 size, num_cores = adev->hw_cfg.dsp_cores;
@@ -55,15 +56,11 @@ int skl_log_buffer_offset(struct avs_dev *adev, u32 core)
static int
skl_log_buffer_status(struct avs_dev *adev, union avs_notify_msg *msg)
{
- unsigned long flags;
void __iomem *buf;
u16 size, write, offset;
- spin_lock_irqsave(&adev->dbg.trace_lock, flags);
- if (!kfifo_initialized(&adev->dbg.trace_fifo)) {
- spin_unlock_irqrestore(&adev->dbg.trace_lock, flags);
+ if (!avs_logging_fw(adev))
return 0;
- }
size = avs_log_buffer_size(adev) / 2;
write = readl(avs_sram_addr(adev, AVS_FW_REGS_WINDOW) + FW_REGS_DBG_LOG_WP(msg->log.core));
@@ -72,9 +69,7 @@ skl_log_buffer_status(struct avs_dev *adev, union avs_notify_msg *msg)
/* Address is guaranteed to exist in SRAM2. */
buf = avs_log_buffer_addr(adev, msg->log.core) + offset;
- __kfifo_fromio_locked(&adev->dbg.trace_fifo, buf, size, &adev->dbg.fifo_lock);
- wake_up(&adev->dbg.trace_waitq);
- spin_unlock_irqrestore(&adev->dbg.trace_lock, flags);
+ avs_dump_fw_log_wakeup(adev, buf, size);
return 0;
}
@@ -116,10 +111,10 @@ const struct avs_dsp_ops skl_dsp_ops = {
.load_basefw = avs_cldma_load_basefw,
.load_lib = avs_cldma_load_library,
.transfer_mods = avs_cldma_transfer_modules,
- .enable_logs = skl_enable_logs,
.log_buffer_offset = skl_log_buffer_offset,
.log_buffer_status = skl_log_buffer_status,
.coredump = skl_coredump,
.d0ix_toggle = skl_d0ix_toggle,
.set_d0ix = skl_set_d0ix,
+ AVS_SET_ENABLE_LOGS_OP(skl)
};
diff --git a/sound/soc/intel/avs/utils.c b/sound/soc/intel/avs/utils.c
index 13611dee9787..82416b86662d 100644
--- a/sound/soc/intel/avs/utils.c
+++ b/sound/soc/intel/avs/utils.c
@@ -300,25 +300,3 @@ void avs_release_firmwares(struct avs_dev *adev)
kfree(entry);
}
}
-
-unsigned int __kfifo_fromio_locked(struct kfifo *fifo, const void __iomem *src, unsigned int len,
- spinlock_t *lock)
-{
- struct __kfifo *__fifo = &fifo->kfifo;
- unsigned long flags;
- unsigned int l, off;
-
- spin_lock_irqsave(lock, flags);
- len = min(len, kfifo_avail(fifo));
- off = __fifo->in & __fifo->mask;
- l = min(len, kfifo_size(fifo) - off);
-
- memcpy_fromio(__fifo->data + off, src, l);
- memcpy_fromio(__fifo->data, src + l, len - l);
- /* Make sure data copied from SRAM is visible to all CPUs. */
- smp_mb();
- __fifo->in += len;
- spin_unlock_irqrestore(lock, flags);
-
- return len;
-}
diff --git a/sound/soc/intel/boards/Kconfig b/sound/soc/intel/boards/Kconfig
index a472de1909f4..99308ed85277 100644
--- a/sound/soc/intel/boards/Kconfig
+++ b/sound/soc/intel/boards/Kconfig
@@ -554,10 +554,12 @@ config SND_SOC_INTEL_SOF_NAU8825_MACH
select SND_SOC_RT1015P
select SND_SOC_MAX98373_I2C
select SND_SOC_MAX98357A
+ select SND_SOC_NAU8315
select SND_SOC_DMIC
select SND_SOC_HDAC_HDMI
select SND_SOC_INTEL_HDA_DSP_COMMON
select SND_SOC_INTEL_SOF_MAXIM_COMMON
+ select SND_SOC_INTEL_SOF_REALTEK_COMMON
help
This adds support for ASoC machine driver for SOF platforms
with nau8825 codec.
diff --git a/sound/soc/intel/boards/Makefile b/sound/soc/intel/boards/Makefile
index 7e1a4ff77ac3..d1fd7a2b32db 100644
--- a/sound/soc/intel/boards/Makefile
+++ b/sound/soc/intel/boards/Makefile
@@ -37,8 +37,7 @@ snd-soc-sof_da7219_max98373-objs := sof_da7219_max98373.o
snd-soc-ehl-rt5660-objs := ehl_rt5660.o
snd-soc-sof-ssp-amp-objs := sof_ssp_amp.o
snd-soc-sof-sdw-objs += sof_sdw.o \
- sof_sdw_max98373.o sof_sdw_rt1308.o \
- sof_sdw_rt1316.o sof_sdw_rt1318.o \
+ sof_sdw_max98373.o sof_sdw_rt_amp.o \
sof_sdw_rt5682.o sof_sdw_rt700.o \
sof_sdw_rt711.o sof_sdw_rt711_sdca.o \
sof_sdw_rt715.o sof_sdw_rt715_sdca.o \
diff --git a/sound/soc/intel/boards/bytcht_es8316.c b/sound/soc/intel/boards/bytcht_es8316.c
index 09d1f0f6d686..df157b01df8b 100644
--- a/sound/soc/intel/boards/bytcht_es8316.c
+++ b/sound/soc/intel/boards/bytcht_es8316.c
@@ -497,21 +497,28 @@ static int snd_byt_cht_es8316_mc_probe(struct platform_device *pdev)
if (adev) {
snprintf(codec_name, sizeof(codec_name),
"i2c-%s", acpi_dev_name(adev));
- put_device(&adev->dev);
byt_cht_es8316_dais[dai_index].codecs->name = codec_name;
} else {
dev_err(dev, "Error cannot find '%s' dev\n", mach->id);
return -ENXIO;
}
+ codec_dev = acpi_get_first_physical_node(adev);
+ acpi_dev_put(adev);
+ if (!codec_dev)
+ return -EPROBE_DEFER;
+ priv->codec_dev = get_device(codec_dev);
+
/* override platform name, if required */
byt_cht_es8316_card.dev = dev;
platform_name = mach->mach_params.platform;
ret = snd_soc_fixup_dai_links_platform_name(&byt_cht_es8316_card,
platform_name);
- if (ret)
+ if (ret) {
+ put_device(codec_dev);
return ret;
+ }
/* Check for BYTCR or other platform and setup quirks */
dmi_id = dmi_first_match(byt_cht_es8316_quirk_table);
@@ -539,13 +546,10 @@ static int snd_byt_cht_es8316_mc_probe(struct platform_device *pdev)
/* get the clock */
priv->mclk = devm_clk_get(dev, "pmc_plt_clk_3");
- if (IS_ERR(priv->mclk))
+ if (IS_ERR(priv->mclk)) {
+ put_device(codec_dev);
return dev_err_probe(dev, PTR_ERR(priv->mclk), "clk_get pmc_plt_clk_3 failed\n");
-
- codec_dev = acpi_get_first_physical_node(adev);
- if (!codec_dev)
- return -EPROBE_DEFER;
- priv->codec_dev = get_device(codec_dev);
+ }
if (quirk & BYT_CHT_ES8316_JD_INVERTED)
props[cnt++] = PROPERTY_ENTRY_BOOL("everest,jack-detect-inverted");
diff --git a/sound/soc/intel/boards/bytcr_rt5640.c b/sound/soc/intel/boards/bytcr_rt5640.c
index 6db07b2417ca..79e0039c79a3 100644
--- a/sound/soc/intel/boards/bytcr_rt5640.c
+++ b/sound/soc/intel/boards/bytcr_rt5640.c
@@ -571,6 +571,21 @@ static const struct dmi_system_id byt_rt5640_quirk_table[] = {
BYT_RT5640_MCLK_EN),
},
{
+ /* Advantech MICA-071 */
+ .matches = {
+ DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Advantech"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "MICA-071"),
+ },
+ /* OVCD Th = 1500uA to reliable detect head-phones vs -set */
+ .driver_data = (void *)(BYT_RT5640_IN3_MAP |
+ BYT_RT5640_JD_SRC_JD2_IN4N |
+ BYT_RT5640_OVCD_TH_1500UA |
+ BYT_RT5640_OVCD_SF_0P75 |
+ BYT_RT5640_MONO_SPEAKER |
+ BYT_RT5640_DIFF_MIC |
+ BYT_RT5640_MCLK_EN),
+ },
+ {
.matches = {
DMI_EXACT_MATCH(DMI_SYS_VENDOR, "ARCHOS"),
DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "ARCHOS 80 Cesium"),
@@ -796,6 +811,16 @@ static const struct dmi_system_id byt_rt5640_quirk_table[] = {
BYT_RT5640_SSP0_AIF1 |
BYT_RT5640_MCLK_EN),
},
+ { /* HP Stream 8 */
+ .matches = {
+ DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "HP Stream 8 Tablet"),
+ },
+ .driver_data = (void *)(BYTCR_INPUT_DEFAULTS |
+ BYT_RT5640_JD_NOT_INV |
+ BYT_RT5640_SSP0_AIF1 |
+ BYT_RT5640_MCLK_EN),
+ },
{ /* I.T.Works TW891 */
.matches = {
DMI_EXACT_MATCH(DMI_SYS_VENDOR, "To be filled by O.E.M."),
@@ -1611,13 +1636,18 @@ static int snd_byt_rt5640_mc_probe(struct platform_device *pdev)
if (adev) {
snprintf(byt_rt5640_codec_name, sizeof(byt_rt5640_codec_name),
"i2c-%s", acpi_dev_name(adev));
- put_device(&adev->dev);
byt_rt5640_dais[dai_index].codecs->name = byt_rt5640_codec_name;
} else {
dev_err(dev, "Error cannot find '%s' dev\n", mach->id);
return -ENXIO;
}
+ codec_dev = acpi_get_first_physical_node(adev);
+ acpi_dev_put(adev);
+ if (!codec_dev)
+ return -EPROBE_DEFER;
+ priv->codec_dev = get_device(codec_dev);
+
/*
* swap SSP0 if bytcr is detected
* (will be overridden if DMI quirk is detected)
@@ -1692,11 +1722,6 @@ static int snd_byt_rt5640_mc_probe(struct platform_device *pdev)
byt_rt5640_quirk = quirk_override;
}
- codec_dev = acpi_get_first_physical_node(adev);
- if (!codec_dev)
- return -EPROBE_DEFER;
- priv->codec_dev = get_device(codec_dev);
-
if (byt_rt5640_quirk & BYT_RT5640_JD_HP_ELITEP_1000G2) {
acpi_dev_add_driver_gpios(ACPI_COMPANION(priv->codec_dev),
byt_rt5640_hp_elitepad_1000g2_gpios);
diff --git a/sound/soc/intel/boards/bytcr_rt5651.c b/sound/soc/intel/boards/bytcr_rt5651.c
index 81ac6eeda2e6..8fca9b82d4d0 100644
--- a/sound/soc/intel/boards/bytcr_rt5651.c
+++ b/sound/soc/intel/boards/bytcr_rt5651.c
@@ -922,7 +922,6 @@ static int snd_byt_rt5651_mc_probe(struct platform_device *pdev)
if (adev) {
snprintf(byt_rt5651_codec_name, sizeof(byt_rt5651_codec_name),
"i2c-%s", acpi_dev_name(adev));
- put_device(&adev->dev);
byt_rt5651_dais[dai_index].codecs->name = byt_rt5651_codec_name;
} else {
dev_err(dev, "Error cannot find '%s' dev\n", mach->id);
@@ -930,6 +929,7 @@ static int snd_byt_rt5651_mc_probe(struct platform_device *pdev)
}
codec_dev = acpi_get_first_physical_node(adev);
+ acpi_dev_put(adev);
if (!codec_dev)
return -EPROBE_DEFER;
priv->codec_dev = get_device(codec_dev);
diff --git a/sound/soc/intel/boards/bytcr_wm5102.c b/sound/soc/intel/boards/bytcr_wm5102.c
index 1669eb3bd80f..c0706537f673 100644
--- a/sound/soc/intel/boards/bytcr_wm5102.c
+++ b/sound/soc/intel/boards/bytcr_wm5102.c
@@ -411,9 +411,9 @@ static int snd_byt_wm5102_mc_probe(struct platform_device *pdev)
return -ENOENT;
}
snprintf(codec_name, sizeof(codec_name), "spi-%s", acpi_dev_name(adev));
- put_device(&adev->dev);
codec_dev = bus_find_device_by_name(&spi_bus_type, NULL, codec_name);
+ acpi_dev_put(adev);
if (!codec_dev)
return -EPROBE_DEFER;
diff --git a/sound/soc/intel/boards/sof_cs42l42.c b/sound/soc/intel/boards/sof_cs42l42.c
index e38bd2831e6a..e9d190cb13b0 100644
--- a/sound/soc/intel/boards/sof_cs42l42.c
+++ b/sound/soc/intel/boards/sof_cs42l42.c
@@ -336,6 +336,9 @@ static int create_spk_amp_dai_links(struct device *dev,
links[*id].platforms = platform_component;
links[*id].num_platforms = ARRAY_SIZE(platform_component);
links[*id].dpcm_playback = 1;
+ /* firmware-generated echo reference */
+ links[*id].dpcm_capture = 1;
+
links[*id].no_pcm = 1;
links[*id].cpus = &cpus[*id];
links[*id].num_cpus = 1;
diff --git a/sound/soc/intel/boards/sof_es8336.c b/sound/soc/intel/boards/sof_es8336.c
index 70713e4b07dc..894b6610b9e2 100644
--- a/sound/soc/intel/boards/sof_es8336.c
+++ b/sound/soc/intel/boards/sof_es8336.c
@@ -681,7 +681,6 @@ static int sof_es8336_probe(struct platform_device *pdev)
if (adev) {
snprintf(codec_name, sizeof(codec_name),
"i2c-%s", acpi_dev_name(adev));
- put_device(&adev->dev);
dai_links[0].codecs->name = codec_name;
/* also fixup codec dai name if relevant */
@@ -692,16 +691,19 @@ static int sof_es8336_probe(struct platform_device *pdev)
return -ENXIO;
}
- ret = snd_soc_fixup_dai_links_platform_name(&sof_es8336_card,
- mach->mach_params.platform);
- if (ret)
- return ret;
-
codec_dev = acpi_get_first_physical_node(adev);
+ acpi_dev_put(adev);
if (!codec_dev)
return -EPROBE_DEFER;
priv->codec_dev = get_device(codec_dev);
+ ret = snd_soc_fixup_dai_links_platform_name(&sof_es8336_card,
+ mach->mach_params.platform);
+ if (ret) {
+ put_device(codec_dev);
+ return ret;
+ }
+
if (quirk & SOF_ES8336_JD_INVERTED)
props[cnt++] = PROPERTY_ENTRY_BOOL("everest,jack-detect-inverted");
@@ -783,7 +785,7 @@ static int sof_es8336_remove(struct platform_device *pdev)
struct snd_soc_card *card = platform_get_drvdata(pdev);
struct sof_es8336_private *priv = snd_soc_card_get_drvdata(card);
- cancel_delayed_work(&priv->pcm_pop_work);
+ cancel_delayed_work_sync(&priv->pcm_pop_work);
gpiod_put(priv->gpio_speakers);
device_remove_software_node(priv->codec_dev);
put_device(priv->codec_dev);
diff --git a/sound/soc/intel/boards/sof_nau8825.c b/sound/soc/intel/boards/sof_nau8825.c
index 27880224359d..6794a0249a9a 100644
--- a/sound/soc/intel/boards/sof_nau8825.c
+++ b/sound/soc/intel/boards/sof_nau8825.c
@@ -48,6 +48,7 @@
#define SOF_MAX98373_SPEAKER_AMP_PRESENT BIT(15)
#define SOF_MAX98360A_SPEAKER_AMP_PRESENT BIT(16)
#define SOF_RT1015P_SPEAKER_AMP_PRESENT BIT(17)
+#define SOF_NAU8318_SPEAKER_AMP_PRESENT BIT(18)
static unsigned long sof_nau8825_quirk = SOF_NAU8825_SSP_CODEC(0);
@@ -338,6 +339,13 @@ static struct snd_soc_dai_link_component rt1019p_component[] = {
}
};
+static struct snd_soc_dai_link_component nau8318_components[] = {
+ {
+ .name = "NVTN2012:00",
+ .dai_name = "nau8315-hifi",
+ }
+};
+
static struct snd_soc_dai_link_component dummy_component[] = {
{
.name = "snd-soc-dummy",
@@ -479,13 +487,16 @@ static struct snd_soc_dai_link *sof_card_dai_links_create(struct device *dev,
links[id].num_codecs = ARRAY_SIZE(max_98373_components);
links[id].init = max_98373_spk_codec_init;
links[id].ops = &max_98373_ops;
- /* feedback stream */
- links[id].dpcm_capture = 1;
} else if (sof_nau8825_quirk &
SOF_MAX98360A_SPEAKER_AMP_PRESENT) {
max_98360a_dai_link(&links[id]);
} else if (sof_nau8825_quirk & SOF_RT1015P_SPEAKER_AMP_PRESENT) {
sof_rt1015p_dai_link(&links[id]);
+ } else if (sof_nau8825_quirk &
+ SOF_NAU8318_SPEAKER_AMP_PRESENT) {
+ links[id].codecs = nau8318_components;
+ links[id].num_codecs = ARRAY_SIZE(nau8318_components);
+ links[id].init = speaker_codec_init;
} else {
goto devm_err;
}
@@ -493,6 +504,9 @@ static struct snd_soc_dai_link *sof_card_dai_links_create(struct device *dev,
links[id].platforms = platform_component;
links[id].num_platforms = ARRAY_SIZE(platform_component);
links[id].dpcm_playback = 1;
+ /* feedback stream or firmware-generated echo reference */
+ links[id].dpcm_capture = 1;
+
links[id].no_pcm = 1;
links[id].cpus = &cpus[id];
links[id].num_cpus = 1;
@@ -618,7 +632,7 @@ static const struct platform_device_id board_ids[] = {
},
{
- .name = "adl_rt1019p_nau8825",
+ .name = "adl_rt1019p_8825",
.driver_data = (kernel_ulong_t)(SOF_NAU8825_SSP_CODEC(0) |
SOF_SPEAKER_AMP_PRESENT |
SOF_RT1019P_SPEAKER_AMP_PRESENT |
@@ -626,7 +640,7 @@ static const struct platform_device_id board_ids[] = {
SOF_NAU8825_NUM_HDMIDEV(4)),
},
{
- .name = "adl_max98373_nau8825",
+ .name = "adl_max98373_8825",
.driver_data = (kernel_ulong_t)(SOF_NAU8825_SSP_CODEC(0) |
SOF_SPEAKER_AMP_PRESENT |
SOF_MAX98373_SPEAKER_AMP_PRESENT |
@@ -637,7 +651,7 @@ static const struct platform_device_id board_ids[] = {
},
{
/* The limitation of length of char array, shorten the name */
- .name = "adl_mx98360a_nau8825",
+ .name = "adl_mx98360a_8825",
.driver_data = (kernel_ulong_t)(SOF_NAU8825_SSP_CODEC(0) |
SOF_SPEAKER_AMP_PRESENT |
SOF_MAX98360A_SPEAKER_AMP_PRESENT |
@@ -648,7 +662,7 @@ static const struct platform_device_id board_ids[] = {
},
{
- .name = "adl_rt1015p_nau8825",
+ .name = "adl_rt1015p_8825",
.driver_data = (kernel_ulong_t)(SOF_NAU8825_SSP_CODEC(0) |
SOF_SPEAKER_AMP_PRESENT |
SOF_RT1015P_SPEAKER_AMP_PRESENT |
@@ -657,6 +671,16 @@ static const struct platform_device_id board_ids[] = {
SOF_BT_OFFLOAD_SSP(2) |
SOF_SSP_BT_OFFLOAD_PRESENT),
},
+ {
+ .name = "adl_nau8318_8825",
+ .driver_data = (kernel_ulong_t)(SOF_NAU8825_SSP_CODEC(0) |
+ SOF_SPEAKER_AMP_PRESENT |
+ SOF_NAU8318_SPEAKER_AMP_PRESENT |
+ SOF_NAU8825_SSP_AMP(1) |
+ SOF_NAU8825_NUM_HDMIDEV(4) |
+ SOF_BT_OFFLOAD_SSP(2) |
+ SOF_SSP_BT_OFFLOAD_PRESENT),
+ },
{ }
};
MODULE_DEVICE_TABLE(platform, board_ids);
diff --git a/sound/soc/intel/boards/sof_realtek_common.c b/sound/soc/intel/boards/sof_realtek_common.c
index ff2851fc8930..6c12ca92f371 100644
--- a/sound/soc/intel/boards/sof_realtek_common.c
+++ b/sound/soc/intel/boards/sof_realtek_common.c
@@ -267,7 +267,8 @@ static int rt1015_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
struct snd_soc_dai_link *dai_link = rtd->dai_link;
struct snd_soc_dai *codec_dai;
- int i, clk_freq, ret;
+ int i, clk_freq;
+ int ret = 0;
clk_freq = sof_dai_get_bclk(rtd);
diff --git a/sound/soc/intel/boards/sof_rt5682.c b/sound/soc/intel/boards/sof_rt5682.c
index 4a2f91249b10..71a11d747622 100644
--- a/sound/soc/intel/boards/sof_rt5682.c
+++ b/sound/soc/intel/boards/sof_rt5682.c
@@ -761,8 +761,6 @@ static struct snd_soc_dai_link *sof_card_dai_links_create(struct device *dev,
links[id].num_codecs = ARRAY_SIZE(max_98373_components);
links[id].init = max_98373_spk_codec_init;
links[id].ops = &max_98373_ops;
- /* feedback stream */
- links[id].dpcm_capture = 1;
} else if (sof_rt5682_quirk &
SOF_MAX98360A_SPEAKER_AMP_PRESENT) {
max_98360a_dai_link(&links[id]);
@@ -789,6 +787,9 @@ static struct snd_soc_dai_link *sof_card_dai_links_create(struct device *dev,
links[id].platforms = platform_component;
links[id].num_platforms = ARRAY_SIZE(platform_component);
links[id].dpcm_playback = 1;
+ /* feedback stream or firmware-generated echo reference */
+ links[id].dpcm_capture = 1;
+
links[id].no_pcm = 1;
links[id].cpus = &cpus[id];
links[id].num_cpus = 1;
@@ -1104,6 +1105,12 @@ static const struct platform_device_id board_ids[] = {
SOF_RT5682_SSP_AMP(1) |
SOF_RT5682_NUM_HDMIDEV(4)),
},
+ {
+ .name = "jsl_rt5682",
+ .driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN |
+ SOF_RT5682_MCLK_24MHZ |
+ SOF_RT5682_SSP_CODEC(0)),
+ },
{ }
};
MODULE_DEVICE_TABLE(platform, board_ids);
diff --git a/sound/soc/intel/boards/sof_sdw.c b/sound/soc/intel/boards/sof_sdw.c
index b58c7b35599d..d2ed807abde9 100644
--- a/sound/soc/intel/boards/sof_sdw.c
+++ b/sound/soc/intel/boards/sof_sdw.c
@@ -550,23 +550,23 @@ static struct sof_sdw_codec_info codec_info_list[] = {
.direction = {true, false},
.dai_name = "rt1308-aif",
.ops = &sof_sdw_rt1308_i2s_ops,
- .init = sof_sdw_rt1308_init,
- .exit = sof_sdw_rt1308_exit,
+ .init = sof_sdw_rt_amp_init,
+ .exit = sof_sdw_rt_amp_exit,
.codec_type = SOF_SDW_CODEC_TYPE_AMP,
},
{
.part_id = 0x1316,
.direction = {true, true},
.dai_name = "rt1316-aif",
- .init = sof_sdw_rt1316_init,
- .exit = sof_sdw_rt1316_exit,
+ .init = sof_sdw_rt_amp_init,
+ .exit = sof_sdw_rt_amp_exit,
.codec_type = SOF_SDW_CODEC_TYPE_AMP,
},
{
.part_id = 0x1318,
.direction = {true, true},
.dai_name = "rt1318-aif",
- .init = sof_sdw_rt1318_init,
+ .init = sof_sdw_rt_amp_init,
.codec_type = SOF_SDW_CODEC_TYPE_AMP,
},
{
diff --git a/sound/soc/intel/boards/sof_sdw_common.h b/sound/soc/intel/boards/sof_sdw_common.h
index 54a50f7da4da..350010b0e5f4 100644
--- a/sound/soc/intel/boards/sof_sdw_common.h
+++ b/sound/soc/intel/boards/sof_sdw_common.h
@@ -125,30 +125,18 @@ int sof_sdw_rt700_init(struct snd_soc_card *card,
struct sof_sdw_codec_info *info,
bool playback);
-/* RT1308 support */
+/* RT1308 I2S support */
extern struct snd_soc_ops sof_sdw_rt1308_i2s_ops;
-int sof_sdw_rt1308_init(struct snd_soc_card *card,
+/* generic amp support */
+int sof_sdw_rt_amp_init(struct snd_soc_card *card,
const struct snd_soc_acpi_link_adr *link,
struct snd_soc_dai_link *dai_links,
struct sof_sdw_codec_info *info,
bool playback);
-int sof_sdw_rt1308_exit(struct snd_soc_card *card, struct snd_soc_dai_link *dai_link);
+int sof_sdw_rt_amp_exit(struct snd_soc_card *card, struct snd_soc_dai_link *dai_link);
/* RT1316 support */
-int sof_sdw_rt1316_init(struct snd_soc_card *card,
- const struct snd_soc_acpi_link_adr *link,
- struct snd_soc_dai_link *dai_links,
- struct sof_sdw_codec_info *info,
- bool playback);
-int sof_sdw_rt1316_exit(struct snd_soc_card *card, struct snd_soc_dai_link *dai_link);
-
-/* RT1318 support */
-int sof_sdw_rt1318_init(struct snd_soc_card *card,
- const struct snd_soc_acpi_link_adr *link,
- struct snd_soc_dai_link *dai_links,
- struct sof_sdw_codec_info *info,
- bool playback);
/* RT715 support */
int sof_sdw_rt715_init(struct snd_soc_card *card,
diff --git a/sound/soc/intel/boards/sof_sdw_rt1316.c b/sound/soc/intel/boards/sof_sdw_rt1316.c
deleted file mode 100644
index f6bbea0d3810..000000000000
--- a/sound/soc/intel/boards/sof_sdw_rt1316.c
+++ /dev/null
@@ -1,239 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-// Copyright (c) 2020 Intel Corporation
-
-/*
- * sof_sdw_rt1316 - Helpers to handle RT1316 from generic machine driver
- */
-
-#include <linux/device.h>
-#include <linux/errno.h>
-#include <sound/control.h>
-#include <sound/soc.h>
-#include <sound/soc-acpi.h>
-#include <sound/soc-dapm.h>
-#include <linux/soundwire/sdw.h>
-#include <linux/soundwire/sdw_type.h>
-#include <linux/dmi.h>
-#include "sof_sdw_common.h"
-#include "sof_sdw_amp_coeff_tables.h"
-
-struct rt1316_platform_data {
- const unsigned char *bq_params;
- const unsigned int bq_params_cnt;
-};
-
-static const struct rt1316_platform_data dell_0b00_platform_data = {
- .bq_params = dell_0b00_bq_params,
- .bq_params_cnt = ARRAY_SIZE(dell_0b00_bq_params),
-};
-
-static const struct dmi_system_id dmi_platform_data[] = {
- /* AlderLake devices */
- {
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
- DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0B00")
- },
- .driver_data = (void *)&dell_0b00_platform_data,
- },
- {
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
- DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0B01")
- },
- .driver_data = (void *)&dell_0b00_platform_data,
- },
- {
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
- DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0AFF")
- },
- .driver_data = (void *)&dell_0b00_platform_data,
- },
- {
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
- DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0AFE")
- },
- .driver_data = (void *)&dell_0b00_platform_data,
- },
-};
-
-static int rt1316_add_device_props(struct device *sdw_dev)
-{
- struct property_entry props[3] = {};
- struct fwnode_handle *fwnode;
- const struct dmi_system_id *dmi_data;
- const struct rt1316_platform_data *pdata;
- unsigned char params[RT1316_MAX_BQ_REG];
- int ret;
-
- dmi_data = dmi_first_match(dmi_platform_data);
- if (!dmi_data)
- return 0;
-
- pdata = dmi_data->driver_data;
- memcpy(&params, pdata->bq_params, sizeof(unsigned char) * pdata->bq_params_cnt);
-
- props[0] = PROPERTY_ENTRY_U8_ARRAY("realtek,bq-params", params);
- props[1] = PROPERTY_ENTRY_U32("realtek,bq-params-cnt", pdata->bq_params_cnt);
-
- fwnode = fwnode_create_software_node(props, NULL);
- if (IS_ERR(fwnode))
- return PTR_ERR(fwnode);
-
- ret = device_add_software_node(sdw_dev, to_software_node(fwnode));
-
- fwnode_handle_put(fwnode);
-
- return ret;
-}
-
-static const struct snd_soc_dapm_widget rt1316_widgets[] = {
- SND_SOC_DAPM_SPK("Speaker", NULL),
-};
-
-/*
- * dapm routes for rt1316 will be registered dynamically according
- * to the number of rt1316 used. The first two entries will be registered
- * for one codec case, and the last two entries are also registered
- * if two 1316s are used.
- */
-static const struct snd_soc_dapm_route rt1316_map[] = {
- { "Speaker", NULL, "rt1316-1 SPOL" },
- { "Speaker", NULL, "rt1316-1 SPOR" },
- { "Speaker", NULL, "rt1316-2 SPOL" },
- { "Speaker", NULL, "rt1316-2 SPOR" },
-};
-
-static const struct snd_kcontrol_new rt1316_controls[] = {
- SOC_DAPM_PIN_SWITCH("Speaker"),
-};
-
-static int first_spk_init(struct snd_soc_pcm_runtime *rtd)
-{
- struct snd_soc_card *card = rtd->card;
- int ret;
-
- card->components = devm_kasprintf(card->dev, GFP_KERNEL,
- "%s spk:rt1316",
- card->components);
- if (!card->components)
- return -ENOMEM;
-
- ret = snd_soc_add_card_controls(card, rt1316_controls,
- ARRAY_SIZE(rt1316_controls));
- if (ret) {
- dev_err(card->dev, "rt1316 controls addition failed: %d\n", ret);
- return ret;
- }
-
- ret = snd_soc_dapm_new_controls(&card->dapm, rt1316_widgets,
- ARRAY_SIZE(rt1316_widgets));
- if (ret) {
- dev_err(card->dev, "rt1316 widgets addition failed: %d\n", ret);
- return ret;
- }
-
- ret = snd_soc_dapm_add_routes(&card->dapm, rt1316_map, 2);
- if (ret)
- dev_err(rtd->dev, "failed to add first SPK map: %d\n", ret);
-
- return ret;
-}
-
-static int second_spk_init(struct snd_soc_pcm_runtime *rtd)
-{
- struct snd_soc_card *card = rtd->card;
- int ret;
-
- ret = snd_soc_dapm_add_routes(&card->dapm, rt1316_map + 2, 2);
- if (ret)
- dev_err(rtd->dev, "failed to add second SPK map: %d\n", ret);
-
- return ret;
-}
-
-static int all_spk_init(struct snd_soc_pcm_runtime *rtd)
-{
- int ret;
-
- ret = first_spk_init(rtd);
- if (ret)
- return ret;
-
- return second_spk_init(rtd);
-}
-
-int sof_sdw_rt1316_exit(struct snd_soc_card *card, struct snd_soc_dai_link *dai_link)
-{
- struct mc_private *ctx = snd_soc_card_get_drvdata(card);
-
- if (ctx->amp_dev1) {
- device_remove_software_node(ctx->amp_dev1);
- put_device(ctx->amp_dev1);
- }
-
- if (ctx->amp_dev2) {
- device_remove_software_node(ctx->amp_dev2);
- put_device(ctx->amp_dev2);
- }
-
- return 0;
-}
-
-int sof_sdw_rt1316_init(struct snd_soc_card *card,
- const struct snd_soc_acpi_link_adr *link,
- struct snd_soc_dai_link *dai_links,
- struct sof_sdw_codec_info *info,
- bool playback)
-{
- struct mc_private *ctx = snd_soc_card_get_drvdata(card);
- struct device *sdw_dev1, *sdw_dev2;
- int ret;
-
- /* Count amp number and do init on playback link only. */
- if (!playback)
- return 0;
-
- info->amp_num++;
- if (info->amp_num == 1)
- dai_links->init = first_spk_init;
-
- if (info->amp_num == 2) {
- sdw_dev1 = bus_find_device_by_name(&sdw_bus_type, NULL, dai_links->codecs[0].name);
- if (!sdw_dev1)
- return -EPROBE_DEFER;
-
- ret = rt1316_add_device_props(sdw_dev1);
- if (ret < 0) {
- put_device(sdw_dev1);
- return ret;
- }
- ctx->amp_dev1 = sdw_dev1;
-
- sdw_dev2 = bus_find_device_by_name(&sdw_bus_type, NULL, dai_links->codecs[1].name);
- if (!sdw_dev2)
- return -EPROBE_DEFER;
-
- ret = rt1316_add_device_props(sdw_dev2);
- if (ret < 0) {
- put_device(sdw_dev2);
- return ret;
- }
- ctx->amp_dev2 = sdw_dev2;
-
- /*
- * if two 1316s are in one dai link, the init function
- * in this dai link will be first set for the first speaker,
- * and it should be reset to initialize all speakers when
- * the second speaker is found.
- */
- if (dai_links->init)
- dai_links->init = all_spk_init;
- else
- dai_links->init = second_spk_init;
- }
-
- return 0;
-}
diff --git a/sound/soc/intel/boards/sof_sdw_rt1318.c b/sound/soc/intel/boards/sof_sdw_rt1318.c
deleted file mode 100644
index dbee4bf5c814..000000000000
--- a/sound/soc/intel/boards/sof_sdw_rt1318.c
+++ /dev/null
@@ -1,120 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-// Copyright (c) 2022 Intel Corporation
-
-/*
- * sof_sdw_rt1318 - Helpers to handle RT1318 from generic machine driver
- */
-
-#include <linux/device.h>
-#include <linux/errno.h>
-#include <sound/control.h>
-#include <sound/soc.h>
-#include <sound/soc-acpi.h>
-#include <sound/soc-dapm.h>
-#include "sof_sdw_common.h"
-
-static const struct snd_soc_dapm_widget rt1318_widgets[] = {
- SND_SOC_DAPM_SPK("Speaker", NULL),
-};
-
-/*
- * dapm routes for rt1318 will be registered dynamically according
- * to the number of rt1318 used. The first two entries will be registered
- * for one codec case, and the last two entries are also registered
- * if two 1318s are used.
- */
-static const struct snd_soc_dapm_route rt1318_map[] = {
- { "Speaker", NULL, "rt1318-1 SPOL" },
- { "Speaker", NULL, "rt1318-1 SPOR" },
- { "Speaker", NULL, "rt1318-2 SPOL" },
- { "Speaker", NULL, "rt1318-2 SPOR" },
-};
-
-static const struct snd_kcontrol_new rt1318_controls[] = {
- SOC_DAPM_PIN_SWITCH("Speaker"),
-};
-
-static int first_spk_init(struct snd_soc_pcm_runtime *rtd)
-{
- struct snd_soc_card *card = rtd->card;
- int ret;
-
- card->components = devm_kasprintf(card->dev, GFP_KERNEL,
- "%s spk:rt1318",
- card->components);
- if (!card->components)
- return -ENOMEM;
-
- ret = snd_soc_add_card_controls(card, rt1318_controls,
- ARRAY_SIZE(rt1318_controls));
- if (ret) {
- dev_err(card->dev, "rt1318 controls addition failed: %d\n", ret);
- return ret;
- }
-
- ret = snd_soc_dapm_new_controls(&card->dapm, rt1318_widgets,
- ARRAY_SIZE(rt1318_widgets));
- if (ret) {
- dev_err(card->dev, "rt1318 widgets addition failed: %d\n", ret);
- return ret;
- }
-
- ret = snd_soc_dapm_add_routes(&card->dapm, rt1318_map, 2);
- if (ret)
- dev_err(rtd->dev, "failed to add first SPK map: %d\n", ret);
-
- return ret;
-}
-
-static int second_spk_init(struct snd_soc_pcm_runtime *rtd)
-{
- struct snd_soc_card *card = rtd->card;
- int ret;
-
- ret = snd_soc_dapm_add_routes(&card->dapm, rt1318_map + 2, 2);
- if (ret)
- dev_err(rtd->dev, "failed to add second SPK map: %d\n", ret);
-
- return ret;
-}
-
-static int all_spk_init(struct snd_soc_pcm_runtime *rtd)
-{
- int ret;
-
- ret = first_spk_init(rtd);
- if (ret)
- return ret;
-
- return second_spk_init(rtd);
-}
-
-int sof_sdw_rt1318_init(struct snd_soc_card *card,
- const struct snd_soc_acpi_link_adr *link,
- struct snd_soc_dai_link *dai_links,
- struct sof_sdw_codec_info *info,
- bool playback)
-{
- /* Count amp number and do init on playback link only. */
- if (!playback)
- return 0;
-
- info->amp_num++;
- if (info->amp_num == 1)
- dai_links->init = first_spk_init;
-
- if (info->amp_num == 2) {
- /*
- * if two 1318s are in one dai link, the init function
- * in this dai link will be first set for the first speaker,
- * and it should be reset to initialize all speakers when
- * the second speaker is found.
- */
- if (dai_links->init)
- dai_links->init = all_spk_init;
- else
- dai_links->init = second_spk_init;
- }
-
- return 0;
-}
diff --git a/sound/soc/intel/boards/sof_sdw_rt1308.c b/sound/soc/intel/boards/sof_sdw_rt_amp.c
index a19b055b9c6f..26bf9e0dd3d2 100644
--- a/sound/soc/intel/boards/sof_sdw_rt1308.c
+++ b/sound/soc/intel/boards/sof_sdw_rt_amp.c
@@ -1,8 +1,8 @@
// SPDX-License-Identifier: GPL-2.0-only
-// Copyright (c) 2020 Intel Corporation
+// Copyright (c) 2022 Intel Corporation
/*
- * sof_sdw_rt1308 - Helpers to handle RT1308 from generic machine driver
+ * sof_sdw_rt_amp - Helpers to handle RT1308/RT1316/RT1318 from generic machine driver
*/
#include <linux/device.h>
@@ -18,16 +18,26 @@
#include "sof_sdw_amp_coeff_tables.h"
#include "../../codecs/rt1308.h"
-struct rt1308_platform_data {
+#define CODEC_NAME_SIZE 7
+
+/* choose a larger value to resolve compatibility issues */
+#define RT_AMP_MAX_BQ_REG RT1316_MAX_BQ_REG
+
+struct rt_amp_platform_data {
const unsigned char *bq_params;
const unsigned int bq_params_cnt;
};
-static const struct rt1308_platform_data dell_0a5d_platform_data = {
+static const struct rt_amp_platform_data dell_0a5d_platform_data = {
.bq_params = dell_0a5d_bq_params,
.bq_params_cnt = ARRAY_SIZE(dell_0a5d_bq_params),
};
+static const struct rt_amp_platform_data dell_0b00_platform_data = {
+ .bq_params = dell_0b00_bq_params,
+ .bq_params_cnt = ARRAY_SIZE(dell_0b00_bq_params),
+};
+
static const struct dmi_system_id dmi_platform_data[] = {
/* CometLake devices */
{
@@ -59,15 +69,45 @@ static const struct dmi_system_id dmi_platform_data[] = {
},
.driver_data = (void *)&dell_0a5d_platform_data,
},
+ /* AlderLake devices */
+ {
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0B00")
+ },
+ .driver_data = (void *)&dell_0b00_platform_data,
+ },
+ {
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0B01")
+ },
+ .driver_data = (void *)&dell_0b00_platform_data,
+ },
+ {
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0AFF")
+ },
+ .driver_data = (void *)&dell_0b00_platform_data,
+ },
+ {
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0AFE")
+ },
+ .driver_data = (void *)&dell_0b00_platform_data,
+ },
+ {},
};
-static int rt1308_add_device_props(struct device *sdw_dev)
+static int rt_amp_add_device_props(struct device *sdw_dev)
{
struct property_entry props[3] = {};
struct fwnode_handle *fwnode;
const struct dmi_system_id *dmi_data;
- const struct rt1308_platform_data *pdata;
- unsigned char params[RT1308_MAX_BQ_REG];
+ const struct rt_amp_platform_data *pdata;
+ unsigned char params[RT_AMP_MAX_BQ_REG];
int ret;
dmi_data = dmi_first_match(dmi_platform_data);
@@ -91,15 +131,19 @@ static int rt1308_add_device_props(struct device *sdw_dev)
return ret;
}
-static const struct snd_soc_dapm_widget rt1308_widgets[] = {
+static const struct snd_kcontrol_new rt_amp_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Speaker"),
+};
+
+static const struct snd_soc_dapm_widget rt_amp_widgets[] = {
SND_SOC_DAPM_SPK("Speaker", NULL),
};
/*
- * dapm routes for rt1308 will be registered dynamically according
- * to the number of rt1308 used. The first two entries will be registered
- * for one codec case, and the last two entries are also registered
- * if two 1308s are used.
+ * dapm routes for rt1308/rt1316/rt1318 will be registered dynamically
+ * according to the number of rt1308/rt1316/rt1318 used. The first two
+ * entries will be registered for one codec case, and the last two entries
+ * are also registered if two 1308s/1316s/1318s are used.
*/
static const struct snd_soc_dapm_route rt1308_map[] = {
{ "Speaker", NULL, "rt1308-1 SPOL" },
@@ -108,36 +152,69 @@ static const struct snd_soc_dapm_route rt1308_map[] = {
{ "Speaker", NULL, "rt1308-2 SPOR" },
};
-static const struct snd_kcontrol_new rt1308_controls[] = {
- SOC_DAPM_PIN_SWITCH("Speaker"),
+static const struct snd_soc_dapm_route rt1316_map[] = {
+ { "Speaker", NULL, "rt1316-1 SPOL" },
+ { "Speaker", NULL, "rt1316-1 SPOR" },
+ { "Speaker", NULL, "rt1316-2 SPOL" },
+ { "Speaker", NULL, "rt1316-2 SPOR" },
};
+static const struct snd_soc_dapm_route rt1318_map[] = {
+ { "Speaker", NULL, "rt1318-1 SPOL" },
+ { "Speaker", NULL, "rt1318-1 SPOR" },
+ { "Speaker", NULL, "rt1318-2 SPOL" },
+ { "Speaker", NULL, "rt1318-2 SPOR" },
+};
+
+static const struct snd_soc_dapm_route *get_codec_name_and_route(struct snd_soc_pcm_runtime *rtd,
+ char *codec_name)
+{
+ const char *dai_name;
+
+ dai_name = rtd->dai_link->codecs->dai_name;
+
+ /* get the codec name */
+ snprintf(codec_name, CODEC_NAME_SIZE, "%s", dai_name);
+
+ /* choose the right codec's map */
+ if (strcmp(codec_name, "rt1308") == 0)
+ return rt1308_map;
+ else if (strcmp(codec_name, "rt1316") == 0)
+ return rt1316_map;
+ else
+ return rt1318_map;
+}
+
static int first_spk_init(struct snd_soc_pcm_runtime *rtd)
{
struct snd_soc_card *card = rtd->card;
+ const struct snd_soc_dapm_route *rt_amp_map;
+ char codec_name[CODEC_NAME_SIZE];
int ret;
+ rt_amp_map = get_codec_name_and_route(rtd, codec_name);
+
card->components = devm_kasprintf(card->dev, GFP_KERNEL,
- "%s spk:rt1308",
- card->components);
+ "%s spk:%s",
+ card->components, codec_name);
if (!card->components)
return -ENOMEM;
- ret = snd_soc_add_card_controls(card, rt1308_controls,
- ARRAY_SIZE(rt1308_controls));
+ ret = snd_soc_add_card_controls(card, rt_amp_controls,
+ ARRAY_SIZE(rt_amp_controls));
if (ret) {
- dev_err(card->dev, "rt1308 controls addition failed: %d\n", ret);
+ dev_err(card->dev, "%s controls addition failed: %d\n", codec_name, ret);
return ret;
}
- ret = snd_soc_dapm_new_controls(&card->dapm, rt1308_widgets,
- ARRAY_SIZE(rt1308_widgets));
+ ret = snd_soc_dapm_new_controls(&card->dapm, rt_amp_widgets,
+ ARRAY_SIZE(rt_amp_widgets));
if (ret) {
- dev_err(card->dev, "rt1308 widgets addition failed: %d\n", ret);
+ dev_err(card->dev, "%s widgets addition failed: %d\n", codec_name, ret);
return ret;
}
- ret = snd_soc_dapm_add_routes(&card->dapm, rt1308_map, 2);
+ ret = snd_soc_dapm_add_routes(&card->dapm, rt_amp_map, 2);
if (ret)
dev_err(rtd->dev, "failed to add first SPK map: %d\n", ret);
@@ -147,9 +224,13 @@ static int first_spk_init(struct snd_soc_pcm_runtime *rtd)
static int second_spk_init(struct snd_soc_pcm_runtime *rtd)
{
struct snd_soc_card *card = rtd->card;
+ const struct snd_soc_dapm_route *rt_amp_map;
+ char codec_name[CODEC_NAME_SIZE];
int ret;
- ret = snd_soc_dapm_add_routes(&card->dapm, rt1308_map + 2, 2);
+ rt_amp_map = get_codec_name_and_route(rtd, codec_name);
+
+ ret = snd_soc_dapm_add_routes(&card->dapm, rt_amp_map + 2, 2);
if (ret)
dev_err(rtd->dev, "failed to add second SPK map: %d\n", ret);
@@ -204,7 +285,7 @@ struct snd_soc_ops sof_sdw_rt1308_i2s_ops = {
.hw_params = rt1308_i2s_hw_params,
};
-int sof_sdw_rt1308_exit(struct snd_soc_card *card, struct snd_soc_dai_link *dai_link)
+int sof_sdw_rt_amp_exit(struct snd_soc_card *card, struct snd_soc_dai_link *dai_link)
{
struct mc_private *ctx = snd_soc_card_get_drvdata(card);
@@ -221,7 +302,7 @@ int sof_sdw_rt1308_exit(struct snd_soc_card *card, struct snd_soc_dai_link *dai_
return 0;
}
-int sof_sdw_rt1308_init(struct snd_soc_card *card,
+int sof_sdw_rt_amp_init(struct snd_soc_card *card,
const struct snd_soc_acpi_link_adr *link,
struct snd_soc_dai_link *dai_links,
struct sof_sdw_codec_info *info,
@@ -244,7 +325,7 @@ int sof_sdw_rt1308_init(struct snd_soc_card *card,
if (!sdw_dev1)
return -EPROBE_DEFER;
- ret = rt1308_add_device_props(sdw_dev1);
+ ret = rt_amp_add_device_props(sdw_dev1);
if (ret < 0) {
put_device(sdw_dev1);
return ret;
@@ -255,7 +336,7 @@ int sof_sdw_rt1308_init(struct snd_soc_card *card,
if (!sdw_dev2)
return -EPROBE_DEFER;
- ret = rt1308_add_device_props(sdw_dev2);
+ ret = rt_amp_add_device_props(sdw_dev2);
if (ret < 0) {
put_device(sdw_dev2);
return ret;
@@ -263,7 +344,7 @@ int sof_sdw_rt1308_init(struct snd_soc_card *card,
ctx->amp_dev2 = sdw_dev2;
/*
- * if two 1308s are in one dai link, the init function
+ * if two amps are in one dai link, the init function
* in this dai link will be first set for the first speaker,
* and it should be reset to initialize all speakers when
* the second speaker is found.
diff --git a/sound/soc/intel/boards/sof_ssp_amp.c b/sound/soc/intel/boards/sof_ssp_amp.c
index 94d25aeb6e7c..7b74f122e340 100644
--- a/sound/soc/intel/boards/sof_ssp_amp.c
+++ b/sound/soc/intel/boards/sof_ssp_amp.c
@@ -258,13 +258,12 @@ static struct snd_soc_dai_link *sof_card_dai_links_create(struct device *dev,
sof_rt1308_dai_link(&links[id]);
} else if (sof_ssp_amp_quirk & SOF_CS35L41_SPEAKER_AMP_PRESENT) {
cs35l41_set_dai_link(&links[id]);
-
- /* feedback from amplifier */
- links[id].dpcm_capture = 1;
}
links[id].platforms = platform_component;
links[id].num_platforms = ARRAY_SIZE(platform_component);
links[id].dpcm_playback = 1;
+ /* feedback from amplifier or firmware-generated echo reference */
+ links[id].dpcm_capture = 1;
links[id].no_pcm = 1;
links[id].cpus = &cpus[id];
links[id].num_cpus = 1;
diff --git a/sound/soc/intel/common/soc-acpi-intel-adl-match.c b/sound/soc/intel/common/soc-acpi-intel-adl-match.c
index 60aee56f94bd..56ee5fef66a8 100644
--- a/sound/soc/intel/common/soc-acpi-intel-adl-match.c
+++ b/sound/soc/intel/common/soc-acpi-intel-adl-match.c
@@ -450,6 +450,11 @@ static const struct snd_soc_acpi_codecs adl_lt6911_hdmi = {
.codecs = {"INTC10B0"}
};
+static const struct snd_soc_acpi_codecs adl_nau8318_amp = {
+ .num_codecs = 1,
+ .codecs = {"NVTN2012"}
+};
+
struct snd_soc_acpi_mach snd_soc_acpi_intel_adl_machines[] = {
{
.comp_ids = &adl_rt5682_rt5682s_hp,
@@ -474,21 +479,21 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_adl_machines[] = {
},
{
.id = "10508825",
- .drv_name = "adl_rt1019p_nau8825",
+ .drv_name = "adl_rt1019p_8825",
.machine_quirk = snd_soc_acpi_codec_list,
.quirk_data = &adl_rt1019p_amp,
.sof_tplg_filename = "sof-adl-rt1019-nau8825.tplg",
},
{
.id = "10508825",
- .drv_name = "adl_max98373_nau8825",
+ .drv_name = "adl_max98373_8825",
.machine_quirk = snd_soc_acpi_codec_list,
.quirk_data = &adl_max98373_amp,
.sof_tplg_filename = "sof-adl-max98373-nau8825.tplg",
},
{
.id = "10508825",
- .drv_name = "adl_mx98360a_nau8825",
+ .drv_name = "adl_mx98360a_8825",
.machine_quirk = snd_soc_acpi_codec_list,
.quirk_data = &adl_max98360a_amp,
.sof_tplg_filename = "sof-adl-max98360a-nau8825.tplg",
@@ -502,13 +507,20 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_adl_machines[] = {
},
{
.id = "10508825",
- .drv_name = "adl_rt1015p_nau8825",
+ .drv_name = "adl_rt1015p_8825",
.machine_quirk = snd_soc_acpi_codec_list,
.quirk_data = &adl_rt1015p_amp,
.sof_tplg_filename = "sof-adl-rt1015-nau8825.tplg",
},
{
.id = "10508825",
+ .drv_name = "adl_nau8318_8825",
+ .machine_quirk = snd_soc_acpi_codec_list,
+ .quirk_data = &adl_nau8318_amp,
+ .sof_tplg_filename = "sof-adl-nau8318-nau8825.tplg",
+ },
+ {
+ .id = "10508825",
.drv_name = "sof_nau8825",
.sof_tplg_filename = "sof-adl-nau8825.tplg",
},
diff --git a/sound/soc/intel/common/soc-acpi-intel-jsl-match.c b/sound/soc/intel/common/soc-acpi-intel-jsl-match.c
index b95c4b2cda94..f5c7e1bbded0 100644
--- a/sound/soc/intel/common/soc-acpi-intel-jsl-match.c
+++ b/sound/soc/intel/common/soc-acpi-intel-jsl-match.c
@@ -79,6 +79,11 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_jsl_machines[] = {
.sof_tplg_filename = "sof-jsl-rt5682-mx98360a.tplg",
},
{
+ .comp_ids = &rt5682_rt5682s_hp,
+ .drv_name = "jsl_rt5682",
+ .sof_tplg_filename = "sof-jsl-rt5682.tplg",
+ },
+ {
.id = "10134242",
.drv_name = "jsl_cs4242_mx98360a",
.machine_quirk = snd_soc_acpi_codec_list,
diff --git a/sound/soc/intel/common/soc-acpi-intel-rpl-match.c b/sound/soc/intel/common/soc-acpi-intel-rpl-match.c
index 3c5229f41bb0..07f96a11ea2f 100644
--- a/sound/soc/intel/common/soc-acpi-intel-rpl-match.c
+++ b/sound/soc/intel/common/soc-acpi-intel-rpl-match.c
@@ -112,7 +112,7 @@ static const struct snd_soc_acpi_adr_device rt1316_1_group2_adr[] = {
static const struct snd_soc_acpi_adr_device rt1318_1_group1_adr[] = {
{
- .adr = 0x000131025D131801ull,
+ .adr = 0x000132025D131801ull,
.num_endpoints = 1,
.endpoints = &spk_l_endpoint,
.name_prefix = "rt1318-1"
@@ -203,6 +203,25 @@ static const struct snd_soc_acpi_link_adr rpl_sdw_rt711_link2_rt1316_link01_rt71
{}
};
+static const struct snd_soc_acpi_link_adr rpl_sdw_rt711_link2_rt1316_link01[] = {
+ {
+ .mask = BIT(2),
+ .num_adr = ARRAY_SIZE(rt711_sdca_2_adr),
+ .adr_d = rt711_sdca_2_adr,
+ },
+ {
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(rt1316_0_group2_adr),
+ .adr_d = rt1316_0_group2_adr,
+ },
+ {
+ .mask = BIT(1),
+ .num_adr = ARRAY_SIZE(rt1316_1_group2_adr),
+ .adr_d = rt1316_1_group2_adr,
+ },
+ {}
+};
+
static const struct snd_soc_acpi_link_adr rpl_sdw_rt711_link0_rt1318_link12_rt714_link3[] = {
{
.mask = BIT(0),
@@ -227,6 +246,25 @@ static const struct snd_soc_acpi_link_adr rpl_sdw_rt711_link0_rt1318_link12_rt71
{}
};
+static const struct snd_soc_acpi_link_adr rpl_sdw_rt711_link0_rt1318_link12[] = {
+ {
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(rt711_sdca_0_adr),
+ .adr_d = rt711_sdca_0_adr,
+ },
+ {
+ .mask = BIT(1),
+ .num_adr = ARRAY_SIZE(rt1318_1_group1_adr),
+ .adr_d = rt1318_1_group1_adr,
+ },
+ {
+ .mask = BIT(2),
+ .num_adr = ARRAY_SIZE(rt1318_2_group1_adr),
+ .adr_d = rt1318_2_group1_adr,
+ },
+ {}
+};
+
static const struct snd_soc_acpi_link_adr rpl_sdw_rt1316_link12_rt714_link0[] = {
{
.mask = BIT(1),
@@ -272,12 +310,24 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_rpl_sdw_machines[] = {
.sof_tplg_filename = "sof-rpl-rt711-l0-rt1318-l12-rt714-l3.tplg",
},
{
+ .link_mask = 0x7, /* rt711 on link0 & two rt1318s on link1 and link2 */
+ .links = rpl_sdw_rt711_link0_rt1318_link12,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-rpl-rt711-l0-rt1318-l12.tplg",
+ },
+ {
.link_mask = 0x7, /* rt714 on link0 & two rt1316s on link1 and link2 */
.links = rpl_sdw_rt1316_link12_rt714_link0,
.drv_name = "sof_sdw",
.sof_tplg_filename = "sof-rpl-rt1316-l12-rt714-l0.tplg",
},
{
+ .link_mask = 0x7, /* rt711 on link2 & two rt1316s on link0 and link1 */
+ .links = rpl_sdw_rt711_link2_rt1316_link01,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-rpl-rt711-l2-rt1316-l01.tplg",
+ },
+ {
.link_mask = 0x1, /* link0 required */
.links = rpl_rvp,
.drv_name = "sof_sdw",
diff --git a/sound/soc/intel/skylake/skl-sst-cldma.c b/sound/soc/intel/skylake/skl-sst-cldma.c
index b91f7a652a2b..b0204ea00f07 100644
--- a/sound/soc/intel/skylake/skl-sst-cldma.c
+++ b/sound/soc/intel/skylake/skl-sst-cldma.c
@@ -11,6 +11,7 @@
#include <linux/io.h>
#include <linux/mm.h>
#include <linux/delay.h>
+#include <sound/hda_register.h>
#include "../common/sst-dsp.h"
#include "../common/sst-dsp-priv.h"
@@ -79,21 +80,25 @@ static void skl_cldma_setup_bdle(struct sst_dsp *ctx,
__le32 **bdlp, int size, int with_ioc)
{
__le32 *bdl = *bdlp;
+ int remaining = ctx->cl_dev.bufsize;
+ int offset = 0;
ctx->cl_dev.frags = 0;
- while (size > 0) {
- phys_addr_t addr = virt_to_phys(dmab_data->area +
- (ctx->cl_dev.frags * ctx->cl_dev.bufsize));
+ while (remaining > 0) {
+ phys_addr_t addr;
+ int chunk;
+ addr = snd_sgbuf_get_addr(dmab_data, offset);
bdl[0] = cpu_to_le32(lower_32_bits(addr));
bdl[1] = cpu_to_le32(upper_32_bits(addr));
+ chunk = snd_sgbuf_get_chunk_size(dmab_data, offset, size);
+ bdl[2] = cpu_to_le32(chunk);
- bdl[2] = cpu_to_le32(ctx->cl_dev.bufsize);
-
- size -= ctx->cl_dev.bufsize;
- bdl[3] = (size || !with_ioc) ? 0 : cpu_to_le32(0x01);
+ remaining -= chunk;
+ bdl[3] = (remaining > 0) ? 0 : cpu_to_le32(0x01);
bdl += 4;
+ offset += chunk;
ctx->cl_dev.frags++;
}
}
@@ -338,15 +343,15 @@ int skl_cldma_prepare(struct sst_dsp *ctx)
ctx->cl_dev.ops.cl_stop_dma = skl_cldma_stop;
/* Allocate buffer*/
- ret = ctx->dsp_ops.alloc_dma_buf(ctx->dev,
- &ctx->cl_dev.dmab_data, ctx->cl_dev.bufsize);
+ ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV_SG, ctx->dev, ctx->cl_dev.bufsize,
+ &ctx->cl_dev.dmab_data);
if (ret < 0) {
dev_err(ctx->dev, "Alloc buffer for base fw failed: %x\n", ret);
return ret;
}
+
/* Setup Code loader BDL */
- ret = ctx->dsp_ops.alloc_dma_buf(ctx->dev,
- &ctx->cl_dev.dmab_bdl, PAGE_SIZE);
+ ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, ctx->dev, BDL_SIZE, &ctx->cl_dev.dmab_bdl);
if (ret < 0) {
dev_err(ctx->dev, "Alloc buffer for blde failed: %x\n", ret);
ctx->dsp_ops.free_dma_buf(ctx->dev, &ctx->cl_dev.dmab_data);
diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c
index e06eac592da1..b20643b83401 100644
--- a/sound/soc/intel/skylake/skl-topology.c
+++ b/sound/soc/intel/skylake/skl-topology.c
@@ -582,36 +582,10 @@ static int skl_tplg_unload_pipe_modules(struct skl_dev *skl,
return ret;
}
-static bool skl_tplg_is_multi_fmt(struct skl_dev *skl, struct skl_pipe *pipe)
+static void skl_tplg_set_pipe_config_idx(struct skl_pipe *pipe, int idx)
{
- struct skl_pipe_fmt *cur_fmt;
- struct skl_pipe_fmt *next_fmt;
- int i;
-
- if (pipe->nr_cfgs <= 1)
- return false;
-
- if (pipe->conn_type != SKL_PIPE_CONN_TYPE_FE)
- return true;
-
- for (i = 0; i < pipe->nr_cfgs - 1; i++) {
- if (pipe->direction == SNDRV_PCM_STREAM_PLAYBACK) {
- cur_fmt = &pipe->configs[i].out_fmt;
- next_fmt = &pipe->configs[i + 1].out_fmt;
- } else {
- cur_fmt = &pipe->configs[i].in_fmt;
- next_fmt = &pipe->configs[i + 1].in_fmt;
- }
-
- if (!CHECK_HW_PARAMS(cur_fmt->channels, cur_fmt->freq,
- cur_fmt->bps,
- next_fmt->channels,
- next_fmt->freq,
- next_fmt->bps))
- return true;
- }
-
- return false;
+ pipe->cur_config_idx = idx;
+ pipe->memory_pages = pipe->configs[idx].mem_pages;
}
/*
@@ -632,24 +606,14 @@ skl_tplg_get_pipe_config(struct skl_dev *skl, struct skl_module_cfg *mconfig)
int i;
if (pipe->nr_cfgs == 0) {
- pipe->cur_config_idx = 0;
- return 0;
- }
-
- if (skl_tplg_is_multi_fmt(skl, pipe)) {
- pipe->cur_config_idx = pipe->pipe_config_idx;
- pipe->memory_pages = pconfig->mem_pages;
- dev_dbg(skl->dev, "found pipe config idx:%d\n",
- pipe->cur_config_idx);
+ skl_tplg_set_pipe_config_idx(pipe, 0);
return 0;
}
if (pipe->conn_type == SKL_PIPE_CONN_TYPE_NONE || pipe->nr_cfgs == 1) {
dev_dbg(skl->dev, "No conn_type or just 1 pathcfg, taking 0th for %d\n",
pipe->ppl_id);
- pipe->cur_config_idx = 0;
- pipe->memory_pages = pconfig->mem_pages;
-
+ skl_tplg_set_pipe_config_idx(pipe, 0);
return 0;
}
@@ -668,10 +632,8 @@ skl_tplg_get_pipe_config(struct skl_dev *skl, struct skl_module_cfg *mconfig)
if (CHECK_HW_PARAMS(params->ch, params->s_freq, params->s_fmt,
fmt->channels, fmt->freq, fmt->bps)) {
- pipe->cur_config_idx = i;
- pipe->memory_pages = pconfig->mem_pages;
+ skl_tplg_set_pipe_config_idx(pipe, i);
dev_dbg(skl->dev, "Using pipe config: %d\n", i);
-
return 0;
}
}
@@ -1391,9 +1353,9 @@ static int skl_tplg_multi_config_set_get(struct snd_kcontrol *kcontrol,
return -EIO;
if (is_set)
- pipe->pipe_config_idx = ucontrol->value.enumerated.item[0];
+ skl_tplg_set_pipe_config_idx(pipe, ucontrol->value.enumerated.item[0]);
else
- ucontrol->value.enumerated.item[0] = pipe->pipe_config_idx;
+ ucontrol->value.enumerated.item[0] = pipe->cur_config_idx;
return 0;
}
@@ -1837,20 +1799,28 @@ static int skl_tplg_be_fill_pipe_params(struct snd_soc_dai *dai,
{
struct nhlt_specific_cfg *cfg;
struct skl_pipe *pipe = mconfig->pipe;
+ struct skl_pipe_params save = *pipe->p_params;
struct skl_pipe_fmt *pipe_fmt;
struct skl_dev *skl = get_skl_ctx(dai->dev);
int link_type = skl_tplg_be_link_type(mconfig->dev_type);
u8 dev_type = skl_tplg_be_dev_type(mconfig->dev_type);
+ int ret;
skl_tplg_fill_dma_id(mconfig, params);
if (link_type == NHLT_LINK_HDA)
return 0;
+ *pipe->p_params = *params;
+ ret = skl_tplg_get_pipe_config(skl, mconfig);
+ if (ret)
+ goto err;
+
+ dev_dbg(skl->dev, "%s using pipe config: %d\n", __func__, pipe->cur_config_idx);
if (pipe->direction == SNDRV_PCM_STREAM_PLAYBACK)
- pipe_fmt = &pipe->configs[pipe->pipe_config_idx].out_fmt;
+ pipe_fmt = &pipe->configs[pipe->cur_config_idx].out_fmt;
else
- pipe_fmt = &pipe->configs[pipe->pipe_config_idx].in_fmt;
+ pipe_fmt = &pipe->configs[pipe->cur_config_idx].in_fmt;
/* update the blob based on virtual bus_id*/
cfg = intel_nhlt_get_endpoint_blob(dai->dev, skl->nhlt,
@@ -1865,10 +1835,15 @@ static int skl_tplg_be_fill_pipe_params(struct snd_soc_dai *dai,
dev_err(dai->dev, "Blob NULL for id:%d type:%d dirn:%d ch:%d, freq:%d, fmt:%d\n",
mconfig->vbus_id, link_type, params->stream,
params->ch, params->s_freq, params->s_fmt);
- return -EINVAL;
+ ret = -EINVAL;
+ goto err;
}
return 0;
+
+err:
+ *pipe->p_params = save;
+ return ret;
}
static int skl_tplg_be_set_src_pipe_params(struct snd_soc_dai *dai,
diff --git a/sound/soc/intel/skylake/skl-topology.h b/sound/soc/intel/skylake/skl-topology.h
index 017ac0ef324d..6db0fd7bad49 100644
--- a/sound/soc/intel/skylake/skl-topology.h
+++ b/sound/soc/intel/skylake/skl-topology.h
@@ -324,7 +324,6 @@ struct skl_pipe {
struct skl_path_config configs[SKL_MAX_PATH_CONFIGS];
struct list_head w_list;
bool passthru;
- u32 pipe_config_idx;
};
enum skl_module_state {
diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c
index 9bd9f9866898..998bd0232cf1 100644
--- a/sound/soc/intel/skylake/skl.c
+++ b/sound/soc/intel/skylake/skl.c
@@ -1107,7 +1107,10 @@ static void skl_shutdown(struct pci_dev *pci)
if (!skl->init_done)
return;
- snd_hdac_stop_streams_and_chip(bus);
+ snd_hdac_stop_streams(bus);
+ snd_hdac_ext_bus_link_power_down_all(bus);
+ skl_dsp_sleep(skl->dsp);
+
list_for_each_entry(s, &bus->stream_list, list) {
stream = stream_to_hdac_ext_stream(s);
snd_hdac_ext_stream_decouple(bus, stream, false);
diff --git a/sound/soc/mediatek/Kconfig b/sound/soc/mediatek/Kconfig
index 363fa4d47680..b027fba8233d 100644
--- a/sound/soc/mediatek/Kconfig
+++ b/sound/soc/mediatek/Kconfig
@@ -182,10 +182,12 @@ config SND_SOC_MT8186_MT6366_DA7219_MAX98357
If unsure select "N".
config SND_SOC_MT8186_MT6366_RT1019_RT5682S
- tristate "ASoC Audio driver for MT8186 with RT1019 RT5682S codec"
+ tristate "ASoC Audio driver for MT8186 with RT1019 RT5682S MAX98357A/MAX98360 codec"
depends on I2C && GPIOLIB
depends on SND_SOC_MT8186 && MTK_PMIC_WRAP
+ select SND_SOC_MAX98357A
select SND_SOC_MT6358
+ select SND_SOC_MAX98357A
select SND_SOC_RT1015P
select SND_SOC_RT5682S
select SND_SOC_BT_SCO
diff --git a/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5514.c b/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5514.c
index 12f40c81b101..f803f121659d 100644
--- a/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5514.c
+++ b/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5514.c
@@ -200,14 +200,16 @@ static int mt8173_rt5650_rt5514_dev_probe(struct platform_device *pdev)
if (!mt8173_rt5650_rt5514_dais[DAI_LINK_CODEC_I2S].codecs[0].of_node) {
dev_err(&pdev->dev,
"Property 'audio-codec' missing or invalid\n");
- return -EINVAL;
+ ret = -EINVAL;
+ goto out;
}
mt8173_rt5650_rt5514_dais[DAI_LINK_CODEC_I2S].codecs[1].of_node =
of_parse_phandle(pdev->dev.of_node, "mediatek,audio-codec", 1);
if (!mt8173_rt5650_rt5514_dais[DAI_LINK_CODEC_I2S].codecs[1].of_node) {
dev_err(&pdev->dev,
"Property 'audio-codec' missing or invalid\n");
- return -EINVAL;
+ ret = -EINVAL;
+ goto out;
}
mt8173_rt5650_rt5514_codec_conf[0].dlc.of_node =
mt8173_rt5650_rt5514_dais[DAI_LINK_CODEC_I2S].codecs[1].of_node;
@@ -216,6 +218,7 @@ static int mt8173_rt5650_rt5514_dev_probe(struct platform_device *pdev)
ret = devm_snd_soc_register_card(&pdev->dev, card);
+out:
of_node_put(platform_node);
return ret;
}
diff --git a/sound/soc/mediatek/mt8183/mt8183-mt6358-ts3a227-max98357.c b/sound/soc/mediatek/mt8183/mt8183-mt6358-ts3a227-max98357.c
index 8fb473543cf9..ce9aedde7e1e 100644
--- a/sound/soc/mediatek/mt8183/mt8183-mt6358-ts3a227-max98357.c
+++ b/sound/soc/mediatek/mt8183/mt8183-mt6358-ts3a227-max98357.c
@@ -677,8 +677,10 @@ mt8183_mt6358_ts3a227_max98357_dev_probe(struct platform_device *pdev)
}
card = (struct snd_soc_card *)of_device_get_match_data(&pdev->dev);
- if (!card)
+ if (!card) {
+ of_node_put(platform_node);
return -EINVAL;
+ }
card->dev = &pdev->dev;
ec_codec = of_parse_phandle(pdev->dev.of_node, "mediatek,ec-codec", 0);
@@ -767,8 +769,10 @@ mt8183_mt6358_ts3a227_max98357_dev_probe(struct platform_device *pdev)
}
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
- if (!priv)
- return -ENOMEM;
+ if (!priv) {
+ ret = -ENOMEM;
+ goto out;
+ }
snd_soc_card_set_drvdata(card, priv);
@@ -776,7 +780,8 @@ mt8183_mt6358_ts3a227_max98357_dev_probe(struct platform_device *pdev)
if (IS_ERR(priv->pinctrl)) {
dev_err(&pdev->dev, "%s devm_pinctrl_get failed\n",
__func__);
- return PTR_ERR(priv->pinctrl);
+ ret = PTR_ERR(priv->pinctrl);
+ goto out;
}
for (i = 0; i < PIN_STATE_MAX; i++) {
@@ -809,6 +814,7 @@ mt8183_mt6358_ts3a227_max98357_dev_probe(struct platform_device *pdev)
ret = devm_snd_soc_register_card(&pdev->dev, card);
+out:
of_node_put(platform_node);
of_node_put(ec_codec);
of_node_put(hdmi_codec);
diff --git a/sound/soc/mediatek/mt8186/mt8186-mt6366-rt1019-rt5682s.c b/sound/soc/mediatek/mt8186/mt8186-mt6366-rt1019-rt5682s.c
index 8f77a0bc1dc8..af44e331dae8 100644
--- a/sound/soc/mediatek/mt8186/mt8186-mt6366-rt1019-rt5682s.c
+++ b/sound/soc/mediatek/mt8186/mt8186-mt6366-rt1019-rt5682s.c
@@ -1083,6 +1083,21 @@ static struct snd_soc_card mt8186_mt6366_rt1019_rt5682s_soc_card = {
.num_configs = ARRAY_SIZE(mt8186_mt6366_rt1019_rt5682s_codec_conf),
};
+static struct snd_soc_card mt8186_mt6366_rt5682s_max98360_soc_card = {
+ .name = "mt8186_rt5682s_max98360",
+ .owner = THIS_MODULE,
+ .dai_link = mt8186_mt6366_rt1019_rt5682s_dai_links,
+ .num_links = ARRAY_SIZE(mt8186_mt6366_rt1019_rt5682s_dai_links),
+ .controls = mt8186_mt6366_rt1019_rt5682s_controls,
+ .num_controls = ARRAY_SIZE(mt8186_mt6366_rt1019_rt5682s_controls),
+ .dapm_widgets = mt8186_mt6366_rt1019_rt5682s_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(mt8186_mt6366_rt1019_rt5682s_widgets),
+ .dapm_routes = mt8186_mt6366_rt1019_rt5682s_routes,
+ .num_dapm_routes = ARRAY_SIZE(mt8186_mt6366_rt1019_rt5682s_routes),
+ .codec_conf = mt8186_mt6366_rt1019_rt5682s_codec_conf,
+ .num_configs = ARRAY_SIZE(mt8186_mt6366_rt1019_rt5682s_codec_conf),
+};
+
static int mt8186_mt6366_rt1019_rt5682s_dev_probe(struct platform_device *pdev)
{
struct snd_soc_card *card;
@@ -1232,9 +1247,14 @@ err_adsp_node:
#if IS_ENABLED(CONFIG_OF)
static const struct of_device_id mt8186_mt6366_rt1019_rt5682s_dt_match[] = {
- { .compatible = "mediatek,mt8186-mt6366-rt1019-rt5682s-sound",
+ {
+ .compatible = "mediatek,mt8186-mt6366-rt1019-rt5682s-sound",
.data = &mt8186_mt6366_rt1019_rt5682s_soc_card,
},
+ {
+ .compatible = "mediatek,mt8186-mt6366-rt5682s-max98360-sound",
+ .data = &mt8186_mt6366_rt5682s_max98360_soc_card,
+ },
{}
};
MODULE_DEVICE_TABLE(of, mt8186_mt6366_rt1019_rt5682s_dt_match);
diff --git a/sound/soc/mediatek/mt8195/mt8195-mt6359.c b/sound/soc/mediatek/mt8195/mt8195-mt6359.c
index 61be66f47723..4682748d82be 100644
--- a/sound/soc/mediatek/mt8195/mt8195-mt6359.c
+++ b/sound/soc/mediatek/mt8195/mt8195-mt6359.c
@@ -633,6 +633,32 @@ static const struct snd_soc_ops mt8195_rt1011_etdm_ops = {
.hw_params = mt8195_rt1011_etdm_hw_params,
};
+static int mt8195_sof_be_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+ struct snd_soc_component *cmpnt_afe = NULL;
+ struct snd_soc_pcm_runtime *runtime;
+
+ /* find afe component */
+ for_each_card_rtds(rtd->card, runtime) {
+ cmpnt_afe = snd_soc_rtdcom_lookup(runtime, AFE_PCM_NAME);
+ if (cmpnt_afe)
+ break;
+ }
+
+ if (cmpnt_afe && !pm_runtime_active(cmpnt_afe->dev)) {
+ dev_err(rtd->dev, "afe pm runtime is not active!!\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_ops mt8195_sof_be_ops = {
+ .hw_params = mt8195_sof_be_hw_params,
+};
+
static int mt8195_rt1011_init(struct snd_soc_pcm_runtime *rtd)
{
struct snd_soc_card *card = rtd->card;
@@ -1272,24 +1298,28 @@ static struct snd_soc_dai_link mt8195_mt6359_dai_links[] = {
.name = "AFE_SOF_DL2",
.no_pcm = 1,
.dpcm_playback = 1,
+ .ops = &mt8195_sof_be_ops,
SND_SOC_DAILINK_REG(AFE_SOF_DL2),
},
[DAI_LINK_SOF_DL3_BE] = {
.name = "AFE_SOF_DL3",
.no_pcm = 1,
.dpcm_playback = 1,
+ .ops = &mt8195_sof_be_ops,
SND_SOC_DAILINK_REG(AFE_SOF_DL3),
},
[DAI_LINK_SOF_UL4_BE] = {
.name = "AFE_SOF_UL4",
.no_pcm = 1,
.dpcm_capture = 1,
+ .ops = &mt8195_sof_be_ops,
SND_SOC_DAILINK_REG(AFE_SOF_UL4),
},
[DAI_LINK_SOF_UL5_BE] = {
.name = "AFE_SOF_UL5",
.no_pcm = 1,
.dpcm_capture = 1,
+ .ops = &mt8195_sof_be_ops,
SND_SOC_DAILINK_REG(AFE_SOF_UL5),
},
};
diff --git a/sound/soc/qcom/Kconfig b/sound/soc/qcom/Kconfig
index 96a6d4731e6f..e7b00d1d9e99 100644
--- a/sound/soc/qcom/Kconfig
+++ b/sound/soc/qcom/Kconfig
@@ -2,7 +2,6 @@
menuconfig SND_SOC_QCOM
tristate "ASoC support for QCOM platforms"
depends on ARCH_QCOM || COMPILE_TEST
- imply SND_SOC_QCOM_COMMON
help
Say Y or M if you want to add support to use audio devices
in Qualcomm Technologies SOC-based platforms.
@@ -60,14 +59,16 @@ config SND_SOC_STORM
config SND_SOC_APQ8016_SBC
tristate "SoC Audio support for APQ8016 SBC platforms"
select SND_SOC_LPASS_APQ8016
- depends on SND_SOC_QCOM_COMMON
+ select SND_SOC_QCOM_COMMON
help
Support for Qualcomm Technologies LPASS audio block in
APQ8016 SOC-based systems.
Say Y if you want to use audio devices on MI2S.
config SND_SOC_QCOM_COMMON
- depends on SOUNDWIRE
+ tristate
+
+config SND_SOC_QCOM_SDW
tristate
config SND_SOC_QDSP6_COMMON
@@ -144,7 +145,7 @@ config SND_SOC_MSM8996
depends on QCOM_APR
depends on COMMON_CLK
select SND_SOC_QDSP6
- depends on SND_SOC_QCOM_COMMON
+ select SND_SOC_QCOM_COMMON
help
Support for Qualcomm Technologies LPASS audio block in
APQ8096 SoC-based systems.
@@ -155,7 +156,7 @@ config SND_SOC_SDM845
depends on QCOM_APR && I2C && SOUNDWIRE
depends on COMMON_CLK
select SND_SOC_QDSP6
- depends on SND_SOC_QCOM_COMMON
+ select SND_SOC_QCOM_COMMON
select SND_SOC_RT5663
select SND_SOC_MAX98927
imply SND_SOC_CROS_EC_CODEC
@@ -169,7 +170,8 @@ config SND_SOC_SM8250
depends on QCOM_APR && SOUNDWIRE
depends on COMMON_CLK
select SND_SOC_QDSP6
- depends on SND_SOC_QCOM_COMMON
+ select SND_SOC_QCOM_COMMON
+ select SND_SOC_QCOM_SDW
help
To add support for audio on Qualcomm Technologies Inc.
SM8250 SoC-based systems.
@@ -180,7 +182,8 @@ config SND_SOC_SC8280XP
depends on QCOM_APR && SOUNDWIRE
depends on COMMON_CLK
select SND_SOC_QDSP6
- depends on SND_SOC_QCOM_COMMON
+ select SND_SOC_QCOM_COMMON
+ select SND_SOC_QCOM_SDW
help
To add support for audio on Qualcomm Technologies Inc.
SC8280XP SoC-based systems.
@@ -190,7 +193,7 @@ config SND_SOC_SC7180
tristate "SoC Machine driver for SC7180 boards"
depends on I2C && GPIOLIB
depends on SOUNDWIRE || SOUNDWIRE=n
- depends on SND_SOC_QCOM_COMMON
+ select SND_SOC_QCOM_COMMON
select SND_SOC_LPASS_SC7180
select SND_SOC_MAX98357A
select SND_SOC_RT5682_I2C
@@ -204,7 +207,7 @@ config SND_SOC_SC7180
config SND_SOC_SC7280
tristate "SoC Machine driver for SC7280 boards"
depends on I2C && SOUNDWIRE
- depends on SND_SOC_QCOM_COMMON
+ select SND_SOC_QCOM_COMMON
select SND_SOC_LPASS_SC7280
select SND_SOC_MAX98357A
select SND_SOC_WCD938X_SDW
diff --git a/sound/soc/qcom/Makefile b/sound/soc/qcom/Makefile
index 8b97172cf990..254350d9dc06 100644
--- a/sound/soc/qcom/Makefile
+++ b/sound/soc/qcom/Makefile
@@ -28,6 +28,7 @@ snd-soc-sdm845-objs := sdm845.o
snd-soc-sm8250-objs := sm8250.o
snd-soc-sc8280xp-objs := sc8280xp.o
snd-soc-qcom-common-objs := common.o
+snd-soc-qcom-sdw-objs := sdw.o
obj-$(CONFIG_SND_SOC_STORM) += snd-soc-storm.o
obj-$(CONFIG_SND_SOC_APQ8016_SBC) += snd-soc-apq8016-sbc.o
@@ -38,6 +39,7 @@ obj-$(CONFIG_SND_SOC_SC8280XP) += snd-soc-sc8280xp.o
obj-$(CONFIG_SND_SOC_SDM845) += snd-soc-sdm845.o
obj-$(CONFIG_SND_SOC_SM8250) += snd-soc-sm8250.o
obj-$(CONFIG_SND_SOC_QCOM_COMMON) += snd-soc-qcom-common.o
+obj-$(CONFIG_SND_SOC_QCOM_SDW) += snd-soc-qcom-sdw.o
#DSP lib
obj-$(CONFIG_SND_SOC_QDSP6) += qdsp6/
diff --git a/sound/soc/qcom/common.c b/sound/soc/qcom/common.c
index 49c74c1662a3..96fe80241fb4 100644
--- a/sound/soc/qcom/common.c
+++ b/sound/soc/qcom/common.c
@@ -180,120 +180,6 @@ err_put_np:
}
EXPORT_SYMBOL_GPL(qcom_snd_parse_of);
-int qcom_snd_sdw_prepare(struct snd_pcm_substream *substream,
- struct sdw_stream_runtime *sruntime,
- bool *stream_prepared)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
- int ret;
-
- if (!sruntime)
- return 0;
-
- switch (cpu_dai->id) {
- case WSA_CODEC_DMA_RX_0:
- case WSA_CODEC_DMA_RX_1:
- case RX_CODEC_DMA_RX_0:
- case RX_CODEC_DMA_RX_1:
- case TX_CODEC_DMA_TX_0:
- case TX_CODEC_DMA_TX_1:
- case TX_CODEC_DMA_TX_2:
- case TX_CODEC_DMA_TX_3:
- break;
- default:
- return 0;
- }
-
- if (*stream_prepared) {
- sdw_disable_stream(sruntime);
- sdw_deprepare_stream(sruntime);
- *stream_prepared = false;
- }
-
- ret = sdw_prepare_stream(sruntime);
- if (ret)
- return ret;
-
- /**
- * NOTE: there is a strict hw requirement about the ordering of port
- * enables and actual WSA881x PA enable. PA enable should only happen
- * after soundwire ports are enabled if not DC on the line is
- * accumulated resulting in Click/Pop Noise
- * PA enable/mute are handled as part of codec DAPM and digital mute.
- */
-
- ret = sdw_enable_stream(sruntime);
- if (ret) {
- sdw_deprepare_stream(sruntime);
- return ret;
- }
- *stream_prepared = true;
-
- return ret;
-}
-EXPORT_SYMBOL_GPL(qcom_snd_sdw_prepare);
-
-int qcom_snd_sdw_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params,
- struct sdw_stream_runtime **psruntime)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *codec_dai;
- struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
- struct sdw_stream_runtime *sruntime;
- int i;
-
- switch (cpu_dai->id) {
- case WSA_CODEC_DMA_RX_0:
- case RX_CODEC_DMA_RX_0:
- case RX_CODEC_DMA_RX_1:
- case TX_CODEC_DMA_TX_0:
- case TX_CODEC_DMA_TX_1:
- case TX_CODEC_DMA_TX_2:
- case TX_CODEC_DMA_TX_3:
- for_each_rtd_codec_dais(rtd, i, codec_dai) {
- sruntime = snd_soc_dai_get_stream(codec_dai, substream->stream);
- if (sruntime != ERR_PTR(-ENOTSUPP))
- *psruntime = sruntime;
- }
- break;
- }
-
- return 0;
-
-}
-EXPORT_SYMBOL_GPL(qcom_snd_sdw_hw_params);
-
-int qcom_snd_sdw_hw_free(struct snd_pcm_substream *substream,
- struct sdw_stream_runtime *sruntime, bool *stream_prepared)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
-
- switch (cpu_dai->id) {
- case WSA_CODEC_DMA_RX_0:
- case WSA_CODEC_DMA_RX_1:
- case RX_CODEC_DMA_RX_0:
- case RX_CODEC_DMA_RX_1:
- case TX_CODEC_DMA_TX_0:
- case TX_CODEC_DMA_TX_1:
- case TX_CODEC_DMA_TX_2:
- case TX_CODEC_DMA_TX_3:
- if (sruntime && *stream_prepared) {
- sdw_disable_stream(sruntime);
- sdw_deprepare_stream(sruntime);
- *stream_prepared = false;
- }
- break;
- default:
- break;
- }
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(qcom_snd_sdw_hw_free);
-
int qcom_snd_wcd_jack_setup(struct snd_soc_pcm_runtime *rtd,
struct snd_soc_jack *jack, bool *jack_setup)
{
diff --git a/sound/soc/qcom/common.h b/sound/soc/qcom/common.h
index 3ef5bb6d12df..d7f80ee5ae26 100644
--- a/sound/soc/qcom/common.h
+++ b/sound/soc/qcom/common.h
@@ -5,19 +5,9 @@
#define __QCOM_SND_COMMON_H__
#include <sound/soc.h>
-#include <linux/soundwire/sdw.h>
int qcom_snd_parse_of(struct snd_soc_card *card);
int qcom_snd_wcd_jack_setup(struct snd_soc_pcm_runtime *rtd,
struct snd_soc_jack *jack, bool *jack_setup);
-int qcom_snd_sdw_prepare(struct snd_pcm_substream *substream,
- struct sdw_stream_runtime *runtime,
- bool *stream_prepared);
-int qcom_snd_sdw_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params,
- struct sdw_stream_runtime **psruntime);
-int qcom_snd_sdw_hw_free(struct snd_pcm_substream *substream,
- struct sdw_stream_runtime *sruntime,
- bool *stream_prepared);
#endif
diff --git a/sound/soc/qcom/lpass-cpu.c b/sound/soc/qcom/lpass-cpu.c
index 54353842dc07..dbdaaa85ce48 100644
--- a/sound/soc/qcom/lpass-cpu.c
+++ b/sound/soc/qcom/lpass-cpu.c
@@ -1037,10 +1037,11 @@ static void of_lpass_cpu_parse_dai_data(struct device *dev,
struct lpass_data *data)
{
struct device_node *node;
- int ret, id;
+ int ret, i, id;
/* Allow all channels by default for backwards compatibility */
- for (id = 0; id < data->variant->num_dai; id++) {
+ for (i = 0; i < data->variant->num_dai; i++) {
+ id = data->variant->dai_driver[i].id;
data->mi2s_playback_sd_mode[id] = LPAIF_I2SCTL_MODE_8CH;
data->mi2s_capture_sd_mode[id] = LPAIF_I2SCTL_MODE_8CH;
}
diff --git a/sound/soc/qcom/sc8280xp.c b/sound/soc/qcom/sc8280xp.c
index ade44ad7c585..14d9fea33d16 100644
--- a/sound/soc/qcom/sc8280xp.c
+++ b/sound/soc/qcom/sc8280xp.c
@@ -12,6 +12,7 @@
#include <linux/input-event-codes.h>
#include "qdsp6/q6afe.h"
#include "common.h"
+#include "sdw.h"
#define DRIVER_NAME "sc8280xp"
diff --git a/sound/soc/qcom/sdw.c b/sound/soc/qcom/sdw.c
new file mode 100644
index 000000000000..10249519a39e
--- /dev/null
+++ b/sound/soc/qcom/sdw.c
@@ -0,0 +1,123 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2018, Linaro Limited.
+// Copyright (c) 2018, The Linux Foundation. All rights reserved.
+
+#include <linux/module.h>
+#include <sound/soc.h>
+#include "qdsp6/q6afe.h"
+#include "sdw.h"
+
+int qcom_snd_sdw_prepare(struct snd_pcm_substream *substream,
+ struct sdw_stream_runtime *sruntime,
+ bool *stream_prepared)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
+ int ret;
+
+ if (!sruntime)
+ return 0;
+
+ switch (cpu_dai->id) {
+ case WSA_CODEC_DMA_RX_0:
+ case WSA_CODEC_DMA_RX_1:
+ case RX_CODEC_DMA_RX_0:
+ case RX_CODEC_DMA_RX_1:
+ case TX_CODEC_DMA_TX_0:
+ case TX_CODEC_DMA_TX_1:
+ case TX_CODEC_DMA_TX_2:
+ case TX_CODEC_DMA_TX_3:
+ break;
+ default:
+ return 0;
+ }
+
+ if (*stream_prepared) {
+ sdw_disable_stream(sruntime);
+ sdw_deprepare_stream(sruntime);
+ *stream_prepared = false;
+ }
+
+ ret = sdw_prepare_stream(sruntime);
+ if (ret)
+ return ret;
+
+ /**
+ * NOTE: there is a strict hw requirement about the ordering of port
+ * enables and actual WSA881x PA enable. PA enable should only happen
+ * after soundwire ports are enabled if not DC on the line is
+ * accumulated resulting in Click/Pop Noise
+ * PA enable/mute are handled as part of codec DAPM and digital mute.
+ */
+
+ ret = sdw_enable_stream(sruntime);
+ if (ret) {
+ sdw_deprepare_stream(sruntime);
+ return ret;
+ }
+ *stream_prepared = true;
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(qcom_snd_sdw_prepare);
+
+int qcom_snd_sdw_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct sdw_stream_runtime **psruntime)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *codec_dai;
+ struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
+ struct sdw_stream_runtime *sruntime;
+ int i;
+
+ switch (cpu_dai->id) {
+ case WSA_CODEC_DMA_RX_0:
+ case RX_CODEC_DMA_RX_0:
+ case RX_CODEC_DMA_RX_1:
+ case TX_CODEC_DMA_TX_0:
+ case TX_CODEC_DMA_TX_1:
+ case TX_CODEC_DMA_TX_2:
+ case TX_CODEC_DMA_TX_3:
+ for_each_rtd_codec_dais(rtd, i, codec_dai) {
+ sruntime = snd_soc_dai_get_stream(codec_dai, substream->stream);
+ if (sruntime != ERR_PTR(-ENOTSUPP))
+ *psruntime = sruntime;
+ }
+ break;
+ }
+
+ return 0;
+
+}
+EXPORT_SYMBOL_GPL(qcom_snd_sdw_hw_params);
+
+int qcom_snd_sdw_hw_free(struct snd_pcm_substream *substream,
+ struct sdw_stream_runtime *sruntime, bool *stream_prepared)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
+
+ switch (cpu_dai->id) {
+ case WSA_CODEC_DMA_RX_0:
+ case WSA_CODEC_DMA_RX_1:
+ case RX_CODEC_DMA_RX_0:
+ case RX_CODEC_DMA_RX_1:
+ case TX_CODEC_DMA_TX_0:
+ case TX_CODEC_DMA_TX_1:
+ case TX_CODEC_DMA_TX_2:
+ case TX_CODEC_DMA_TX_3:
+ if (sruntime && *stream_prepared) {
+ sdw_disable_stream(sruntime);
+ sdw_deprepare_stream(sruntime);
+ *stream_prepared = false;
+ }
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(qcom_snd_sdw_hw_free);
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/qcom/sdw.h b/sound/soc/qcom/sdw.h
new file mode 100644
index 000000000000..d74cbb84da13
--- /dev/null
+++ b/sound/soc/qcom/sdw.h
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+// Copyright (c) 2018, The Linux Foundation. All rights reserved.
+
+#ifndef __QCOM_SND_SDW_H__
+#define __QCOM_SND_SDW_H__
+
+#include <linux/soundwire/sdw.h>
+
+int qcom_snd_sdw_prepare(struct snd_pcm_substream *substream,
+ struct sdw_stream_runtime *runtime,
+ bool *stream_prepared);
+int qcom_snd_sdw_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct sdw_stream_runtime **psruntime);
+int qcom_snd_sdw_hw_free(struct snd_pcm_substream *substream,
+ struct sdw_stream_runtime *sruntime,
+ bool *stream_prepared);
+#endif
diff --git a/sound/soc/qcom/sm8250.c b/sound/soc/qcom/sm8250.c
index 8dbe9ef41b1c..9626a9ef78c2 100644
--- a/sound/soc/qcom/sm8250.c
+++ b/sound/soc/qcom/sm8250.c
@@ -12,6 +12,7 @@
#include <linux/input-event-codes.h>
#include "qdsp6/q6afe.h"
#include "common.h"
+#include "sdw.h"
#define DRIVER_NAME "sm8250"
#define MI2S_BCLK_RATE 1536000
diff --git a/sound/soc/rockchip/rockchip_pdm.c b/sound/soc/rockchip/rockchip_pdm.c
index a7549f827235..5b1e47bdc376 100644
--- a/sound/soc/rockchip/rockchip_pdm.c
+++ b/sound/soc/rockchip/rockchip_pdm.c
@@ -431,6 +431,7 @@ static int rockchip_pdm_runtime_resume(struct device *dev)
ret = clk_prepare_enable(pdm->hclk);
if (ret) {
+ clk_disable_unprepare(pdm->clk);
dev_err(pdm->dev, "hclock enable failed %d\n", ret);
return ret;
}
diff --git a/sound/soc/rockchip/rockchip_spdif.c b/sound/soc/rockchip/rockchip_spdif.c
index 8bef572d3cbc..5b4f00457587 100644
--- a/sound/soc/rockchip/rockchip_spdif.c
+++ b/sound/soc/rockchip/rockchip_spdif.c
@@ -88,6 +88,7 @@ static int __maybe_unused rk_spdif_runtime_resume(struct device *dev)
ret = clk_prepare_enable(spdif->hclk);
if (ret) {
+ clk_disable_unprepare(spdif->mclk);
dev_err(spdif->dev, "hclk clock enable failed %d\n", ret);
return ret;
}
diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c
index a7810c78ffa1..579a44d81d9a 100644
--- a/sound/soc/soc-pcm.c
+++ b/sound/soc/soc-pcm.c
@@ -709,8 +709,17 @@ static int soc_pcm_clean(struct snd_soc_pcm_runtime *rtd,
snd_soc_dpcm_mutex_assert_held(rtd);
- if (!rollback)
+ if (!rollback) {
snd_soc_runtime_deactivate(rtd, substream->stream);
+ /* clear the corresponding DAIs parameters when going to be inactive */
+ for_each_rtd_dais(rtd, i, dai) {
+ if (snd_soc_dai_active(dai) == 0)
+ soc_pcm_set_dai_params(dai, NULL);
+
+ if (snd_soc_dai_stream_active(dai, substream->stream) == 0)
+ snd_soc_dai_digital_mute(dai, 1, substream->stream);
+ }
+ }
for_each_rtd_dais(rtd, i, dai)
snd_soc_dai_shutdown(dai, substream, rollback);
@@ -940,15 +949,6 @@ static int soc_pcm_hw_clean(struct snd_soc_pcm_runtime *rtd,
snd_soc_dpcm_mutex_assert_held(rtd);
- /* clear the corresponding DAIs parameters when going to be inactive */
- for_each_rtd_dais(rtd, i, dai) {
- if (snd_soc_dai_active(dai) == 1)
- soc_pcm_set_dai_params(dai, NULL);
-
- if (snd_soc_dai_stream_active(dai, substream->stream) == 1)
- snd_soc_dai_digital_mute(dai, 1, substream->stream);
- }
-
/* run the stream event */
snd_soc_dapm_stream_stop(rtd, substream->stream);
diff --git a/sound/soc/sof/core.c b/sound/soc/sof/core.c
index 3e6141d03770..625977a29d8a 100644
--- a/sound/soc/sof/core.c
+++ b/sound/soc/sof/core.c
@@ -475,19 +475,10 @@ EXPORT_SYMBOL(snd_sof_device_remove);
int snd_sof_device_shutdown(struct device *dev)
{
struct snd_sof_dev *sdev = dev_get_drvdata(dev);
- struct snd_sof_pdata *pdata = sdev->pdata;
if (IS_ENABLED(CONFIG_SND_SOC_SOF_PROBE_WORK_QUEUE))
cancel_work_sync(&sdev->probe_work);
- /*
- * make sure clients and machine driver(s) are unregistered to force
- * all userspace devices to be closed prior to the DSP shutdown sequence
- */
- sof_unregister_clients(sdev);
-
- snd_sof_machine_unregister(sdev, pdata);
-
if (sdev->fw_state == SOF_FW_BOOT_COMPLETE)
return snd_sof_shutdown(sdev);
diff --git a/sound/soc/sof/debug.c b/sound/soc/sof/debug.c
index d9a3ce7b69e1..ade0507328af 100644
--- a/sound/soc/sof/debug.c
+++ b/sound/soc/sof/debug.c
@@ -353,7 +353,9 @@ int snd_sof_dbg_init(struct snd_sof_dev *sdev)
return err;
}
- return 0;
+ return snd_sof_debugfs_buf_item(sdev, &sdev->fw_state,
+ sizeof(sdev->fw_state),
+ "fw_state", 0444);
}
EXPORT_SYMBOL_GPL(snd_sof_dbg_init);
diff --git a/sound/soc/sof/intel/hda-dsp.c b/sound/soc/sof/intel/hda-dsp.c
index 5fa29df54b42..b4eacae8564c 100644
--- a/sound/soc/sof/intel/hda-dsp.c
+++ b/sound/soc/sof/intel/hda-dsp.c
@@ -878,6 +878,78 @@ int hda_dsp_suspend(struct snd_sof_dev *sdev, u32 target_state)
return snd_sof_dsp_set_power_state(sdev, &target_dsp_state);
}
+static unsigned int hda_dsp_check_for_dma_streams(struct snd_sof_dev *sdev)
+{
+ struct hdac_bus *bus = sof_to_bus(sdev);
+ struct hdac_stream *s;
+ unsigned int active_streams = 0;
+ int sd_offset;
+ u32 val;
+
+ list_for_each_entry(s, &bus->stream_list, list) {
+ sd_offset = SOF_STREAM_SD_OFFSET(s);
+ val = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR,
+ sd_offset);
+ if (val & SOF_HDA_SD_CTL_DMA_START)
+ active_streams |= BIT(s->index);
+ }
+
+ return active_streams;
+}
+
+static int hda_dsp_s5_quirk(struct snd_sof_dev *sdev)
+{
+ int ret;
+
+ /*
+ * Do not assume a certain timing between the prior
+ * suspend flow, and running of this quirk function.
+ * This is needed if the controller was just put
+ * to reset before calling this function.
+ */
+ usleep_range(500, 1000);
+
+ /*
+ * Take controller out of reset to flush DMA
+ * transactions.
+ */
+ ret = hda_dsp_ctrl_link_reset(sdev, false);
+ if (ret < 0)
+ return ret;
+
+ usleep_range(500, 1000);
+
+ /* Restore state for shutdown, back to reset */
+ ret = hda_dsp_ctrl_link_reset(sdev, true);
+ if (ret < 0)
+ return ret;
+
+ return ret;
+}
+
+int hda_dsp_shutdown_dma_flush(struct snd_sof_dev *sdev)
+{
+ unsigned int active_streams;
+ int ret, ret2;
+
+ /* check if DMA cleanup has been successful */
+ active_streams = hda_dsp_check_for_dma_streams(sdev);
+
+ sdev->system_suspend_target = SOF_SUSPEND_S3;
+ ret = snd_sof_suspend(sdev->dev);
+
+ if (active_streams) {
+ dev_warn(sdev->dev,
+ "There were active DSP streams (%#x) at shutdown, trying to recover\n",
+ active_streams);
+ ret2 = hda_dsp_s5_quirk(sdev);
+ if (ret2 < 0)
+ dev_err(sdev->dev, "shutdown recovery failed (%d)\n", ret2);
+ }
+
+ return ret;
+}
+
int hda_dsp_shutdown(struct snd_sof_dev *sdev)
{
sdev->system_suspend_target = SOF_SUSPEND_S3;
diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c
index 14a2f8701350..81c697e20108 100644
--- a/sound/soc/sof/intel/hda.c
+++ b/sound/soc/sof/intel/hda.c
@@ -206,6 +206,7 @@ static int hda_sdw_probe(struct snd_sof_dev *sdev)
memset(&res, 0, sizeof(res));
+ res.hw_ops = &sdw_intel_cnl_hw_ops;
res.mmio_base = sdev->bar[HDA_DSP_BAR];
res.shim_base = hdev->desc->sdw_shim_base;
res.alh_base = hdev->desc->sdw_alh_base;
@@ -1727,3 +1728,4 @@ MODULE_IMPORT_NS(SND_SOC_SOF_HDA_AUDIO_CODEC_I915);
MODULE_IMPORT_NS(SND_SOC_SOF_XTENSA);
MODULE_IMPORT_NS(SND_INTEL_SOUNDWIRE_ACPI);
MODULE_IMPORT_NS(SOUNDWIRE_INTEL_INIT);
+MODULE_IMPORT_NS(SOUNDWIRE_INTEL);
diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h
index 022ce80968dd..caccaf8fba9c 100644
--- a/sound/soc/sof/intel/hda.h
+++ b/sound/soc/sof/intel/hda.h
@@ -592,6 +592,7 @@ int hda_dsp_resume(struct snd_sof_dev *sdev);
int hda_dsp_runtime_suspend(struct snd_sof_dev *sdev);
int hda_dsp_runtime_resume(struct snd_sof_dev *sdev);
int hda_dsp_runtime_idle(struct snd_sof_dev *sdev);
+int hda_dsp_shutdown_dma_flush(struct snd_sof_dev *sdev);
int hda_dsp_shutdown(struct snd_sof_dev *sdev);
int hda_dsp_set_hw_params_upon_resume(struct snd_sof_dev *sdev);
void hda_dsp_dump(struct snd_sof_dev *sdev, u32 flags);
diff --git a/sound/soc/sof/intel/tgl.c b/sound/soc/sof/intel/tgl.c
index 30f2f49ee149..58ac3a46e6a7 100644
--- a/sound/soc/sof/intel/tgl.c
+++ b/sound/soc/sof/intel/tgl.c
@@ -60,7 +60,7 @@ int sof_tgl_ops_init(struct snd_sof_dev *sdev)
memcpy(&sof_tgl_ops, &sof_hda_common_ops, sizeof(struct snd_sof_dsp_ops));
/* probe/remove/shutdown */
- sof_tgl_ops.shutdown = hda_dsp_shutdown;
+ sof_tgl_ops.shutdown = hda_dsp_shutdown_dma_flush;
if (sdev->pdata->ipc_type == SOF_IPC) {
/* doorbell */
diff --git a/sound/soc/sof/ipc4-mtrace.c b/sound/soc/sof/ipc4-mtrace.c
index 70dea8ae706e..0ec6ef681012 100644
--- a/sound/soc/sof/ipc4-mtrace.c
+++ b/sound/soc/sof/ipc4-mtrace.c
@@ -344,9 +344,10 @@ static ssize_t sof_ipc4_priority_mask_dfs_write(struct file *file,
size_t count, loff_t *ppos)
{
struct sof_mtrace_priv *priv = file->private_data;
- int id, ret;
+ unsigned int id;
char *buf;
u32 mask;
+ int ret;
/*
* To update Nth mask entry, write:
@@ -357,9 +358,9 @@ static ssize_t sof_ipc4_priority_mask_dfs_write(struct file *file,
if (IS_ERR(buf))
return PTR_ERR(buf);
- ret = sscanf(buf, "%d,0x%x", &id, &mask);
+ ret = sscanf(buf, "%u,0x%x", &id, &mask);
if (ret != 2) {
- ret = sscanf(buf, "%d,%x", &id, &mask);
+ ret = sscanf(buf, "%u,%x", &id, &mask);
if (ret != 2) {
ret = -EINVAL;
goto out;
diff --git a/sound/soc/sof/mediatek/mtk-adsp-common.c b/sound/soc/sof/mediatek/mtk-adsp-common.c
index 1e0769c668a7..de8dbe27cd0d 100644
--- a/sound/soc/sof/mediatek/mtk-adsp-common.c
+++ b/sound/soc/sof/mediatek/mtk-adsp-common.c
@@ -60,7 +60,7 @@ void mtk_adsp_dump(struct snd_sof_dev *sdev, u32 flags)
{
char *level = (flags & SOF_DBG_DUMP_OPTIONAL) ? KERN_DEBUG : KERN_ERR;
struct sof_ipc_dsp_oops_xtensa xoops;
- struct sof_ipc_panic_info panic_info;
+ struct sof_ipc_panic_info panic_info = {};
u32 stack[MTK_ADSP_STACK_DUMP_SIZE];
u32 status;
diff --git a/sound/soc/sof/pm.c b/sound/soc/sof/pm.c
index df740be645e8..8722bbd7fd3d 100644
--- a/sound/soc/sof/pm.c
+++ b/sound/soc/sof/pm.c
@@ -182,7 +182,7 @@ static int sof_suspend(struct device *dev, bool runtime_suspend)
const struct sof_ipc_pm_ops *pm_ops = sdev->ipc->ops->pm;
const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg;
pm_message_t pm_state;
- u32 target_state = 0;
+ u32 target_state = snd_sof_dsp_power_target(sdev);
int ret;
/* do nothing if dsp suspend callback is not set */
@@ -192,6 +192,9 @@ static int sof_suspend(struct device *dev, bool runtime_suspend)
if (runtime_suspend && !sof_ops(sdev)->runtime_suspend)
return 0;
+ if (tplg_ops && tplg_ops->tear_down_all_pipelines)
+ tplg_ops->tear_down_all_pipelines(sdev, false);
+
if (sdev->fw_state != SOF_FW_BOOT_COMPLETE)
goto suspend;
@@ -206,7 +209,6 @@ static int sof_suspend(struct device *dev, bool runtime_suspend)
}
}
- target_state = snd_sof_dsp_power_target(sdev);
pm_state.event = target_state;
/* Skip to platform-specific suspend if DSP is entering D0 */
@@ -217,9 +219,6 @@ static int sof_suspend(struct device *dev, bool runtime_suspend)
goto suspend;
}
- if (tplg_ops->tear_down_all_pipelines)
- tplg_ops->tear_down_all_pipelines(sdev, false);
-
/* suspend DMA trace */
sof_fw_trace_suspend(sdev, pm_state);
diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c
index 7306a2649857..865c367eb2f2 100644
--- a/sound/soc/sof/sof-audio.c
+++ b/sound/soc/sof/sof-audio.c
@@ -271,9 +271,9 @@ sof_unprepare_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widg
struct snd_sof_widget *swidget = widget->dobj.private;
struct snd_soc_dapm_path *p;
- /* return if the widget is in use or if it is already unprepared */
- if (!swidget->prepared || swidget->use_count > 1)
- return;
+ /* skip if the widget is in use or if it is already unprepared */
+ if (!swidget || !swidget->prepared || swidget->use_count > 0)
+ goto sink_unprepare;
if (widget_ops[widget->id].ipc_unprepare)
/* unprepare the source widget */
@@ -281,6 +281,7 @@ sof_unprepare_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widg
swidget->prepared = false;
+sink_unprepare:
/* unprepare all widgets in the sink paths */
snd_soc_dapm_widget_for_each_sink_path(widget, p) {
if (!p->walking && p->sink->dobj.private) {
@@ -303,7 +304,7 @@ sof_prepare_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget
struct snd_soc_dapm_path *p;
int ret;
- if (!widget_ops[widget->id].ipc_prepare || swidget->prepared)
+ if (!swidget || !widget_ops[widget->id].ipc_prepare || swidget->prepared)
goto sink_prepare;
/* prepare the source widget */
@@ -326,7 +327,8 @@ sink_prepare:
p->walking = false;
if (ret < 0) {
/* unprepare the source widget */
- if (widget_ops[widget->id].ipc_unprepare && swidget->prepared) {
+ if (widget_ops[widget->id].ipc_unprepare &&
+ swidget && swidget->prepared) {
widget_ops[widget->id].ipc_unprepare(swidget);
swidget->prepared = false;
}
@@ -429,11 +431,11 @@ sof_walk_widgets_in_order(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget_l
for_each_dapm_widgets(list, i, widget) {
/* starting widget for playback is AIF type */
- if (dir == SNDRV_PCM_STREAM_PLAYBACK && !WIDGET_IS_AIF(widget->id))
+ if (dir == SNDRV_PCM_STREAM_PLAYBACK && widget->id != snd_soc_dapm_aif_in)
continue;
/* starting widget for capture is DAI type */
- if (dir == SNDRV_PCM_STREAM_CAPTURE && !WIDGET_IS_DAI(widget->id))
+ if (dir == SNDRV_PCM_STREAM_CAPTURE && widget->id != snd_soc_dapm_dai_out)
continue;
switch (op) {
diff --git a/sound/sound_core.c b/sound/sound_core.c
index 3332fe321737..3e7dd6fcb7cf 100644
--- a/sound/sound_core.c
+++ b/sound/sound_core.c
@@ -30,7 +30,7 @@ MODULE_DESCRIPTION("Core sound module");
MODULE_AUTHOR("Alan Cox");
MODULE_LICENSE("GPL");
-static char *sound_devnode(struct device *dev, umode_t *mode)
+static char *sound_devnode(const struct device *dev, umode_t *mode)
{
if (MAJOR(dev->devt) == SOUND_MAJOR)
return NULL;
diff --git a/sound/synth/emux/emux.c b/sound/synth/emux/emux.c
index a870759d179e..0006c3ddb51d 100644
--- a/sound/synth/emux/emux.c
+++ b/sound/synth/emux/emux.c
@@ -129,7 +129,7 @@ int snd_emux_free(struct snd_emux *emu)
if (! emu)
return -EINVAL;
- del_timer_sync(&emu->tlist);
+ timer_shutdown_sync(&emu->tlist);
snd_emux_proc_free(emu);
snd_emux_delete_virmidi(emu);
diff --git a/sound/usb/card.h b/sound/usb/card.h
index 40061550105a..6ec95b2edf86 100644
--- a/sound/usb/card.h
+++ b/sound/usb/card.h
@@ -131,6 +131,7 @@ struct snd_usb_endpoint {
bool lowlatency_playback; /* low-latency playback mode */
bool need_setup; /* (re-)need for hw_params? */
bool need_prepare; /* (re-)need for prepare? */
+ bool fixed_rate; /* skip rate setup */
/* for hw constraints */
const struct audioformat *cur_audiofmt;
diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c
index 4aaf0784940b..419302e2057e 100644
--- a/sound/usb/endpoint.c
+++ b/sound/usb/endpoint.c
@@ -769,7 +769,8 @@ struct snd_usb_endpoint *
snd_usb_endpoint_open(struct snd_usb_audio *chip,
const struct audioformat *fp,
const struct snd_pcm_hw_params *params,
- bool is_sync_ep)
+ bool is_sync_ep,
+ bool fixed_rate)
{
struct snd_usb_endpoint *ep;
int ep_num = is_sync_ep ? fp->sync_ep : fp->endpoint;
@@ -825,6 +826,7 @@ snd_usb_endpoint_open(struct snd_usb_audio *chip,
ep->implicit_fb_sync = fp->implicit_fb;
ep->need_setup = true;
ep->need_prepare = true;
+ ep->fixed_rate = fixed_rate;
usb_audio_dbg(chip, " channels=%d, rate=%d, format=%s, period_bytes=%d, periods=%d, implicit_fb=%d\n",
ep->cur_channels, ep->cur_rate,
@@ -1413,11 +1415,13 @@ static int init_sample_rate(struct snd_usb_audio *chip,
if (clock && !clock->need_setup)
return 0;
- err = snd_usb_init_sample_rate(chip, ep->cur_audiofmt, rate);
- if (err < 0) {
- if (clock)
- clock->rate = 0; /* reset rate */
- return err;
+ if (!ep->fixed_rate) {
+ err = snd_usb_init_sample_rate(chip, ep->cur_audiofmt, rate);
+ if (err < 0) {
+ if (clock)
+ clock->rate = 0; /* reset rate */
+ return err;
+ }
}
if (clock)
diff --git a/sound/usb/endpoint.h b/sound/usb/endpoint.h
index e67ea28faa54..924f4351588c 100644
--- a/sound/usb/endpoint.h
+++ b/sound/usb/endpoint.h
@@ -14,7 +14,8 @@ struct snd_usb_endpoint *
snd_usb_endpoint_open(struct snd_usb_audio *chip,
const struct audioformat *fp,
const struct snd_pcm_hw_params *params,
- bool is_sync_ep);
+ bool is_sync_ep,
+ bool fixed_rate);
void snd_usb_endpoint_close(struct snd_usb_audio *chip,
struct snd_usb_endpoint *ep);
int snd_usb_endpoint_set_params(struct snd_usb_audio *chip,
diff --git a/sound/usb/implicit.c b/sound/usb/implicit.c
index f3e8484b3d9c..4727043fd745 100644
--- a/sound/usb/implicit.c
+++ b/sound/usb/implicit.c
@@ -15,6 +15,7 @@
#include "usbaudio.h"
#include "card.h"
#include "helper.h"
+#include "pcm.h"
#include "implicit.h"
enum {
@@ -455,7 +456,8 @@ const struct audioformat *
snd_usb_find_implicit_fb_sync_format(struct snd_usb_audio *chip,
const struct audioformat *target,
const struct snd_pcm_hw_params *params,
- int stream)
+ int stream,
+ bool *fixed_rate)
{
struct snd_usb_substream *subs;
const struct audioformat *fp, *sync_fmt = NULL;
@@ -469,7 +471,7 @@ snd_usb_find_implicit_fb_sync_format(struct snd_usb_audio *chip,
subs = find_matching_substream(chip, stream, target->sync_ep,
target->fmt_type);
if (!subs)
- return sync_fmt;
+ goto end;
high_score = 0;
list_for_each_entry(fp, &subs->fmt_list, list) {
@@ -483,6 +485,9 @@ snd_usb_find_implicit_fb_sync_format(struct snd_usb_audio *chip,
}
}
+ end:
+ if (fixed_rate)
+ *fixed_rate = snd_usb_pcm_has_fixed_rate(subs);
return sync_fmt;
}
diff --git a/sound/usb/implicit.h b/sound/usb/implicit.h
index ccb415a0ea86..7f1577b6c4d3 100644
--- a/sound/usb/implicit.h
+++ b/sound/usb/implicit.h
@@ -9,6 +9,6 @@ const struct audioformat *
snd_usb_find_implicit_fb_sync_format(struct snd_usb_audio *chip,
const struct audioformat *target,
const struct snd_pcm_hw_params *params,
- int stream);
+ int stream, bool *fixed_rate);
#endif /* __USBAUDIO_IMPLICIT_H */
diff --git a/sound/usb/line6/driver.c b/sound/usb/line6/driver.c
index 59faa5a9a714..b67617b68e50 100644
--- a/sound/usb/line6/driver.c
+++ b/sound/usb/line6/driver.c
@@ -304,7 +304,8 @@ static void line6_data_received(struct urb *urb)
for (;;) {
done =
line6_midibuf_read(mb, line6->buffer_message,
- LINE6_MIDI_MESSAGE_MAXLEN);
+ LINE6_MIDI_MESSAGE_MAXLEN,
+ LINE6_MIDIBUF_READ_RX);
if (done <= 0)
break;
diff --git a/sound/usb/line6/midi.c b/sound/usb/line6/midi.c
index ba0e2b7e8fe1..0838632c788e 100644
--- a/sound/usb/line6/midi.c
+++ b/sound/usb/line6/midi.c
@@ -44,7 +44,8 @@ static void line6_midi_transmit(struct snd_rawmidi_substream *substream)
int req, done;
for (;;) {
- req = min(line6_midibuf_bytes_free(mb), line6->max_packet_size);
+ req = min3(line6_midibuf_bytes_free(mb), line6->max_packet_size,
+ LINE6_FALLBACK_MAXPACKETSIZE);
done = snd_rawmidi_transmit_peek(substream, chunk, req);
if (done == 0)
@@ -56,7 +57,8 @@ static void line6_midi_transmit(struct snd_rawmidi_substream *substream)
for (;;) {
done = line6_midibuf_read(mb, chunk,
- LINE6_FALLBACK_MAXPACKETSIZE);
+ LINE6_FALLBACK_MAXPACKETSIZE,
+ LINE6_MIDIBUF_READ_TX);
if (done == 0)
break;
diff --git a/sound/usb/line6/midibuf.c b/sound/usb/line6/midibuf.c
index 6a70463f82c4..e7f830f7526c 100644
--- a/sound/usb/line6/midibuf.c
+++ b/sound/usb/line6/midibuf.c
@@ -9,6 +9,7 @@
#include "midibuf.h"
+
static int midibuf_message_length(unsigned char code)
{
int message_length;
@@ -20,12 +21,7 @@ static int midibuf_message_length(unsigned char code)
message_length = length[(code >> 4) - 8];
} else {
- /*
- Note that according to the MIDI specification 0xf2 is
- the "Song Position Pointer", but this is used by Line 6
- to send sysex messages to the host.
- */
- static const int length[] = { -1, 2, -1, 2, -1, -1, 1, 1, 1, 1,
+ static const int length[] = { -1, 2, 2, 2, -1, -1, 1, 1, 1, -1,
1, 1, 1, -1, 1, 1
};
message_length = length[code & 0x0f];
@@ -125,7 +121,7 @@ int line6_midibuf_write(struct midi_buffer *this, unsigned char *data,
}
int line6_midibuf_read(struct midi_buffer *this, unsigned char *data,
- int length)
+ int length, int read_type)
{
int bytes_used;
int length1, length2;
@@ -148,9 +144,22 @@ int line6_midibuf_read(struct midi_buffer *this, unsigned char *data,
length1 = this->size - this->pos_read;
- /* check MIDI command length */
command = this->buf[this->pos_read];
+ /*
+ PODxt always has status byte lower nibble set to 0010,
+ when it means to send 0000, so we correct if here so
+ that control/program changes come on channel 1 and
+ sysex message status byte is correct
+ */
+ if (read_type == LINE6_MIDIBUF_READ_RX) {
+ if (command == 0xb2 || command == 0xc2 || command == 0xf2) {
+ unsigned char fixed = command & 0xf0;
+ this->buf[this->pos_read] = fixed;
+ command = fixed;
+ }
+ }
+ /* check MIDI command length */
if (command & 0x80) {
midi_length = midibuf_message_length(command);
this->command_prev = command;
diff --git a/sound/usb/line6/midibuf.h b/sound/usb/line6/midibuf.h
index 124a8f9f7e96..542e8d836f87 100644
--- a/sound/usb/line6/midibuf.h
+++ b/sound/usb/line6/midibuf.h
@@ -8,6 +8,9 @@
#ifndef MIDIBUF_H
#define MIDIBUF_H
+#define LINE6_MIDIBUF_READ_TX 0
+#define LINE6_MIDIBUF_READ_RX 1
+
struct midi_buffer {
unsigned char *buf;
int size;
@@ -23,7 +26,7 @@ extern void line6_midibuf_destroy(struct midi_buffer *mb);
extern int line6_midibuf_ignore(struct midi_buffer *mb, int length);
extern int line6_midibuf_init(struct midi_buffer *mb, int size, int split);
extern int line6_midibuf_read(struct midi_buffer *mb, unsigned char *data,
- int length);
+ int length, int read_type);
extern void line6_midibuf_reset(struct midi_buffer *mb);
extern int line6_midibuf_write(struct midi_buffer *mb, unsigned char *data,
int length);
diff --git a/sound/usb/line6/pod.c b/sound/usb/line6/pod.c
index cd41aa7f0385..d173971e5f02 100644
--- a/sound/usb/line6/pod.c
+++ b/sound/usb/line6/pod.c
@@ -159,8 +159,9 @@ static struct line6_pcm_properties pod_pcm_properties = {
.bytes_per_channel = 3 /* SNDRV_PCM_FMTBIT_S24_3LE */
};
+
static const char pod_version_header[] = {
- 0xf2, 0x7e, 0x7f, 0x06, 0x02
+ 0xf0, 0x7e, 0x7f, 0x06, 0x02
};
static char *pod_alloc_sysex_buffer(struct usb_line6_pod *pod, int code,
diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c
index 9557bd4d1bbc..d959da7a1afb 100644
--- a/sound/usb/pcm.c
+++ b/sound/usb/pcm.c
@@ -157,6 +157,34 @@ find_substream_format(struct snd_usb_substream *subs,
true, subs);
}
+bool snd_usb_pcm_has_fixed_rate(struct snd_usb_substream *subs)
+{
+ const struct audioformat *fp;
+ struct snd_usb_audio *chip;
+ int rate = -1;
+
+ if (!subs)
+ return false;
+ chip = subs->stream->chip;
+ if (!(chip->quirk_flags & QUIRK_FLAG_FIXED_RATE))
+ return false;
+ list_for_each_entry(fp, &subs->fmt_list, list) {
+ if (fp->rates & SNDRV_PCM_RATE_CONTINUOUS)
+ return false;
+ if (fp->nr_rates < 1)
+ continue;
+ if (fp->nr_rates > 1)
+ return false;
+ if (rate < 0) {
+ rate = fp->rate_table[0];
+ continue;
+ }
+ if (rate != fp->rate_table[0])
+ return false;
+ }
+ return true;
+}
+
static int init_pitch_v1(struct snd_usb_audio *chip, int ep)
{
struct usb_device *dev = chip->dev;
@@ -450,12 +478,14 @@ static int snd_usb_hw_params(struct snd_pcm_substream *substream,
struct snd_usb_audio *chip = subs->stream->chip;
const struct audioformat *fmt;
const struct audioformat *sync_fmt;
+ bool fixed_rate, sync_fixed_rate;
int ret;
ret = snd_media_start_pipeline(subs);
if (ret)
return ret;
+ fixed_rate = snd_usb_pcm_has_fixed_rate(subs);
fmt = find_substream_format(subs, hw_params);
if (!fmt) {
usb_audio_dbg(chip,
@@ -469,7 +499,8 @@ static int snd_usb_hw_params(struct snd_pcm_substream *substream,
if (fmt->implicit_fb) {
sync_fmt = snd_usb_find_implicit_fb_sync_format(chip, fmt,
hw_params,
- !substream->stream);
+ !substream->stream,
+ &sync_fixed_rate);
if (!sync_fmt) {
usb_audio_dbg(chip,
"cannot find sync format: ep=0x%x, iface=%d:%d, format=%s, rate=%d, channels=%d\n",
@@ -482,6 +513,7 @@ static int snd_usb_hw_params(struct snd_pcm_substream *substream,
}
} else {
sync_fmt = fmt;
+ sync_fixed_rate = fixed_rate;
}
ret = snd_usb_lock_shutdown(chip);
@@ -496,10 +528,12 @@ static int snd_usb_hw_params(struct snd_pcm_substream *substream,
if (snd_usb_endpoint_compatible(chip, subs->data_endpoint,
fmt, hw_params))
goto unlock;
+ if (stop_endpoints(subs, false))
+ sync_pending_stops(subs);
close_endpoints(chip, subs);
}
- subs->data_endpoint = snd_usb_endpoint_open(chip, fmt, hw_params, false);
+ subs->data_endpoint = snd_usb_endpoint_open(chip, fmt, hw_params, false, fixed_rate);
if (!subs->data_endpoint) {
ret = -EINVAL;
goto unlock;
@@ -508,7 +542,8 @@ static int snd_usb_hw_params(struct snd_pcm_substream *substream,
if (fmt->sync_ep) {
subs->sync_endpoint = snd_usb_endpoint_open(chip, sync_fmt,
hw_params,
- fmt == sync_fmt);
+ fmt == sync_fmt,
+ sync_fixed_rate);
if (!subs->sync_endpoint) {
ret = -EINVAL;
goto unlock;
@@ -757,11 +792,27 @@ static int apply_hw_params_minmax(struct snd_interval *it, unsigned int rmin,
return changed;
}
+/* get the specified endpoint object that is being used by other streams
+ * (i.e. the parameter is locked)
+ */
+static const struct snd_usb_endpoint *
+get_endpoint_in_use(struct snd_usb_audio *chip, int endpoint,
+ const struct snd_usb_endpoint *ref_ep)
+{
+ const struct snd_usb_endpoint *ep;
+
+ ep = snd_usb_get_endpoint(chip, endpoint);
+ if (ep && ep->cur_audiofmt && (ep != ref_ep || ep->opened > 1))
+ return ep;
+ return NULL;
+}
+
static int hw_rule_rate(struct snd_pcm_hw_params *params,
struct snd_pcm_hw_rule *rule)
{
struct snd_usb_substream *subs = rule->private;
struct snd_usb_audio *chip = subs->stream->chip;
+ const struct snd_usb_endpoint *ep;
const struct audioformat *fp;
struct snd_interval *it = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
unsigned int rmin, rmax, r;
@@ -773,6 +824,29 @@ static int hw_rule_rate(struct snd_pcm_hw_params *params,
list_for_each_entry(fp, &subs->fmt_list, list) {
if (!hw_check_valid_format(subs, params, fp))
continue;
+
+ ep = get_endpoint_in_use(chip, fp->endpoint,
+ subs->data_endpoint);
+ if (ep) {
+ hwc_debug("rate limit %d for ep#%x\n",
+ ep->cur_rate, fp->endpoint);
+ rmin = min(rmin, ep->cur_rate);
+ rmax = max(rmax, ep->cur_rate);
+ continue;
+ }
+
+ if (fp->implicit_fb) {
+ ep = get_endpoint_in_use(chip, fp->sync_ep,
+ subs->sync_endpoint);
+ if (ep) {
+ hwc_debug("rate limit %d for sync_ep#%x\n",
+ ep->cur_rate, fp->sync_ep);
+ rmin = min(rmin, ep->cur_rate);
+ rmax = max(rmax, ep->cur_rate);
+ continue;
+ }
+ }
+
r = snd_usb_endpoint_get_clock_rate(chip, fp->clock);
if (r > 0) {
if (!snd_interval_test(it, r))
@@ -842,6 +916,8 @@ static int hw_rule_format(struct snd_pcm_hw_params *params,
struct snd_pcm_hw_rule *rule)
{
struct snd_usb_substream *subs = rule->private;
+ struct snd_usb_audio *chip = subs->stream->chip;
+ const struct snd_usb_endpoint *ep;
const struct audioformat *fp;
struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
u64 fbits;
@@ -851,6 +927,27 @@ static int hw_rule_format(struct snd_pcm_hw_params *params,
list_for_each_entry(fp, &subs->fmt_list, list) {
if (!hw_check_valid_format(subs, params, fp))
continue;
+
+ ep = get_endpoint_in_use(chip, fp->endpoint,
+ subs->data_endpoint);
+ if (ep) {
+ hwc_debug("format limit %d for ep#%x\n",
+ ep->cur_format, fp->endpoint);
+ fbits |= pcm_format_to_bits(ep->cur_format);
+ continue;
+ }
+
+ if (fp->implicit_fb) {
+ ep = get_endpoint_in_use(chip, fp->sync_ep,
+ subs->sync_endpoint);
+ if (ep) {
+ hwc_debug("format limit %d for sync_ep#%x\n",
+ ep->cur_format, fp->sync_ep);
+ fbits |= pcm_format_to_bits(ep->cur_format);
+ continue;
+ }
+ }
+
fbits |= fp->formats;
}
return apply_hw_params_format_bits(fmt, fbits);
@@ -883,98 +980,95 @@ static int hw_rule_period_time(struct snd_pcm_hw_params *params,
return apply_hw_params_minmax(it, pmin, UINT_MAX);
}
-/* get the EP or the sync EP for implicit fb when it's already set up */
-static const struct snd_usb_endpoint *
-get_sync_ep_from_substream(struct snd_usb_substream *subs)
-{
- struct snd_usb_audio *chip = subs->stream->chip;
- const struct audioformat *fp;
- const struct snd_usb_endpoint *ep;
-
- list_for_each_entry(fp, &subs->fmt_list, list) {
- ep = snd_usb_get_endpoint(chip, fp->endpoint);
- if (ep && ep->cur_audiofmt) {
- /* if EP is already opened solely for this substream,
- * we still allow us to change the parameter; otherwise
- * this substream has to follow the existing parameter
- */
- if (ep->cur_audiofmt != subs->cur_audiofmt || ep->opened > 1)
- return ep;
- }
- if (!fp->implicit_fb)
- continue;
- /* for the implicit fb, check the sync ep as well */
- ep = snd_usb_get_endpoint(chip, fp->sync_ep);
- if (ep && ep->cur_audiofmt)
- return ep;
- }
- return NULL;
-}
-
/* additional hw constraints for implicit feedback mode */
-static int hw_rule_format_implicit_fb(struct snd_pcm_hw_params *params,
- struct snd_pcm_hw_rule *rule)
-{
- struct snd_usb_substream *subs = rule->private;
- const struct snd_usb_endpoint *ep;
- struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
-
- ep = get_sync_ep_from_substream(subs);
- if (!ep)
- return 0;
-
- hwc_debug("applying %s\n", __func__);
- return apply_hw_params_format_bits(fmt, pcm_format_to_bits(ep->cur_format));
-}
-
-static int hw_rule_rate_implicit_fb(struct snd_pcm_hw_params *params,
- struct snd_pcm_hw_rule *rule)
-{
- struct snd_usb_substream *subs = rule->private;
- const struct snd_usb_endpoint *ep;
- struct snd_interval *it;
-
- ep = get_sync_ep_from_substream(subs);
- if (!ep)
- return 0;
-
- hwc_debug("applying %s\n", __func__);
- it = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
- return apply_hw_params_minmax(it, ep->cur_rate, ep->cur_rate);
-}
-
static int hw_rule_period_size_implicit_fb(struct snd_pcm_hw_params *params,
struct snd_pcm_hw_rule *rule)
{
struct snd_usb_substream *subs = rule->private;
+ struct snd_usb_audio *chip = subs->stream->chip;
+ const struct audioformat *fp;
const struct snd_usb_endpoint *ep;
struct snd_interval *it;
+ unsigned int rmin, rmax;
- ep = get_sync_ep_from_substream(subs);
- if (!ep)
- return 0;
-
- hwc_debug("applying %s\n", __func__);
it = hw_param_interval(params, SNDRV_PCM_HW_PARAM_PERIOD_SIZE);
- return apply_hw_params_minmax(it, ep->cur_period_frames,
- ep->cur_period_frames);
+ hwc_debug("hw_rule_period_size: (%u,%u)\n", it->min, it->max);
+ rmin = UINT_MAX;
+ rmax = 0;
+ list_for_each_entry(fp, &subs->fmt_list, list) {
+ if (!hw_check_valid_format(subs, params, fp))
+ continue;
+ ep = get_endpoint_in_use(chip, fp->endpoint,
+ subs->data_endpoint);
+ if (ep) {
+ hwc_debug("period size limit %d for ep#%x\n",
+ ep->cur_period_frames, fp->endpoint);
+ rmin = min(rmin, ep->cur_period_frames);
+ rmax = max(rmax, ep->cur_period_frames);
+ continue;
+ }
+
+ if (fp->implicit_fb) {
+ ep = get_endpoint_in_use(chip, fp->sync_ep,
+ subs->sync_endpoint);
+ if (ep) {
+ hwc_debug("period size limit %d for sync_ep#%x\n",
+ ep->cur_period_frames, fp->sync_ep);
+ rmin = min(rmin, ep->cur_period_frames);
+ rmax = max(rmax, ep->cur_period_frames);
+ continue;
+ }
+ }
+ }
+
+ if (!rmax)
+ return 0; /* no limit by implicit fb */
+ return apply_hw_params_minmax(it, rmin, rmax);
}
static int hw_rule_periods_implicit_fb(struct snd_pcm_hw_params *params,
struct snd_pcm_hw_rule *rule)
{
struct snd_usb_substream *subs = rule->private;
+ struct snd_usb_audio *chip = subs->stream->chip;
+ const struct audioformat *fp;
const struct snd_usb_endpoint *ep;
struct snd_interval *it;
+ unsigned int rmin, rmax;
- ep = get_sync_ep_from_substream(subs);
- if (!ep)
- return 0;
-
- hwc_debug("applying %s\n", __func__);
it = hw_param_interval(params, SNDRV_PCM_HW_PARAM_PERIODS);
- return apply_hw_params_minmax(it, ep->cur_buffer_periods,
- ep->cur_buffer_periods);
+ hwc_debug("hw_rule_periods: (%u,%u)\n", it->min, it->max);
+ rmin = UINT_MAX;
+ rmax = 0;
+ list_for_each_entry(fp, &subs->fmt_list, list) {
+ if (!hw_check_valid_format(subs, params, fp))
+ continue;
+ ep = get_endpoint_in_use(chip, fp->endpoint,
+ subs->data_endpoint);
+ if (ep) {
+ hwc_debug("periods limit %d for ep#%x\n",
+ ep->cur_buffer_periods, fp->endpoint);
+ rmin = min(rmin, ep->cur_buffer_periods);
+ rmax = max(rmax, ep->cur_buffer_periods);
+ continue;
+ }
+
+ if (fp->implicit_fb) {
+ ep = get_endpoint_in_use(chip, fp->sync_ep,
+ subs->sync_endpoint);
+ if (ep) {
+ hwc_debug("periods limit %d for sync_ep#%x\n",
+ ep->cur_buffer_periods, fp->sync_ep);
+ rmin = min(rmin, ep->cur_buffer_periods);
+ rmax = max(rmax, ep->cur_buffer_periods);
+ continue;
+ }
+ }
+ }
+
+ if (!rmax)
+ return 0; /* no limit by implicit fb */
+ return apply_hw_params_minmax(it, rmin, rmax);
}
/*
@@ -1083,16 +1177,6 @@ static int setup_hw_info(struct snd_pcm_runtime *runtime, struct snd_usb_substre
return err;
/* additional hw constraints for implicit fb */
- err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_FORMAT,
- hw_rule_format_implicit_fb, subs,
- SNDRV_PCM_HW_PARAM_FORMAT, -1);
- if (err < 0)
- return err;
- err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
- hw_rule_rate_implicit_fb, subs,
- SNDRV_PCM_HW_PARAM_RATE, -1);
- if (err < 0)
- return err;
err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
hw_rule_period_size_implicit_fb, subs,
SNDRV_PCM_HW_PARAM_PERIOD_SIZE, -1);
diff --git a/sound/usb/pcm.h b/sound/usb/pcm.h
index 493a4e34d78d..388fe2ba346d 100644
--- a/sound/usb/pcm.h
+++ b/sound/usb/pcm.h
@@ -6,6 +6,8 @@ void snd_usb_set_pcm_ops(struct snd_pcm *pcm, int stream);
int snd_usb_pcm_suspend(struct snd_usb_stream *as);
int snd_usb_pcm_resume(struct snd_usb_stream *as);
+bool snd_usb_pcm_has_fixed_rate(struct snd_usb_substream *as);
+
int snd_usb_init_pitch(struct snd_usb_audio *chip,
const struct audioformat *fmt);
void snd_usb_preallocate_buffer(struct snd_usb_substream *subs);
diff --git a/sound/usb/quirks-table.h b/sound/usb/quirks-table.h
index 874fcf245747..271884e35003 100644
--- a/sound/usb/quirks-table.h
+++ b/sound/usb/quirks-table.h
@@ -76,6 +76,8 @@
{ USB_DEVICE_VENDOR_SPEC(0x041e, 0x3f0a) },
/* E-Mu 0204 USB */
{ USB_DEVICE_VENDOR_SPEC(0x041e, 0x3f19) },
+/* Ktmicro Usb_audio device */
+{ USB_DEVICE_VENDOR_SPEC(0x31b2, 0x0011) },
/*
* Creative Technology, Ltd Live! Cam Sync HD [VF0770]
diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c
index 58b37bfc885c..3ecd1ba7fd4b 100644
--- a/sound/usb/quirks.c
+++ b/sound/usb/quirks.c
@@ -2152,6 +2152,10 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = {
QUIRK_FLAG_GENERIC_IMPLICIT_FB),
DEVICE_FLG(0x0525, 0xa4ad, /* Hamedal C20 usb camero */
QUIRK_FLAG_IFACE_SKIP_CLOSE),
+ DEVICE_FLG(0x0ecb, 0x205c, /* JBL Quantum610 Wireless */
+ QUIRK_FLAG_FIXED_RATE),
+ DEVICE_FLG(0x0ecb, 0x2069, /* JBL Quantum810 Wireless */
+ QUIRK_FLAG_FIXED_RATE),
/* Vendor matches */
VENDOR_FLG(0x045e, /* MS Lifecam */
diff --git a/sound/usb/stream.c b/sound/usb/stream.c
index f75601ca2d52..f10f4e6d3fb8 100644
--- a/sound/usb/stream.c
+++ b/sound/usb/stream.c
@@ -1222,6 +1222,12 @@ static int __snd_usb_parse_audio_interface(struct snd_usb_audio *chip,
if (err < 0)
return err;
}
+
+ /* try to set the interface... */
+ usb_set_interface(chip->dev, iface_no, 0);
+ snd_usb_init_pitch(chip, fp);
+ snd_usb_init_sample_rate(chip, fp, fp->rate_max);
+ usb_set_interface(chip->dev, iface_no, altno);
}
return 0;
}
diff --git a/sound/usb/usbaudio.h b/sound/usb/usbaudio.h
index 2aba508a4831..f5a8dca66457 100644
--- a/sound/usb/usbaudio.h
+++ b/sound/usb/usbaudio.h
@@ -175,6 +175,9 @@ extern bool snd_usb_skip_validation;
* QUIRK_FLAG_FORCE_IFACE_RESET
* Force an interface reset whenever stopping & restarting a stream
* (e.g. after xrun)
+ * QUIRK_FLAG_FIXED_RATE
+ * Do not set PCM rate (frequency) when only one rate is available
+ * for the given endpoint.
*/
#define QUIRK_FLAG_GET_SAMPLE_RATE (1U << 0)
@@ -198,5 +201,6 @@ extern bool snd_usb_skip_validation;
#define QUIRK_FLAG_SKIP_IMPLICIT_FB (1U << 18)
#define QUIRK_FLAG_IFACE_SKIP_CLOSE (1U << 19)
#define QUIRK_FLAG_FORCE_IFACE_RESET (1U << 20)
+#define QUIRK_FLAG_FIXED_RATE (1U << 21)
#endif /* __USBAUDIO_H */
diff --git a/sound/xen/xen_snd_front.c b/sound/xen/xen_snd_front.c
index 4041748c12e5..b66e037710d0 100644
--- a/sound/xen/xen_snd_front.c
+++ b/sound/xen/xen_snd_front.c
@@ -311,7 +311,7 @@ static int xen_drv_probe(struct xenbus_device *xb_dev,
return xenbus_switch_state(xb_dev, XenbusStateInitialising);
}
-static int xen_drv_remove(struct xenbus_device *dev)
+static void xen_drv_remove(struct xenbus_device *dev)
{
struct xen_snd_front_info *front_info = dev_get_drvdata(&dev->dev);
int to = 100;
@@ -345,7 +345,6 @@ static int xen_drv_remove(struct xenbus_device *dev)
xen_snd_drv_fini(front_info);
xenbus_frontend_closed(dev);
- return 0;
}
static const struct xenbus_device_id xen_drv_ids[] = {