diff options
Diffstat (limited to 'sound/soc/intel')
34 files changed, 1299 insertions, 555 deletions
diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig index ac799de4f7fd..4b9e498e3303 100644 --- a/sound/soc/intel/Kconfig +++ b/sound/soc/intel/Kconfig @@ -217,6 +217,7 @@ config SND_SOC_INTEL_AVS select SND_SOC_ACPI if ACPI select SND_SOC_TOPOLOGY select SND_SOC_HDA + select SND_SOC_COMPRESS if DEBUG_FS select SND_HDA_EXT_CORE select SND_HDA_DSP_LOADER select SND_INTEL_DSP_CONFIG diff --git a/sound/soc/intel/avs/Makefile b/sound/soc/intel/avs/Makefile index 919212825f21..1c6924a1ebca 100644 --- a/sound/soc/intel/avs/Makefile +++ b/sound/soc/intel/avs/Makefile @@ -9,6 +9,10 @@ snd-soc-avs-objs += trace.o # tell define_trace.h where to find the trace header CFLAGS_trace.o := -I$(src) +ifneq ($(CONFIG_DEBUG_FS),) +snd-soc-avs-objs += probes.o debugfs.o +endif + obj-$(CONFIG_SND_SOC_INTEL_AVS) += snd-soc-avs.o # Machine support diff --git a/sound/soc/intel/avs/apl.c b/sound/soc/intel/avs/apl.c index 7c8ce98eda9d..02683dce277a 100644 --- a/sound/soc/intel/avs/apl.c +++ b/sound/soc/intel/avs/apl.c @@ -13,8 +13,9 @@ #include "path.h" #include "topology.h" -static int apl_enable_logs(struct avs_dev *adev, enum avs_log_enable enable, u32 aging_period, - u32 fifo_full_period, unsigned long resource_mask, u32 *priorities) +static int __maybe_unused +apl_enable_logs(struct avs_dev *adev, enum avs_log_enable enable, u32 aging_period, + u32 fifo_full_period, unsigned long resource_mask, u32 *priorities) { struct apl_log_state_info *info; u32 size, num_cores = adev->hw_cfg.dsp_cores; @@ -50,7 +51,6 @@ static int apl_enable_logs(struct avs_dev *adev, enum avs_log_enable enable, u32 static int apl_log_buffer_status(struct avs_dev *adev, union avs_notify_msg *msg) { struct apl_log_buffer_layout layout; - unsigned long flags; void __iomem *addr, *buf; addr = avs_log_buffer_addr(adev, msg->log.core); @@ -59,26 +59,20 @@ static int apl_log_buffer_status(struct avs_dev *adev, union avs_notify_msg *msg memcpy_fromio(&layout, addr, sizeof(layout)); - spin_lock_irqsave(&adev->dbg.trace_lock, flags); - if (!kfifo_initialized(&adev->dbg.trace_fifo)) + if (!avs_logging_fw(adev)) /* consume the logs regardless of consumer presence */ goto update_read_ptr; buf = apl_log_payload_addr(addr); if (layout.read_ptr > layout.write_ptr) { - __kfifo_fromio_locked(&adev->dbg.trace_fifo, buf + layout.read_ptr, - apl_log_payload_size(adev) - layout.read_ptr, - &adev->dbg.fifo_lock); + avs_dump_fw_log(adev, buf + layout.read_ptr, + apl_log_payload_size(adev) - layout.read_ptr); layout.read_ptr = 0; } - __kfifo_fromio_locked(&adev->dbg.trace_fifo, buf + layout.read_ptr, - layout.write_ptr - layout.read_ptr, &adev->dbg.fifo_lock); - - wake_up(&adev->dbg.trace_waitq); + avs_dump_fw_log_wakeup(adev, buf + layout.read_ptr, layout.write_ptr - layout.read_ptr); update_read_ptr: - spin_unlock_irqrestore(&adev->dbg.trace_lock, flags); writel(layout.write_ptr, addr); return 0; } @@ -140,7 +134,7 @@ static int apl_coredump(struct avs_dev *adev, union avs_notify_msg *msg) * gathered before dumping stack */ lbs_msg.log.core = msg->ext.coredump.core_id; - avs_dsp_op(adev, log_buffer_status, &lbs_msg); + avs_log_buffer_status_locked(adev, &lbs_msg); } pos = dump + AVS_FW_REGS_SIZE; @@ -243,10 +237,10 @@ const struct avs_dsp_ops apl_dsp_ops = { .load_basefw = avs_hda_load_basefw, .load_lib = avs_hda_load_library, .transfer_mods = avs_hda_transfer_modules, - .enable_logs = apl_enable_logs, .log_buffer_offset = skl_log_buffer_offset, .log_buffer_status = apl_log_buffer_status, .coredump = apl_coredump, .d0ix_toggle = apl_d0ix_toggle, .set_d0ix = apl_set_d0ix, + AVS_SET_ENABLE_LOGS_OP(apl) }; diff --git a/sound/soc/intel/avs/avs.h b/sound/soc/intel/avs/avs.h index 8d05b27608fe..d7fccdcb9c16 100644 --- a/sound/soc/intel/avs/avs.h +++ b/sound/soc/intel/avs/avs.h @@ -9,6 +9,7 @@ #ifndef __SOUND_SOC_INTEL_AVS_H #define __SOUND_SOC_INTEL_AVS_H +#include <linux/debugfs.h> #include <linux/device.h> #include <linux/firmware.h> #include <linux/kfifo.h> @@ -93,16 +94,6 @@ struct avs_fw_entry { struct list_head node; }; -struct avs_debug { - struct kfifo trace_fifo; - spinlock_t fifo_lock; /* serialize I/O for trace_fifo */ - spinlock_t trace_lock; /* serialize debug window I/O between each LOG_BUFFER_STATUS */ - wait_queue_head_t trace_waitq; - u32 aging_timer_period; - u32 fifo_full_timer_period; - u32 logged_resources; /* context dependent: core or library */ -}; - /* * struct avs_dev - Intel HD-Audio driver data * @@ -146,7 +137,18 @@ struct avs_dev { spinlock_t path_list_lock; struct mutex path_mutex; - struct avs_debug dbg; + spinlock_t trace_lock; /* serialize debug window I/O between each LOG_BUFFER_STATUS */ +#ifdef CONFIG_DEBUG_FS + struct kfifo trace_fifo; + wait_queue_head_t trace_waitq; + u32 aging_timer_period; + u32 fifo_full_timer_period; + u32 logged_resources; /* context dependent: core or library */ + struct dentry *debugfs_root; + /* probes */ + struct hdac_ext_stream *extractor; + unsigned int num_probe_streams; +#endif }; /* from hda_bus to avs_dev */ @@ -321,6 +323,9 @@ struct avs_soc_component { extern const struct snd_soc_dai_ops avs_dai_fe_ops; +int avs_soc_component_register(struct device *dev, const char *name, + const struct snd_soc_component_driver *drv, + struct snd_soc_dai_driver *cpu_dais, int num_cpu_dais); int avs_dmic_platform_register(struct avs_dev *adev, const char *name); int avs_i2s_platform_register(struct avs_dev *adev, const char *name, unsigned long port_mask, unsigned long *tdms); @@ -331,9 +336,6 @@ void avs_unregister_all_boards(struct avs_dev *adev); /* Firmware tracing helpers */ -unsigned int __kfifo_fromio_locked(struct kfifo *fifo, const void __iomem *src, unsigned int len, - spinlock_t *lock); - #define avs_log_buffer_size(adev) \ ((adev)->fw_cfg.trace_log_bytes / (adev)->hw_cfg.dsp_cores) @@ -344,6 +346,18 @@ unsigned int __kfifo_fromio_locked(struct kfifo *fifo, const void __iomem *src, (avs_sram_addr(adev, AVS_DEBUG_WINDOW) + __offset); \ }) +static inline int avs_log_buffer_status_locked(struct avs_dev *adev, union avs_notify_msg *msg) +{ + unsigned long flags; + int ret; + + spin_lock_irqsave(&adev->trace_lock, flags); + ret = avs_dsp_op(adev, log_buffer_status, msg); + spin_unlock_irqrestore(&adev->trace_lock, flags); + + return ret; +} + struct apl_log_buffer_layout { u32 read_ptr; u32 write_ptr; @@ -356,4 +370,42 @@ struct apl_log_buffer_layout { #define apl_log_payload_addr(addr) \ (addr + sizeof(struct apl_log_buffer_layout)) +#ifdef CONFIG_DEBUG_FS +#define AVS_SET_ENABLE_LOGS_OP(name) \ + .enable_logs = name##_enable_logs + +bool avs_logging_fw(struct avs_dev *adev); +void avs_dump_fw_log(struct avs_dev *adev, const void __iomem *src, unsigned int len); +void avs_dump_fw_log_wakeup(struct avs_dev *adev, const void __iomem *src, unsigned int len); + +int avs_probe_platform_register(struct avs_dev *adev, const char *name); + +void avs_debugfs_init(struct avs_dev *adev); +void avs_debugfs_exit(struct avs_dev *adev); +#else +#define AVS_SET_ENABLE_LOGS_OP(name) + +static inline bool avs_logging_fw(struct avs_dev *adev) +{ + return false; +} + +static inline void avs_dump_fw_log(struct avs_dev *adev, const void __iomem *src, unsigned int len) +{ +} + +static inline void +avs_dump_fw_log_wakeup(struct avs_dev *adev, const void __iomem *src, unsigned int len) +{ +} + +static inline int avs_probe_platform_register(struct avs_dev *adev, const char *name) +{ + return 0; +} + +static inline void avs_debugfs_init(struct avs_dev *adev) { } +static inline void avs_debugfs_exit(struct avs_dev *adev) { } +#endif + #endif /* __SOUND_SOC_INTEL_AVS_H */ diff --git a/sound/soc/intel/avs/board_selection.c b/sound/soc/intel/avs/board_selection.c index 02cc1ce8f5f5..b2823c2107f7 100644 --- a/sound/soc/intel/avs/board_selection.c +++ b/sound/soc/intel/avs/board_selection.c @@ -291,6 +291,33 @@ static void board_pdev_unregister(void *data) platform_device_unregister(data); } +static int __maybe_unused avs_register_probe_board(struct avs_dev *adev) +{ + struct platform_device *board; + struct snd_soc_acpi_mach mach = {{0}}; + int ret; + + ret = avs_probe_platform_register(adev, "probe-platform"); + if (ret < 0) + return ret; + + mach.mach_params.platform = "probe-platform"; + + board = platform_device_register_data(NULL, "avs_probe_mb", PLATFORM_DEVID_NONE, + (const void *)&mach, sizeof(mach)); + if (IS_ERR(board)) { + dev_err(adev->dev, "probe board register failed\n"); + return PTR_ERR(board); + } + + ret = devm_add_action(adev->dev, board_pdev_unregister, board); + if (ret < 0) { + platform_device_unregister(board); + return ret; + } + return 0; +} + static int avs_register_dmic_board(struct avs_dev *adev) { struct platform_device *codec, *board; @@ -500,6 +527,12 @@ int avs_register_all_boards(struct avs_dev *adev) { int ret; +#ifdef CONFIG_DEBUG_FS + ret = avs_register_probe_board(adev); + if (ret < 0) + dev_warn(adev->dev, "enumerate PROBE endpoints failed: %d\n", ret); +#endif + ret = avs_register_dmic_board(adev); if (ret < 0) dev_warn(adev->dev, "enumerate DMIC endpoints failed: %d\n", diff --git a/sound/soc/intel/avs/boards/Kconfig b/sound/soc/intel/avs/boards/Kconfig index 9bd40fdd9028..e4c230efe8d7 100644 --- a/sound/soc/intel/avs/boards/Kconfig +++ b/sound/soc/intel/avs/boards/Kconfig @@ -77,6 +77,14 @@ config SND_SOC_INTEL_AVS_MACH_NAU8825 Say Y or m if you have such a device. This is a recommended option. If unsure select "N". +config SND_SOC_INTEL_AVS_MACH_PROBE + tristate "Probing (data) board" + depends on DEBUG_FS + select SND_HWDEP + help + This adds support for data probing board which can be used to + gather data from runtime stream over compress operations. + config SND_SOC_INTEL_AVS_MACH_RT274 tristate "rt274 in I2S mode" depends on I2C diff --git a/sound/soc/intel/avs/boards/Makefile b/sound/soc/intel/avs/boards/Makefile index 4d70b8d09ce5..b81343420370 100644 --- a/sound/soc/intel/avs/boards/Makefile +++ b/sound/soc/intel/avs/boards/Makefile @@ -8,6 +8,7 @@ snd-soc-avs-max98927-objs := max98927.o snd-soc-avs-max98357a-objs := max98357a.o snd-soc-avs-max98373-objs := max98373.o snd-soc-avs-nau8825-objs := nau8825.o +snd-soc-avs-probe-objs := probe.o snd-soc-avs-rt274-objs := rt274.o snd-soc-avs-rt286-objs := rt286.o snd-soc-avs-rt298-objs := rt298.o @@ -22,6 +23,7 @@ obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_MAX98927) += snd-soc-avs-max98927.o obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_MAX98357A) += snd-soc-avs-max98357a.o obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_MAX98373) += snd-soc-avs-max98373.o obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_NAU8825) += snd-soc-avs-nau8825.o +obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_PROBE) += snd-soc-avs-probe.o obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_RT274) += snd-soc-avs-rt274.o obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_RT286) += snd-soc-avs-rt286.o obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_RT298) += snd-soc-avs-rt298.o diff --git a/sound/soc/intel/avs/boards/probe.c b/sound/soc/intel/avs/boards/probe.c new file mode 100644 index 000000000000..411acaee74f9 --- /dev/null +++ b/sound/soc/intel/avs/boards/probe.c @@ -0,0 +1,64 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// Copyright(c) 2021-2022 Intel Corporation. All rights reserved. +// +// Authors: Cezary Rojewski <cezary.rojewski@intel.com> +// Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com> +// + +#include <linux/device.h> +#include <linux/module.h> +#include <sound/soc.h> +#include <sound/soc-acpi.h> + +SND_SOC_DAILINK_DEF(dummy, DAILINK_COMP_ARRAY(COMP_DUMMY())); +SND_SOC_DAILINK_DEF(probe_cp, DAILINK_COMP_ARRAY(COMP_CPU("Probe Extraction CPU DAI"))); +SND_SOC_DAILINK_DEF(platform, DAILINK_COMP_ARRAY(COMP_PLATFORM("probe-platform"))); + +static struct snd_soc_dai_link probe_mb_dai_links[] = { + { + .name = "Compress Probe Capture", + .nonatomic = 1, + SND_SOC_DAILINK_REG(probe_cp, dummy, platform), + }, +}; + +static int avs_probe_mb_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct snd_soc_acpi_mach *mach; + struct snd_soc_card *card; + int ret; + + mach = dev_get_platdata(dev); + + card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL); + if (!card) + return -ENOMEM; + + card->name = "avs_probe_mb"; + card->dev = dev; + card->owner = THIS_MODULE; + card->dai_link = probe_mb_dai_links; + card->num_links = ARRAY_SIZE(probe_mb_dai_links); + card->fully_routed = true; + + ret = snd_soc_fixup_dai_links_platform_name(card, mach->mach_params.platform); + if (ret) + return ret; + + return devm_snd_soc_register_card(dev, card); +} + +static struct platform_driver avs_probe_mb_driver = { + .probe = avs_probe_mb_probe, + .driver = { + .name = "avs_probe_mb", + .pm = &snd_soc_pm_ops, + }, +}; + +module_platform_driver(avs_probe_mb_driver); + +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:avs_probe_mb"); diff --git a/sound/soc/intel/avs/core.c b/sound/soc/intel/avs/core.c index f7bc06404dbc..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 new file mode 100644 index 000000000000..bdd388ec01ea --- /dev/null +++ b/sound/soc/intel/avs/debugfs.c @@ -0,0 +1,436 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// Copyright(c) 2021-2022 Intel Corporation. All rights reserved. +// +// Authors: Cezary Rojewski <cezary.rojewski@intel.com> +// Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com> +// + +#include <linux/debugfs.h> +#include <linux/kfifo.h> +#include <linux/wait.h> +#include <linux/sched/signal.h> +#include <sound/soc.h> +#include "avs.h" +#include "messages.h" + +static unsigned int __kfifo_fromio(struct kfifo *fifo, const void __iomem *src, unsigned int len) +{ + struct __kfifo *__fifo = &fifo->kfifo; + unsigned int l, off; + + len = min(len, kfifo_avail(fifo)); + off = __fifo->in & __fifo->mask; + l = min(len, kfifo_size(fifo) - off); + + memcpy_fromio(__fifo->data + off, src, l); + memcpy_fromio(__fifo->data, src + l, len - l); + /* Make sure data copied from SRAM is visible to all CPUs. */ + smp_mb(); + __fifo->in += len; + + return len; +} + +bool avs_logging_fw(struct avs_dev *adev) +{ + return kfifo_initialized(&adev->trace_fifo); +} + +void avs_dump_fw_log(struct avs_dev *adev, const void __iomem *src, unsigned int len) +{ + __kfifo_fromio(&adev->trace_fifo, src, len); +} + +void avs_dump_fw_log_wakeup(struct avs_dev *adev, const void __iomem *src, unsigned int len) +{ + avs_dump_fw_log(adev, src, len); + wake_up(&adev->trace_waitq); +} + +static ssize_t fw_regs_read(struct file *file, char __user *to, size_t count, loff_t *ppos) +{ + struct avs_dev *adev = file->private_data; + char *buf; + int ret; + + buf = kzalloc(AVS_FW_REGS_SIZE, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + memcpy_fromio(buf, avs_sram_addr(adev, AVS_FW_REGS_WINDOW), AVS_FW_REGS_SIZE); + + ret = simple_read_from_buffer(to, count, ppos, buf, AVS_FW_REGS_SIZE); + kfree(buf); + return ret; +} + +static const struct file_operations fw_regs_fops = { + .open = simple_open, + .read = fw_regs_read, + .llseek = no_llseek, +}; + +static ssize_t debug_window_read(struct file *file, char __user *to, size_t count, loff_t *ppos) +{ + struct avs_dev *adev = file->private_data; + size_t size; + char *buf; + int ret; + + size = adev->hw_cfg.dsp_cores * AVS_WINDOW_CHUNK_SIZE; + buf = kzalloc(size, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + memcpy_fromio(buf, avs_sram_addr(adev, AVS_DEBUG_WINDOW), size); + + ret = simple_read_from_buffer(to, count, ppos, buf, size); + kfree(buf); + return ret; +} + +static const struct file_operations debug_window_fops = { + .open = simple_open, + .read = debug_window_read, + .llseek = no_llseek, +}; + +static ssize_t probe_points_read(struct file *file, char __user *to, size_t count, loff_t *ppos) +{ + struct avs_dev *adev = file->private_data; + struct avs_probe_point_desc *desc; + size_t num_desc, len = 0; + char *buf; + int i, ret; + + /* Prevent chaining, send and dump IPC value just once. */ + if (*ppos) + return 0; + + buf = kzalloc(PAGE_SIZE, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + ret = avs_ipc_probe_get_points(adev, &desc, &num_desc); + if (ret) { + ret = AVS_IPC_RET(ret); + goto exit; + } + + for (i = 0; i < num_desc; i++) { + ret = snprintf(buf + len, PAGE_SIZE - len, + "Id: %#010x Purpose: %d Node id: %#x\n", + desc[i].id.value, desc[i].purpose, desc[i].node_id.val); + if (ret < 0) + goto free_desc; + len += ret; + } + + ret = simple_read_from_buffer(to, count, ppos, buf, len); +free_desc: + kfree(desc); +exit: + kfree(buf); + return ret; +} + +static ssize_t probe_points_write(struct file *file, const char __user *from, size_t count, + loff_t *ppos) +{ + struct avs_dev *adev = file->private_data; + struct avs_probe_point_desc *desc; + u32 *array, num_elems; + size_t bytes; + int ret; + + ret = parse_int_array_user(from, count, (int **)&array); + if (ret < 0) + return ret; + + num_elems = *array; + bytes = sizeof(*array) * num_elems; + if (bytes % sizeof(*desc)) { + ret = -EINVAL; + goto exit; + } + + desc = (struct avs_probe_point_desc *)&array[1]; + ret = avs_ipc_probe_connect_points(adev, desc, bytes / sizeof(*desc)); + if (ret) + ret = AVS_IPC_RET(ret); + else + ret = count; +exit: + kfree(array); + return ret; +} + +static const struct file_operations probe_points_fops = { + .open = simple_open, + .read = probe_points_read, + .write = probe_points_write, + .llseek = no_llseek, +}; + +static ssize_t probe_points_disconnect_write(struct file *file, const char __user *from, + size_t count, loff_t *ppos) +{ + struct avs_dev *adev = file->private_data; + union avs_probe_point_id *id; + u32 *array, num_elems; + size_t bytes; + int ret; + + ret = parse_int_array_user(from, count, (int **)&array); + if (ret < 0) + return ret; + + num_elems = *array; + bytes = sizeof(*array) * num_elems; + if (bytes % sizeof(*id)) { + ret = -EINVAL; + goto exit; + } + + id = (union avs_probe_point_id *)&array[1]; + ret = avs_ipc_probe_disconnect_points(adev, id, bytes / sizeof(*id)); + if (ret) + ret = AVS_IPC_RET(ret); + else + ret = count; +exit: + kfree(array); + return ret; +} + +static const struct file_operations probe_points_disconnect_fops = { + .open = simple_open, + .write = probe_points_disconnect_write, + .llseek = default_llseek, +}; + +static ssize_t strace_read(struct file *file, char __user *to, size_t count, loff_t *ppos) +{ + struct avs_dev *adev = file->private_data; + struct kfifo *fifo = &adev->trace_fifo; + unsigned int copied; + + if (kfifo_is_empty(fifo)) { + DEFINE_WAIT(wait); + + prepare_to_wait(&adev->trace_waitq, &wait, TASK_INTERRUPTIBLE); + if (!signal_pending(current)) + schedule(); + finish_wait(&adev->trace_waitq, &wait); + } + + if (kfifo_to_user(fifo, to, count, &copied)) + return -EFAULT; + *ppos += copied; + return copied; +} + +static int strace_open(struct inode *inode, struct file *file) +{ + struct avs_dev *adev = inode->i_private; + int ret; + + if (kfifo_initialized(&adev->trace_fifo)) + return -EBUSY; + + ret = kfifo_alloc(&adev->trace_fifo, PAGE_SIZE, GFP_KERNEL); + if (ret < 0) + return ret; + + file->private_data = adev; + return 0; +} + +static int strace_release(struct inode *inode, struct file *file) +{ + union avs_notify_msg msg = AVS_NOTIFICATION(LOG_BUFFER_STATUS); + struct avs_dev *adev = file->private_data; + unsigned long resource_mask; + unsigned long flags, i; + u32 num_cores; + + resource_mask = adev->logged_resources; + num_cores = adev->hw_cfg.dsp_cores; + + spin_lock_irqsave(&adev->trace_lock, flags); + + /* Gather any remaining logs. */ + for_each_set_bit(i, &resource_mask, num_cores) { + msg.log.core = i; + avs_dsp_op(adev, log_buffer_status, &msg); + } + + kfifo_free(&adev->trace_fifo); + + spin_unlock_irqrestore(&adev->trace_lock, flags); + + return 0; +} + +static const struct file_operations strace_fops = { + .llseek = default_llseek, + .read = strace_read, + .open = strace_open, + .release = strace_release, +}; + +#define DISABLE_TIMERS UINT_MAX + +static int enable_logs(struct avs_dev *adev, u32 resource_mask, u32 *priorities) +{ + int ret; + + /* Logging demands D0i0 state from DSP. */ + if (!adev->logged_resources) { + pm_runtime_get_sync(adev->dev); + + ret = avs_dsp_disable_d0ix(adev); + if (ret) + goto err_d0ix; + } + + ret = avs_ipc_set_system_time(adev); + if (ret && ret != AVS_IPC_NOT_SUPPORTED) { + ret = AVS_IPC_RET(ret); + goto err_ipc; + } + + ret = avs_dsp_op(adev, enable_logs, AVS_LOG_ENABLE, adev->aging_timer_period, + adev->fifo_full_timer_period, resource_mask, priorities); + if (ret) + goto err_ipc; + + adev->logged_resources |= resource_mask; + return 0; + +err_ipc: + if (!adev->logged_resources) { + avs_dsp_enable_d0ix(adev); +err_d0ix: + pm_runtime_mark_last_busy(adev->dev); + pm_runtime_put_autosuspend(adev->dev); + } + + return ret; +} + +static int disable_logs(struct avs_dev *adev, u32 resource_mask) +{ + int ret; + + /* Check if there's anything to do. */ + if (!adev->logged_resources) + return 0; + + ret = avs_dsp_op(adev, enable_logs, AVS_LOG_DISABLE, DISABLE_TIMERS, DISABLE_TIMERS, + resource_mask, NULL); + + /* + * If IPC fails causing recovery, logged_resources is already zero + * so unsetting bits is still safe. + */ + adev->logged_resources &= ~resource_mask; + + /* If that's the last resource, allow for D3. */ + if (!adev->logged_resources) { + avs_dsp_enable_d0ix(adev); + pm_runtime_mark_last_busy(adev->dev); + pm_runtime_put_autosuspend(adev->dev); + } + + return ret; +} + +static ssize_t trace_control_read(struct file *file, char __user *to, size_t count, loff_t *ppos) +{ + struct avs_dev *adev = file->private_data; + char buf[64]; + int len; + + len = snprintf(buf, sizeof(buf), "0x%08x\n", adev->logged_resources); + + return simple_read_from_buffer(to, count, ppos, buf, len); +} + +static ssize_t trace_control_write(struct file *file, const char __user *from, size_t count, + loff_t *ppos) +{ + struct avs_dev *adev = file->private_data; + u32 *array, num_elems; + u32 resource_mask; + int ret; + + ret = parse_int_array_user(from, count, (int **)&array); + if (ret < 0) + return ret; + + num_elems = *array; + resource_mask = array[1]; + + /* + * Disable if just resource mask is provided - no log priority flags. + * + * Enable input format: mask, prio1, .., prioN + * Where 'N' equals number of bits set in the 'mask'. + */ + if (num_elems == 1) { + ret = disable_logs(adev, resource_mask); + } else { + if (num_elems != (hweight_long(resource_mask) + 1)) { + ret = -EINVAL; + goto free_array; + } + + ret = enable_logs(adev, resource_mask, &array[2]); + } + + if (!ret) + ret = count; +free_array: + kfree(array); + return ret; +} + +static const struct file_operations trace_control_fops = { + .llseek = default_llseek, + .read = trace_control_read, + .write = trace_control_write, + .open = simple_open, +}; + +void avs_debugfs_init(struct avs_dev *adev) +{ + init_waitqueue_head(&adev->trace_waitq); + spin_lock_init(&adev->trace_lock); + + adev->debugfs_root = debugfs_create_dir("avs", snd_soc_debugfs_root); + + /* Initialize timer periods with recommended defaults. */ + adev->aging_timer_period = 10; + adev->fifo_full_timer_period = 10; + + debugfs_create_file("strace", 0444, adev->debugfs_root, adev, &strace_fops); + debugfs_create_file("trace_control", 0644, adev->debugfs_root, adev, &trace_control_fops); + debugfs_create_file("fw_regs", 0444, adev->debugfs_root, adev, &fw_regs_fops); + debugfs_create_file("debug_window", 0444, adev->debugfs_root, adev, &debug_window_fops); + + debugfs_create_u32("trace_aging_period", 0644, adev->debugfs_root, + &adev->aging_timer_period); + debugfs_create_u32("trace_fifo_full_period", 0644, adev->debugfs_root, + &adev->fifo_full_timer_period); + + debugfs_create_file("probe_points", 0644, adev->debugfs_root, adev, &probe_points_fops); + debugfs_create_file("probe_points_disconnect", 0200, adev->debugfs_root, adev, + &probe_points_disconnect_fops); +} + +void avs_debugfs_exit(struct avs_dev *adev) +{ + debugfs_remove_recursive(adev->debugfs_root); +} diff --git a/sound/soc/intel/avs/ipc.c b/sound/soc/intel/avs/ipc.c index af8a260093f4..bdf013c3dd12 100644 --- a/sound/soc/intel/avs/ipc.c +++ b/sound/soc/intel/avs/ipc.c @@ -266,7 +266,7 @@ static void avs_dsp_process_notification(struct avs_dev *adev, u64 header) break; case AVS_NOTIFY_LOG_BUFFER_STATUS: - avs_dsp_op(adev, log_buffer_status, &msg); + avs_log_buffer_status_locked(adev, &msg); break; case AVS_NOTIFY_EXCEPTION_CAUGHT: diff --git a/sound/soc/intel/avs/messages.c b/sound/soc/intel/avs/messages.c index 6b0fecbf07c3..e11ae4246416 100644 --- a/sound/soc/intel/avs/messages.c +++ b/sound/soc/intel/avs/messages.c @@ -685,6 +685,24 @@ int avs_ipc_get_modules_info(struct avs_dev *adev, struct avs_mods_info **info) return 0; } +int avs_ipc_copier_set_sink_format(struct avs_dev *adev, u16 module_id, + u8 instance_id, u32 sink_id, + const struct avs_audio_format *src_fmt, + const struct avs_audio_format *sink_fmt) +{ + struct avs_copier_sink_format cpr_fmt; + + cpr_fmt.sink_id = sink_id; + /* Firmware expects driver to resend copier's input format. */ + cpr_fmt.src_fmt = *src_fmt; + cpr_fmt.sink_fmt = *sink_fmt; + + return avs_ipc_set_large_config(adev, module_id, instance_id, + AVS_COPIER_SET_SINK_FORMAT, + (u8 *)&cpr_fmt, sizeof(cpr_fmt)); +} + +#ifdef CONFIG_DEBUG_FS int avs_ipc_set_enable_logs(struct avs_dev *adev, u8 *log_info, size_t size) { return avs_ipc_set_large_config(adev, AVS_BASEFW_MOD_ID, AVS_BASEFW_INST_ID, @@ -705,19 +723,81 @@ int avs_ipc_set_system_time(struct avs_dev *adev) AVS_BASEFW_SYSTEM_TIME, (u8 *)&sys_time, sizeof(sys_time)); } -int avs_ipc_copier_set_sink_format(struct avs_dev *adev, u16 module_id, - u8 instance_id, u32 sink_id, - const struct avs_audio_format *src_fmt, - const struct avs_audio_format *sink_fmt) +int avs_ipc_probe_get_dma(struct avs_dev *adev, struct avs_probe_dma **dmas, size_t *num_dmas) { - struct avs_copier_sink_format cpr_fmt; + size_t payload_size; + u32 module_id; + u8 *payload; + int ret; - cpr_fmt.sink_id = sink_id; - /* Firmware expects driver to resend copier's input format. */ - cpr_fmt.src_fmt = *src_fmt; - cpr_fmt.sink_fmt = *sink_fmt; + module_id = avs_get_module_id(adev, &AVS_PROBE_MOD_UUID); - return avs_ipc_set_large_config(adev, module_id, instance_id, - AVS_COPIER_SET_SINK_FORMAT, - (u8 *)&cpr_fmt, sizeof(cpr_fmt)); + ret = avs_ipc_get_large_config(adev, module_id, AVS_PROBE_INST_ID, AVS_PROBE_INJECTION_DMA, + NULL, 0, &payload, &payload_size); + if (ret) + return ret; + + *dmas = (struct avs_probe_dma *)payload; + *num_dmas = payload_size / sizeof(**dmas); + + return 0; +} + +int avs_ipc_probe_attach_dma(struct avs_dev *adev, struct avs_probe_dma *dmas, size_t num_dmas) +{ + u32 module_id = avs_get_module_id(adev, &AVS_PROBE_MOD_UUID); + + return avs_ipc_set_large_config(adev, module_id, AVS_PROBE_INST_ID, AVS_PROBE_INJECTION_DMA, + (u8 *)dmas, array_size(sizeof(*dmas), num_dmas)); +} + +int avs_ipc_probe_detach_dma(struct avs_dev *adev, union avs_connector_node_id *node_ids, + size_t num_node_ids) +{ + u32 module_id = avs_get_module_id(adev, &AVS_PROBE_MOD_UUID); + + return avs_ipc_set_large_config(adev, module_id, AVS_PROBE_INST_ID, + AVS_PROBE_INJECTION_DMA_DETACH, (u8 *)node_ids, + array_size(sizeof(*node_ids), num_node_ids)); +} + +int avs_ipc_probe_get_points(struct avs_dev *adev, struct avs_probe_point_desc **descs, + size_t *num_descs) +{ + size_t payload_size; + u32 module_id; + u8 *payload; + int ret; + + module_id = avs_get_module_id(adev, &AVS_PROBE_MOD_UUID); + + ret = avs_ipc_get_large_config(adev, module_id, AVS_PROBE_INST_ID, AVS_PROBE_POINTS, NULL, + 0, &payload, &payload_size); + if (ret) + return ret; + + *descs = (struct avs_probe_point_desc *)payload; + *num_descs = payload_size / sizeof(**descs); + + return 0; +} + +int avs_ipc_probe_connect_points(struct avs_dev *adev, struct avs_probe_point_desc *descs, + size_t num_descs) +{ + u32 module_id = avs_get_module_id(adev, &AVS_PROBE_MOD_UUID); + + return avs_ipc_set_large_config(adev, module_id, AVS_PROBE_INST_ID, AVS_PROBE_POINTS, + (u8 *)descs, array_size(sizeof(*descs), num_descs)); +} + +int avs_ipc_probe_disconnect_points(struct avs_dev *adev, union avs_probe_point_id *ids, + size_t num_ids) +{ + u32 module_id = avs_get_module_id(adev, &AVS_PROBE_MOD_UUID); + + return avs_ipc_set_large_config(adev, module_id, AVS_PROBE_INST_ID, + AVS_PROBE_POINTS_DISCONNECT, (u8 *)ids, + array_size(sizeof(*ids), num_ids)); } +#endif diff --git a/sound/soc/intel/avs/messages.h b/sound/soc/intel/avs/messages.h index 02b3b7a74783..9dd835527e02 100644 --- a/sound/soc/intel/avs/messages.h +++ b/sound/soc/intel/avs/messages.h @@ -802,4 +802,57 @@ int avs_ipc_copier_set_sink_format(struct avs_dev *adev, u16 module_id, const struct avs_audio_format *src_fmt, const struct avs_audio_format *sink_fmt); +#define AVS_PROBE_INST_ID 0 + +enum avs_probe_runtime_param { + AVS_PROBE_INJECTION_DMA = 1, + AVS_PROBE_INJECTION_DMA_DETACH, + AVS_PROBE_POINTS, + AVS_PROBE_POINTS_DISCONNECT, +}; + +struct avs_probe_dma { + union avs_connector_node_id node_id; + u32 dma_buffer_size; +} __packed; + +enum avs_probe_type { + AVS_PROBE_TYPE_INPUT = 0, + AVS_PROBE_TYPE_OUTPUT, + AVS_PROBE_TYPE_INTERNAL +}; + +union avs_probe_point_id { + u32 value; + struct { + u32 module_id:16; + u32 instance_id:8; + u32 type:2; + u32 index:6; + } id; +} __packed; + +enum avs_connection_purpose { + AVS_CONNECTION_PURPOSE_EXTRACT = 0, + AVS_CONNECTION_PURPOSE_INJECT, + AVS_CONNECTION_PURPOSE_INJECT_REEXTRACT, +}; + +struct avs_probe_point_desc { + union avs_probe_point_id id; + u32 purpose; + union avs_connector_node_id node_id; +} __packed; + +int avs_ipc_probe_get_dma(struct avs_dev *adev, struct avs_probe_dma **dmas, size_t *num_dmas); +int avs_ipc_probe_attach_dma(struct avs_dev *adev, struct avs_probe_dma *dmas, size_t num_dmas); +int avs_ipc_probe_detach_dma(struct avs_dev *adev, union avs_connector_node_id *node_ids, + size_t num_node_ids); +int avs_ipc_probe_get_points(struct avs_dev *adev, struct avs_probe_point_desc **descs, + size_t *num_descs); +int avs_ipc_probe_connect_points(struct avs_dev *adev, struct avs_probe_point_desc *descs, + size_t num_descs); +int avs_ipc_probe_disconnect_points(struct avs_dev *adev, union avs_probe_point_id *ids, + size_t num_ids); + #endif /* __SOUND_SOC_INTEL_AVS_MSGS_H */ diff --git a/sound/soc/intel/avs/pcm.c b/sound/soc/intel/avs/pcm.c index 70d687fa9923..f930c5e86a84 100644 --- a/sound/soc/intel/avs/pcm.c +++ b/sound/soc/intel/avs/pcm.c @@ -1126,9 +1126,9 @@ static const struct snd_soc_component_driver avs_component_driver = { .topology_name_prefix = "intel/avs", }; -static int avs_soc_component_register(struct device *dev, const char *name, - const struct snd_soc_component_driver *drv, - struct snd_soc_dai_driver *cpu_dais, int num_cpu_dais) +int avs_soc_component_register(struct device *dev, const char *name, + const struct snd_soc_component_driver *drv, + struct snd_soc_dai_driver *cpu_dais, int num_cpu_dais) { struct avs_soc_component *acomp; int ret; diff --git a/sound/soc/intel/avs/probes.c b/sound/soc/intel/avs/probes.c new file mode 100644 index 000000000000..29d63f2a9616 --- /dev/null +++ b/sound/soc/intel/avs/probes.c @@ -0,0 +1,313 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// Copyright(c) 2021-2022 Intel Corporation. All rights reserved. +// +// Authors: Cezary Rojewski <cezary.rojewski@intel.com> +// Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com> +// + +#include <sound/compress_driver.h> +#include <sound/hdaudio_ext.h> +#include <sound/hdaudio.h> +#include <sound/soc.h> +#include "avs.h" +#include "messages.h" + +static int avs_dsp_init_probe(struct avs_dev *adev, union avs_connector_node_id node_id, + size_t buffer_size) +{ + struct avs_probe_cfg cfg = {{0}}; + struct avs_module_entry mentry; + u16 dummy; + + avs_get_module_entry(adev, &AVS_PROBE_MOD_UUID, &mentry); + + /* + * Probe module uses no cycles, audio data format and input and output + * frame sizes are unused. It is also not owned by any pipeline. + */ + cfg.base.ibs = 1; + /* BSS module descriptor is always segment of index=2. */ + cfg.base.is_pages = mentry.segments[2].flags.length; + cfg.gtw_cfg.node_id = node_id; + cfg.gtw_cfg.dma_buffer_size = buffer_size; + + return avs_dsp_init_module(adev, mentry.module_id, INVALID_PIPELINE_ID, 0, 0, &cfg, + sizeof(cfg), &dummy); +} + +static void avs_dsp_delete_probe(struct avs_dev *adev) +{ + struct avs_module_entry mentry; + + avs_get_module_entry(adev, &AVS_PROBE_MOD_UUID, &mentry); + + /* There is only ever one probe module instance. */ + avs_dsp_delete_module(adev, mentry.module_id, 0, INVALID_PIPELINE_ID, 0); +} + +static inline struct hdac_ext_stream *avs_compr_get_host_stream(struct snd_compr_stream *cstream) +{ + return cstream->runtime->private_data; +} + +static int avs_probe_compr_open(struct snd_compr_stream *cstream, struct snd_soc_dai *dai) +{ + struct avs_dev *adev = to_avs_dev(dai->dev); + struct hdac_bus *bus = &adev->base.core; + struct hdac_ext_stream *host_stream; + + if (adev->extractor) { + dev_err(dai->dev, "Cannot open more than one extractor stream\n"); + return -EEXIST; + } + + host_stream = snd_hdac_ext_cstream_assign(bus, cstream); + if (!host_stream) { + dev_err(dai->dev, "Failed to assign HDAudio stream for extraction\n"); + return -EBUSY; + } + + adev->extractor = host_stream; + hdac_stream(host_stream)->curr_pos = 0; + cstream->runtime->private_data = host_stream; + + return 0; +} + +static int avs_probe_compr_free(struct snd_compr_stream *cstream, struct snd_soc_dai *dai) +{ + struct hdac_ext_stream *host_stream = avs_compr_get_host_stream(cstream); + struct avs_dev *adev = to_avs_dev(dai->dev); + struct avs_probe_point_desc *desc; + /* Extractor node identifier. */ + unsigned int vindex = INVALID_NODE_ID.vindex; + size_t num_desc; + int i, ret; + + /* Disconnect all probe points. */ + ret = avs_ipc_probe_get_points(adev, &desc, &num_desc); + if (ret) { + dev_err(dai->dev, "get probe points failed: %d\n", ret); + ret = AVS_IPC_RET(ret); + goto exit; + } + + for (i = 0; i < num_desc; i++) + if (desc[i].node_id.vindex == vindex) + avs_ipc_probe_disconnect_points(adev, &desc[i].id, 1); + kfree(desc); + +exit: + if (adev->num_probe_streams) { + adev->num_probe_streams--; + if (!adev->num_probe_streams) { + avs_dsp_delete_probe(adev); + avs_dsp_enable_d0ix(adev); + } + } + + snd_hdac_stream_cleanup(hdac_stream(host_stream)); + hdac_stream(host_stream)->prepared = 0; + snd_hdac_ext_stream_release(host_stream, HDAC_EXT_STREAM_TYPE_HOST); + + snd_compr_free_pages(cstream); + adev->extractor = NULL; + + return ret; +} + +static int avs_probe_compr_set_params(struct snd_compr_stream *cstream, + struct snd_compr_params *params, struct snd_soc_dai *dai) +{ + struct hdac_ext_stream *host_stream = avs_compr_get_host_stream(cstream); + struct snd_compr_runtime *rtd = cstream->runtime; + struct avs_dev *adev = to_avs_dev(dai->dev); + /* compr params do not store bit depth, default to S32_LE. */ + snd_pcm_format_t format = SNDRV_PCM_FORMAT_S32_LE; + unsigned int format_val; + int bps, ret; + + hdac_stream(host_stream)->bufsize = 0; + hdac_stream(host_stream)->period_bytes = 0; + hdac_stream(host_stream)->format_val = 0; + cstream->dma_buffer.dev.type = SNDRV_DMA_TYPE_DEV_SG; + cstream->dma_buffer.dev.dev = adev->dev; + + ret = snd_compr_malloc_pages(cstream, rtd->buffer_size); + if (ret < 0) + return ret; + bps = snd_pcm_format_physical_width(format); + if (bps < 0) + return bps; + format_val = snd_hdac_calc_stream_format(params->codec.sample_rate, params->codec.ch_out, + format, bps, 0); + ret = snd_hdac_stream_set_params(hdac_stream(host_stream), format_val); + if (ret < 0) + return ret; + ret = snd_hdac_stream_setup(hdac_stream(host_stream)); + if (ret < 0) + return ret; + + hdac_stream(host_stream)->prepared = 1; + + if (!adev->num_probe_streams) { + union avs_connector_node_id node_id; + + /* D0ix not allowed during probing. */ + ret = avs_dsp_disable_d0ix(adev); + if (ret) + return ret; + + node_id.vindex = hdac_stream(host_stream)->stream_tag - 1; + node_id.dma_type = AVS_DMA_HDA_HOST_INPUT; + + ret = avs_dsp_init_probe(adev, node_id, rtd->dma_bytes); + if (ret < 0) { + dev_err(dai->dev, "probe init failed: %d\n", ret); + avs_dsp_enable_d0ix(adev); + return ret; + } + } + + adev->num_probe_streams++; + return 0; +} + +static int avs_probe_compr_trigger(struct snd_compr_stream *cstream, int cmd, + struct snd_soc_dai *dai) +{ + struct hdac_ext_stream *host_stream = avs_compr_get_host_stream(cstream); + struct avs_dev *adev = to_avs_dev(dai->dev); + struct hdac_bus *bus = &adev->base.core; + unsigned long cookie; + + if (!hdac_stream(host_stream)->prepared) + return -EPIPE; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + case SNDRV_PCM_TRIGGER_RESUME: + spin_lock_irqsave(&bus->reg_lock, cookie); + snd_hdac_stream_start(hdac_stream(host_stream), true); + spin_unlock_irqrestore(&bus->reg_lock, cookie); + break; + + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_STOP: + spin_lock_irqsave(&bus->reg_lock, cookie); + snd_hdac_stream_stop(hdac_stream(host_stream)); + spin_unlock_irqrestore(&bus->reg_lock, cookie); + break; + + default: + return -EINVAL; + } + + return 0; +} + +static int avs_probe_compr_pointer(struct snd_compr_stream *cstream, + struct snd_compr_tstamp *tstamp, struct snd_soc_dai *dai) +{ + struct hdac_ext_stream *host_stream = avs_compr_get_host_stream(cstream); + struct snd_soc_pcm_stream *pstream; + + pstream = &dai->driver->capture; + tstamp->copied_total = hdac_stream(host_stream)->curr_pos; + tstamp->sampling_rate = snd_pcm_rate_bit_to_rate(pstream->rates); + + return 0; +} + +static int avs_probe_compr_copy(struct snd_soc_component *comp, struct snd_compr_stream *cstream, + char __user *buf, size_t count) +{ + struct snd_compr_runtime *rtd = cstream->runtime; + unsigned int offset, n; + void *ptr; + int ret; + + if (count > rtd->buffer_size) + count = rtd->buffer_size; + + div_u64_rem(rtd->total_bytes_transferred, rtd->buffer_size, &offset); + ptr = rtd->dma_area + offset; + n = rtd->buffer_size - offset; + + if (count < n) { + ret = copy_to_user(buf, ptr, count); + } else { + ret = copy_to_user(buf, ptr, n); + ret += copy_to_user(buf + n, rtd->dma_area, count - n); + } + + if (ret) + return count - ret; + return count; +} + +static const struct snd_soc_cdai_ops avs_probe_dai_ops = { + .startup = avs_probe_compr_open, + .shutdown = avs_probe_compr_free, + .set_params = avs_probe_compr_set_params, + .trigger = avs_probe_compr_trigger, + .pointer = avs_probe_compr_pointer, +}; + +static const struct snd_compress_ops avs_probe_compress_ops = { + .copy = avs_probe_compr_copy, +}; + +static struct snd_soc_dai_driver probe_cpu_dais[] = { +{ + .name = "Probe Extraction CPU DAI", + .compress_new = snd_soc_new_compress, + .cops = &avs_probe_dai_ops, + .capture = { + .stream_name = "Probe Extraction", + .channels_min = 1, + .channels_max = 8, + .rates = SNDRV_PCM_RATE_48000, + .rate_min = 48000, + .rate_max = 48000, + }, +}, +}; + +static int avs_probe_component_probe(struct snd_soc_component *component) +{ + struct avs_soc_component *acomp = to_avs_soc_component(component); + struct avs_dev *adev = to_avs_dev(component->dev); + + mutex_lock(&adev->comp_list_mutex); + list_add_tail(&acomp->node, &adev->comp_list); + mutex_unlock(&adev->comp_list_mutex); + return 0; +} + +static void avs_probe_component_remove(struct snd_soc_component *component) +{ + struct avs_soc_component *acomp = to_avs_soc_component(component); + struct avs_dev *adev = to_avs_dev(component->dev); + + mutex_lock(&adev->comp_list_mutex); + list_del(&acomp->node); + mutex_unlock(&adev->comp_list_mutex); +} + +static const struct snd_soc_component_driver avs_probe_component_driver = { + .name = "avs-probe-compr", + .probe = avs_probe_component_probe, + .remove = avs_probe_component_remove, + .compress_ops = &avs_probe_compress_ops, + .module_get_upon_open = 1, /* increment refcount when a stream is opened */ +}; + +int avs_probe_platform_register(struct avs_dev *adev, const char *name) +{ + return avs_soc_component_register(adev->dev, name, &avs_probe_component_driver, + probe_cpu_dais, ARRAY_SIZE(probe_cpu_dais)); +} diff --git a/sound/soc/intel/avs/registers.h b/sound/soc/intel/avs/registers.h index 95be86148cf3..2b464e466ed5 100644 --- a/sound/soc/intel/avs/registers.h +++ b/sound/soc/intel/avs/registers.h @@ -59,7 +59,8 @@ #define AVS_FW_REG_STATUS(adev) (AVS_FW_REG_BASE(adev) + 0x0) #define AVS_FW_REG_ERROR_CODE(adev) (AVS_FW_REG_BASE(adev) + 0x4) -#define AVS_FW_REGS_SIZE PAGE_SIZE +#define AVS_WINDOW_CHUNK_SIZE PAGE_SIZE +#define AVS_FW_REGS_SIZE AVS_WINDOW_CHUNK_SIZE #define AVS_FW_REGS_WINDOW 0 /* DSP -> HOST communication window */ #define AVS_UPLINK_WINDOW AVS_FW_REGS_WINDOW diff --git a/sound/soc/intel/avs/skl.c b/sound/soc/intel/avs/skl.c index dc98b5cf900f..6bb8bbc70442 100644 --- a/sound/soc/intel/avs/skl.c +++ b/sound/soc/intel/avs/skl.c @@ -12,8 +12,9 @@ #include "avs.h" #include "messages.h" -static int skl_enable_logs(struct avs_dev *adev, enum avs_log_enable enable, u32 aging_period, - u32 fifo_full_period, unsigned long resource_mask, u32 *priorities) +static int __maybe_unused +skl_enable_logs(struct avs_dev *adev, enum avs_log_enable enable, u32 aging_period, + u32 fifo_full_period, unsigned long resource_mask, u32 *priorities) { struct skl_log_state_info *info; u32 size, num_cores = adev->hw_cfg.dsp_cores; @@ -55,15 +56,11 @@ int skl_log_buffer_offset(struct avs_dev *adev, u32 core) static int skl_log_buffer_status(struct avs_dev *adev, union avs_notify_msg *msg) { - unsigned long flags; void __iomem *buf; u16 size, write, offset; - spin_lock_irqsave(&adev->dbg.trace_lock, flags); - if (!kfifo_initialized(&adev->dbg.trace_fifo)) { - spin_unlock_irqrestore(&adev->dbg.trace_lock, flags); + if (!avs_logging_fw(adev)) return 0; - } size = avs_log_buffer_size(adev) / 2; write = readl(avs_sram_addr(adev, AVS_FW_REGS_WINDOW) + FW_REGS_DBG_LOG_WP(msg->log.core)); @@ -72,9 +69,7 @@ skl_log_buffer_status(struct avs_dev *adev, union avs_notify_msg *msg) /* Address is guaranteed to exist in SRAM2. */ buf = avs_log_buffer_addr(adev, msg->log.core) + offset; - __kfifo_fromio_locked(&adev->dbg.trace_fifo, buf, size, &adev->dbg.fifo_lock); - wake_up(&adev->dbg.trace_waitq); - spin_unlock_irqrestore(&adev->dbg.trace_lock, flags); + avs_dump_fw_log_wakeup(adev, buf, size); return 0; } @@ -116,10 +111,10 @@ const struct avs_dsp_ops skl_dsp_ops = { .load_basefw = avs_cldma_load_basefw, .load_lib = avs_cldma_load_library, .transfer_mods = avs_cldma_transfer_modules, - .enable_logs = skl_enable_logs, .log_buffer_offset = skl_log_buffer_offset, .log_buffer_status = skl_log_buffer_status, .coredump = skl_coredump, .d0ix_toggle = skl_d0ix_toggle, .set_d0ix = skl_set_d0ix, + AVS_SET_ENABLE_LOGS_OP(skl) }; diff --git a/sound/soc/intel/avs/utils.c b/sound/soc/intel/avs/utils.c index 13611dee9787..82416b86662d 100644 --- a/sound/soc/intel/avs/utils.c +++ b/sound/soc/intel/avs/utils.c @@ -300,25 +300,3 @@ void avs_release_firmwares(struct avs_dev *adev) kfree(entry); } } - -unsigned int __kfifo_fromio_locked(struct kfifo *fifo, const void __iomem *src, unsigned int len, - spinlock_t *lock) -{ - struct __kfifo *__fifo = &fifo->kfifo; - unsigned long flags; - unsigned int l, off; - - spin_lock_irqsave(lock, flags); - len = min(len, kfifo_avail(fifo)); - off = __fifo->in & __fifo->mask; - l = min(len, kfifo_size(fifo) - off); - - memcpy_fromio(__fifo->data + off, src, l); - memcpy_fromio(__fifo->data, src + l, len - l); - /* Make sure data copied from SRAM is visible to all CPUs. */ - smp_mb(); - __fifo->in += len; - spin_unlock_irqrestore(lock, flags); - - return len; -} diff --git a/sound/soc/intel/boards/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/bytcr_rt5640.c b/sound/soc/intel/boards/bytcr_rt5640.c index 6db07b2417ca..4699ca79f3ea 100644 --- a/sound/soc/intel/boards/bytcr_rt5640.c +++ b/sound/soc/intel/boards/bytcr_rt5640.c @@ -571,6 +571,21 @@ static const struct dmi_system_id byt_rt5640_quirk_table[] = { BYT_RT5640_MCLK_EN), }, { + /* Advantech MICA-071 */ + .matches = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Advantech"), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "MICA-071"), + }, + /* OVCD Th = 1500uA to reliable detect head-phones vs -set */ + .driver_data = (void *)(BYT_RT5640_IN3_MAP | + BYT_RT5640_JD_SRC_JD2_IN4N | + BYT_RT5640_OVCD_TH_1500UA | + BYT_RT5640_OVCD_SF_0P75 | + BYT_RT5640_MONO_SPEAKER | + BYT_RT5640_DIFF_MIC | + BYT_RT5640_MCLK_EN), + }, + { .matches = { DMI_EXACT_MATCH(DMI_SYS_VENDOR, "ARCHOS"), DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "ARCHOS 80 Cesium"), @@ -796,6 +811,16 @@ static const struct dmi_system_id byt_rt5640_quirk_table[] = { BYT_RT5640_SSP0_AIF1 | BYT_RT5640_MCLK_EN), }, + { /* HP Stream 8 */ + .matches = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "HP Stream 8 Tablet"), + }, + .driver_data = (void *)(BYTCR_INPUT_DEFAULTS | + BYT_RT5640_JD_NOT_INV | + BYT_RT5640_SSP0_AIF1 | + BYT_RT5640_MCLK_EN), + }, { /* I.T.Works TW891 */ .matches = { DMI_EXACT_MATCH(DMI_SYS_VENDOR, "To be filled by O.E.M."), 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); diff --git a/sound/soc/intel/boards/sof_realtek_common.c b/sound/soc/intel/boards/sof_realtek_common.c index ff2851fc8930..6c12ca92f371 100644 --- a/sound/soc/intel/boards/sof_realtek_common.c +++ b/sound/soc/intel/boards/sof_realtek_common.c @@ -267,7 +267,8 @@ static int rt1015_hw_params(struct snd_pcm_substream *substream, struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); struct snd_soc_dai_link *dai_link = rtd->dai_link; struct snd_soc_dai *codec_dai; - int i, clk_freq, ret; + int i, clk_freq; + int ret = 0; clk_freq = sof_dai_get_bclk(rtd); diff --git a/sound/soc/intel/boards/sof_rt5682.c b/sound/soc/intel/boards/sof_rt5682.c index 4a2f91249b10..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/boards/sof_sdw.c b/sound/soc/intel/boards/sof_sdw.c index b58c7b35599d..d2ed807abde9 100644 --- a/sound/soc/intel/boards/sof_sdw.c +++ b/sound/soc/intel/boards/sof_sdw.c @@ -550,23 +550,23 @@ static struct sof_sdw_codec_info codec_info_list[] = { .direction = {true, false}, .dai_name = "rt1308-aif", .ops = &sof_sdw_rt1308_i2s_ops, - .init = sof_sdw_rt1308_init, - .exit = sof_sdw_rt1308_exit, + .init = sof_sdw_rt_amp_init, + .exit = sof_sdw_rt_amp_exit, .codec_type = SOF_SDW_CODEC_TYPE_AMP, }, { .part_id = 0x1316, .direction = {true, true}, .dai_name = "rt1316-aif", - .init = sof_sdw_rt1316_init, - .exit = sof_sdw_rt1316_exit, + .init = sof_sdw_rt_amp_init, + .exit = sof_sdw_rt_amp_exit, .codec_type = SOF_SDW_CODEC_TYPE_AMP, }, { .part_id = 0x1318, .direction = {true, true}, .dai_name = "rt1318-aif", - .init = sof_sdw_rt1318_init, + .init = sof_sdw_rt_amp_init, .codec_type = SOF_SDW_CODEC_TYPE_AMP, }, { diff --git a/sound/soc/intel/boards/sof_sdw_common.h b/sound/soc/intel/boards/sof_sdw_common.h index 54a50f7da4da..350010b0e5f4 100644 --- a/sound/soc/intel/boards/sof_sdw_common.h +++ b/sound/soc/intel/boards/sof_sdw_common.h @@ -125,30 +125,18 @@ int sof_sdw_rt700_init(struct snd_soc_card *card, struct sof_sdw_codec_info *info, bool playback); -/* RT1308 support */ +/* RT1308 I2S support */ extern struct snd_soc_ops sof_sdw_rt1308_i2s_ops; -int sof_sdw_rt1308_init(struct snd_soc_card *card, +/* generic amp support */ +int sof_sdw_rt_amp_init(struct snd_soc_card *card, const struct snd_soc_acpi_link_adr *link, struct snd_soc_dai_link *dai_links, struct sof_sdw_codec_info *info, bool playback); -int sof_sdw_rt1308_exit(struct snd_soc_card *card, struct snd_soc_dai_link *dai_link); +int sof_sdw_rt_amp_exit(struct snd_soc_card *card, struct snd_soc_dai_link *dai_link); /* RT1316 support */ -int sof_sdw_rt1316_init(struct snd_soc_card *card, - const struct snd_soc_acpi_link_adr *link, - struct snd_soc_dai_link *dai_links, - struct sof_sdw_codec_info *info, - bool playback); -int sof_sdw_rt1316_exit(struct snd_soc_card *card, struct snd_soc_dai_link *dai_link); - -/* RT1318 support */ -int sof_sdw_rt1318_init(struct snd_soc_card *card, - const struct snd_soc_acpi_link_adr *link, - struct snd_soc_dai_link *dai_links, - struct sof_sdw_codec_info *info, - bool playback); /* RT715 support */ int sof_sdw_rt715_init(struct snd_soc_card *card, diff --git a/sound/soc/intel/boards/sof_sdw_rt1316.c b/sound/soc/intel/boards/sof_sdw_rt1316.c deleted file mode 100644 index f6bbea0d3810..000000000000 --- a/sound/soc/intel/boards/sof_sdw_rt1316.c +++ /dev/null @@ -1,239 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -// Copyright (c) 2020 Intel Corporation - -/* - * sof_sdw_rt1316 - Helpers to handle RT1316 from generic machine driver - */ - -#include <linux/device.h> -#include <linux/errno.h> -#include <sound/control.h> -#include <sound/soc.h> -#include <sound/soc-acpi.h> -#include <sound/soc-dapm.h> -#include <linux/soundwire/sdw.h> -#include <linux/soundwire/sdw_type.h> -#include <linux/dmi.h> -#include "sof_sdw_common.h" -#include "sof_sdw_amp_coeff_tables.h" - -struct rt1316_platform_data { - const unsigned char *bq_params; - const unsigned int bq_params_cnt; -}; - -static const struct rt1316_platform_data dell_0b00_platform_data = { - .bq_params = dell_0b00_bq_params, - .bq_params_cnt = ARRAY_SIZE(dell_0b00_bq_params), -}; - -static const struct dmi_system_id dmi_platform_data[] = { - /* AlderLake devices */ - { - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), - DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0B00") - }, - .driver_data = (void *)&dell_0b00_platform_data, - }, - { - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), - DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0B01") - }, - .driver_data = (void *)&dell_0b00_platform_data, - }, - { - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), - DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0AFF") - }, - .driver_data = (void *)&dell_0b00_platform_data, - }, - { - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), - DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0AFE") - }, - .driver_data = (void *)&dell_0b00_platform_data, - }, -}; - -static int rt1316_add_device_props(struct device *sdw_dev) -{ - struct property_entry props[3] = {}; - struct fwnode_handle *fwnode; - const struct dmi_system_id *dmi_data; - const struct rt1316_platform_data *pdata; - unsigned char params[RT1316_MAX_BQ_REG]; - int ret; - - dmi_data = dmi_first_match(dmi_platform_data); - if (!dmi_data) - return 0; - - pdata = dmi_data->driver_data; - memcpy(¶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 <linux/device.h> -#include <linux/errno.h> -#include <sound/control.h> -#include <sound/soc.h> -#include <sound/soc-acpi.h> -#include <sound/soc-dapm.h> -#include "sof_sdw_common.h" - -static const struct snd_soc_dapm_widget rt1318_widgets[] = { - SND_SOC_DAPM_SPK("Speaker", NULL), -}; - -/* - * dapm routes for rt1318 will be registered dynamically according - * to the number of rt1318 used. The first two entries will be registered - * for one codec case, and the last two entries are also registered - * if two 1318s are used. - */ -static const struct snd_soc_dapm_route rt1318_map[] = { - { "Speaker", NULL, "rt1318-1 SPOL" }, - { "Speaker", NULL, "rt1318-1 SPOR" }, - { "Speaker", NULL, "rt1318-2 SPOL" }, - { "Speaker", NULL, "rt1318-2 SPOR" }, -}; - -static const struct snd_kcontrol_new rt1318_controls[] = { - SOC_DAPM_PIN_SWITCH("Speaker"), -}; - -static int first_spk_init(struct snd_soc_pcm_runtime *rtd) -{ - struct snd_soc_card *card = rtd->card; - int ret; - - card->components = devm_kasprintf(card->dev, GFP_KERNEL, - "%s spk:rt1318", - card->components); - if (!card->components) - return -ENOMEM; - - ret = snd_soc_add_card_controls(card, rt1318_controls, - ARRAY_SIZE(rt1318_controls)); - if (ret) { - dev_err(card->dev, "rt1318 controls addition failed: %d\n", ret); - return ret; - } - - ret = snd_soc_dapm_new_controls(&card->dapm, rt1318_widgets, - ARRAY_SIZE(rt1318_widgets)); - if (ret) { - dev_err(card->dev, "rt1318 widgets addition failed: %d\n", ret); - return ret; - } - - ret = snd_soc_dapm_add_routes(&card->dapm, rt1318_map, 2); - if (ret) - dev_err(rtd->dev, "failed to add first SPK map: %d\n", ret); - - return ret; -} - -static int second_spk_init(struct snd_soc_pcm_runtime *rtd) -{ - struct snd_soc_card *card = rtd->card; - int ret; - - ret = snd_soc_dapm_add_routes(&card->dapm, rt1318_map + 2, 2); - if (ret) - dev_err(rtd->dev, "failed to add second SPK map: %d\n", ret); - - return ret; -} - -static int all_spk_init(struct snd_soc_pcm_runtime *rtd) -{ - int ret; - - ret = first_spk_init(rtd); - if (ret) - return ret; - - return second_spk_init(rtd); -} - -int sof_sdw_rt1318_init(struct snd_soc_card *card, - const struct snd_soc_acpi_link_adr *link, - struct snd_soc_dai_link *dai_links, - struct sof_sdw_codec_info *info, - bool playback) -{ - /* Count amp number and do init on playback link only. */ - if (!playback) - return 0; - - info->amp_num++; - if (info->amp_num == 1) - dai_links->init = first_spk_init; - - if (info->amp_num == 2) { - /* - * if two 1318s are in one dai link, the init function - * in this dai link will be first set for the first speaker, - * and it should be reset to initialize all speakers when - * the second speaker is found. - */ - if (dai_links->init) - dai_links->init = all_spk_init; - else - dai_links->init = second_spk_init; - } - - return 0; -} diff --git a/sound/soc/intel/boards/sof_sdw_rt1308.c b/sound/soc/intel/boards/sof_sdw_rt_amp.c index a19b055b9c6f..26bf9e0dd3d2 100644 --- a/sound/soc/intel/boards/sof_sdw_rt1308.c +++ b/sound/soc/intel/boards/sof_sdw_rt_amp.c @@ -1,8 +1,8 @@ // SPDX-License-Identifier: GPL-2.0-only -// Copyright (c) 2020 Intel Corporation +// Copyright (c) 2022 Intel Corporation /* - * sof_sdw_rt1308 - Helpers to handle RT1308 from generic machine driver + * sof_sdw_rt_amp - Helpers to handle RT1308/RT1316/RT1318 from generic machine driver */ #include <linux/device.h> @@ -18,16 +18,26 @@ #include "sof_sdw_amp_coeff_tables.h" #include "../../codecs/rt1308.h" -struct rt1308_platform_data { +#define CODEC_NAME_SIZE 7 + +/* choose a larger value to resolve compatibility issues */ +#define RT_AMP_MAX_BQ_REG RT1316_MAX_BQ_REG + +struct rt_amp_platform_data { const unsigned char *bq_params; const unsigned int bq_params_cnt; }; -static const struct rt1308_platform_data dell_0a5d_platform_data = { +static const struct rt_amp_platform_data dell_0a5d_platform_data = { .bq_params = dell_0a5d_bq_params, .bq_params_cnt = ARRAY_SIZE(dell_0a5d_bq_params), }; +static const struct rt_amp_platform_data dell_0b00_platform_data = { + .bq_params = dell_0b00_bq_params, + .bq_params_cnt = ARRAY_SIZE(dell_0b00_bq_params), +}; + static const struct dmi_system_id dmi_platform_data[] = { /* CometLake devices */ { @@ -59,15 +69,45 @@ static const struct dmi_system_id dmi_platform_data[] = { }, .driver_data = (void *)&dell_0a5d_platform_data, }, + /* AlderLake devices */ + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), + DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0B00") + }, + .driver_data = (void *)&dell_0b00_platform_data, + }, + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), + DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0B01") + }, + .driver_data = (void *)&dell_0b00_platform_data, + }, + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), + DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0AFF") + }, + .driver_data = (void *)&dell_0b00_platform_data, + }, + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), + DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0AFE") + }, + .driver_data = (void *)&dell_0b00_platform_data, + }, + {}, }; -static int rt1308_add_device_props(struct device *sdw_dev) +static int rt_amp_add_device_props(struct device *sdw_dev) { struct property_entry props[3] = {}; struct fwnode_handle *fwnode; const struct dmi_system_id *dmi_data; - const struct rt1308_platform_data *pdata; - unsigned char params[RT1308_MAX_BQ_REG]; + const struct rt_amp_platform_data *pdata; + unsigned char params[RT_AMP_MAX_BQ_REG]; int ret; dmi_data = dmi_first_match(dmi_platform_data); @@ -91,15 +131,19 @@ static int rt1308_add_device_props(struct device *sdw_dev) return ret; } -static const struct snd_soc_dapm_widget rt1308_widgets[] = { +static const struct snd_kcontrol_new rt_amp_controls[] = { + SOC_DAPM_PIN_SWITCH("Speaker"), +}; + +static const struct snd_soc_dapm_widget rt_amp_widgets[] = { SND_SOC_DAPM_SPK("Speaker", NULL), }; /* - * dapm routes for rt1308 will be registered dynamically according - * to the number of rt1308 used. The first two entries will be registered - * for one codec case, and the last two entries are also registered - * if two 1308s are used. + * dapm routes for rt1308/rt1316/rt1318 will be registered dynamically + * according to the number of rt1308/rt1316/rt1318 used. The first two + * entries will be registered for one codec case, and the last two entries + * are also registered if two 1308s/1316s/1318s are used. */ static const struct snd_soc_dapm_route rt1308_map[] = { { "Speaker", NULL, "rt1308-1 SPOL" }, @@ -108,36 +152,69 @@ static const struct snd_soc_dapm_route rt1308_map[] = { { "Speaker", NULL, "rt1308-2 SPOR" }, }; -static const struct snd_kcontrol_new rt1308_controls[] = { - SOC_DAPM_PIN_SWITCH("Speaker"), +static const struct snd_soc_dapm_route rt1316_map[] = { + { "Speaker", NULL, "rt1316-1 SPOL" }, + { "Speaker", NULL, "rt1316-1 SPOR" }, + { "Speaker", NULL, "rt1316-2 SPOL" }, + { "Speaker", NULL, "rt1316-2 SPOR" }, }; +static const struct snd_soc_dapm_route rt1318_map[] = { + { "Speaker", NULL, "rt1318-1 SPOL" }, + { "Speaker", NULL, "rt1318-1 SPOR" }, + { "Speaker", NULL, "rt1318-2 SPOL" }, + { "Speaker", NULL, "rt1318-2 SPOR" }, +}; + +static const struct snd_soc_dapm_route *get_codec_name_and_route(struct snd_soc_pcm_runtime *rtd, + char *codec_name) +{ + const char *dai_name; + + dai_name = rtd->dai_link->codecs->dai_name; + + /* get the codec name */ + snprintf(codec_name, CODEC_NAME_SIZE, "%s", dai_name); + + /* choose the right codec's map */ + if (strcmp(codec_name, "rt1308") == 0) + return rt1308_map; + else if (strcmp(codec_name, "rt1316") == 0) + return rt1316_map; + else + return rt1318_map; +} + static int first_spk_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_card *card = rtd->card; + const struct snd_soc_dapm_route *rt_amp_map; + char codec_name[CODEC_NAME_SIZE]; int ret; + rt_amp_map = get_codec_name_and_route(rtd, codec_name); + card->components = devm_kasprintf(card->dev, GFP_KERNEL, - "%s spk:rt1308", - card->components); + "%s spk:%s", + card->components, codec_name); if (!card->components) return -ENOMEM; - ret = snd_soc_add_card_controls(card, rt1308_controls, - ARRAY_SIZE(rt1308_controls)); + ret = snd_soc_add_card_controls(card, rt_amp_controls, + ARRAY_SIZE(rt_amp_controls)); if (ret) { - dev_err(card->dev, "rt1308 controls addition failed: %d\n", ret); + dev_err(card->dev, "%s controls addition failed: %d\n", codec_name, ret); return ret; } - ret = snd_soc_dapm_new_controls(&card->dapm, rt1308_widgets, - ARRAY_SIZE(rt1308_widgets)); + ret = snd_soc_dapm_new_controls(&card->dapm, rt_amp_widgets, + ARRAY_SIZE(rt_amp_widgets)); if (ret) { - dev_err(card->dev, "rt1308 widgets addition failed: %d\n", ret); + dev_err(card->dev, "%s widgets addition failed: %d\n", codec_name, ret); return ret; } - ret = snd_soc_dapm_add_routes(&card->dapm, rt1308_map, 2); + ret = snd_soc_dapm_add_routes(&card->dapm, rt_amp_map, 2); if (ret) dev_err(rtd->dev, "failed to add first SPK map: %d\n", ret); @@ -147,9 +224,13 @@ static int first_spk_init(struct snd_soc_pcm_runtime *rtd) static int second_spk_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_card *card = rtd->card; + const struct snd_soc_dapm_route *rt_amp_map; + char codec_name[CODEC_NAME_SIZE]; int ret; - ret = snd_soc_dapm_add_routes(&card->dapm, rt1308_map + 2, 2); + rt_amp_map = get_codec_name_and_route(rtd, codec_name); + + ret = snd_soc_dapm_add_routes(&card->dapm, rt_amp_map + 2, 2); if (ret) dev_err(rtd->dev, "failed to add second SPK map: %d\n", ret); @@ -204,7 +285,7 @@ struct snd_soc_ops sof_sdw_rt1308_i2s_ops = { .hw_params = rt1308_i2s_hw_params, }; -int sof_sdw_rt1308_exit(struct snd_soc_card *card, struct snd_soc_dai_link *dai_link) +int sof_sdw_rt_amp_exit(struct snd_soc_card *card, struct snd_soc_dai_link *dai_link) { struct mc_private *ctx = snd_soc_card_get_drvdata(card); @@ -221,7 +302,7 @@ int sof_sdw_rt1308_exit(struct snd_soc_card *card, struct snd_soc_dai_link *dai_ return 0; } -int sof_sdw_rt1308_init(struct snd_soc_card *card, +int sof_sdw_rt_amp_init(struct snd_soc_card *card, const struct snd_soc_acpi_link_adr *link, struct snd_soc_dai_link *dai_links, struct sof_sdw_codec_info *info, @@ -244,7 +325,7 @@ int sof_sdw_rt1308_init(struct snd_soc_card *card, if (!sdw_dev1) return -EPROBE_DEFER; - ret = rt1308_add_device_props(sdw_dev1); + ret = rt_amp_add_device_props(sdw_dev1); if (ret < 0) { put_device(sdw_dev1); return ret; @@ -255,7 +336,7 @@ int sof_sdw_rt1308_init(struct snd_soc_card *card, if (!sdw_dev2) return -EPROBE_DEFER; - ret = rt1308_add_device_props(sdw_dev2); + ret = rt_amp_add_device_props(sdw_dev2); if (ret < 0) { put_device(sdw_dev2); return ret; @@ -263,7 +344,7 @@ int sof_sdw_rt1308_init(struct snd_soc_card *card, ctx->amp_dev2 = sdw_dev2; /* - * if two 1308s are in one dai link, the init function + * if two amps are in one dai link, the init function * in this dai link will be first set for the first speaker, * and it should be reset to initialize all speakers when * the second speaker is found. diff --git a/sound/soc/intel/common/soc-acpi-intel-jsl-match.c b/sound/soc/intel/common/soc-acpi-intel-jsl-match.c index b95c4b2cda94..f5c7e1bbded0 100644 --- a/sound/soc/intel/common/soc-acpi-intel-jsl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-jsl-match.c @@ -79,6 +79,11 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_jsl_machines[] = { .sof_tplg_filename = "sof-jsl-rt5682-mx98360a.tplg", }, { + .comp_ids = &rt5682_rt5682s_hp, + .drv_name = "jsl_rt5682", + .sof_tplg_filename = "sof-jsl-rt5682.tplg", + }, + { .id = "10134242", .drv_name = "jsl_cs4242_mx98360a", .machine_quirk = snd_soc_acpi_codec_list, diff --git a/sound/soc/intel/common/soc-acpi-intel-rpl-match.c b/sound/soc/intel/common/soc-acpi-intel-rpl-match.c index 3c5229f41bb0..31b43116e3d8 100644 --- a/sound/soc/intel/common/soc-acpi-intel-rpl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-rpl-match.c @@ -112,7 +112,7 @@ static const struct snd_soc_acpi_adr_device rt1316_1_group2_adr[] = { static const struct snd_soc_acpi_adr_device rt1318_1_group1_adr[] = { { - .adr = 0x000131025D131801ull, + .adr = 0x000132025D131801ull, .num_endpoints = 1, .endpoints = &spk_l_endpoint, .name_prefix = "rt1318-1" diff --git a/sound/soc/intel/skylake/skl-sst-cldma.c b/sound/soc/intel/skylake/skl-sst-cldma.c index b91f7a652a2b..b0204ea00f07 100644 --- a/sound/soc/intel/skylake/skl-sst-cldma.c +++ b/sound/soc/intel/skylake/skl-sst-cldma.c @@ -11,6 +11,7 @@ #include <linux/io.h> #include <linux/mm.h> #include <linux/delay.h> +#include <sound/hda_register.h> #include "../common/sst-dsp.h" #include "../common/sst-dsp-priv.h" @@ -79,21 +80,25 @@ static void skl_cldma_setup_bdle(struct sst_dsp *ctx, __le32 **bdlp, int size, int with_ioc) { __le32 *bdl = *bdlp; + int remaining = ctx->cl_dev.bufsize; + int offset = 0; ctx->cl_dev.frags = 0; - while (size > 0) { - phys_addr_t addr = virt_to_phys(dmab_data->area + - (ctx->cl_dev.frags * ctx->cl_dev.bufsize)); + while (remaining > 0) { + phys_addr_t addr; + int chunk; + addr = snd_sgbuf_get_addr(dmab_data, offset); bdl[0] = cpu_to_le32(lower_32_bits(addr)); bdl[1] = cpu_to_le32(upper_32_bits(addr)); + chunk = snd_sgbuf_get_chunk_size(dmab_data, offset, size); + bdl[2] = cpu_to_le32(chunk); - bdl[2] = cpu_to_le32(ctx->cl_dev.bufsize); - - size -= ctx->cl_dev.bufsize; - bdl[3] = (size || !with_ioc) ? 0 : cpu_to_le32(0x01); + remaining -= chunk; + bdl[3] = (remaining > 0) ? 0 : cpu_to_le32(0x01); bdl += 4; + offset += chunk; ctx->cl_dev.frags++; } } @@ -338,15 +343,15 @@ int skl_cldma_prepare(struct sst_dsp *ctx) ctx->cl_dev.ops.cl_stop_dma = skl_cldma_stop; /* Allocate buffer*/ - ret = ctx->dsp_ops.alloc_dma_buf(ctx->dev, - &ctx->cl_dev.dmab_data, ctx->cl_dev.bufsize); + ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV_SG, ctx->dev, ctx->cl_dev.bufsize, + &ctx->cl_dev.dmab_data); if (ret < 0) { dev_err(ctx->dev, "Alloc buffer for base fw failed: %x\n", ret); return ret; } + /* Setup Code loader BDL */ - ret = ctx->dsp_ops.alloc_dma_buf(ctx->dev, - &ctx->cl_dev.dmab_bdl, PAGE_SIZE); + ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, ctx->dev, BDL_SIZE, &ctx->cl_dev.dmab_bdl); if (ret < 0) { dev_err(ctx->dev, "Alloc buffer for blde failed: %x\n", ret); ctx->dsp_ops.free_dma_buf(ctx->dev, &ctx->cl_dev.dmab_data); diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index e06eac592da1..b20643b83401 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -582,36 +582,10 @@ static int skl_tplg_unload_pipe_modules(struct skl_dev *skl, return ret; } -static bool skl_tplg_is_multi_fmt(struct skl_dev *skl, struct skl_pipe *pipe) +static void skl_tplg_set_pipe_config_idx(struct skl_pipe *pipe, int idx) { - struct skl_pipe_fmt *cur_fmt; - struct skl_pipe_fmt *next_fmt; - int i; - - if (pipe->nr_cfgs <= 1) - return false; - - if (pipe->conn_type != SKL_PIPE_CONN_TYPE_FE) - return true; - - for (i = 0; i < pipe->nr_cfgs - 1; i++) { - if (pipe->direction == SNDRV_PCM_STREAM_PLAYBACK) { - cur_fmt = &pipe->configs[i].out_fmt; - next_fmt = &pipe->configs[i + 1].out_fmt; - } else { - cur_fmt = &pipe->configs[i].in_fmt; - next_fmt = &pipe->configs[i + 1].in_fmt; - } - - if (!CHECK_HW_PARAMS(cur_fmt->channels, cur_fmt->freq, - cur_fmt->bps, - next_fmt->channels, - next_fmt->freq, - next_fmt->bps)) - return true; - } - - return false; + pipe->cur_config_idx = idx; + pipe->memory_pages = pipe->configs[idx].mem_pages; } /* @@ -632,24 +606,14 @@ skl_tplg_get_pipe_config(struct skl_dev *skl, struct skl_module_cfg *mconfig) int i; if (pipe->nr_cfgs == 0) { - pipe->cur_config_idx = 0; - return 0; - } - - if (skl_tplg_is_multi_fmt(skl, pipe)) { - pipe->cur_config_idx = pipe->pipe_config_idx; - pipe->memory_pages = pconfig->mem_pages; - dev_dbg(skl->dev, "found pipe config idx:%d\n", - pipe->cur_config_idx); + skl_tplg_set_pipe_config_idx(pipe, 0); return 0; } if (pipe->conn_type == SKL_PIPE_CONN_TYPE_NONE || pipe->nr_cfgs == 1) { dev_dbg(skl->dev, "No conn_type or just 1 pathcfg, taking 0th for %d\n", pipe->ppl_id); - pipe->cur_config_idx = 0; - pipe->memory_pages = pconfig->mem_pages; - + skl_tplg_set_pipe_config_idx(pipe, 0); return 0; } @@ -668,10 +632,8 @@ skl_tplg_get_pipe_config(struct skl_dev *skl, struct skl_module_cfg *mconfig) if (CHECK_HW_PARAMS(params->ch, params->s_freq, params->s_fmt, fmt->channels, fmt->freq, fmt->bps)) { - pipe->cur_config_idx = i; - pipe->memory_pages = pconfig->mem_pages; + skl_tplg_set_pipe_config_idx(pipe, i); dev_dbg(skl->dev, "Using pipe config: %d\n", i); - return 0; } } @@ -1391,9 +1353,9 @@ static int skl_tplg_multi_config_set_get(struct snd_kcontrol *kcontrol, return -EIO; if (is_set) - pipe->pipe_config_idx = ucontrol->value.enumerated.item[0]; + skl_tplg_set_pipe_config_idx(pipe, ucontrol->value.enumerated.item[0]); else - ucontrol->value.enumerated.item[0] = pipe->pipe_config_idx; + ucontrol->value.enumerated.item[0] = pipe->cur_config_idx; return 0; } @@ -1837,20 +1799,28 @@ static int skl_tplg_be_fill_pipe_params(struct snd_soc_dai *dai, { struct nhlt_specific_cfg *cfg; struct skl_pipe *pipe = mconfig->pipe; + struct skl_pipe_params save = *pipe->p_params; struct skl_pipe_fmt *pipe_fmt; struct skl_dev *skl = get_skl_ctx(dai->dev); int link_type = skl_tplg_be_link_type(mconfig->dev_type); u8 dev_type = skl_tplg_be_dev_type(mconfig->dev_type); + int ret; skl_tplg_fill_dma_id(mconfig, params); if (link_type == NHLT_LINK_HDA) return 0; + *pipe->p_params = *params; + ret = skl_tplg_get_pipe_config(skl, mconfig); + if (ret) + goto err; + + dev_dbg(skl->dev, "%s using pipe config: %d\n", __func__, pipe->cur_config_idx); if (pipe->direction == SNDRV_PCM_STREAM_PLAYBACK) - pipe_fmt = &pipe->configs[pipe->pipe_config_idx].out_fmt; + pipe_fmt = &pipe->configs[pipe->cur_config_idx].out_fmt; else - pipe_fmt = &pipe->configs[pipe->pipe_config_idx].in_fmt; + pipe_fmt = &pipe->configs[pipe->cur_config_idx].in_fmt; /* update the blob based on virtual bus_id*/ cfg = intel_nhlt_get_endpoint_blob(dai->dev, skl->nhlt, @@ -1865,10 +1835,15 @@ static int skl_tplg_be_fill_pipe_params(struct snd_soc_dai *dai, dev_err(dai->dev, "Blob NULL for id:%d type:%d dirn:%d ch:%d, freq:%d, fmt:%d\n", mconfig->vbus_id, link_type, params->stream, params->ch, params->s_freq, params->s_fmt); - return -EINVAL; + ret = -EINVAL; + goto err; } return 0; + +err: + *pipe->p_params = save; + return ret; } static int skl_tplg_be_set_src_pipe_params(struct snd_soc_dai *dai, diff --git a/sound/soc/intel/skylake/skl-topology.h b/sound/soc/intel/skylake/skl-topology.h index 017ac0ef324d..6db0fd7bad49 100644 --- a/sound/soc/intel/skylake/skl-topology.h +++ b/sound/soc/intel/skylake/skl-topology.h @@ -324,7 +324,6 @@ struct skl_pipe { struct skl_path_config configs[SKL_MAX_PATH_CONFIGS]; struct list_head w_list; bool passthru; - u32 pipe_config_idx; }; enum skl_module_state { diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index 9bd9f9866898..998bd0232cf1 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -1107,7 +1107,10 @@ static void skl_shutdown(struct pci_dev *pci) if (!skl->init_done) return; - snd_hdac_stop_streams_and_chip(bus); + snd_hdac_stop_streams(bus); + snd_hdac_ext_bus_link_power_down_all(bus); + skl_dsp_sleep(skl->dsp); + list_for_each_entry(s, &bus->stream_list, list) { stream = stream_to_hdac_ext_stream(s); snd_hdac_ext_stream_decouple(bus, stream, false); |