From 97d73d978271ade27fc751ad606f23c1c4c43678 Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Fri, 2 Dec 2022 16:28:26 +0100 Subject: ALSA: hda: Allow for compress stream to hdac_ext_stream assignment Currently only PCM streams can enlist hdac_stream for their data transfer. Add cstream field to hdac_ext_stream to expose possibility of compress stream assignment in place of PCM one. Limited to HOST-type only as there no other users on the horizon. Signed-off-by: Cezary Rojewski Acked-by: Takashi Iwai Link: https://lore.kernel.org/r/20221202152841.672536-2-cezary.rojewski@intel.com Signed-off-by: Mark Brown --- sound/hda/ext/hdac_ext_stream.c | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) (limited to 'sound') 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 #include #include +#include /** * 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); -- cgit v1.2.3 From f6b1254664a0a15c8bbe0a17b2c86840aa38d3d7 Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Fri, 2 Dec 2022 16:28:27 +0100 Subject: ALSA: hda: Prepare for compress stream support Before introducing compress specific changes, adjust BDL and parameters setup functions so these are not tightly coupled with PCM streams. Signed-off-by: Cezary Rojewski Acked-by: Takashi Iwai Link: https://lore.kernel.org/r/20221202152841.672536-3-cezary.rojewski@intel.com Signed-off-by: Mark Brown --- sound/hda/hdac_stream.c | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) (limited to 'sound') diff --git a/sound/hda/hdac_stream.c b/sound/hda/hdac_stream.c index 3b250ee7f6a7..8a12c6347914 100644 --- a/sound/hda/hdac_stream.c +++ b/sound/hda/hdac_stream.c @@ -487,11 +487,15 @@ 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_pcm_runtime *runtime; + struct snd_dma_buffer *dmab; __le32 *bdl; int i, ofs, periods, period_bytes; int pos_adj, pos_align; + runtime = substream->runtime; + dmab = snd_pcm_get_dma_buf(substream); + /* reset BDL address */ snd_hdac_stream_writel(azx_dev, SD_BDLPL, 0); snd_hdac_stream_writel(azx_dev, SD_BDLPU, 0); @@ -505,7 +509,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 +522,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 +532,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 +561,25 @@ 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; + unsigned int bufsize, period_bytes; + unsigned int no_period_wakeup; int err; if (!substream) return -EINVAL; - runtime = substream->runtime; bufsize = snd_pcm_lib_buffer_bytes(substream); period_bytes = snd_pcm_lib_period_bytes(substream); + no_period_wakeup = substream->runtime->no_period_wakeup; 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; -- cgit v1.2.3 From 3e9582267e3a06bfd9622dbd2304a8cfac977b43 Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Fri, 2 Dec 2022 16:28:28 +0100 Subject: ALSA: hda: Interrupt servicing and BDL setup for compress streams Account for compress streams when receiving and servicing buffer completed interrupts. In case of compress stream enlisting hdac_stream for data transfer, setup BDL entries much like it is the case for PCM streams. Signed-off-by: Divya Prakash Signed-off-by: Cezary Rojewski Acked-by: Takashi Iwai Link: https://lore.kernel.org/r/20221202152841.672536-4-cezary.rojewski@intel.com Signed-off-by: Mark Brown --- sound/hda/hdac_controller.c | 4 ++-- sound/hda/hdac_stream.c | 27 ++++++++++++++++++++------- 2 files changed, 22 insertions(+), 9 deletions(-) (limited to 'sound') 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 8a12c6347914..8f625402505f 100644 --- a/sound/hda/hdac_stream.c +++ b/sound/hda/hdac_stream.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -487,14 +488,19 @@ 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; + 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; - runtime = substream->runtime; - dmab = snd_pcm_get_dma_buf(substream); + if (substream) { + runtime = substream->runtime; + dmab = snd_pcm_get_dma_buf(substream); + } else if (cstream) { + dmab = snd_pcm_get_dma_buf(cstream); + } /* reset BDL address */ snd_hdac_stream_writel(azx_dev, SD_BDLPL, 0); @@ -562,15 +568,22 @@ int snd_hdac_stream_set_params(struct hdac_stream *azx_dev, unsigned int format_val) { struct snd_pcm_substream *substream = azx_dev->substream; + 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; - bufsize = snd_pcm_lib_buffer_bytes(substream); - period_bytes = snd_pcm_lib_period_bytes(substream); - no_period_wakeup = substream->runtime->no_period_wakeup; + } if (bufsize != azx_dev->bufsize || period_bytes != azx_dev->period_bytes || -- cgit v1.2.3 From bb03099bf2253fcd1a4d57e6f5ee4e8000911e77 Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Fri, 2 Dec 2022 16:28:29 +0100 Subject: ASoC: Intel: avs: Introduce avs_log_buffer_status_locked() Simplify locking of firmware log gathering by providing single location for such purpose. Signed-off-by: Cezary Rojewski Link: https://lore.kernel.org/r/20221202152841.672536-5-cezary.rojewski@intel.com Signed-off-by: Mark Brown --- sound/soc/intel/avs/apl.c | 5 +---- sound/soc/intel/avs/avs.h | 12 ++++++++++++ sound/soc/intel/avs/ipc.c | 2 +- sound/soc/intel/avs/skl.c | 7 +------ 4 files changed, 15 insertions(+), 11 deletions(-) (limited to 'sound') diff --git a/sound/soc/intel/avs/apl.c b/sound/soc/intel/avs/apl.c index 7c8ce98eda9d..821d5a9ad25f 100644 --- a/sound/soc/intel/avs/apl.c +++ b/sound/soc/intel/avs/apl.c @@ -50,7 +50,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,7 +58,6 @@ 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)) /* consume the logs regardless of consumer presence */ goto update_read_ptr; @@ -78,7 +76,6 @@ static int apl_log_buffer_status(struct avs_dev *adev, union avs_notify_msg *msg wake_up(&adev->dbg.trace_waitq); update_read_ptr: - spin_unlock_irqrestore(&adev->dbg.trace_lock, flags); writel(layout.write_ptr, addr); return 0; } @@ -140,7 +137,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; diff --git a/sound/soc/intel/avs/avs.h b/sound/soc/intel/avs/avs.h index 8d05b27608fe..1c89af6240d2 100644 --- a/sound/soc/intel/avs/avs.h +++ b/sound/soc/intel/avs/avs.h @@ -344,6 +344,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->dbg.trace_lock, flags); + ret = avs_dsp_op(adev, log_buffer_status, msg); + spin_unlock_irqrestore(&adev->dbg.trace_lock, flags); + + return ret; +} + struct apl_log_buffer_layout { u32 read_ptr; u32 write_ptr; 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/skl.c b/sound/soc/intel/avs/skl.c index dc98b5cf900f..ff690e99d960 100644 --- a/sound/soc/intel/avs/skl.c +++ b/sound/soc/intel/avs/skl.c @@ -55,15 +55,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 (!kfifo_initialized(&adev->dbg.trace_fifo)) 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)); @@ -74,7 +70,6 @@ skl_log_buffer_status(struct avs_dev *adev, union avs_notify_msg *msg) 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); return 0; } -- cgit v1.2.3 From 58029b7734ec84738aeb8fb391e625832bb6b0a6 Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Fri, 2 Dec 2022 16:28:30 +0100 Subject: ASoC: Intel: avs: Drop fifo_lock Log gathering is already locked, thanks to ->trace_lock. Signed-off-by: Cezary Rojewski Link: https://lore.kernel.org/r/20221202152841.672536-6-cezary.rojewski@intel.com Signed-off-by: Mark Brown --- sound/soc/intel/avs/apl.c | 9 ++++----- sound/soc/intel/avs/avs.h | 4 +--- sound/soc/intel/avs/skl.c | 2 +- sound/soc/intel/avs/utils.c | 6 +----- 4 files changed, 7 insertions(+), 14 deletions(-) (limited to 'sound') diff --git a/sound/soc/intel/avs/apl.c b/sound/soc/intel/avs/apl.c index 821d5a9ad25f..66672ffd95df 100644 --- a/sound/soc/intel/avs/apl.c +++ b/sound/soc/intel/avs/apl.c @@ -65,13 +65,12 @@ static int apl_log_buffer_status(struct avs_dev *adev, union avs_notify_msg *msg 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); + __kfifo_fromio(&adev->dbg.trace_fifo, 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); + __kfifo_fromio(&adev->dbg.trace_fifo, buf + layout.read_ptr, + layout.write_ptr - layout.read_ptr); wake_up(&adev->dbg.trace_waitq); diff --git a/sound/soc/intel/avs/avs.h b/sound/soc/intel/avs/avs.h index 1c89af6240d2..957151ecf39a 100644 --- a/sound/soc/intel/avs/avs.h +++ b/sound/soc/intel/avs/avs.h @@ -95,7 +95,6 @@ struct avs_fw_entry { 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; @@ -331,8 +330,7 @@ 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); +unsigned int __kfifo_fromio(struct kfifo *fifo, const void __iomem *src, unsigned int len); #define avs_log_buffer_size(adev) \ ((adev)->fw_cfg.trace_log_bytes / (adev)->hw_cfg.dsp_cores) diff --git a/sound/soc/intel/avs/skl.c b/sound/soc/intel/avs/skl.c index ff690e99d960..936cd44eb73e 100644 --- a/sound/soc/intel/avs/skl.c +++ b/sound/soc/intel/avs/skl.c @@ -68,7 +68,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); + __kfifo_fromio(&adev->dbg.trace_fifo, buf, size); wake_up(&adev->dbg.trace_waitq); return 0; diff --git a/sound/soc/intel/avs/utils.c b/sound/soc/intel/avs/utils.c index 13611dee9787..75ad434d7dfb 100644 --- a/sound/soc/intel/avs/utils.c +++ b/sound/soc/intel/avs/utils.c @@ -301,14 +301,11 @@ void avs_release_firmwares(struct avs_dev *adev) } } -unsigned int __kfifo_fromio_locked(struct kfifo *fifo, const void __iomem *src, unsigned int len, - spinlock_t *lock) +unsigned int __kfifo_fromio(struct kfifo *fifo, const void __iomem *src, unsigned int len) { 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); @@ -318,7 +315,6 @@ unsigned int __kfifo_fromio_locked(struct kfifo *fifo, const void __iomem *src, /* Make sure data copied from SRAM is visible to all CPUs. */ smp_mb(); __fifo->in += len; - spin_unlock_irqrestore(lock, flags); return len; } -- cgit v1.2.3 From 9e3c15beb8976771f95ba30b3da8bd35dc7188ac Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Fri, 2 Dec 2022 16:28:31 +0100 Subject: ASoC: Intel: avs: Introduce debug-context aware helpers Debug-related fields and log-dumping are useful when debugfs is enabled. Define them under CONFIG_DEBUG_FS and provide stubs when the config is disabled so that the code that makes use of these needs not to be complicated unnecessarily. Members that are duplicated by this patch will be removed by the follow up changes. Signed-off-by: Cezary Rojewski Link: https://lore.kernel.org/r/20221202152841.672536-7-cezary.rojewski@intel.com Signed-off-by: Mark Brown --- sound/soc/intel/avs/Makefile | 4 ++++ sound/soc/intel/avs/avs.h | 29 +++++++++++++++++++++++++++++ sound/soc/intel/avs/debugfs.c | 28 ++++++++++++++++++++++++++++ 3 files changed, 61 insertions(+) create mode 100644 sound/soc/intel/avs/debugfs.c (limited to 'sound') diff --git a/sound/soc/intel/avs/Makefile b/sound/soc/intel/avs/Makefile index 919212825f21..a211a0b7b4a8 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 += debugfs.o +endif + obj-$(CONFIG_SND_SOC_INTEL_AVS) += snd-soc-avs.o # Machine support diff --git a/sound/soc/intel/avs/avs.h b/sound/soc/intel/avs/avs.h index 957151ecf39a..3687d03f87d4 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 #include #include #include @@ -146,6 +147,14 @@ struct avs_dev { 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 */ +#endif }; /* from hda_bus to avs_dev */ @@ -366,4 +375,24 @@ struct apl_log_buffer_layout { #define apl_log_payload_addr(addr) \ (addr + sizeof(struct apl_log_buffer_layout)) +#ifdef CONFIG_DEBUG_FS +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); +#else +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) +{ +} +#endif + #endif /* __SOUND_SOC_INTEL_AVS_H */ diff --git a/sound/soc/intel/avs/debugfs.c b/sound/soc/intel/avs/debugfs.c new file mode 100644 index 000000000000..ac3889e21542 --- /dev/null +++ b/sound/soc/intel/avs/debugfs.c @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// Copyright(c) 2021-2022 Intel Corporation. All rights reserved. +// +// Authors: Cezary Rojewski +// Amadeusz Slawinski +// + +#include +#include +#include +#include "avs.h" + +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); +} -- cgit v1.2.3 From b3eefa5d8dbfe5286c3308fa706fc9c45b38fe19 Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Fri, 2 Dec 2022 16:28:32 +0100 Subject: ASoC: Intel: avs: Make enable_logs() dependent on DEBUG_FS Without debug filesystem present, this code is redundant. Operations: log_buffer_status and log_buffer_offset are left as is as EXCEPTION_CAUGHT and even unexpected LOG_BUFFER_STATUS notifications may occur without user ever touching debugfs. Signed-off-by: Cezary Rojewski Link: https://lore.kernel.org/r/20221202152841.672536-8-cezary.rojewski@intel.com Signed-off-by: Mark Brown --- sound/soc/intel/avs/apl.c | 7 ++++--- sound/soc/intel/avs/avs.h | 5 +++++ sound/soc/intel/avs/messages.c | 36 +++++++++++++++++++----------------- sound/soc/intel/avs/skl.c | 7 ++++--- 4 files changed, 32 insertions(+), 23 deletions(-) (limited to 'sound') diff --git a/sound/soc/intel/avs/apl.c b/sound/soc/intel/avs/apl.c index 66672ffd95df..beef308c9428 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; @@ -239,10 +240,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 3687d03f87d4..f8f11d8b5936 100644 --- a/sound/soc/intel/avs/avs.h +++ b/sound/soc/intel/avs/avs.h @@ -376,10 +376,15 @@ struct apl_log_buffer_layout { (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); #else +#define AVS_SET_ENABLE_LOGS_OP(name) + static inline bool avs_logging_fw(struct avs_dev *adev) { return false; diff --git a/sound/soc/intel/avs/messages.c b/sound/soc/intel/avs/messages.c index 6b0fecbf07c3..f734d49e42be 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, @@ -704,20 +722,4 @@ int avs_ipc_set_system_time(struct avs_dev *adev) return avs_ipc_set_large_config(adev, AVS_BASEFW_MOD_ID, AVS_BASEFW_INST_ID, 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) -{ - 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)); -} +#endif diff --git a/sound/soc/intel/avs/skl.c b/sound/soc/intel/avs/skl.c index 936cd44eb73e..c5edb0b0df14 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; @@ -111,10 +112,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) }; -- cgit v1.2.3 From f7de161fc8d5e1ebac3c361a37b1d748e7086330 Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Fri, 2 Dec 2022 16:28:33 +0100 Subject: ASoC: Intel: avs: Drop usage of debug members in non-debug code Switch to debug-context aware wrappers instead of accessing debug members directly allowing for readable separation of debug and non-debug related code. Duplicates are removed along the way. Signed-off-by: Cezary Rojewski Link: https://lore.kernel.org/r/20221202152841.672536-9-cezary.rojewski@intel.com Signed-off-by: Mark Brown --- sound/soc/intel/avs/apl.c | 11 ++++------- sound/soc/intel/avs/avs.h | 16 ++-------------- sound/soc/intel/avs/debugfs.c | 18 ++++++++++++++++++ sound/soc/intel/avs/skl.c | 5 ++--- sound/soc/intel/avs/utils.c | 18 ------------------ 5 files changed, 26 insertions(+), 42 deletions(-) (limited to 'sound') diff --git a/sound/soc/intel/avs/apl.c b/sound/soc/intel/avs/apl.c index beef308c9428..02683dce277a 100644 --- a/sound/soc/intel/avs/apl.c +++ b/sound/soc/intel/avs/apl.c @@ -59,21 +59,18 @@ static int apl_log_buffer_status(struct avs_dev *adev, union avs_notify_msg *msg memcpy_fromio(&layout, addr, sizeof(layout)); - 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(&adev->dbg.trace_fifo, buf + layout.read_ptr, - apl_log_payload_size(adev) - layout.read_ptr); + avs_dump_fw_log(adev, buf + layout.read_ptr, + apl_log_payload_size(adev) - layout.read_ptr); layout.read_ptr = 0; } - __kfifo_fromio(&adev->dbg.trace_fifo, buf + layout.read_ptr, - layout.write_ptr - layout.read_ptr); - - 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: writel(layout.write_ptr, addr); diff --git a/sound/soc/intel/avs/avs.h b/sound/soc/intel/avs/avs.h index f8f11d8b5936..7a9fb27d3845 100644 --- a/sound/soc/intel/avs/avs.h +++ b/sound/soc/intel/avs/avs.h @@ -94,15 +94,6 @@ struct avs_fw_entry { struct list_head node; }; -struct avs_debug { - struct kfifo 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,6 @@ 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; @@ -339,8 +329,6 @@ void avs_unregister_all_boards(struct avs_dev *adev); /* Firmware tracing helpers */ -unsigned int __kfifo_fromio(struct kfifo *fifo, const void __iomem *src, unsigned int len); - #define avs_log_buffer_size(adev) \ ((adev)->fw_cfg.trace_log_bytes / (adev)->hw_cfg.dsp_cores) @@ -356,9 +344,9 @@ static inline int avs_log_buffer_status_locked(struct avs_dev *adev, union avs_n unsigned long flags; int ret; - spin_lock_irqsave(&adev->dbg.trace_lock, flags); + spin_lock_irqsave(&adev->trace_lock, flags); ret = avs_dsp_op(adev, log_buffer_status, msg); - spin_unlock_irqrestore(&adev->dbg.trace_lock, flags); + spin_unlock_irqrestore(&adev->trace_lock, flags); return ret; } diff --git a/sound/soc/intel/avs/debugfs.c b/sound/soc/intel/avs/debugfs.c index ac3889e21542..78705bcb09fb 100644 --- a/sound/soc/intel/avs/debugfs.c +++ b/sound/soc/intel/avs/debugfs.c @@ -11,6 +11,24 @@ #include #include "avs.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); diff --git a/sound/soc/intel/avs/skl.c b/sound/soc/intel/avs/skl.c index c5edb0b0df14..6bb8bbc70442 100644 --- a/sound/soc/intel/avs/skl.c +++ b/sound/soc/intel/avs/skl.c @@ -59,7 +59,7 @@ skl_log_buffer_status(struct avs_dev *adev, union avs_notify_msg *msg) void __iomem *buf; u16 size, write, offset; - if (!kfifo_initialized(&adev->dbg.trace_fifo)) + if (!avs_logging_fw(adev)) return 0; size = avs_log_buffer_size(adev) / 2; @@ -69,8 +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(&adev->dbg.trace_fifo, buf, size); - wake_up(&adev->dbg.trace_waitq); + avs_dump_fw_log_wakeup(adev, buf, size); return 0; } diff --git a/sound/soc/intel/avs/utils.c b/sound/soc/intel/avs/utils.c index 75ad434d7dfb..82416b86662d 100644 --- a/sound/soc/intel/avs/utils.c +++ b/sound/soc/intel/avs/utils.c @@ -300,21 +300,3 @@ void avs_release_firmwares(struct avs_dev *adev) kfree(entry); } } - -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; -} -- cgit v1.2.3 From dab8d000e25c3e91154efca287434a4f78ab65d2 Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Fri, 2 Dec 2022 16:28:34 +0100 Subject: ASoC: Intel: avs: Add data probing requests Data probing is a cAVS firmware functionality that allows for data extraction and injection directly from or to DMA stream. To support it, new functions and types are added. These facilitate communication with the firmware. Total of eight IPCs: - probe module initialization and cleanup - addition and removal of probe points - addition and removal of injection DMAs - dumping list of currently connected probe points or enlisted DMAs Signed-off-by: Cezary Rojewski Link: https://lore.kernel.org/r/20221202152841.672536-10-cezary.rojewski@intel.com Signed-off-by: Mark Brown --- sound/soc/intel/avs/Makefile | 2 +- sound/soc/intel/avs/messages.c | 78 ++++++++++++++++++++++++++++++++++++++++++ sound/soc/intel/avs/messages.h | 53 ++++++++++++++++++++++++++++ sound/soc/intel/avs/probes.c | 46 +++++++++++++++++++++++++ 4 files changed, 178 insertions(+), 1 deletion(-) create mode 100644 sound/soc/intel/avs/probes.c (limited to 'sound') diff --git a/sound/soc/intel/avs/Makefile b/sound/soc/intel/avs/Makefile index a211a0b7b4a8..1c6924a1ebca 100644 --- a/sound/soc/intel/avs/Makefile +++ b/sound/soc/intel/avs/Makefile @@ -10,7 +10,7 @@ snd-soc-avs-objs += trace.o CFLAGS_trace.o := -I$(src) ifneq ($(CONFIG_DEBUG_FS),) -snd-soc-avs-objs += debugfs.o +snd-soc-avs-objs += probes.o debugfs.o endif obj-$(CONFIG_SND_SOC_INTEL_AVS) += snd-soc-avs.o diff --git a/sound/soc/intel/avs/messages.c b/sound/soc/intel/avs/messages.c index f734d49e42be..e11ae4246416 100644 --- a/sound/soc/intel/avs/messages.c +++ b/sound/soc/intel/avs/messages.c @@ -722,4 +722,82 @@ int avs_ipc_set_system_time(struct avs_dev *adev) return avs_ipc_set_large_config(adev, AVS_BASEFW_MOD_ID, AVS_BASEFW_INST_ID, AVS_BASEFW_SYSTEM_TIME, (u8 *)&sys_time, sizeof(sys_time)); } + +int avs_ipc_probe_get_dma(struct avs_dev *adev, struct avs_probe_dma **dmas, size_t *num_dmas) +{ + 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_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/probes.c b/sound/soc/intel/avs/probes.c new file mode 100644 index 000000000000..339bad6fec22 --- /dev/null +++ b/sound/soc/intel/avs/probes.c @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// Copyright(c) 2021-2022 Intel Corporation. All rights reserved. +// +// Authors: Cezary Rojewski +// Amadeusz Slawinski +// + +#include "avs.h" +#include "messages.h" + +__maybe_unused +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); +} + +__maybe_unused +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); +} -- cgit v1.2.3 From 700462f55493c6831ad71b209eaebe310dcf11fd Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Fri, 2 Dec 2022 16:28:35 +0100 Subject: ASoC: Intel: avs: Probe compress operations Add compress operations handlers for data extraction through probes. A single HDAudio stream is enlisted for said purpose. Operations follow same protocol as for standard PCM streaming on HOST side. Signed-off-by: Cezary Rojewski Link: https://lore.kernel.org/r/20221202152841.672536-11-cezary.rojewski@intel.com Signed-off-by: Mark Brown --- sound/soc/intel/Kconfig | 1 + sound/soc/intel/avs/avs.h | 3 + sound/soc/intel/avs/probes.c | 224 ++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 225 insertions(+), 3 deletions(-) (limited to 'sound') 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/avs.h b/sound/soc/intel/avs/avs.h index 7a9fb27d3845..e5e7c72eb511 100644 --- a/sound/soc/intel/avs/avs.h +++ b/sound/soc/intel/avs/avs.h @@ -144,6 +144,9 @@ struct avs_dev { u32 aging_timer_period; u32 fifo_full_timer_period; u32 logged_resources; /* context dependent: core or library */ + /* probes */ + struct hdac_ext_stream *extractor; + unsigned int num_probe_streams; #endif }; diff --git a/sound/soc/intel/avs/probes.c b/sound/soc/intel/avs/probes.c index 339bad6fec22..e90284ec8500 100644 --- a/sound/soc/intel/avs/probes.c +++ b/sound/soc/intel/avs/probes.c @@ -6,13 +6,15 @@ // Amadeusz Slawinski // +#include +#include +#include +#include #include "avs.h" #include "messages.h" -__maybe_unused 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; @@ -34,7 +36,6 @@ static int avs_dsp_init_probe(struct avs_dev *adev, union avs_connector_node_id sizeof(cfg), &dummy); } -__maybe_unused static void avs_dsp_delete_probe(struct avs_dev *adev) { struct avs_module_entry mentry; @@ -44,3 +45,220 @@ static void avs_dsp_delete_probe(struct avs_dev *adev) /* 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; +} + +__maybe_unused +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, +}; + +__maybe_unused +static const struct snd_compress_ops avs_probe_compress_ops = { + .copy = avs_probe_compr_copy, +}; -- cgit v1.2.3 From ed914a2a45a45e7d8f900ae8997ca4573792afcc Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Fri, 2 Dec 2022 16:28:36 +0100 Subject: ASoC: Intel: avs: Data probing soc-component Define stub component for data probing. Stub as most operations from standard PCM case do not apply here. Specific bits are CPU DAIs and compress_ops. FE DAIs can link against these new CPU DAI to create new compress devices. Signed-off-by: Cezary Rojewski Link: https://lore.kernel.org/r/20221202152841.672536-12-cezary.rojewski@intel.com Signed-off-by: Mark Brown --- sound/soc/intel/avs/avs.h | 10 +++++++++ sound/soc/intel/avs/pcm.c | 6 ++--- sound/soc/intel/avs/probes.c | 53 ++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 64 insertions(+), 5 deletions(-) (limited to 'sound') diff --git a/sound/soc/intel/avs/avs.h b/sound/soc/intel/avs/avs.h index e5e7c72eb511..e19d8d89455d 100644 --- a/sound/soc/intel/avs/avs.h +++ b/sound/soc/intel/avs/avs.h @@ -322,6 +322,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); @@ -373,6 +376,8 @@ struct apl_log_buffer_layout { 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); #else #define AVS_SET_ENABLE_LOGS_OP(name) @@ -389,6 +394,11 @@ 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; +} #endif #endif /* __SOUND_SOC_INTEL_AVS_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 index e90284ec8500..29d63f2a9616 100644 --- a/sound/soc/intel/avs/probes.c +++ b/sound/soc/intel/avs/probes.c @@ -249,7 +249,6 @@ static int avs_probe_compr_copy(struct snd_soc_component *comp, struct snd_compr return count; } -__maybe_unused static const struct snd_soc_cdai_ops avs_probe_dai_ops = { .startup = avs_probe_compr_open, .shutdown = avs_probe_compr_free, @@ -258,7 +257,57 @@ static const struct snd_soc_cdai_ops avs_probe_dai_ops = { .pointer = avs_probe_compr_pointer, }; -__maybe_unused 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)); +} -- cgit v1.2.3 From e17527e167ae5bd71fc9cb67da4e73bbb050e6f7 Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Fri, 2 Dec 2022 16:28:37 +0100 Subject: ASoC: Intel: avs: Add probe machine board Stub machine board driver with no custom DAPM routes and single FE DAI link for userspace to interact with. Signed-off-by: Cezary Rojewski Link: https://lore.kernel.org/r/20221202152841.672536-13-cezary.rojewski@intel.com Signed-off-by: Mark Brown --- sound/soc/intel/avs/board_selection.c | 33 ++++++++++++++++++ sound/soc/intel/avs/boards/Kconfig | 8 +++++ sound/soc/intel/avs/boards/Makefile | 2 ++ sound/soc/intel/avs/boards/probe.c | 64 +++++++++++++++++++++++++++++++++++ 4 files changed, 107 insertions(+) create mode 100644 sound/soc/intel/avs/boards/probe.c (limited to 'sound') 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 +// Amadeusz Slawinski +// + +#include +#include +#include +#include + +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"); -- cgit v1.2.3 From 5a565ba23abe478f3d4c3b0c8798bcb5215b82f5 Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Fri, 2 Dec 2022 16:28:38 +0100 Subject: ASoC: Intel: avs: Probing and firmware tracing over debugfs Define debugfs subdirectory delegated for IPC communication with DSP. Input format: uint,uint,(...) which are later translated into DWORDS sequence and further into instances of struct of interest given the IPC type. For Extractor probes, following have been enabled: - PROBE_POINT_ADD (echo <..> probe_points) - PROBE_POINT_REMOVE (echo <..> probe_points_remove) - PROBE_POINT_INFO (cat probe_points) Signed-off-by: Cezary Rojewski Link: https://lore.kernel.org/r/20221202152841.672536-14-cezary.rojewski@intel.com Signed-off-by: Mark Brown --- sound/soc/intel/avs/avs.h | 7 + sound/soc/intel/avs/core.c | 2 + sound/soc/intel/avs/debugfs.c | 326 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 335 insertions(+) (limited to 'sound') diff --git a/sound/soc/intel/avs/avs.h b/sound/soc/intel/avs/avs.h index e19d8d89455d..d7fccdcb9c16 100644 --- a/sound/soc/intel/avs/avs.h +++ b/sound/soc/intel/avs/avs.h @@ -144,6 +144,7 @@ struct avs_dev { 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; @@ -378,6 +379,9 @@ void avs_dump_fw_log(struct avs_dev *adev, const void __iomem *src, unsigned int 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) @@ -399,6 +403,9 @@ static inline int avs_probe_platform_register(struct avs_dev *adev, const char * { 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/core.c b/sound/soc/intel/avs/core.c index f7bc06404dbc..2ca24273c491 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); @@ -491,6 +492,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); diff --git a/sound/soc/intel/avs/debugfs.c b/sound/soc/intel/avs/debugfs.c index 78705bcb09fb..678572ee6b9d 100644 --- a/sound/soc/intel/avs/debugfs.c +++ b/sound/soc/intel/avs/debugfs.c @@ -9,7 +9,10 @@ #include #include #include +#include +#include #include "avs.h" +#include "messages.h" static unsigned int __kfifo_fromio(struct kfifo *fifo, const void __iomem *src, unsigned int len) { @@ -44,3 +47,326 @@ void avs_dump_fw_log_wakeup(struct avs_dev *adev, const void __iomem *src, unsig avs_dump_fw_log(adev, src, len); wake_up(&adev->trace_waitq); } + +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) +{ + struct avs_dev *adev = file->private_data; + unsigned long flags; + + spin_lock_irqsave(&adev->trace_lock, flags); + 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_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); +} -- cgit v1.2.3 From 34d27c71707c4ed615105376e0f3907d99b1b271 Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Fri, 2 Dec 2022 16:28:39 +0100 Subject: ASoC: Intel: avs: Gather remaining logs on strace_release() When user closes the tracer, some logs may still remain in the tail of the buffer as firmware sends LOG_BUFFER_STATUS notification only when certain threshold of data is reached. Add whatever is left to already gathered logs so no information is lost. Signed-off-by: Cezary Rojewski Link: https://lore.kernel.org/r/20221202152841.672536-15-cezary.rojewski@intel.com Signed-off-by: Mark Brown --- sound/soc/intel/avs/debugfs.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/intel/avs/debugfs.c b/sound/soc/intel/avs/debugfs.c index 678572ee6b9d..e7b0b99824aa 100644 --- a/sound/soc/intel/avs/debugfs.c +++ b/sound/soc/intel/avs/debugfs.c @@ -201,11 +201,25 @@ static int strace_open(struct inode *inode, struct file *file) 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 flags; + 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; -- cgit v1.2.3 From 870f6e5abba95ac78e750b61cf8f3f15be96796f Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Fri, 2 Dec 2022 16:28:40 +0100 Subject: ASoC: Intel: avs: Allow for dumping FW_REGS area SRAM0 window begins with a block of memory, usually of size PAGE_SIZE, dedicated to the base firmware registers. When debugging firmware, it is desirable to be able to dump them at will. Signed-off-by: Cezary Rojewski Link: https://lore.kernel.org/r/20221202152841.672536-16-cezary.rojewski@intel.com Signed-off-by: Mark Brown --- sound/soc/intel/avs/debugfs.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) (limited to 'sound') diff --git a/sound/soc/intel/avs/debugfs.c b/sound/soc/intel/avs/debugfs.c index e7b0b99824aa..e9042d4328c4 100644 --- a/sound/soc/intel/avs/debugfs.c +++ b/sound/soc/intel/avs/debugfs.c @@ -48,6 +48,29 @@ void avs_dump_fw_log_wakeup(struct avs_dev *adev, const void __iomem *src, unsig 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 probe_points_read(struct file *file, char __user *to, size_t count, loff_t *ppos) { struct avs_dev *adev = file->private_data; @@ -369,6 +392,7 @@ void avs_debugfs_init(struct avs_dev *adev) 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_u32("trace_aging_period", 0644, adev->debugfs_root, &adev->aging_timer_period); -- cgit v1.2.3 From 85ac9c8c8eed76e8a320a9e017c6d36e2a52745b Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Fri, 2 Dec 2022 16:28:41 +0100 Subject: ASoC: Intel: avs: Allow for dumping debug window snapshot Add new read-only debugfs entry which dumps entire content of the SRAM window 2 i.e.: the debug window. Signed-off-by: Cezary Rojewski Link: https://lore.kernel.org/r/20221202152841.672536-17-cezary.rojewski@intel.com Signed-off-by: Mark Brown --- sound/soc/intel/avs/debugfs.c | 26 ++++++++++++++++++++++++++ sound/soc/intel/avs/registers.h | 3 ++- 2 files changed, 28 insertions(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/intel/avs/debugfs.c b/sound/soc/intel/avs/debugfs.c index e9042d4328c4..bdd388ec01ea 100644 --- a/sound/soc/intel/avs/debugfs.c +++ b/sound/soc/intel/avs/debugfs.c @@ -71,6 +71,31 @@ static const struct file_operations fw_regs_fops = { .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; @@ -393,6 +418,7 @@ void avs_debugfs_init(struct avs_dev *adev) 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); 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 -- cgit v1.2.3 From 1b41beaa7a58467505ec3023af8aad74f878b888 Mon Sep 17 00:00:00 2001 From: Yang Yingliang Date: Mon, 5 Dec 2022 22:37:21 +0800 Subject: ASoC: sof_es8336: fix possible use-after-free in sof_es8336_remove() sof_es8336_remove() calls cancel_delayed_work(). However, that function does not wait until the work function finishes. This means that the callback function may still be running after the driver's remove function has finished, which would result in a use-after-free. Fix by calling cancel_delayed_work_sync(), which ensures that the work is properly cancelled, no longer running, and unable to re-schedule itself. Fixes: 89cdb224f2ab ("ASoC: sof_es8336: reduce pop noise on speaker") Signed-off-by: Yang Yingliang Acked-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20221205143721.3988988-1-yangyingliang@huawei.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/sof_es8336.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/intel/boards/sof_es8336.c b/sound/soc/intel/boards/sof_es8336.c index 70713e4b07dc..773e5d1d87d4 100644 --- a/sound/soc/intel/boards/sof_es8336.c +++ b/sound/soc/intel/boards/sof_es8336.c @@ -783,7 +783,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); -- cgit v1.2.3 From 72d9a541d7f186f0ec97c71ba7e477dd9bf4155f Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Mon, 5 Dec 2022 09:53:25 +0100 Subject: ASoC: Intel: Skylake: Update pipe_config_idx before filling BE params Without updating the index before BE copier config is filled with hardware parameters, outdated parameters are used instead. Signed-off-by: Cezary Rojewski Tested-by: Lukasz Majczak Link: https://lore.kernel.org/r/20221205085330.857665-2-cezary.rojewski@intel.com Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl-topology.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index e06eac592da1..fc3d719d93e1 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -1837,16 +1837,24 @@ 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->pipe_config_idx); if (pipe->direction == SNDRV_PCM_STREAM_PLAYBACK) pipe_fmt = &pipe->configs[pipe->pipe_config_idx].out_fmt; else @@ -1865,10 +1873,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, -- cgit v1.2.3 From b0d16e54e7559f2055123ea7b1d9ff1bb808ebad Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Mon, 5 Dec 2022 09:53:26 +0100 Subject: ASoC: Intel: Skylake: Remove skl_tplg_is_multi_fmt() Rather than forcing userspace to select proper format with enumerable kcontrols, select it ourselves based on provided hw_params. Signed-off-by: Cezary Rojewski Tested-by: Lukasz Majczak Link: https://lore.kernel.org/r/20221205085330.857665-3-cezary.rojewski@intel.com Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl-topology.c | 40 ---------------------------------- 1 file changed, 40 deletions(-) (limited to 'sound') diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index fc3d719d93e1..f144acae1b44 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -582,38 +582,6 @@ 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) -{ - 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; -} - /* * Here, we select pipe format based on the pipe type and pipe * direction to determine the current config index for the pipeline. @@ -636,14 +604,6 @@ skl_tplg_get_pipe_config(struct skl_dev *skl, struct skl_module_cfg *mconfig) 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); - 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); -- cgit v1.2.3 From 75ab3c00769009e32e5cf51c8b503de4f73114e4 Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Mon, 5 Dec 2022 09:53:27 +0100 Subject: ASoC: Intel: Skylake: Drop pipe_config_idx Field ->pipe_config_idx duplicates the job of ->cur_config_idx so remove it. Signed-off-by: Cezary Rojewski Tested-by: Lukasz Majczak Link: https://lore.kernel.org/r/20221205085330.857665-4-cezary.rojewski@intel.com Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl-topology.c | 10 +++++----- sound/soc/intel/skylake/skl-topology.h | 1 - 2 files changed, 5 insertions(+), 6 deletions(-) (limited to 'sound') diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index f144acae1b44..567a3b661ce4 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -1351,9 +1351,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]; + pipe->cur_config_idx = 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; } @@ -1814,11 +1814,11 @@ static int skl_tplg_be_fill_pipe_params(struct snd_soc_dai *dai, if (ret) goto err; - dev_dbg(skl->dev, "%s using pipe config: %d\n", __func__, pipe->pipe_config_idx); + 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, 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 { -- cgit v1.2.3 From 4ac587f3578c5ca490e4df55af6403f5474eb2f0 Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Mon, 5 Dec 2022 09:53:28 +0100 Subject: ASoC: Intel: Skylake: Introduce single place for pipe-config selection Provide a single location for pipe config selection where all fields that have to be updated whenever ->pipe_config_idx changes can be updated accordingly. Signed-off-by: Cezary Rojewski Tested-by: Lukasz Majczak Link: https://lore.kernel.org/r/20221205085330.857665-5-cezary.rojewski@intel.com Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl-topology.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) (limited to 'sound') diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index 567a3b661ce4..b20643b83401 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -582,6 +582,12 @@ static int skl_tplg_unload_pipe_modules(struct skl_dev *skl, return ret; } +static void skl_tplg_set_pipe_config_idx(struct skl_pipe *pipe, int idx) +{ + pipe->cur_config_idx = idx; + pipe->memory_pages = pipe->configs[idx].mem_pages; +} + /* * Here, we select pipe format based on the pipe type and pipe * direction to determine the current config index for the pipeline. @@ -600,16 +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; + 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; } @@ -628,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; } } @@ -1351,7 +1353,7 @@ static int skl_tplg_multi_config_set_get(struct snd_kcontrol *kcontrol, return -EIO; if (is_set) - pipe->cur_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->cur_config_idx; -- cgit v1.2.3 From 171107237246d66bce04f3769d33648f896b4ce3 Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Mon, 5 Dec 2022 09:53:29 +0100 Subject: ASoC: Intel: Skylake: Fix driver hang during shutdown AudioDSP cores and HDAudio links need to be turned off on shutdown to ensure no communication or data transfer occurs during the procedure. Fixes: c5a76a246989 ("ASoC: Intel: Skylake: Add shutdown callback") Signed-off-by: Cezary Rojewski Tested-by: Lukasz Majczak Link: https://lore.kernel.org/r/20221205085330.857665-6-cezary.rojewski@intel.com Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'sound') 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); -- cgit v1.2.3 From 451d85c46cf719a09a052510d4d4cd920103163a Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Mon, 5 Dec 2022 09:53:30 +0100 Subject: ASoC: Intel: Skylake: Use SG allocation for SKL-based firmware load Resign from ->alloc_dma_buf() and use snd_dma_alloc_pages() directly. For data i.e.: base firmware binary transfer, make use of SG allocation to better adapt to memory-limited environment. For BDL descriptor, given its small size this is not required. Signed-off-by: Cezary Rojewski Tested-by: Lukasz Majczak Link: https://lore.kernel.org/r/20221205085330.857665-7-cezary.rojewski@intel.com Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl-sst-cldma.c | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) (limited to 'sound') 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 #include #include +#include #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); -- cgit v1.2.3 From c0660fce5e0672b9fcffaae02184d58c8ed2aec1 Mon Sep 17 00:00:00 2001 From: Brent Lu Date: Tue, 6 Dec 2022 15:25:05 -0600 Subject: ASoC: Intel: sof_rt5682: add jsl_rt5682 board config This configuration supports JSL boards which implement ALC5682I-VD/VS on SSP0 port. Reviewed-by: Bard Liao Reviewed-by: Ranjani Sridharan Signed-off-by: Brent Lu Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20221206212507.359993-2-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/sof_rt5682.c | 6 ++++++ sound/soc/intel/common/soc-acpi-intel-jsl-match.c | 5 +++++ 2 files changed, 11 insertions(+) (limited to 'sound') diff --git a/sound/soc/intel/boards/sof_rt5682.c b/sound/soc/intel/boards/sof_rt5682.c index 4a2f91249b10..2eabc4b0fafa 100644 --- a/sound/soc/intel/boards/sof_rt5682.c +++ b/sound/soc/intel/boards/sof_rt5682.c @@ -1104,6 +1104,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/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 @@ -78,6 +78,11 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_jsl_machines[] = { .quirk_data = &mx98360a_spk, .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", -- cgit v1.2.3 From 5c10da436ebd93f9bfa244ea933773d14b566499 Mon Sep 17 00:00:00 2001 From: Gongjun Song Date: Tue, 6 Dec 2022 15:25:06 -0600 Subject: ASoC: Intel: sof_sdw: use common helpers for all Realtek amps sof_sdw_rt1308.c/sof_sdw_rt1316.c/sof_sdw_rt1318.c handle amp in basically the same way, optimized and merged into one file. Reviewed-by: Ranjani Sridharan Reviewed-by: Bard Liao Signed-off-by: Gongjun Song Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20221206212507.359993-3-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/Makefile | 3 +- sound/soc/intel/boards/sof_sdw.c | 10 +- sound/soc/intel/boards/sof_sdw_common.h | 20 +- sound/soc/intel/boards/sof_sdw_rt1308.c | 278 ------------------------- sound/soc/intel/boards/sof_sdw_rt1316.c | 239 --------------------- sound/soc/intel/boards/sof_sdw_rt1318.c | 120 ----------- sound/soc/intel/boards/sof_sdw_rt_amp.c | 359 ++++++++++++++++++++++++++++++++ 7 files changed, 369 insertions(+), 660 deletions(-) delete mode 100644 sound/soc/intel/boards/sof_sdw_rt1308.c delete mode 100644 sound/soc/intel/boards/sof_sdw_rt1316.c delete mode 100644 sound/soc/intel/boards/sof_sdw_rt1318.c create mode 100644 sound/soc/intel/boards/sof_sdw_rt_amp.c (limited to 'sound') 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/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_rt1308.c b/sound/soc/intel/boards/sof_sdw_rt1308.c deleted file mode 100644 index a19b055b9c6f..000000000000 --- a/sound/soc/intel/boards/sof_sdw_rt1308.c +++ /dev/null @@ -1,278 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -// Copyright (c) 2020 Intel Corporation - -/* - * sof_sdw_rt1308 - Helpers to handle RT1308 from generic machine driver - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "sof_sdw_common.h" -#include "sof_sdw_amp_coeff_tables.h" -#include "../../codecs/rt1308.h" - -struct rt1308_platform_data { - const unsigned char *bq_params; - const unsigned int bq_params_cnt; -}; - -static const struct rt1308_platform_data dell_0a5d_platform_data = { - .bq_params = dell_0a5d_bq_params, - .bq_params_cnt = ARRAY_SIZE(dell_0a5d_bq_params), -}; - -static const struct dmi_system_id dmi_platform_data[] = { - /* CometLake devices */ - { - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), - DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0990") - }, - .driver_data = (void *)&dell_0a5d_platform_data, - }, - { - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), - DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "098F") - }, - .driver_data = (void *)&dell_0a5d_platform_data, - }, - /* TigerLake devices */ - { - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), - DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0A5D") - }, - .driver_data = (void *)&dell_0a5d_platform_data, - }, - { - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), - DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0A5E") - }, - .driver_data = (void *)&dell_0a5d_platform_data, - }, -}; - -static int rt1308_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]; - int ret; - - dmi_data = dmi_first_match(dmi_platform_data); - if (!dmi_data) - return 0; - - pdata = dmi_data->driver_data; - memcpy(¶ms, 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 rt1308_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. - */ -static const struct snd_soc_dapm_route rt1308_map[] = { - { "Speaker", NULL, "rt1308-1 SPOL" }, - { "Speaker", NULL, "rt1308-1 SPOR" }, - { "Speaker", NULL, "rt1308-2 SPOL" }, - { "Speaker", NULL, "rt1308-2 SPOR" }, -}; - -static const struct snd_kcontrol_new rt1308_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:rt1308", - card->components); - if (!card->components) - return -ENOMEM; - - ret = snd_soc_add_card_controls(card, rt1308_controls, - ARRAY_SIZE(rt1308_controls)); - if (ret) { - dev_err(card->dev, "rt1308 controls addition failed: %d\n", ret); - return ret; - } - - ret = snd_soc_dapm_new_controls(&card->dapm, rt1308_widgets, - ARRAY_SIZE(rt1308_widgets)); - if (ret) { - dev_err(card->dev, "rt1308 widgets addition failed: %d\n", ret); - return ret; - } - - ret = snd_soc_dapm_add_routes(&card->dapm, rt1308_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, rt1308_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); -} - -static int rt1308_i2s_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_card *card = rtd->card; - struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); - int clk_id, clk_freq, pll_out; - int err; - - clk_id = RT1308_PLL_S_MCLK; - clk_freq = 38400000; - - pll_out = params_rate(params) * 512; - - /* Set rt1308 pll */ - err = snd_soc_dai_set_pll(codec_dai, 0, clk_id, clk_freq, pll_out); - if (err < 0) { - dev_err(card->dev, "Failed to set RT1308 PLL: %d\n", err); - return err; - } - - /* Set rt1308 sysclk */ - err = snd_soc_dai_set_sysclk(codec_dai, RT1308_FS_SYS_S_PLL, pll_out, - SND_SOC_CLOCK_IN); - if (err < 0) { - dev_err(card->dev, "Failed to set RT1308 SYSCLK: %d\n", err); - return err; - } - - return 0; -} - -/* machine stream operations */ -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) -{ - 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_rt1308_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 = rt1308_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 = rt1308_add_device_props(sdw_dev2); - if (ret < 0) { - put_device(sdw_dev2); - return ret; - } - ctx->amp_dev2 = sdw_dev2; - - /* - * if two 1308s 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_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 -#include -#include -#include -#include -#include -#include -#include -#include -#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(¶ms, 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 -#include -#include -#include -#include -#include -#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_rt_amp.c b/sound/soc/intel/boards/sof_sdw_rt_amp.c new file mode 100644 index 000000000000..26bf9e0dd3d2 --- /dev/null +++ b/sound/soc/intel/boards/sof_sdw_rt_amp.c @@ -0,0 +1,359 @@ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (c) 2022 Intel Corporation + +/* + * sof_sdw_rt_amp - Helpers to handle RT1308/RT1316/RT1318 from generic machine driver + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "sof_sdw_common.h" +#include "sof_sdw_amp_coeff_tables.h" +#include "../../codecs/rt1308.h" + +#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 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 */ + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), + DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0990") + }, + .driver_data = (void *)&dell_0a5d_platform_data, + }, + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), + DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "098F") + }, + .driver_data = (void *)&dell_0a5d_platform_data, + }, + /* TigerLake devices */ + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), + DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0A5D") + }, + .driver_data = (void *)&dell_0a5d_platform_data, + }, + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), + DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0A5E") + }, + .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 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 rt_amp_platform_data *pdata; + unsigned char params[RT_AMP_MAX_BQ_REG]; + int ret; + + dmi_data = dmi_first_match(dmi_platform_data); + if (!dmi_data) + return 0; + + pdata = dmi_data->driver_data; + memcpy(¶ms, 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_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/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" }, + { "Speaker", NULL, "rt1308-1 SPOR" }, + { "Speaker", NULL, "rt1308-2 SPOL" }, + { "Speaker", NULL, "rt1308-2 SPOR" }, +}; + +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:%s", + card->components, codec_name); + if (!card->components) + return -ENOMEM; + + ret = snd_soc_add_card_controls(card, rt_amp_controls, + ARRAY_SIZE(rt_amp_controls)); + if (ret) { + dev_err(card->dev, "%s controls addition failed: %d\n", codec_name, ret); + return ret; + } + + ret = snd_soc_dapm_new_controls(&card->dapm, rt_amp_widgets, + ARRAY_SIZE(rt_amp_widgets)); + if (ret) { + dev_err(card->dev, "%s widgets addition failed: %d\n", codec_name, ret); + return ret; + } + + 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); + + return ret; +} + +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; + + 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); + + 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); +} + +static int rt1308_i2s_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_card *card = rtd->card; + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); + int clk_id, clk_freq, pll_out; + int err; + + clk_id = RT1308_PLL_S_MCLK; + clk_freq = 38400000; + + pll_out = params_rate(params) * 512; + + /* Set rt1308 pll */ + err = snd_soc_dai_set_pll(codec_dai, 0, clk_id, clk_freq, pll_out); + if (err < 0) { + dev_err(card->dev, "Failed to set RT1308 PLL: %d\n", err); + return err; + } + + /* Set rt1308 sysclk */ + err = snd_soc_dai_set_sysclk(codec_dai, RT1308_FS_SYS_S_PLL, pll_out, + SND_SOC_CLOCK_IN); + if (err < 0) { + dev_err(card->dev, "Failed to set RT1308 SYSCLK: %d\n", err); + return err; + } + + return 0; +} + +/* machine stream operations */ +struct snd_soc_ops sof_sdw_rt1308_i2s_ops = { + .hw_params = rt1308_i2s_hw_params, +}; + +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); + + 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_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) +{ + 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 = rt_amp_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 = rt_amp_add_device_props(sdw_dev2); + if (ret < 0) { + put_device(sdw_dev2); + return ret; + } + ctx->amp_dev2 = sdw_dev2; + + /* + * 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. + */ + if (dai_links->init) + dai_links->init = all_spk_init; + else + dai_links->init = second_spk_init; + } + + return 0; +} -- cgit v1.2.3 From 47d2b66fec133cb27da3a551334686e465d19469 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Tue, 6 Dec 2022 15:25:07 -0600 Subject: ASoC: Intel: sof_realtek_common: set ret = 0 as initial value 'ret' will not be initialized if dai_fmt is not DSP_A or DSP_B. Reviewed-by: Ranjani Sridharan Signed-off-by: Bard Liao Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20221206212507.359993-4-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/sof_realtek_common.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'sound') 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); -- cgit v1.2.3 From 3327d721114c109ba0575f86f8fda3b525404054 Mon Sep 17 00:00:00 2001 From: Wang Yufen Date: Mon, 5 Dec 2022 18:04:24 +0800 Subject: ASoC: mediatek: mt8173-rt5650-rt5514: fix refcount leak in mt8173_rt5650_rt5514_dev_probe() The node returned by of_parse_phandle() with refcount incremented, of_node_put() needs be called when finish using it. So add it in the error path in mt8173_rt5650_rt5514_dev_probe(). Fixes: 0d1d7a664288 ("ASoC: mediatek: Refine mt8173 driver and change config option") Signed-off-by: Wang Yufen Link: https://lore.kernel.org/r/1670234664-24246-1-git-send-email-wangyufen@huawei.com Signed-off-by: Mark Brown --- sound/soc/mediatek/mt8173/mt8173-rt5650-rt5514.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'sound') 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; } -- cgit v1.2.3 From 8ab2d12c726f0fde0692fa5d81d8019b3dcd62d0 Mon Sep 17 00:00:00 2001 From: Wang Yufen Date: Mon, 5 Dec 2022 16:15:27 +0800 Subject: ASoC: audio-graph-card: fix refcount leak of cpu_ep in __graph_for_each_link() The of_get_next_child() returns a node with refcount incremented, and decrements the refcount of prev. So in the error path of the while loop, of_node_put() needs be called for cpu_ep. Fixes: fce9b90c1ab7 ("ASoC: audio-graph-card: cleanup DAI link loop method - step2") Signed-off-by: Wang Yufen Acked-by: Kuninori Morimoto Link: https://lore.kernel.org/r/1670228127-13835-1-git-send-email-wangyufen@huawei.com Signed-off-by: Mark Brown --- sound/soc/generic/audio-graph-card.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'sound') 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; } -- cgit v1.2.3 From ef0a098efb36660326c133af9b5a04a96a00e3ca Mon Sep 17 00:00:00 2001 From: Wang Jingjin Date: Mon, 5 Dec 2022 11:28:02 +0800 Subject: ASoC: rockchip: pdm: Add missing clk_disable_unprepare() in rockchip_pdm_runtime_resume() The clk_disable_unprepare() should be called in the error handling of rockchip_pdm_runtime_resume(). Fixes: fc05a5b22253 ("ASoC: rockchip: add support for pdm controller") Signed-off-by: Wang Jingjin Link: https://lore.kernel.org/r/20221205032802.2422983-1-wangjingjin1@huawei.com Signed-off-by: Mark Brown --- sound/soc/rockchip/rockchip_pdm.c | 1 + 1 file changed, 1 insertion(+) (limited to 'sound') 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; } -- cgit v1.2.3 From a39bc7cf8e284653fb6fd9d897f269f4ac80cf52 Mon Sep 17 00:00:00 2001 From: ye xingchen Date: Mon, 5 Dec 2022 19:43:47 +0800 Subject: ASoC: imx-audmux: use sysfs_emit() to instead of scnprintf() Follow the advice of the Documentation/filesystems/sysfs.rst and show() should only use sysfs_emit() or sysfs_emit_at() when formatting the value to be returned to user space. Signed-off-by: ye xingchen Link: https://lore.kernel.org/r/202212051943476482106@zte.com.cn Signed-off-by: Mark Brown --- sound/soc/fsl/imx-audmux.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'sound') 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, -- cgit v1.2.3 From 38eef3be38ab895959c442702864212cc3beb96c Mon Sep 17 00:00:00 2001 From: Wang Yufen Date: Mon, 5 Dec 2022 17:56:28 +0800 Subject: ASoC: mediatek: mt8183: fix refcount leak in mt8183_mt6358_ts3a227_max98357_dev_probe() The node returned by of_parse_phandle() with refcount incremented, of_node_put() needs be called when finish using it. So add it in the error path in mt8183_mt6358_ts3a227_max98357_dev_probe(). Fixes: 11c0269017b2 ("ASoC: Mediatek: MT8183: Add machine driver with TS3A227") Signed-off-by: Wang Yufen Link: https://lore.kernel.org/r/1670234188-23596-1-git-send-email-wangyufen@huawei.com Signed-off-by: Mark Brown --- sound/soc/mediatek/mt8183/mt8183-mt6358-ts3a227-max98357.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) (limited to 'sound') 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); -- cgit v1.2.3 From 81ed7d9de18768fe0cb3d74a7a163a8c082e1346 Mon Sep 17 00:00:00 2001 From: Jiapeng Chong Date: Mon, 5 Dec 2022 15:35:07 +0800 Subject: ASoC: codecs: wcd-clsh: Remove the unused function The function wcd_clsh_set_buck_mode() is defined in the wcd-clsh-v2.c file, but not called elsewhere, so remove this unused function. sound/soc/codecs/wcd-clsh-v2.c:133:20: warning: unused function 'wcd_clsh_enable_status'. Link: https://bugzilla.openanolis.cn/show_bug.cgi?id=3348 Reported-by: Abaci Robot Signed-off-by: Jiapeng Chong Link: https://lore.kernel.org/r/20221205073507.36071-1-jiapeng.chong@linux.alibaba.com Signed-off-by: Mark Brown --- sound/soc/codecs/wcd-clsh-v2.c | 6 ------ 1 file changed, 6 deletions(-) (limited to 'sound') 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) { -- cgit v1.2.3 From 1da681e52853f0abfbfff8c69833d31e538ff9c0 Mon Sep 17 00:00:00 2001 From: Chancel Liu Date: Mon, 28 Nov 2022 14:09:50 +0800 Subject: ASoC: soc-pcm.c: Clear DAIs parameters after stream_active is updated DAIs parameters should be cleared if there's no active stream. Before, we implemented it in soc_pcm_hw_free() by detecting stream_active. If the running stream is the last active stream, we're going to clear parameters. However it will cause DAIs parameters never be cleared if there're more than one stream. For example, we have stream1 and stream2 about to stop. stream2 executes soc_pcm_hw_free() before stream1 executes soc_pcm_close(). At the moment, stream2 should clear DAIs parameters. Since stream_active is not yet updated by stream1 in soc_pcm_close(), stream2 will not clear DAIs parameters. In result both stream1 and stream2 don't clear the parameters. This patch moves DAIs parameters cleanup after stream_active is updated. Signed-off-by: Chancel Liu Link: https://lore.kernel.org/r/20221128060950.3540845-1-chancel.liu@nxp.com Signed-off-by: Mark Brown --- sound/soc/soc-pcm.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) (limited to 'sound') 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); -- cgit v1.2.3