Commit 8e378ea1 authored by Mark Brown's avatar Mark Brown

ASoC: Intel: avs: Data probing and fw logging

Merge series from Cezary Rojewski <cezary.rojewski@intel.com>:

The patchset focuses on debug functionality for the avs-driver.
Two major blocks are covered here: data probing and AudioDSP firmware
logging. Both are configured and controlled through debugfs.

Data probing is a AudioDSP debug functionality which allows for
gathering the actual data that is being routed to or from a module.
Helps in debugging its processing capabilities - navigate to a specific
module which may have caused a glitch within a pipeline (set of modules
bound together).

First few allow for assigning compress stream to a HDAudio stream, what
is currently limited to pcm substreams only. These patches were already
present on this list and reviewed in the past [1].

The next few tidy existing debug-related code up so it's ready for
addition of new functionalities and make it clear which part of the avs
is debug related and which is not. These also simplify the existing
locking around the trace fifo.

Afterward, debug-related IPCs are defined along with stub soc-component
and compress DAI operations. Not much is done there as it's not a
standard PCM streaming scenario. Most code found in compress operations
is inherited from the HOST side of HDAudio streaming found in pcm.c
file of the driver.

Finally, a debugfs file operations are defined. These facilitate
connecting to DSP modules from which the data shall be gathered as well
as control and configuration of firmware logging. Additionally, entries
are added to allow for dumping snapshots of key memory windows.
parents 1b41beaa 85ac9c8c
...@@ -75,6 +75,8 @@ struct hdac_ext_stream *snd_hdac_ext_stream_assign(struct hdac_bus *bus, ...@@ -75,6 +75,8 @@ struct hdac_ext_stream *snd_hdac_ext_stream_assign(struct hdac_bus *bus,
struct snd_pcm_substream *substream, struct snd_pcm_substream *substream,
int type); int type);
void snd_hdac_ext_stream_release(struct hdac_ext_stream *hext_stream, int type); void snd_hdac_ext_stream_release(struct hdac_ext_stream *hext_stream, int type);
struct hdac_ext_stream *snd_hdac_ext_cstream_assign(struct hdac_bus *bus,
struct snd_compr_stream *cstream);
void snd_hdac_ext_stream_decouple_locked(struct hdac_bus *bus, void snd_hdac_ext_stream_decouple_locked(struct hdac_bus *bus,
struct hdac_ext_stream *hext_stream, bool decouple); struct hdac_ext_stream *hext_stream, bool decouple);
void snd_hdac_ext_stream_decouple(struct hdac_bus *bus, void snd_hdac_ext_stream_decouple(struct hdac_bus *bus,
......
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
#include <sound/pcm.h> #include <sound/pcm.h>
#include <sound/hda_register.h> #include <sound/hda_register.h>
#include <sound/hdaudio_ext.h> #include <sound/hdaudio_ext.h>
#include <sound/compress_driver.h>
/** /**
* snd_hdac_ext_stream_init - initialize each stream (aka device) * 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) ...@@ -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); 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);
...@@ -578,8 +578,8 @@ int snd_hdac_bus_handle_stream_irq(struct hdac_bus *bus, unsigned int status, ...@@ -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); sd_status = snd_hdac_stream_readb(azx_dev, SD_STS);
snd_hdac_stream_writeb(azx_dev, SD_STS, SD_INT_MASK); snd_hdac_stream_writeb(azx_dev, SD_STS, SD_INT_MASK);
handled |= 1 << azx_dev->index; handled |= 1 << azx_dev->index;
if (!azx_dev->substream || !azx_dev->running || if ((!azx_dev->substream && !azx_dev->cstream) ||
!(sd_status & SD_INT_COMPLETE)) !azx_dev->running || !(sd_status & SD_INT_COMPLETE))
continue; continue;
if (ack) if (ack)
ack(bus, azx_dev); ack(bus, azx_dev);
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/export.h> #include <linux/export.h>
#include <linux/clocksource.h> #include <linux/clocksource.h>
#include <sound/compress_driver.h>
#include <sound/core.h> #include <sound/core.h>
#include <sound/pcm.h> #include <sound/pcm.h>
#include <sound/hdaudio.h> #include <sound/hdaudio.h>
...@@ -487,11 +488,20 @@ int snd_hdac_stream_setup_periods(struct hdac_stream *azx_dev) ...@@ -487,11 +488,20 @@ int snd_hdac_stream_setup_periods(struct hdac_stream *azx_dev)
{ {
struct hdac_bus *bus = azx_dev->bus; struct hdac_bus *bus = azx_dev->bus;
struct snd_pcm_substream *substream = azx_dev->substream; struct snd_pcm_substream *substream = azx_dev->substream;
struct snd_pcm_runtime *runtime = substream->runtime; struct snd_compr_stream *cstream = azx_dev->cstream;
struct snd_pcm_runtime *runtime = NULL;
struct snd_dma_buffer *dmab;
__le32 *bdl; __le32 *bdl;
int i, ofs, periods, period_bytes; int i, ofs, periods, period_bytes;
int pos_adj, pos_align; int pos_adj, pos_align;
if (substream) {
runtime = substream->runtime;
dmab = snd_pcm_get_dma_buf(substream);
} else if (cstream) {
dmab = snd_pcm_get_dma_buf(cstream);
}
/* reset BDL address */ /* reset BDL address */
snd_hdac_stream_writel(azx_dev, SD_BDLPL, 0); snd_hdac_stream_writel(azx_dev, SD_BDLPL, 0);
snd_hdac_stream_writel(azx_dev, SD_BDLPU, 0); snd_hdac_stream_writel(azx_dev, SD_BDLPU, 0);
...@@ -505,7 +515,7 @@ int snd_hdac_stream_setup_periods(struct hdac_stream *azx_dev) ...@@ -505,7 +515,7 @@ int snd_hdac_stream_setup_periods(struct hdac_stream *azx_dev)
azx_dev->frags = 0; azx_dev->frags = 0;
pos_adj = bus->bdl_pos_adj; 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_align = pos_adj;
pos_adj = DIV_ROUND_UP(pos_adj * runtime->rate, 48000); pos_adj = DIV_ROUND_UP(pos_adj * runtime->rate, 48000);
if (!pos_adj) if (!pos_adj)
...@@ -518,8 +528,7 @@ int snd_hdac_stream_setup_periods(struct hdac_stream *azx_dev) ...@@ -518,8 +528,7 @@ int snd_hdac_stream_setup_periods(struct hdac_stream *azx_dev)
pos_adj); pos_adj);
pos_adj = 0; pos_adj = 0;
} else { } else {
ofs = setup_bdle(bus, snd_pcm_get_dma_buf(substream), ofs = setup_bdle(bus, dmab, azx_dev,
azx_dev,
&bdl, ofs, pos_adj, true); &bdl, ofs, pos_adj, true);
if (ofs < 0) if (ofs < 0)
goto error; goto error;
...@@ -529,13 +538,11 @@ int snd_hdac_stream_setup_periods(struct hdac_stream *azx_dev) ...@@ -529,13 +538,11 @@ int snd_hdac_stream_setup_periods(struct hdac_stream *azx_dev)
for (i = 0; i < periods; i++) { for (i = 0; i < periods; i++) {
if (i == periods - 1 && pos_adj) if (i == periods - 1 && pos_adj)
ofs = setup_bdle(bus, snd_pcm_get_dma_buf(substream), ofs = setup_bdle(bus, dmab, azx_dev,
azx_dev, &bdl, ofs, &bdl, ofs, period_bytes - pos_adj, 0);
period_bytes - pos_adj, 0);
else else
ofs = setup_bdle(bus, snd_pcm_get_dma_buf(substream), ofs = setup_bdle(bus, dmab, azx_dev,
azx_dev, &bdl, ofs, &bdl, ofs, period_bytes,
period_bytes,
!azx_dev->no_period_wakeup); !azx_dev->no_period_wakeup);
if (ofs < 0) if (ofs < 0)
goto error; goto error;
...@@ -560,26 +567,32 @@ EXPORT_SYMBOL_GPL(snd_hdac_stream_setup_periods); ...@@ -560,26 +567,32 @@ EXPORT_SYMBOL_GPL(snd_hdac_stream_setup_periods);
int snd_hdac_stream_set_params(struct hdac_stream *azx_dev, int snd_hdac_stream_set_params(struct hdac_stream *azx_dev,
unsigned int format_val) unsigned int format_val)
{ {
unsigned int bufsize, period_bytes;
struct snd_pcm_substream *substream = azx_dev->substream; struct snd_pcm_substream *substream = azx_dev->substream;
struct snd_pcm_runtime *runtime; struct snd_compr_stream *cstream = azx_dev->cstream;
unsigned int bufsize, period_bytes;
unsigned int no_period_wakeup;
int err; 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; return -EINVAL;
runtime = substream->runtime; }
bufsize = snd_pcm_lib_buffer_bytes(substream);
period_bytes = snd_pcm_lib_period_bytes(substream);
if (bufsize != azx_dev->bufsize || if (bufsize != azx_dev->bufsize ||
period_bytes != azx_dev->period_bytes || period_bytes != azx_dev->period_bytes ||
format_val != azx_dev->format_val || 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->bufsize = bufsize;
azx_dev->period_bytes = period_bytes; azx_dev->period_bytes = period_bytes;
azx_dev->format_val = format_val; 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); err = snd_hdac_stream_setup_periods(azx_dev);
if (err < 0) if (err < 0)
return err; return err;
......
...@@ -217,6 +217,7 @@ config SND_SOC_INTEL_AVS ...@@ -217,6 +217,7 @@ config SND_SOC_INTEL_AVS
select SND_SOC_ACPI if ACPI select SND_SOC_ACPI if ACPI
select SND_SOC_TOPOLOGY select SND_SOC_TOPOLOGY
select SND_SOC_HDA select SND_SOC_HDA
select SND_SOC_COMPRESS if DEBUG_FS
select SND_HDA_EXT_CORE select SND_HDA_EXT_CORE
select SND_HDA_DSP_LOADER select SND_HDA_DSP_LOADER
select SND_INTEL_DSP_CONFIG select SND_INTEL_DSP_CONFIG
......
...@@ -9,6 +9,10 @@ snd-soc-avs-objs += trace.o ...@@ -9,6 +9,10 @@ snd-soc-avs-objs += trace.o
# tell define_trace.h where to find the trace header # tell define_trace.h where to find the trace header
CFLAGS_trace.o := -I$(src) CFLAGS_trace.o := -I$(src)
ifneq ($(CONFIG_DEBUG_FS),)
snd-soc-avs-objs += probes.o debugfs.o
endif
obj-$(CONFIG_SND_SOC_INTEL_AVS) += snd-soc-avs.o obj-$(CONFIG_SND_SOC_INTEL_AVS) += snd-soc-avs.o
# Machine support # Machine support
......
...@@ -13,8 +13,9 @@ ...@@ -13,8 +13,9 @@
#include "path.h" #include "path.h"
#include "topology.h" #include "topology.h"
static int apl_enable_logs(struct avs_dev *adev, enum avs_log_enable enable, u32 aging_period, static int __maybe_unused
u32 fifo_full_period, unsigned long resource_mask, u32 *priorities) 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; struct apl_log_state_info *info;
u32 size, num_cores = adev->hw_cfg.dsp_cores; u32 size, num_cores = adev->hw_cfg.dsp_cores;
...@@ -50,7 +51,6 @@ static int apl_enable_logs(struct avs_dev *adev, enum avs_log_enable enable, u32 ...@@ -50,7 +51,6 @@ static int apl_enable_logs(struct avs_dev *adev, enum avs_log_enable enable, u32
static int apl_log_buffer_status(struct avs_dev *adev, union avs_notify_msg *msg) static int apl_log_buffer_status(struct avs_dev *adev, union avs_notify_msg *msg)
{ {
struct apl_log_buffer_layout layout; struct apl_log_buffer_layout layout;
unsigned long flags;
void __iomem *addr, *buf; void __iomem *addr, *buf;
addr = avs_log_buffer_addr(adev, msg->log.core); addr = avs_log_buffer_addr(adev, msg->log.core);
...@@ -59,26 +59,20 @@ static int apl_log_buffer_status(struct avs_dev *adev, union avs_notify_msg *msg ...@@ -59,26 +59,20 @@ static int apl_log_buffer_status(struct avs_dev *adev, union avs_notify_msg *msg
memcpy_fromio(&layout, addr, sizeof(layout)); memcpy_fromio(&layout, addr, sizeof(layout));
spin_lock_irqsave(&adev->dbg.trace_lock, flags); if (!avs_logging_fw(adev))
if (!kfifo_initialized(&adev->dbg.trace_fifo))
/* consume the logs regardless of consumer presence */ /* consume the logs regardless of consumer presence */
goto update_read_ptr; goto update_read_ptr;
buf = apl_log_payload_addr(addr); buf = apl_log_payload_addr(addr);
if (layout.read_ptr > layout.write_ptr) { if (layout.read_ptr > layout.write_ptr) {
__kfifo_fromio_locked(&adev->dbg.trace_fifo, buf + layout.read_ptr, avs_dump_fw_log(adev, buf + layout.read_ptr,
apl_log_payload_size(adev) - layout.read_ptr, apl_log_payload_size(adev) - layout.read_ptr);
&adev->dbg.fifo_lock);
layout.read_ptr = 0; layout.read_ptr = 0;
} }
__kfifo_fromio_locked(&adev->dbg.trace_fifo, buf + layout.read_ptr, avs_dump_fw_log_wakeup(adev, buf + layout.read_ptr, layout.write_ptr - layout.read_ptr);
layout.write_ptr - layout.read_ptr, &adev->dbg.fifo_lock);
wake_up(&adev->dbg.trace_waitq);
update_read_ptr: update_read_ptr:
spin_unlock_irqrestore(&adev->dbg.trace_lock, flags);
writel(layout.write_ptr, addr); writel(layout.write_ptr, addr);
return 0; return 0;
} }
...@@ -140,7 +134,7 @@ static int apl_coredump(struct avs_dev *adev, union avs_notify_msg *msg) ...@@ -140,7 +134,7 @@ static int apl_coredump(struct avs_dev *adev, union avs_notify_msg *msg)
* gathered before dumping stack * gathered before dumping stack
*/ */
lbs_msg.log.core = msg->ext.coredump.core_id; 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; pos = dump + AVS_FW_REGS_SIZE;
...@@ -243,10 +237,10 @@ const struct avs_dsp_ops apl_dsp_ops = { ...@@ -243,10 +237,10 @@ const struct avs_dsp_ops apl_dsp_ops = {
.load_basefw = avs_hda_load_basefw, .load_basefw = avs_hda_load_basefw,
.load_lib = avs_hda_load_library, .load_lib = avs_hda_load_library,
.transfer_mods = avs_hda_transfer_modules, .transfer_mods = avs_hda_transfer_modules,
.enable_logs = apl_enable_logs,
.log_buffer_offset = skl_log_buffer_offset, .log_buffer_offset = skl_log_buffer_offset,
.log_buffer_status = apl_log_buffer_status, .log_buffer_status = apl_log_buffer_status,
.coredump = apl_coredump, .coredump = apl_coredump,
.d0ix_toggle = apl_d0ix_toggle, .d0ix_toggle = apl_d0ix_toggle,
.set_d0ix = apl_set_d0ix, .set_d0ix = apl_set_d0ix,
AVS_SET_ENABLE_LOGS_OP(apl)
}; };
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#ifndef __SOUND_SOC_INTEL_AVS_H #ifndef __SOUND_SOC_INTEL_AVS_H
#define __SOUND_SOC_INTEL_AVS_H #define __SOUND_SOC_INTEL_AVS_H
#include <linux/debugfs.h>
#include <linux/device.h> #include <linux/device.h>
#include <linux/firmware.h> #include <linux/firmware.h>
#include <linux/kfifo.h> #include <linux/kfifo.h>
...@@ -93,16 +94,6 @@ struct avs_fw_entry { ...@@ -93,16 +94,6 @@ struct avs_fw_entry {
struct list_head node; struct list_head node;
}; };
struct avs_debug {
struct kfifo trace_fifo;
spinlock_t fifo_lock; /* serialize I/O for trace_fifo */
spinlock_t trace_lock; /* serialize debug window I/O between each LOG_BUFFER_STATUS */
wait_queue_head_t trace_waitq;
u32 aging_timer_period;
u32 fifo_full_timer_period;
u32 logged_resources; /* context dependent: core or library */
};
/* /*
* struct avs_dev - Intel HD-Audio driver data * struct avs_dev - Intel HD-Audio driver data
* *
...@@ -146,7 +137,18 @@ struct avs_dev { ...@@ -146,7 +137,18 @@ struct avs_dev {
spinlock_t path_list_lock; spinlock_t path_list_lock;
struct mutex path_mutex; struct mutex path_mutex;
struct avs_debug dbg; spinlock_t trace_lock; /* serialize debug window I/O between each LOG_BUFFER_STATUS */
#ifdef CONFIG_DEBUG_FS
struct kfifo trace_fifo;
wait_queue_head_t trace_waitq;
u32 aging_timer_period;
u32 fifo_full_timer_period;
u32 logged_resources; /* context dependent: core or library */
struct dentry *debugfs_root;
/* probes */
struct hdac_ext_stream *extractor;
unsigned int num_probe_streams;
#endif
}; };
/* from hda_bus to avs_dev */ /* from hda_bus to avs_dev */
...@@ -321,6 +323,9 @@ struct avs_soc_component { ...@@ -321,6 +323,9 @@ struct avs_soc_component {
extern const struct snd_soc_dai_ops avs_dai_fe_ops; 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_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, int avs_i2s_platform_register(struct avs_dev *adev, const char *name, unsigned long port_mask,
unsigned long *tdms); unsigned long *tdms);
...@@ -331,9 +336,6 @@ void avs_unregister_all_boards(struct avs_dev *adev); ...@@ -331,9 +336,6 @@ void avs_unregister_all_boards(struct avs_dev *adev);
/* Firmware tracing helpers */ /* Firmware tracing helpers */
unsigned int __kfifo_fromio_locked(struct kfifo *fifo, const void __iomem *src, unsigned int len,
spinlock_t *lock);
#define avs_log_buffer_size(adev) \ #define avs_log_buffer_size(adev) \
((adev)->fw_cfg.trace_log_bytes / (adev)->hw_cfg.dsp_cores) ((adev)->fw_cfg.trace_log_bytes / (adev)->hw_cfg.dsp_cores)
...@@ -344,6 +346,18 @@ unsigned int __kfifo_fromio_locked(struct kfifo *fifo, const void __iomem *src, ...@@ -344,6 +346,18 @@ unsigned int __kfifo_fromio_locked(struct kfifo *fifo, const void __iomem *src,
(avs_sram_addr(adev, AVS_DEBUG_WINDOW) + __offset); \ (avs_sram_addr(adev, AVS_DEBUG_WINDOW) + __offset); \
}) })
static inline int avs_log_buffer_status_locked(struct avs_dev *adev, union avs_notify_msg *msg)
{
unsigned long flags;
int ret;
spin_lock_irqsave(&adev->trace_lock, flags);
ret = avs_dsp_op(adev, log_buffer_status, msg);
spin_unlock_irqrestore(&adev->trace_lock, flags);
return ret;
}
struct apl_log_buffer_layout { struct apl_log_buffer_layout {
u32 read_ptr; u32 read_ptr;
u32 write_ptr; u32 write_ptr;
...@@ -356,4 +370,42 @@ struct apl_log_buffer_layout { ...@@ -356,4 +370,42 @@ struct apl_log_buffer_layout {
#define apl_log_payload_addr(addr) \ #define apl_log_payload_addr(addr) \
(addr + sizeof(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);
int avs_probe_platform_register(struct avs_dev *adev, const char *name);
void avs_debugfs_init(struct avs_dev *adev);
void avs_debugfs_exit(struct avs_dev *adev);
#else
#define AVS_SET_ENABLE_LOGS_OP(name)
static inline bool avs_logging_fw(struct avs_dev *adev)
{
return false;
}
static inline void avs_dump_fw_log(struct avs_dev *adev, const void __iomem *src, unsigned int len)
{
}
static inline void
avs_dump_fw_log_wakeup(struct avs_dev *adev, const void __iomem *src, unsigned int len)
{
}
static inline int avs_probe_platform_register(struct avs_dev *adev, const char *name)
{
return 0;
}
static inline void avs_debugfs_init(struct avs_dev *adev) { }
static inline void avs_debugfs_exit(struct avs_dev *adev) { }
#endif
#endif /* __SOUND_SOC_INTEL_AVS_H */ #endif /* __SOUND_SOC_INTEL_AVS_H */
...@@ -291,6 +291,33 @@ static void board_pdev_unregister(void *data) ...@@ -291,6 +291,33 @@ static void board_pdev_unregister(void *data)
platform_device_unregister(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) static int avs_register_dmic_board(struct avs_dev *adev)
{ {
struct platform_device *codec, *board; struct platform_device *codec, *board;
...@@ -500,6 +527,12 @@ int avs_register_all_boards(struct avs_dev *adev) ...@@ -500,6 +527,12 @@ int avs_register_all_boards(struct avs_dev *adev)
{ {
int ret; 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); ret = avs_register_dmic_board(adev);
if (ret < 0) if (ret < 0)
dev_warn(adev->dev, "enumerate DMIC endpoints failed: %d\n", dev_warn(adev->dev, "enumerate DMIC endpoints failed: %d\n",
......
...@@ -77,6 +77,14 @@ config SND_SOC_INTEL_AVS_MACH_NAU8825 ...@@ -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. Say Y or m if you have such a device. This is a recommended option.
If unsure select "N". 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 config SND_SOC_INTEL_AVS_MACH_RT274
tristate "rt274 in I2S mode" tristate "rt274 in I2S mode"
depends on I2C depends on I2C
......
...@@ -8,6 +8,7 @@ snd-soc-avs-max98927-objs := max98927.o ...@@ -8,6 +8,7 @@ snd-soc-avs-max98927-objs := max98927.o
snd-soc-avs-max98357a-objs := max98357a.o snd-soc-avs-max98357a-objs := max98357a.o
snd-soc-avs-max98373-objs := max98373.o snd-soc-avs-max98373-objs := max98373.o
snd-soc-avs-nau8825-objs := nau8825.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-rt274-objs := rt274.o
snd-soc-avs-rt286-objs := rt286.o snd-soc-avs-rt286-objs := rt286.o
snd-soc-avs-rt298-objs := rt298.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 ...@@ -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_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_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_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_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_RT286) += snd-soc-avs-rt286.o
obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_RT298) += snd-soc-avs-rt298.o obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_RT298) += snd-soc-avs-rt298.o
......
// SPDX-License-Identifier: GPL-2.0-only
//
// Copyright(c) 2021-2022 Intel Corporation. All rights reserved.
//
// Authors: Cezary Rojewski <cezary.rojewski@intel.com>
// Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
//
#include <linux/device.h>
#include <linux/module.h>
#include <sound/soc.h>
#include <sound/soc-acpi.h>
SND_SOC_DAILINK_DEF(dummy, DAILINK_COMP_ARRAY(COMP_DUMMY()));
SND_SOC_DAILINK_DEF(probe_cp, DAILINK_COMP_ARRAY(COMP_CPU("Probe Extraction CPU DAI")));
SND_SOC_DAILINK_DEF(platform, DAILINK_COMP_ARRAY(COMP_PLATFORM("probe-platform")));
static struct snd_soc_dai_link probe_mb_dai_links[] = {
{
.name = "Compress Probe Capture",
.nonatomic = 1,
SND_SOC_DAILINK_REG(probe_cp, dummy, platform),
},
};
static int avs_probe_mb_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct snd_soc_acpi_mach *mach;
struct snd_soc_card *card;
int ret;
mach = dev_get_platdata(dev);
card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
if (!card)
return -ENOMEM;
card->name = "avs_probe_mb";
card->dev = dev;
card->owner = THIS_MODULE;
card->dai_link = probe_mb_dai_links;
card->num_links = ARRAY_SIZE(probe_mb_dai_links);
card->fully_routed = true;
ret = snd_soc_fixup_dai_links_platform_name(card, mach->mach_params.platform);
if (ret)
return ret;
return devm_snd_soc_register_card(dev, card);
}
static struct platform_driver avs_probe_mb_driver = {
.probe = avs_probe_mb_probe,
.driver = {
.name = "avs_probe_mb",
.pm = &snd_soc_pm_ops,
},
};
module_platform_driver(avs_probe_mb_driver);
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:avs_probe_mb");
...@@ -214,6 +214,7 @@ static void avs_hda_probe_work(struct work_struct *work) ...@@ -214,6 +214,7 @@ static void avs_hda_probe_work(struct work_struct *work)
adev->nhlt = intel_nhlt_init(adev->dev); adev->nhlt = intel_nhlt_init(adev->dev);
if (!adev->nhlt) if (!adev->nhlt)
dev_info(bus->dev, "platform has no NHLT\n"); dev_info(bus->dev, "platform has no NHLT\n");
avs_debugfs_init(adev);
avs_register_all_boards(adev); avs_register_all_boards(adev);
...@@ -491,6 +492,7 @@ static void avs_pci_remove(struct pci_dev *pci) ...@@ -491,6 +492,7 @@ static void avs_pci_remove(struct pci_dev *pci)
avs_unregister_all_boards(adev); avs_unregister_all_boards(adev);
avs_debugfs_exit(adev);
if (adev->nhlt) if (adev->nhlt)
intel_nhlt_free(adev->nhlt); intel_nhlt_free(adev->nhlt);
......
// SPDX-License-Identifier: GPL-2.0-only
//
// Copyright(c) 2021-2022 Intel Corporation. All rights reserved.
//
// Authors: Cezary Rojewski <cezary.rojewski@intel.com>
// Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
//
#include <linux/debugfs.h>
#include <linux/kfifo.h>
#include <linux/wait.h>
#include <linux/sched/signal.h>
#include <sound/soc.h>
#include "avs.h"
#include "messages.h"
static unsigned int __kfifo_fromio(struct kfifo *fifo, const void __iomem *src, unsigned int len)
{
struct __kfifo *__fifo = &fifo->kfifo;
unsigned int l, off;
len = min(len, kfifo_avail(fifo));
off = __fifo->in & __fifo->mask;
l = min(len, kfifo_size(fifo) - off);
memcpy_fromio(__fifo->data + off, src, l);
memcpy_fromio(__fifo->data, src + l, len - l);
/* Make sure data copied from SRAM is visible to all CPUs. */
smp_mb();
__fifo->in += len;
return len;
}
bool avs_logging_fw(struct avs_dev *adev)
{
return kfifo_initialized(&adev->trace_fifo);
}
void avs_dump_fw_log(struct avs_dev *adev, const void __iomem *src, unsigned int len)
{
__kfifo_fromio(&adev->trace_fifo, src, len);
}
void avs_dump_fw_log_wakeup(struct avs_dev *adev, const void __iomem *src, unsigned int len)
{
avs_dump_fw_log(adev, src, len);
wake_up(&adev->trace_waitq);
}
static ssize_t fw_regs_read(struct file *file, char __user *to, size_t count, loff_t *ppos)
{
struct avs_dev *adev = file->private_data;
char *buf;
int ret;
buf = kzalloc(AVS_FW_REGS_SIZE, GFP_KERNEL);
if (!buf)
return -ENOMEM;
memcpy_fromio(buf, avs_sram_addr(adev, AVS_FW_REGS_WINDOW), AVS_FW_REGS_SIZE);
ret = simple_read_from_buffer(to, count, ppos, buf, AVS_FW_REGS_SIZE);
kfree(buf);
return ret;
}
static const struct file_operations fw_regs_fops = {
.open = simple_open,
.read = fw_regs_read,
.llseek = no_llseek,
};
static ssize_t debug_window_read(struct file *file, char __user *to, size_t count, loff_t *ppos)
{
struct avs_dev *adev = file->private_data;
size_t size;
char *buf;
int ret;
size = adev->hw_cfg.dsp_cores * AVS_WINDOW_CHUNK_SIZE;
buf = kzalloc(size, GFP_KERNEL);
if (!buf)
return -ENOMEM;
memcpy_fromio(buf, avs_sram_addr(adev, AVS_DEBUG_WINDOW), size);
ret = simple_read_from_buffer(to, count, ppos, buf, size);
kfree(buf);
return ret;
}
static const struct file_operations debug_window_fops = {
.open = simple_open,
.read = debug_window_read,
.llseek = no_llseek,
};
static ssize_t probe_points_read(struct file *file, char __user *to, size_t count, loff_t *ppos)
{
struct avs_dev *adev = file->private_data;
struct avs_probe_point_desc *desc;
size_t num_desc, len = 0;
char *buf;
int i, ret;
/* Prevent chaining, send and dump IPC value just once. */
if (*ppos)
return 0;
buf = kzalloc(PAGE_SIZE, GFP_KERNEL);
if (!buf)
return -ENOMEM;
ret = avs_ipc_probe_get_points(adev, &desc, &num_desc);
if (ret) {
ret = AVS_IPC_RET(ret);
goto exit;
}
for (i = 0; i < num_desc; i++) {
ret = snprintf(buf + len, PAGE_SIZE - len,
"Id: %#010x Purpose: %d Node id: %#x\n",
desc[i].id.value, desc[i].purpose, desc[i].node_id.val);
if (ret < 0)
goto free_desc;
len += ret;
}
ret = simple_read_from_buffer(to, count, ppos, buf, len);
free_desc:
kfree(desc);
exit:
kfree(buf);
return ret;
}
static ssize_t probe_points_write(struct file *file, const char __user *from, size_t count,
loff_t *ppos)
{
struct avs_dev *adev = file->private_data;
struct avs_probe_point_desc *desc;
u32 *array, num_elems;
size_t bytes;
int ret;
ret = parse_int_array_user(from, count, (int **)&array);
if (ret < 0)
return ret;
num_elems = *array;
bytes = sizeof(*array) * num_elems;
if (bytes % sizeof(*desc)) {
ret = -EINVAL;
goto exit;
}
desc = (struct avs_probe_point_desc *)&array[1];
ret = avs_ipc_probe_connect_points(adev, desc, bytes / sizeof(*desc));
if (ret)
ret = AVS_IPC_RET(ret);
else
ret = count;
exit:
kfree(array);
return ret;
}
static const struct file_operations probe_points_fops = {
.open = simple_open,
.read = probe_points_read,
.write = probe_points_write,
.llseek = no_llseek,
};
static ssize_t probe_points_disconnect_write(struct file *file, const char __user *from,
size_t count, loff_t *ppos)
{
struct avs_dev *adev = file->private_data;
union avs_probe_point_id *id;
u32 *array, num_elems;
size_t bytes;
int ret;
ret = parse_int_array_user(from, count, (int **)&array);
if (ret < 0)
return ret;
num_elems = *array;
bytes = sizeof(*array) * num_elems;
if (bytes % sizeof(*id)) {
ret = -EINVAL;
goto exit;
}
id = (union avs_probe_point_id *)&array[1];
ret = avs_ipc_probe_disconnect_points(adev, id, bytes / sizeof(*id));
if (ret)
ret = AVS_IPC_RET(ret);
else
ret = count;
exit:
kfree(array);
return ret;
}
static const struct file_operations probe_points_disconnect_fops = {
.open = simple_open,
.write = probe_points_disconnect_write,
.llseek = default_llseek,
};
static ssize_t strace_read(struct file *file, char __user *to, size_t count, loff_t *ppos)
{
struct avs_dev *adev = file->private_data;
struct kfifo *fifo = &adev->trace_fifo;
unsigned int copied;
if (kfifo_is_empty(fifo)) {
DEFINE_WAIT(wait);
prepare_to_wait(&adev->trace_waitq, &wait, TASK_INTERRUPTIBLE);
if (!signal_pending(current))
schedule();
finish_wait(&adev->trace_waitq, &wait);
}
if (kfifo_to_user(fifo, to, count, &copied))
return -EFAULT;
*ppos += copied;
return copied;
}
static int strace_open(struct inode *inode, struct file *file)
{
struct avs_dev *adev = inode->i_private;
int ret;
if (kfifo_initialized(&adev->trace_fifo))
return -EBUSY;
ret = kfifo_alloc(&adev->trace_fifo, PAGE_SIZE, GFP_KERNEL);
if (ret < 0)
return ret;
file->private_data = adev;
return 0;
}
static int strace_release(struct inode *inode, struct file *file)
{
union avs_notify_msg msg = AVS_NOTIFICATION(LOG_BUFFER_STATUS);
struct avs_dev *adev = file->private_data;
unsigned long resource_mask;
unsigned long flags, i;
u32 num_cores;
resource_mask = adev->logged_resources;
num_cores = adev->hw_cfg.dsp_cores;
spin_lock_irqsave(&adev->trace_lock, flags);
/* Gather any remaining logs. */
for_each_set_bit(i, &resource_mask, num_cores) {
msg.log.core = i;
avs_dsp_op(adev, log_buffer_status, &msg);
}
kfifo_free(&adev->trace_fifo);
spin_unlock_irqrestore(&adev->trace_lock, flags);
return 0;
}
static const struct file_operations strace_fops = {
.llseek = default_llseek,
.read = strace_read,
.open = strace_open,
.release = strace_release,
};
#define DISABLE_TIMERS UINT_MAX
static int enable_logs(struct avs_dev *adev, u32 resource_mask, u32 *priorities)
{
int ret;
/* Logging demands D0i0 state from DSP. */
if (!adev->logged_resources) {
pm_runtime_get_sync(adev->dev);
ret = avs_dsp_disable_d0ix(adev);
if (ret)
goto err_d0ix;
}
ret = avs_ipc_set_system_time(adev);
if (ret && ret != AVS_IPC_NOT_SUPPORTED) {
ret = AVS_IPC_RET(ret);
goto err_ipc;
}
ret = avs_dsp_op(adev, enable_logs, AVS_LOG_ENABLE, adev->aging_timer_period,
adev->fifo_full_timer_period, resource_mask, priorities);
if (ret)
goto err_ipc;
adev->logged_resources |= resource_mask;
return 0;
err_ipc:
if (!adev->logged_resources) {
avs_dsp_enable_d0ix(adev);
err_d0ix:
pm_runtime_mark_last_busy(adev->dev);
pm_runtime_put_autosuspend(adev->dev);
}
return ret;
}
static int disable_logs(struct avs_dev *adev, u32 resource_mask)
{
int ret;
/* Check if there's anything to do. */
if (!adev->logged_resources)
return 0;
ret = avs_dsp_op(adev, enable_logs, AVS_LOG_DISABLE, DISABLE_TIMERS, DISABLE_TIMERS,
resource_mask, NULL);
/*
* If IPC fails causing recovery, logged_resources is already zero
* so unsetting bits is still safe.
*/
adev->logged_resources &= ~resource_mask;
/* If that's the last resource, allow for D3. */
if (!adev->logged_resources) {
avs_dsp_enable_d0ix(adev);
pm_runtime_mark_last_busy(adev->dev);
pm_runtime_put_autosuspend(adev->dev);
}
return ret;
}
static ssize_t trace_control_read(struct file *file, char __user *to, size_t count, loff_t *ppos)
{
struct avs_dev *adev = file->private_data;
char buf[64];
int len;
len = snprintf(buf, sizeof(buf), "0x%08x\n", adev->logged_resources);
return simple_read_from_buffer(to, count, ppos, buf, len);
}
static ssize_t trace_control_write(struct file *file, const char __user *from, size_t count,
loff_t *ppos)
{
struct avs_dev *adev = file->private_data;
u32 *array, num_elems;
u32 resource_mask;
int ret;
ret = parse_int_array_user(from, count, (int **)&array);
if (ret < 0)
return ret;
num_elems = *array;
resource_mask = array[1];
/*
* Disable if just resource mask is provided - no log priority flags.
*
* Enable input format: mask, prio1, .., prioN
* Where 'N' equals number of bits set in the 'mask'.
*/
if (num_elems == 1) {
ret = disable_logs(adev, resource_mask);
} else {
if (num_elems != (hweight_long(resource_mask) + 1)) {
ret = -EINVAL;
goto free_array;
}
ret = enable_logs(adev, resource_mask, &array[2]);
}
if (!ret)
ret = count;
free_array:
kfree(array);
return ret;
}
static const struct file_operations trace_control_fops = {
.llseek = default_llseek,
.read = trace_control_read,
.write = trace_control_write,
.open = simple_open,
};
void avs_debugfs_init(struct avs_dev *adev)
{
init_waitqueue_head(&adev->trace_waitq);
spin_lock_init(&adev->trace_lock);
adev->debugfs_root = debugfs_create_dir("avs", snd_soc_debugfs_root);
/* Initialize timer periods with recommended defaults. */
adev->aging_timer_period = 10;
adev->fifo_full_timer_period = 10;
debugfs_create_file("strace", 0444, adev->debugfs_root, adev, &strace_fops);
debugfs_create_file("trace_control", 0644, adev->debugfs_root, adev, &trace_control_fops);
debugfs_create_file("fw_regs", 0444, adev->debugfs_root, adev, &fw_regs_fops);
debugfs_create_file("debug_window", 0444, adev->debugfs_root, adev, &debug_window_fops);
debugfs_create_u32("trace_aging_period", 0644, adev->debugfs_root,
&adev->aging_timer_period);
debugfs_create_u32("trace_fifo_full_period", 0644, adev->debugfs_root,
&adev->fifo_full_timer_period);
debugfs_create_file("probe_points", 0644, adev->debugfs_root, adev, &probe_points_fops);
debugfs_create_file("probe_points_disconnect", 0200, adev->debugfs_root, adev,
&probe_points_disconnect_fops);
}
void avs_debugfs_exit(struct avs_dev *adev)
{
debugfs_remove_recursive(adev->debugfs_root);
}
...@@ -266,7 +266,7 @@ static void avs_dsp_process_notification(struct avs_dev *adev, u64 header) ...@@ -266,7 +266,7 @@ static void avs_dsp_process_notification(struct avs_dev *adev, u64 header)
break; break;
case AVS_NOTIFY_LOG_BUFFER_STATUS: case AVS_NOTIFY_LOG_BUFFER_STATUS:
avs_dsp_op(adev, log_buffer_status, &msg); avs_log_buffer_status_locked(adev, &msg);
break; break;
case AVS_NOTIFY_EXCEPTION_CAUGHT: case AVS_NOTIFY_EXCEPTION_CAUGHT:
......
...@@ -685,6 +685,24 @@ int avs_ipc_get_modules_info(struct avs_dev *adev, struct avs_mods_info **info) ...@@ -685,6 +685,24 @@ int avs_ipc_get_modules_info(struct avs_dev *adev, struct avs_mods_info **info)
return 0; 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) 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, return avs_ipc_set_large_config(adev, AVS_BASEFW_MOD_ID, AVS_BASEFW_INST_ID,
...@@ -705,19 +723,81 @@ int avs_ipc_set_system_time(struct avs_dev *adev) ...@@ -705,19 +723,81 @@ int avs_ipc_set_system_time(struct avs_dev *adev)
AVS_BASEFW_SYSTEM_TIME, (u8 *)&sys_time, sizeof(sys_time)); AVS_BASEFW_SYSTEM_TIME, (u8 *)&sys_time, sizeof(sys_time));
} }
int avs_ipc_copier_set_sink_format(struct avs_dev *adev, u16 module_id, int avs_ipc_probe_get_dma(struct avs_dev *adev, struct avs_probe_dma **dmas, size_t *num_dmas)
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; size_t payload_size;
u32 module_id;
u8 *payload;
int ret;
cpr_fmt.sink_id = sink_id; module_id = avs_get_module_id(adev, &AVS_PROBE_MOD_UUID);
/* 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, ret = avs_ipc_get_large_config(adev, module_id, AVS_PROBE_INST_ID, AVS_PROBE_INJECTION_DMA,
AVS_COPIER_SET_SINK_FORMAT, NULL, 0, &payload, &payload_size);
(u8 *)&cpr_fmt, sizeof(cpr_fmt)); 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
...@@ -802,4 +802,57 @@ int avs_ipc_copier_set_sink_format(struct avs_dev *adev, u16 module_id, ...@@ -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 *src_fmt,
const struct avs_audio_format *sink_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 */ #endif /* __SOUND_SOC_INTEL_AVS_MSGS_H */
...@@ -1126,9 +1126,9 @@ static const struct snd_soc_component_driver avs_component_driver = { ...@@ -1126,9 +1126,9 @@ static const struct snd_soc_component_driver avs_component_driver = {
.topology_name_prefix = "intel/avs", .topology_name_prefix = "intel/avs",
}; };
static int avs_soc_component_register(struct device *dev, const char *name, int avs_soc_component_register(struct device *dev, const char *name,
const struct snd_soc_component_driver *drv, const struct snd_soc_component_driver *drv,
struct snd_soc_dai_driver *cpu_dais, int num_cpu_dais) struct snd_soc_dai_driver *cpu_dais, int num_cpu_dais)
{ {
struct avs_soc_component *acomp; struct avs_soc_component *acomp;
int ret; int ret;
......
// SPDX-License-Identifier: GPL-2.0-only
//
// Copyright(c) 2021-2022 Intel Corporation. All rights reserved.
//
// Authors: Cezary Rojewski <cezary.rojewski@intel.com>
// Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
//
#include <sound/compress_driver.h>
#include <sound/hdaudio_ext.h>
#include <sound/hdaudio.h>
#include <sound/soc.h>
#include "avs.h"
#include "messages.h"
static int avs_dsp_init_probe(struct avs_dev *adev, union avs_connector_node_id node_id,
size_t buffer_size)
{
struct avs_probe_cfg cfg = {{0}};
struct avs_module_entry mentry;
u16 dummy;
avs_get_module_entry(adev, &AVS_PROBE_MOD_UUID, &mentry);
/*
* Probe module uses no cycles, audio data format and input and output
* frame sizes are unused. It is also not owned by any pipeline.
*/
cfg.base.ibs = 1;
/* BSS module descriptor is always segment of index=2. */
cfg.base.is_pages = mentry.segments[2].flags.length;
cfg.gtw_cfg.node_id = node_id;
cfg.gtw_cfg.dma_buffer_size = buffer_size;
return avs_dsp_init_module(adev, mentry.module_id, INVALID_PIPELINE_ID, 0, 0, &cfg,
sizeof(cfg), &dummy);
}
static void avs_dsp_delete_probe(struct avs_dev *adev)
{
struct avs_module_entry mentry;
avs_get_module_entry(adev, &AVS_PROBE_MOD_UUID, &mentry);
/* There is only ever one probe module instance. */
avs_dsp_delete_module(adev, mentry.module_id, 0, INVALID_PIPELINE_ID, 0);
}
static inline struct hdac_ext_stream *avs_compr_get_host_stream(struct snd_compr_stream *cstream)
{
return cstream->runtime->private_data;
}
static int avs_probe_compr_open(struct snd_compr_stream *cstream, struct snd_soc_dai *dai)
{
struct avs_dev *adev = to_avs_dev(dai->dev);
struct hdac_bus *bus = &adev->base.core;
struct hdac_ext_stream *host_stream;
if (adev->extractor) {
dev_err(dai->dev, "Cannot open more than one extractor stream\n");
return -EEXIST;
}
host_stream = snd_hdac_ext_cstream_assign(bus, cstream);
if (!host_stream) {
dev_err(dai->dev, "Failed to assign HDAudio stream for extraction\n");
return -EBUSY;
}
adev->extractor = host_stream;
hdac_stream(host_stream)->curr_pos = 0;
cstream->runtime->private_data = host_stream;
return 0;
}
static int avs_probe_compr_free(struct snd_compr_stream *cstream, struct snd_soc_dai *dai)
{
struct hdac_ext_stream *host_stream = avs_compr_get_host_stream(cstream);
struct avs_dev *adev = to_avs_dev(dai->dev);
struct avs_probe_point_desc *desc;
/* Extractor node identifier. */
unsigned int vindex = INVALID_NODE_ID.vindex;
size_t num_desc;
int i, ret;
/* Disconnect all probe points. */
ret = avs_ipc_probe_get_points(adev, &desc, &num_desc);
if (ret) {
dev_err(dai->dev, "get probe points failed: %d\n", ret);
ret = AVS_IPC_RET(ret);
goto exit;
}
for (i = 0; i < num_desc; i++)
if (desc[i].node_id.vindex == vindex)
avs_ipc_probe_disconnect_points(adev, &desc[i].id, 1);
kfree(desc);
exit:
if (adev->num_probe_streams) {
adev->num_probe_streams--;
if (!adev->num_probe_streams) {
avs_dsp_delete_probe(adev);
avs_dsp_enable_d0ix(adev);
}
}
snd_hdac_stream_cleanup(hdac_stream(host_stream));
hdac_stream(host_stream)->prepared = 0;
snd_hdac_ext_stream_release(host_stream, HDAC_EXT_STREAM_TYPE_HOST);
snd_compr_free_pages(cstream);
adev->extractor = NULL;
return ret;
}
static int avs_probe_compr_set_params(struct snd_compr_stream *cstream,
struct snd_compr_params *params, struct snd_soc_dai *dai)
{
struct hdac_ext_stream *host_stream = avs_compr_get_host_stream(cstream);
struct snd_compr_runtime *rtd = cstream->runtime;
struct avs_dev *adev = to_avs_dev(dai->dev);
/* compr params do not store bit depth, default to S32_LE. */
snd_pcm_format_t format = SNDRV_PCM_FORMAT_S32_LE;
unsigned int format_val;
int bps, ret;
hdac_stream(host_stream)->bufsize = 0;
hdac_stream(host_stream)->period_bytes = 0;
hdac_stream(host_stream)->format_val = 0;
cstream->dma_buffer.dev.type = SNDRV_DMA_TYPE_DEV_SG;
cstream->dma_buffer.dev.dev = adev->dev;
ret = snd_compr_malloc_pages(cstream, rtd->buffer_size);
if (ret < 0)
return ret;
bps = snd_pcm_format_physical_width(format);
if (bps < 0)
return bps;
format_val = snd_hdac_calc_stream_format(params->codec.sample_rate, params->codec.ch_out,
format, bps, 0);
ret = snd_hdac_stream_set_params(hdac_stream(host_stream), format_val);
if (ret < 0)
return ret;
ret = snd_hdac_stream_setup(hdac_stream(host_stream));
if (ret < 0)
return ret;
hdac_stream(host_stream)->prepared = 1;
if (!adev->num_probe_streams) {
union avs_connector_node_id node_id;
/* D0ix not allowed during probing. */
ret = avs_dsp_disable_d0ix(adev);
if (ret)
return ret;
node_id.vindex = hdac_stream(host_stream)->stream_tag - 1;
node_id.dma_type = AVS_DMA_HDA_HOST_INPUT;
ret = avs_dsp_init_probe(adev, node_id, rtd->dma_bytes);
if (ret < 0) {
dev_err(dai->dev, "probe init failed: %d\n", ret);
avs_dsp_enable_d0ix(adev);
return ret;
}
}
adev->num_probe_streams++;
return 0;
}
static int avs_probe_compr_trigger(struct snd_compr_stream *cstream, int cmd,
struct snd_soc_dai *dai)
{
struct hdac_ext_stream *host_stream = avs_compr_get_host_stream(cstream);
struct avs_dev *adev = to_avs_dev(dai->dev);
struct hdac_bus *bus = &adev->base.core;
unsigned long cookie;
if (!hdac_stream(host_stream)->prepared)
return -EPIPE;
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
case SNDRV_PCM_TRIGGER_RESUME:
spin_lock_irqsave(&bus->reg_lock, cookie);
snd_hdac_stream_start(hdac_stream(host_stream), true);
spin_unlock_irqrestore(&bus->reg_lock, cookie);
break;
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_STOP:
spin_lock_irqsave(&bus->reg_lock, cookie);
snd_hdac_stream_stop(hdac_stream(host_stream));
spin_unlock_irqrestore(&bus->reg_lock, cookie);
break;
default:
return -EINVAL;
}
return 0;
}
static int avs_probe_compr_pointer(struct snd_compr_stream *cstream,
struct snd_compr_tstamp *tstamp, struct snd_soc_dai *dai)
{
struct hdac_ext_stream *host_stream = avs_compr_get_host_stream(cstream);
struct snd_soc_pcm_stream *pstream;
pstream = &dai->driver->capture;
tstamp->copied_total = hdac_stream(host_stream)->curr_pos;
tstamp->sampling_rate = snd_pcm_rate_bit_to_rate(pstream->rates);
return 0;
}
static int avs_probe_compr_copy(struct snd_soc_component *comp, struct snd_compr_stream *cstream,
char __user *buf, size_t count)
{
struct snd_compr_runtime *rtd = cstream->runtime;
unsigned int offset, n;
void *ptr;
int ret;
if (count > rtd->buffer_size)
count = rtd->buffer_size;
div_u64_rem(rtd->total_bytes_transferred, rtd->buffer_size, &offset);
ptr = rtd->dma_area + offset;
n = rtd->buffer_size - offset;
if (count < n) {
ret = copy_to_user(buf, ptr, count);
} else {
ret = copy_to_user(buf, ptr, n);
ret += copy_to_user(buf + n, rtd->dma_area, count - n);
}
if (ret)
return count - ret;
return count;
}
static const struct snd_soc_cdai_ops avs_probe_dai_ops = {
.startup = avs_probe_compr_open,
.shutdown = avs_probe_compr_free,
.set_params = avs_probe_compr_set_params,
.trigger = avs_probe_compr_trigger,
.pointer = avs_probe_compr_pointer,
};
static const struct snd_compress_ops avs_probe_compress_ops = {
.copy = avs_probe_compr_copy,
};
static struct snd_soc_dai_driver probe_cpu_dais[] = {
{
.name = "Probe Extraction CPU DAI",
.compress_new = snd_soc_new_compress,
.cops = &avs_probe_dai_ops,
.capture = {
.stream_name = "Probe Extraction",
.channels_min = 1,
.channels_max = 8,
.rates = SNDRV_PCM_RATE_48000,
.rate_min = 48000,
.rate_max = 48000,
},
},
};
static int avs_probe_component_probe(struct snd_soc_component *component)
{
struct avs_soc_component *acomp = to_avs_soc_component(component);
struct avs_dev *adev = to_avs_dev(component->dev);
mutex_lock(&adev->comp_list_mutex);
list_add_tail(&acomp->node, &adev->comp_list);
mutex_unlock(&adev->comp_list_mutex);
return 0;
}
static void avs_probe_component_remove(struct snd_soc_component *component)
{
struct avs_soc_component *acomp = to_avs_soc_component(component);
struct avs_dev *adev = to_avs_dev(component->dev);
mutex_lock(&adev->comp_list_mutex);
list_del(&acomp->node);
mutex_unlock(&adev->comp_list_mutex);
}
static const struct snd_soc_component_driver avs_probe_component_driver = {
.name = "avs-probe-compr",
.probe = avs_probe_component_probe,
.remove = avs_probe_component_remove,
.compress_ops = &avs_probe_compress_ops,
.module_get_upon_open = 1, /* increment refcount when a stream is opened */
};
int avs_probe_platform_register(struct avs_dev *adev, const char *name)
{
return avs_soc_component_register(adev->dev, name, &avs_probe_component_driver,
probe_cpu_dais, ARRAY_SIZE(probe_cpu_dais));
}
...@@ -59,7 +59,8 @@ ...@@ -59,7 +59,8 @@
#define AVS_FW_REG_STATUS(adev) (AVS_FW_REG_BASE(adev) + 0x0) #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_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 #define AVS_FW_REGS_WINDOW 0
/* DSP -> HOST communication window */ /* DSP -> HOST communication window */
#define AVS_UPLINK_WINDOW AVS_FW_REGS_WINDOW #define AVS_UPLINK_WINDOW AVS_FW_REGS_WINDOW
......
...@@ -12,8 +12,9 @@ ...@@ -12,8 +12,9 @@
#include "avs.h" #include "avs.h"
#include "messages.h" #include "messages.h"
static int skl_enable_logs(struct avs_dev *adev, enum avs_log_enable enable, u32 aging_period, static int __maybe_unused
u32 fifo_full_period, unsigned long resource_mask, u32 *priorities) 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; struct skl_log_state_info *info;
u32 size, num_cores = adev->hw_cfg.dsp_cores; u32 size, num_cores = adev->hw_cfg.dsp_cores;
...@@ -55,15 +56,11 @@ int skl_log_buffer_offset(struct avs_dev *adev, u32 core) ...@@ -55,15 +56,11 @@ int skl_log_buffer_offset(struct avs_dev *adev, u32 core)
static int static int
skl_log_buffer_status(struct avs_dev *adev, union avs_notify_msg *msg) skl_log_buffer_status(struct avs_dev *adev, union avs_notify_msg *msg)
{ {
unsigned long flags;
void __iomem *buf; void __iomem *buf;
u16 size, write, offset; u16 size, write, offset;
spin_lock_irqsave(&adev->dbg.trace_lock, flags); if (!avs_logging_fw(adev))
if (!kfifo_initialized(&adev->dbg.trace_fifo)) {
spin_unlock_irqrestore(&adev->dbg.trace_lock, flags);
return 0; return 0;
}
size = avs_log_buffer_size(adev) / 2; 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)); write = readl(avs_sram_addr(adev, AVS_FW_REGS_WINDOW) + FW_REGS_DBG_LOG_WP(msg->log.core));
...@@ -72,9 +69,7 @@ skl_log_buffer_status(struct avs_dev *adev, union avs_notify_msg *msg) ...@@ -72,9 +69,7 @@ skl_log_buffer_status(struct avs_dev *adev, union avs_notify_msg *msg)
/* Address is guaranteed to exist in SRAM2. */ /* Address is guaranteed to exist in SRAM2. */
buf = avs_log_buffer_addr(adev, msg->log.core) + offset; buf = avs_log_buffer_addr(adev, msg->log.core) + offset;
__kfifo_fromio_locked(&adev->dbg.trace_fifo, buf, size, &adev->dbg.fifo_lock); avs_dump_fw_log_wakeup(adev, buf, size);
wake_up(&adev->dbg.trace_waitq);
spin_unlock_irqrestore(&adev->dbg.trace_lock, flags);
return 0; return 0;
} }
...@@ -116,10 +111,10 @@ const struct avs_dsp_ops skl_dsp_ops = { ...@@ -116,10 +111,10 @@ const struct avs_dsp_ops skl_dsp_ops = {
.load_basefw = avs_cldma_load_basefw, .load_basefw = avs_cldma_load_basefw,
.load_lib = avs_cldma_load_library, .load_lib = avs_cldma_load_library,
.transfer_mods = avs_cldma_transfer_modules, .transfer_mods = avs_cldma_transfer_modules,
.enable_logs = skl_enable_logs,
.log_buffer_offset = skl_log_buffer_offset, .log_buffer_offset = skl_log_buffer_offset,
.log_buffer_status = skl_log_buffer_status, .log_buffer_status = skl_log_buffer_status,
.coredump = skl_coredump, .coredump = skl_coredump,
.d0ix_toggle = skl_d0ix_toggle, .d0ix_toggle = skl_d0ix_toggle,
.set_d0ix = skl_set_d0ix, .set_d0ix = skl_set_d0ix,
AVS_SET_ENABLE_LOGS_OP(skl)
}; };
...@@ -300,25 +300,3 @@ void avs_release_firmwares(struct avs_dev *adev) ...@@ -300,25 +300,3 @@ void avs_release_firmwares(struct avs_dev *adev)
kfree(entry); kfree(entry);
} }
} }
unsigned int __kfifo_fromio_locked(struct kfifo *fifo, const void __iomem *src, unsigned int len,
spinlock_t *lock)
{
struct __kfifo *__fifo = &fifo->kfifo;
unsigned long flags;
unsigned int l, off;
spin_lock_irqsave(lock, flags);
len = min(len, kfifo_avail(fifo));
off = __fifo->in & __fifo->mask;
l = min(len, kfifo_size(fifo) - off);
memcpy_fromio(__fifo->data + off, src, l);
memcpy_fromio(__fifo->data, src + l, len - l);
/* Make sure data copied from SRAM is visible to all CPUs. */
smp_mb();
__fifo->in += len;
spin_unlock_irqrestore(lock, flags);
return len;
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment