From a87a2b52dccd744e93a240643c96a5656158edb0 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Fri, 13 Aug 2021 16:09:20 +0300 Subject: [PATCH 01/32] ASoC: SOF: Move the definition of enum snd_sof_fw_state to global header Move the enum snd_sof_fw_state to include/sound/sof.h for it to be accessible outside of the core SOF stack. Signed-off-by: Peter Ujfalusi --- include/sound/sof.h | 18 ++++++++++++++++++ sound/soc/sof/sof-priv.h | 9 --------- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/include/sound/sof.h b/include/sound/sof.h index 23b374311d1616..989a8006d8efba 100644 --- a/include/sound/sof.h +++ b/include/sound/sof.h @@ -17,6 +17,24 @@ struct snd_sof_dsp_ops; +/** + * enum snd_sof_fw_state - DSP firmware state definitions + * @SOF_FW_BOOT_NOT_STARTED: firmware boot is not yet started + * @SOF_FW_BOOT_PREPARE: preparing for boot (firmware loading for exaqmple) + * @SOF_FW_BOOT_IN_PROGRESS: firmware boot is in progress + * @SOF_FW_BOOT_FAILED: firmware boot failed + * @SOF_FW_BOOT_READY_FAILED: firmware booted but fw_ready op failed + * @SOF_FW_BOOT_COMPLETE: firmware is booted up and functional + */ +enum snd_sof_fw_state { + SOF_FW_BOOT_NOT_STARTED = 0, + SOF_FW_BOOT_PREPARE, + SOF_FW_BOOT_IN_PROGRESS, + SOF_FW_BOOT_FAILED, + SOF_FW_BOOT_READY_FAILED, + SOF_FW_BOOT_COMPLETE, +}; + /* * SOF Platform data. */ diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index 3673ac4bcd354c..9c7ca6a830870b 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -371,15 +371,6 @@ struct snd_sof_ipc_msg { bool ipc_complete; }; -enum snd_sof_fw_state { - SOF_FW_BOOT_NOT_STARTED = 0, - SOF_FW_BOOT_PREPARE, - SOF_FW_BOOT_IN_PROGRESS, - SOF_FW_BOOT_FAILED, - SOF_FW_BOOT_READY_FAILED, /* firmware booted but fw_ready op failed */ - SOF_FW_BOOT_COMPLETE, -}; - /* * SOF Device Level. */ From 4d512ec461da6467720440ed70b5373ba98fe094 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Mon, 16 Aug 2021 11:38:04 +0300 Subject: [PATCH 02/32] ASoC: SOF: Introduce a macro to set the firmware state Add sof_set_fw_state() macro to wrap the sdev->fw_state management to allow actions to be taken when certain state is set or when state is changing. Signed-off-by: Peter Ujfalusi --- sound/soc/sof/core.c | 8 ++++---- sound/soc/sof/ipc.c | 4 ++-- sound/soc/sof/loader.c | 2 +- sound/soc/sof/pm.c | 6 +++--- sound/soc/sof/sof-priv.h | 9 +++++++++ 5 files changed, 19 insertions(+), 10 deletions(-) diff --git a/sound/soc/sof/core.c b/sound/soc/sof/core.c index 2ea29186e397b0..22a1c5090ae0ba 100644 --- a/sound/soc/sof/core.c +++ b/sound/soc/sof/core.c @@ -147,7 +147,7 @@ static int sof_probe_continue(struct snd_sof_dev *sdev) return ret; } - sdev->fw_state = SOF_FW_BOOT_PREPARE; + sof_set_fw_state(sdev, SOF_FW_BOOT_PREPARE); /* check machine info */ ret = sof_machine_check(sdev); @@ -189,7 +189,7 @@ static int sof_probe_continue(struct snd_sof_dev *sdev) goto fw_load_err; } - sdev->fw_state = SOF_FW_BOOT_IN_PROGRESS; + sof_set_fw_state(sdev, SOF_FW_BOOT_IN_PROGRESS); /* * Boot the firmware. The FW boot status will be modified @@ -265,7 +265,7 @@ static int sof_probe_continue(struct snd_sof_dev *sdev) snd_sof_remove(sdev); /* all resources freed, update state to match */ - sdev->fw_state = SOF_FW_BOOT_NOT_STARTED; + sof_set_fw_state(sdev, SOF_FW_BOOT_NOT_STARTED); sdev->first_boot = true; return ret; @@ -300,7 +300,7 @@ int snd_sof_device_probe(struct device *dev, struct snd_sof_pdata *plat_data) sdev->pdata = plat_data; sdev->first_boot = true; - sdev->fw_state = SOF_FW_BOOT_NOT_STARTED; + sof_set_fw_state(sdev, SOF_FW_BOOT_NOT_STARTED); #if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_PROBES) sdev->extractor_stream_tag = SOF_PROBE_INVALID_NODE_ID; #endif diff --git a/sound/soc/sof/ipc.c b/sound/soc/sof/ipc.c index 56ce6227e19a6d..2e3a261a1cddd1 100644 --- a/sound/soc/sof/ipc.c +++ b/sound/soc/sof/ipc.c @@ -450,9 +450,9 @@ void snd_sof_ipc_msgs_rx(struct snd_sof_dev *sdev) if (sdev->fw_state == SOF_FW_BOOT_IN_PROGRESS) { err = sof_ops(sdev)->fw_ready(sdev, cmd); if (err < 0) - sdev->fw_state = SOF_FW_BOOT_READY_FAILED; + sof_set_fw_state(sdev, SOF_FW_BOOT_READY_FAILED); else - sdev->fw_state = SOF_FW_BOOT_COMPLETE; + sof_set_fw_state(sdev, SOF_FW_BOOT_COMPLETE); /* wake up firmware loader */ wake_up(&sdev->boot_wait); diff --git a/sound/soc/sof/loader.c b/sound/soc/sof/loader.c index 194d8080cdca93..caaf9d04b4fcb4 100644 --- a/sound/soc/sof/loader.c +++ b/sound/soc/sof/loader.c @@ -838,7 +838,7 @@ int snd_sof_run_firmware(struct snd_sof_dev *sdev) dev_err(sdev->dev, "error: firmware boot failure\n"); snd_sof_dsp_dbg_dump(sdev, SOF_DBG_DUMP_REGS | SOF_DBG_DUMP_MBOX | SOF_DBG_DUMP_TEXT | SOF_DBG_DUMP_PCI); - sdev->fw_state = SOF_FW_BOOT_FAILED; + sof_set_fw_state(sdev, SOF_FW_BOOT_FAILED); return -EIO; } diff --git a/sound/soc/sof/pm.c b/sound/soc/sof/pm.c index c9e46f13b1f438..6611f6a4227c1d 100644 --- a/sound/soc/sof/pm.c +++ b/sound/soc/sof/pm.c @@ -122,7 +122,7 @@ static int sof_resume(struct device *dev, bool runtime_resume) old_state == SOF_DSP_PM_D0) return 0; - sdev->fw_state = SOF_FW_BOOT_PREPARE; + sof_set_fw_state(sdev, SOF_FW_BOOT_PREPARE); /* load the firmware */ ret = snd_sof_load_firmware(sdev); @@ -133,7 +133,7 @@ static int sof_resume(struct device *dev, bool runtime_resume) return ret; } - sdev->fw_state = SOF_FW_BOOT_IN_PROGRESS; + sof_set_fw_state(sdev, SOF_FW_BOOT_IN_PROGRESS); /* * Boot the firmware. The FW boot status will be modified @@ -257,7 +257,7 @@ static int sof_suspend(struct device *dev, bool runtime_suspend) return ret; /* reset FW state */ - sdev->fw_state = SOF_FW_BOOT_NOT_STARTED; + sof_set_fw_state(sdev, SOF_FW_BOOT_NOT_STARTED); sdev->enabled_cores_mask = 0; return ret; diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index 9c7ca6a830870b..17b4aa358041ca 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -576,6 +576,15 @@ static inline void sof_oops(struct snd_sof_dev *sdev, void *oops) extern const struct dsp_arch_ops sof_xtensa_arch_ops; +/* + * Firmware state tracking + */ +static inline void sof_set_fw_state(struct snd_sof_dev *sdev, + enum snd_sof_fw_state new_state) +{ + sdev->fw_state = new_state; +} + /* * Utilities */ From 44b2a4395e3ca660d09c1af30ae37b6600f186e5 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 18 Aug 2021 14:19:59 +0300 Subject: [PATCH 03/32] ASoC: SOF: debug: Print out the fw_state along with the DSP dump The fw state can be an important information along with the DSP dump. Print it out before the dump. Signed-off-by: Peter Ujfalusi --- sound/soc/sof/debug.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/sof/debug.c b/sound/soc/sof/debug.c index bcd381f495c0c5..34d971126237f5 100644 --- a/sound/soc/sof/debug.c +++ b/sound/soc/sof/debug.c @@ -831,6 +831,7 @@ void snd_sof_dsp_dbg_dump(struct snd_sof_dev *sdev, u32 flags) if (sof_ops(sdev)->dbg_dump && !sdev->dbg_dump_printed) { dev_err(sdev->dev, "------------[ DSP dump start ]------------\n"); + dev_err(sdev->dev, "DSP firmware state: %d\n", sdev->fw_state); sof_ops(sdev)->dbg_dump(sdev, flags); dev_err(sdev->dev, "------------[ DSP dump end ]------------\n"); if (!print_all) From 9115f5bfdf2348048209b6be0a85af0135856c21 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Tue, 17 Aug 2021 09:42:41 +0300 Subject: [PATCH 04/32] ASoC: SOF: Introduce new firmware state: SOF_FW_CRASHED The SOF_FW_CRASHED state is meant to indicate the unfortunate case when the firmware has crashed after a successful boot. Signed-off-by: Peter Ujfalusi --- include/sound/sof.h | 2 ++ sound/soc/sof/debug.c | 1 + sound/soc/sof/ipc.c | 2 +- sound/soc/sof/ops.c | 1 + sound/soc/sof/pm.c | 14 ++++++++++++-- 5 files changed, 17 insertions(+), 3 deletions(-) diff --git a/include/sound/sof.h b/include/sound/sof.h index 989a8006d8efba..977d3aa52f192c 100644 --- a/include/sound/sof.h +++ b/include/sound/sof.h @@ -25,6 +25,7 @@ struct snd_sof_dsp_ops; * @SOF_FW_BOOT_FAILED: firmware boot failed * @SOF_FW_BOOT_READY_FAILED: firmware booted but fw_ready op failed * @SOF_FW_BOOT_COMPLETE: firmware is booted up and functional + * @SOF_FW_CRASHED: firmware crashed after sucessful boot */ enum snd_sof_fw_state { SOF_FW_BOOT_NOT_STARTED = 0, @@ -33,6 +34,7 @@ enum snd_sof_fw_state { SOF_FW_BOOT_FAILED, SOF_FW_BOOT_READY_FAILED, SOF_FW_BOOT_COMPLETE, + SOF_FW_CRASHED, }; /* diff --git a/sound/soc/sof/debug.c b/sound/soc/sof/debug.c index 34d971126237f5..6d937ee7de910e 100644 --- a/sound/soc/sof/debug.c +++ b/sound/soc/sof/debug.c @@ -865,6 +865,7 @@ void snd_sof_handle_fw_exception(struct snd_sof_dev *sdev) /* dump vital information to the logs */ snd_sof_ipc_dump(sdev); snd_sof_dsp_dbg_dump(sdev, SOF_DBG_DUMP_REGS | SOF_DBG_DUMP_MBOX); + sof_set_fw_state(sdev, SOF_FW_CRASHED); snd_sof_trace_notify_for_error(sdev); } EXPORT_SYMBOL(snd_sof_handle_fw_exception); diff --git a/sound/soc/sof/ipc.c b/sound/soc/sof/ipc.c index 2e3a261a1cddd1..7b3efca558fb0d 100644 --- a/sound/soc/sof/ipc.c +++ b/sound/soc/sof/ipc.c @@ -279,7 +279,7 @@ static int sof_ipc_tx_message_unlocked(struct snd_sof_ipc *ipc, u32 header, struct snd_sof_ipc_msg *msg; int ret; - if (ipc->disable_ipc_tx) + if (ipc->disable_ipc_tx || sdev->fw_state == SOF_FW_CRASHED) return -ENODEV; /* diff --git a/sound/soc/sof/ops.c b/sound/soc/sof/ops.c index 11ecebd079073f..92a64114bfd0ac 100644 --- a/sound/soc/sof/ops.c +++ b/sound/soc/sof/ops.c @@ -158,6 +158,7 @@ void snd_sof_dsp_panic(struct snd_sof_dev *sdev, u32 offset) sdev->dsp_oops_offset, offset); snd_sof_dsp_dbg_dump(sdev, SOF_DBG_DUMP_REGS | SOF_DBG_DUMP_MBOX); + sof_set_fw_state(sdev, SOF_FW_CRASHED); snd_sof_trace_notify_for_error(sdev); } EXPORT_SYMBOL(snd_sof_dsp_panic); diff --git a/sound/soc/sof/pm.c b/sound/soc/sof/pm.c index 6611f6a4227c1d..b8712fbaaa9f6c 100644 --- a/sound/soc/sof/pm.c +++ b/sound/soc/sof/pm.c @@ -204,8 +204,11 @@ static int sof_suspend(struct device *dev, bool runtime_suspend) target_state = snd_sof_dsp_power_target(sdev); - /* Skip to platform-specific suspend if DSP is entering D0 */ - if (target_state == SOF_DSP_PM_D0) + /* + * Skip to platform-specific suspend if DSP is entering D0 or if it has + * crashed + */ + if (target_state == SOF_DSP_PM_D0 || sdev->fw_state == SOF_FW_CRASHED) goto suspend; sof_tear_down_pipelines(sdev, false); @@ -312,6 +315,13 @@ int snd_sof_prepare(struct device *dev) /* will suspend to S3 by default */ sdev->system_suspend_target = SOF_SUSPEND_S3; + /* + * if the firmware is crashed then we try to aim for S3 to reboot the + * firmware + */ + if (sdev->fw_state == SOF_FW_CRASHED) + return 0; + if (!desc->use_acpi_target_states) return 0; From 476d4c0b3f691ec9055d40e521de9d2cb4d2a9b9 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Fri, 23 Jul 2021 09:19:26 +0300 Subject: [PATCH 05/32] ASoC: soc-component: Add support module get/put on open for compressed streams The module get/put on open is only supported by pcm streams via snd_soc_component_module_get() / snd_soc_component_module_put() Introduce simple functions to manage the module use count for compressed streams when module_get_upon_open is set to 1 for it. Signed-off-by: Peter Ujfalusi --- sound/soc/soc-component.c | 35 +++++++++++++++++++++++++++++++---- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/sound/soc/soc-component.c b/sound/soc/soc-component.c index 8e8d917d22f8ff..c2d605b6a5a8d6 100644 --- a/sound/soc/soc-component.c +++ b/sound/soc/soc-component.c @@ -425,23 +425,49 @@ EXPORT_SYMBOL_GPL(snd_soc_component_exit_regmap); #endif +static int snd_soc_component_compr_module_get(struct snd_soc_component *component, + struct snd_compr_stream *cstream) +{ + int ret = 0; + + if (component->driver->module_get_upon_open == 1 && + !try_module_get(component->dev->driver->owner)) + ret = -ENODEV; + + return soc_component_ret(component, ret); +} + +static void snd_soc_component_compr_module_put(struct snd_soc_component *component, + struct snd_compr_stream *cstream) +{ + if (component->driver->module_get_upon_open == 1) + module_put(component->dev->driver->owner); +} + int snd_soc_component_compr_open(struct snd_compr_stream *cstream) { struct snd_soc_pcm_runtime *rtd = cstream->private_data; struct snd_soc_component *component; - int i, ret; + int ret = 0; + int i; for_each_rtd_components(rtd, i, component) { + ret = snd_soc_component_compr_module_get(component, cstream); + if (ret < 0) + break; + if (component->driver->compress_ops && component->driver->compress_ops->open) { ret = component->driver->compress_ops->open(component, cstream); - if (ret < 0) - return soc_component_ret(component, ret); + if (ret < 0) { + snd_soc_component_compr_module_put(component, cstream); + break; + } } soc_component_mark_push(component, cstream, compr_open); } - return 0; + return soc_component_ret(component, ret); } EXPORT_SYMBOL_GPL(snd_soc_component_compr_open); @@ -461,6 +487,7 @@ void snd_soc_component_compr_free(struct snd_compr_stream *cstream, component->driver->compress_ops->free(component, cstream); soc_component_mark_pop(component, cstream, compr_open); + snd_soc_component_compr_module_put(component, cstream); } } EXPORT_SYMBOL_GPL(snd_soc_component_compr_free); From 7274b1c2f2430e6a4975c8b181902dd0124a01c6 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Thu, 22 Oct 2020 16:26:46 -0700 Subject: [PATCH 06/32] ASoC: SOF: Introduce IPC SOF client support A client in the SOF (Sound Open Firmware) context is a driver that needs to communicate with the DSP via IPC messages. The SOF core is responsible for serializing the IPC messages to the DSP from the different clients. One example of an SOF client would be an IPC test client that floods the DSP with test IPC messages to validate if the serialization works as expected. Multi-client support will also add the ability to split the existing audio cards into multiple ones, so as to e.g. to deal with HDMI with a dedicated client instead of adding HDMI to all cards. This patch introduces descriptors for SOF client driver and SOF client device along with APIs for registering and unregistering a SOF client driver, sending IPCs from a client device and accessing the SOF core debugfs root entry. Along with this, add a couple of new members to struct snd_sof_dev that will be used for maintaining the list of clients. Signed-off-by: Ranjani Sridharan Co-developed-by: Fred Oh Signed-off-by: Fred Oh Signed-off-by: Peter Ujfalusi --- sound/soc/sof/Kconfig | 7 ++ sound/soc/sof/Makefile | 1 + sound/soc/sof/core.c | 2 + sound/soc/sof/sof-client.c | 137 +++++++++++++++++++++++++++++++++++++ sound/soc/sof/sof-client.h | 41 +++++++++++ sound/soc/sof/sof-priv.h | 28 ++++++++ 6 files changed, 216 insertions(+) create mode 100644 sound/soc/sof/sof-client.c create mode 100644 sound/soc/sof/sof-client.h diff --git a/sound/soc/sof/Kconfig b/sound/soc/sof/Kconfig index cd659493b5dfe3..86cc0557a0de06 100644 --- a/sound/soc/sof/Kconfig +++ b/sound/soc/sof/Kconfig @@ -55,6 +55,13 @@ config SND_SOC_SOF_DEBUG_PROBES Say Y if you want to enable probes. If unsure, select "N". +config SND_SOC_SOF_CLIENT + tristate + select AUXILIARY_BUS + help + This option is not user-selectable but automagically handled by + 'select' statements at a higher level. + config SND_SOC_SOF_DEVELOPER_SUPPORT bool "SOF developer options support" depends on EXPERT diff --git a/sound/soc/sof/Makefile b/sound/soc/sof/Makefile index c5b97c66a9f16b..67999db24b703c 100644 --- a/sound/soc/sof/Makefile +++ b/sound/soc/sof/Makefile @@ -2,6 +2,7 @@ snd-sof-objs := core.o ops.o loader.o ipc.o pcm.o pm.o debug.o topology.o\ control.o trace.o utils.o sof-audio.o stream-ipc.o +snd-sof-$(CONFIG_SND_SOC_SOF_CLIENT) += sof-client.o snd-sof-$(CONFIG_SND_SOC_SOF_DEBUG_PROBES) += sof-probes.o diff --git a/sound/soc/sof/core.c b/sound/soc/sof/core.c index 22a1c5090ae0ba..f8632ae573fa43 100644 --- a/sound/soc/sof/core.c +++ b/sound/soc/sof/core.c @@ -321,9 +321,11 @@ int snd_sof_device_probe(struct device *dev, struct snd_sof_pdata *plat_data) INIT_LIST_HEAD(&sdev->widget_list); INIT_LIST_HEAD(&sdev->dai_list); INIT_LIST_HEAD(&sdev->route_list); + INIT_LIST_HEAD(&sdev->ipc_client_list); spin_lock_init(&sdev->ipc_lock); spin_lock_init(&sdev->hw_lock); mutex_init(&sdev->power_state_access); + mutex_init(&sdev->ipc_client_mutex); /* set default timeouts if none provided */ if (plat_data->desc->ipc_timeout == 0) diff --git a/sound/soc/sof/sof-client.c b/sound/soc/sof/sof-client.c new file mode 100644 index 00000000000000..9b3da76000738a --- /dev/null +++ b/sound/soc/sof/sof-client.c @@ -0,0 +1,137 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// Copyright(c) 2021 Intel Corporation. All rights reserved. +// +// Authors: Ranjani Sridharan +// Peter Ujfalusi +// + +#include +#include +#include +#include +#include +#include "sof-client.h" +#include "sof-priv.h" + +static void sof_client_auxdev_release(struct device *dev) +{ + struct auxiliary_device *auxdev = to_auxiliary_dev(dev); + struct sof_client_dev *cdev = auxiliary_dev_to_sof_client_dev(auxdev); + + kfree(cdev->auxdev.dev.platform_data); + kfree(cdev); +} + +static int sof_client_dev_add_data(struct sof_client_dev *cdev, const void *data, + size_t size) +{ + void *d = NULL; + + if (data) { + d = kmemdup(data, size, GFP_KERNEL); + if (!d) + return -ENOMEM; + } + + cdev->auxdev.dev.platform_data = d; + return 0; +} + +int sof_client_dev_register(struct snd_sof_dev *sdev, const char *name, u32 id, + const void *data, size_t size) +{ + struct auxiliary_device *auxdev; + struct sof_client_dev *cdev; + int ret; + + cdev = kzalloc(sizeof(*cdev), GFP_KERNEL); + if (!cdev) + return -ENOMEM; + + cdev->sdev = sdev; + auxdev = &cdev->auxdev; + auxdev->name = name; + auxdev->dev.parent = sdev->dev; + auxdev->dev.release = sof_client_auxdev_release; + auxdev->id = id; + + ret = sof_client_dev_add_data(cdev, data, size); + if (ret < 0) { + kfree(cdev); + return ret; + } + + ret = auxiliary_device_init(auxdev); + if (ret < 0) { + dev_err(sdev->dev, "error: failed to initialize client dev %s\n", name); + kfree(cdev->auxdev.dev.platform_data); + kfree(cdev); + return ret; + } + + ret = auxiliary_device_add(&cdev->auxdev); + if (ret < 0) { + dev_err(sdev->dev, "error: failed to add client dev %s\n", name); + /* + * sof_client_auxdev_release() will be invoked to free up memory + * allocations through put_device() + */ + auxiliary_device_uninit(&cdev->auxdev); + return ret; + } + + /* add to list of SOF client devices */ + mutex_lock(&sdev->ipc_client_mutex); + list_add(&cdev->list, &sdev->ipc_client_list); + mutex_unlock(&sdev->ipc_client_mutex); + + return 0; +} +EXPORT_SYMBOL_NS_GPL(sof_client_dev_register, SND_SOC_SOF_CLIENT); + +void sof_client_dev_unregister(struct snd_sof_dev *sdev, const char *name, u32 id) +{ + struct sof_client_dev *cdev; + + mutex_lock(&sdev->ipc_client_mutex); + + /* + * sof_client_auxdev_release() will be invoked to free up memory + * allocations through put_device() + */ + list_for_each_entry(cdev, &sdev->ipc_client_list, list) { + if (!strcmp(cdev->auxdev.name, name) && cdev->auxdev.id == id) { + list_del(&cdev->list); + auxiliary_device_delete(&cdev->auxdev); + auxiliary_device_uninit(&cdev->auxdev); + break; + } + } + + mutex_unlock(&sdev->ipc_client_mutex); +} +EXPORT_SYMBOL_NS_GPL(sof_client_dev_unregister, SND_SOC_SOF_CLIENT); + +int sof_client_ipc_tx_message(struct sof_client_dev *cdev, void *ipc_msg, + void *reply_data, size_t reply_bytes) +{ + struct sof_ipc_cmd_hdr *hdr = ipc_msg; + + return sof_ipc_tx_message(cdev->sdev->ipc, hdr->cmd, ipc_msg, hdr->size, + reply_data, reply_bytes); +} +EXPORT_SYMBOL_NS_GPL(sof_client_ipc_tx_message, SND_SOC_SOF_CLIENT); + +struct dentry *sof_client_get_debugfs_root(struct sof_client_dev *cdev) +{ + return cdev->sdev->debugfs_root; +} +EXPORT_SYMBOL_NS_GPL(sof_client_get_debugfs_root, SND_SOC_SOF_CLIENT); + +/* DMA buffer allocation in client drivers must use the core SOF device */ +struct device *sof_client_get_dma_dev(struct sof_client_dev *cdev) +{ + return cdev->sdev->dev; +} +EXPORT_SYMBOL_NS_GPL(sof_client_get_dma_dev, SND_SOC_SOF_CLIENT); diff --git a/sound/soc/sof/sof-client.h b/sound/soc/sof/sof-client.h new file mode 100644 index 00000000000000..1d92b8416ae7c5 --- /dev/null +++ b/sound/soc/sof/sof-client.h @@ -0,0 +1,41 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef __SOC_SOF_CLIENT_H +#define __SOC_SOF_CLIENT_H + +#include +#include +#include + +struct snd_sof_dev; +struct dentry; + +/** + * struct sof_client_dev - SOF client device + * @auxdev: auxiliary device + * @sdev: pointer to SOF core device struct + * @list: item in SOF core client dev list + * @data: device specific data + */ +struct sof_client_dev { + struct auxiliary_device auxdev; + struct snd_sof_dev *sdev; + struct list_head list; + void *data; +}; + +#define sof_client_dev_to_sof_dev(cdev) ((cdev)->sdev) + +#define auxiliary_dev_to_sof_client_dev(auxiliary_dev) \ + container_of(auxiliary_dev, struct sof_client_dev, auxdev) + +#define dev_to_sof_client_dev(dev) \ + container_of(to_auxiliary_dev(dev), struct sof_client_dev, auxdev) + +int sof_client_ipc_tx_message(struct sof_client_dev *cdev, void *ipc_msg, + void *reply_data, size_t reply_bytes); + +struct dentry *sof_client_get_debugfs_root(struct sof_client_dev *cdev); +struct device *sof_client_get_dma_dev(struct sof_client_dev *cdev); + +#endif /* __SOC_SOF_CLIENT_H */ diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index 17b4aa358041ca..52efaf25d20d19 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -480,6 +480,15 @@ struct snd_sof_dev { */ int dsp_core_ref_count[SOF_MAX_DSP_NUM_CORES]; + /* + * Used to keep track of registered IPC client devices so that they can + * be removed when the parent SOF module is removed. + */ + struct list_head ipc_client_list; + + /* mutex to protect client list */ + struct mutex ipc_client_mutex; + void *private; /* core does not touch this */ }; @@ -616,4 +625,23 @@ int sof_stream_pcm_close(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream); int sof_machine_check(struct snd_sof_dev *sdev); + +/* SOF client support */ +#if IS_ENABLED(CONFIG_SND_SOC_SOF_CLIENT) +int sof_client_dev_register(struct snd_sof_dev *sdev, const char *name, u32 id, + const void *data, size_t size); +void sof_client_dev_unregister(struct snd_sof_dev *sdev, const char *name, u32 id); +#else /* CONFIG_SND_SOC_SOF_CLIENT */ +static inline int sof_client_dev_register(struct snd_sof_dev *sdev, const char *name, + u32 id, const void *data, size_t size) +{ + return 0; +} + +static inline void sof_client_dev_unregister(struct snd_sof_dev *sdev, + const char *name, u32 id) +{ +} +#endif /* CONFIG_SND_SOC_SOF_CLIENT */ + #endif From 6a772cea93daf252b508569d0e9cf030e563d390 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Thu, 22 Oct 2020 16:26:50 -0700 Subject: [PATCH 07/32] ASoC: SOF: core/ops: Add support for client registration Add new ops for registering/unregistering clients based on DSP capabilities and/or DT information. Signed-off-by: Ranjani Sridharan Signed-off-by: Peter Ujfalusi --- sound/soc/sof/core.c | 14 ++++++++++++++ sound/soc/sof/sof-client.c | 15 +++++++++++++++ sound/soc/sof/sof-priv.h | 15 +++++++++++++++ 3 files changed, 44 insertions(+) diff --git a/sound/soc/sof/core.c b/sound/soc/sof/core.c index f8632ae573fa43..96a40c56e311fb 100644 --- a/sound/soc/sof/core.c +++ b/sound/soc/sof/core.c @@ -237,6 +237,12 @@ static int sof_probe_continue(struct snd_sof_dev *sdev) goto fw_trace_err; } + ret = sof_register_clients(sdev); + if (ret < 0) { + dev_err(sdev->dev, "error: failed to register clients %d\n", ret); + goto sof_machine_err; + } + /* * Some platforms in SOF, ex: BYT, may not have their platform PM * callbacks set. Increment the usage count so as to @@ -252,6 +258,8 @@ static int sof_probe_continue(struct snd_sof_dev *sdev) return 0; +sof_machine_err: + snd_sof_machine_unregister(sdev, plat_data); fw_trace_err: snd_sof_free_trace(sdev); fw_run_err: @@ -364,6 +372,12 @@ int snd_sof_device_remove(struct device *dev) if (IS_ENABLED(CONFIG_SND_SOC_SOF_PROBE_WORK_QUEUE)) cancel_work_sync(&sdev->probe_work); + /* + * Unregister any registered client device first before IPC and debugfs + * to allow client drivers to be removed cleanly + */ + sof_unregister_clients(sdev); + if (sdev->fw_state > SOF_FW_BOOT_NOT_STARTED) { ret = snd_sof_dsp_power_down_notify(sdev); if (ret < 0) diff --git a/sound/soc/sof/sof-client.c b/sound/soc/sof/sof-client.c index 9b3da76000738a..38ccbadf1851cf 100644 --- a/sound/soc/sof/sof-client.c +++ b/sound/soc/sof/sof-client.c @@ -11,6 +11,7 @@ #include #include #include +#include "ops.h" #include "sof-client.h" #include "sof-priv.h" @@ -38,6 +39,20 @@ static int sof_client_dev_add_data(struct sof_client_dev *cdev, const void *data return 0; } +int sof_register_clients(struct snd_sof_dev *sdev) +{ + if (sof_ops(sdev) && sof_ops(sdev)->register_ipc_clients) + return sof_ops(sdev)->register_ipc_clients(sdev); + + return 0; +} + +void sof_unregister_clients(struct snd_sof_dev *sdev) +{ + if (sof_ops(sdev) && sof_ops(sdev)->unregister_ipc_clients) + sof_ops(sdev)->unregister_ipc_clients(sdev); +} + int sof_client_dev_register(struct snd_sof_dev *sdev, const char *name, u32 id, const void *data, size_t size) { diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index 52efaf25d20d19..b6cb247cc0c44a 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -297,6 +297,10 @@ struct snd_sof_dsp_ops { void (*set_mach_params)(const struct snd_soc_acpi_mach *mach, struct snd_sof_dev *sdev); /* optional */ + /* IPC client ops */ + int (*register_ipc_clients)(struct snd_sof_dev *sdev); /* optional */ + void (*unregister_ipc_clients)(struct snd_sof_dev *sdev); /* optional */ + /* DAI ops */ struct snd_soc_dai_driver *drv; int num_drv; @@ -631,6 +635,8 @@ int sof_machine_check(struct snd_sof_dev *sdev); int sof_client_dev_register(struct snd_sof_dev *sdev, const char *name, u32 id, const void *data, size_t size); void sof_client_dev_unregister(struct snd_sof_dev *sdev, const char *name, u32 id); +int sof_register_clients(struct snd_sof_dev *sdev); +void sof_unregister_clients(struct snd_sof_dev *sdev); #else /* CONFIG_SND_SOC_SOF_CLIENT */ static inline int sof_client_dev_register(struct snd_sof_dev *sdev, const char *name, u32 id, const void *data, size_t size) @@ -642,6 +648,15 @@ static inline void sof_client_dev_unregister(struct snd_sof_dev *sdev, const char *name, u32 id) { } + +static inline int sof_register_clients(struct snd_sof_dev *sdev) +{ + return 0; +} + +static inline void sof_unregister_clients(struct snd_sof_dev *sdev) +{ +} #endif /* CONFIG_SND_SOC_SOF_CLIENT */ #endif From a858cfa8ff5879b39aa16e3818a99906b0822127 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Fri, 23 Jul 2021 11:13:03 +0300 Subject: [PATCH 08/32] ASoC: SOF: core: Unregister machine driver before IPC and debugfs To ensure clean unload of the machine driver, components and topology, do the unregister before we free IPC and debugfs. It is a possibility that part of the unregister we would have IPC communication with the firmware. Suggested-by: Pierre-Louis Bossart Signed-off-by: Peter Ujfalusi --- sound/soc/sof/core.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/sound/soc/sof/core.c b/sound/soc/sof/core.c index 96a40c56e311fb..13e58b602cdba8 100644 --- a/sound/soc/sof/core.c +++ b/sound/soc/sof/core.c @@ -378,6 +378,13 @@ int snd_sof_device_remove(struct device *dev) */ sof_unregister_clients(sdev); + /* + * Unregister machine driver. This will unbind the snd_card which + * will remove the component driver and unload the topology + * before freeing the snd_card. + */ + snd_sof_machine_unregister(sdev, pdata); + if (sdev->fw_state > SOF_FW_BOOT_NOT_STARTED) { ret = snd_sof_dsp_power_down_notify(sdev); if (ret < 0) @@ -389,13 +396,6 @@ int snd_sof_device_remove(struct device *dev) snd_sof_free_trace(sdev); } - /* - * Unregister machine driver. This will unbind the snd_card which - * will remove the component driver and unload the topology - * before freeing the snd_card. - */ - snd_sof_machine_unregister(sdev, pdata); - /* * Unregistering the machine driver results in unloading the topology. * Some widgets, ex: scheduler, attempt to power down the core they are From 49abfdcc66375d63a91e1b7272f97ab803fb5fa0 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Tue, 6 Jul 2021 16:38:08 +0300 Subject: [PATCH 09/32] ASoC: SOF: ipc: Read and pass the whole message to handlers for IPC events Change the parameter list for the firmware initiated message (IPC event) handler functions to: handler(struct snd_sof_dev *sdev, void *full_msg); Allocate memory and read the whole message in snd_sof_ipc_msgs_rx() then pass the pointer to the function handling the message. Do this only if we actually have a function which is tasked to process the given type. Signed-off-by: Peter Ujfalusi --- sound/soc/sof/ipc.c | 76 +++++++++++++++++++++------------------------ 1 file changed, 36 insertions(+), 40 deletions(-) diff --git a/sound/soc/sof/ipc.c b/sound/soc/sof/ipc.c index 7b3efca558fb0d..647fb20d2701de 100644 --- a/sound/soc/sof/ipc.c +++ b/sound/soc/sof/ipc.c @@ -18,8 +18,10 @@ #include "sof-audio.h" #include "ops.h" -static void ipc_trace_message(struct snd_sof_dev *sdev, u32 msg_type); -static void ipc_stream_message(struct snd_sof_dev *sdev, u32 msg_cmd); +typedef void (*ipc_rx_callback)(struct snd_sof_dev *sdev, void *full_msg); + +static void ipc_trace_message(struct snd_sof_dev *sdev, void *full_msg); +static void ipc_stream_message(struct snd_sof_dev *sdev, void *full_msg); /* * IPC message Tx/Rx message handling. @@ -389,44 +391,30 @@ void snd_sof_ipc_reply(struct snd_sof_dev *sdev, u32 msg_id) } EXPORT_SYMBOL(snd_sof_ipc_reply); -static void ipc_comp_notification(struct snd_sof_dev *sdev, - struct sof_ipc_cmd_hdr *hdr) +static void ipc_comp_notification(struct snd_sof_dev *sdev, void *full_msg) { + struct sof_ipc_cmd_hdr *hdr = full_msg; u32 msg_type = hdr->cmd & SOF_CMD_TYPE_MASK; - struct sof_ipc_ctrl_data *cdata; - int ret; switch (msg_type) { case SOF_IPC_COMP_GET_VALUE: case SOF_IPC_COMP_GET_DATA: - cdata = kmalloc(hdr->size, GFP_KERNEL); - if (!cdata) - return; - - /* read back full message */ - ret = snd_sof_ipc_msg_data(sdev, NULL, cdata, hdr->size); - if (ret < 0) { - dev_err(sdev->dev, - "error: failed to read component event: %d\n", ret); - goto err; - } break; default: dev_err(sdev->dev, "error: unhandled component message %#x\n", msg_type); return; } - snd_sof_control_notify(sdev, cdata); - -err: - kfree(cdata); + snd_sof_control_notify(sdev, full_msg); } /* DSP firmware has sent host a message */ void snd_sof_ipc_msgs_rx(struct snd_sof_dev *sdev) { + ipc_rx_callback rx_callback = NULL; struct sof_ipc_cmd_hdr hdr; - u32 cmd, type; + void *full_msg; + u32 cmd; int err; /* read back header */ @@ -438,7 +426,6 @@ void snd_sof_ipc_msgs_rx(struct snd_sof_dev *sdev) ipc_log_header(sdev->dev, "ipc rx", hdr.cmd); cmd = hdr.cmd & SOF_GLB_TYPE_MASK; - type = hdr.cmd & SOF_CMD_TYPE_MASK; /* check message type */ switch (cmd) { @@ -463,20 +450,34 @@ void snd_sof_ipc_msgs_rx(struct snd_sof_dev *sdev) case SOF_IPC_GLB_PM_MSG: break; case SOF_IPC_GLB_COMP_MSG: - ipc_comp_notification(sdev, &hdr); + rx_callback = ipc_comp_notification; break; case SOF_IPC_GLB_STREAM_MSG: - /* need to pass msg id into the function */ - ipc_stream_message(sdev, hdr.cmd); + rx_callback = ipc_stream_message; break; case SOF_IPC_GLB_TRACE_MSG: - ipc_trace_message(sdev, type); + rx_callback = ipc_trace_message; break; default: dev_err(sdev->dev, "error: unknown DSP message 0x%x\n", cmd); break; } + if (rx_callback) { + /* read the full message as there we have rx handler for it */ + full_msg = kmalloc(hdr.size, GFP_KERNEL); + if (!full_msg) + return; + + err = snd_sof_ipc_msg_data(sdev, NULL, full_msg, hdr.size); + if (err < 0) + dev_err(sdev->dev, "failed to read message\n"); + else + rx_callback(sdev, full_msg); + + kfree(full_msg); + } + ipc_log_header(sdev->dev, "ipc rx done", hdr.cmd); } EXPORT_SYMBOL(snd_sof_ipc_msgs_rx); @@ -485,19 +486,14 @@ EXPORT_SYMBOL(snd_sof_ipc_msgs_rx); * IPC trace mechanism. */ -static void ipc_trace_message(struct snd_sof_dev *sdev, u32 msg_type) +static void ipc_trace_message(struct snd_sof_dev *sdev, void *full_msg) { - struct sof_ipc_dma_trace_posn posn; - int ret; + struct sof_ipc_cmd_hdr *hdr = full_msg; + u32 msg_type = hdr->cmd & SOF_CMD_TYPE_MASK; switch (msg_type) { case SOF_IPC_TRACE_DMA_POSITION: - /* read back full message */ - ret = snd_sof_ipc_msg_data(sdev, NULL, &posn, sizeof(posn)); - if (ret < 0) - dev_warn(sdev->dev, "failed to read trace position: %d\n", ret); - else - snd_sof_trace_update_pos(sdev, &posn); + snd_sof_trace_update_pos(sdev, full_msg); break; default: dev_err(sdev->dev, "error: unhandled trace message %#x\n", msg_type); @@ -576,11 +572,11 @@ static void ipc_xrun(struct snd_sof_dev *sdev, u32 msg_id) } /* stream notifications from DSP FW */ -static void ipc_stream_message(struct snd_sof_dev *sdev, u32 msg_cmd) +static void ipc_stream_message(struct snd_sof_dev *sdev, void *full_msg) { - /* get msg cmd type and msd id */ - u32 msg_type = msg_cmd & SOF_CMD_TYPE_MASK; - u32 msg_id = SOF_IPC_MESSAGE_ID(msg_cmd); + struct sof_ipc_cmd_hdr *hdr = full_msg; + u32 msg_type = hdr->cmd & SOF_CMD_TYPE_MASK; + u32 msg_id = SOF_IPC_MESSAGE_ID(hdr->cmd); switch (msg_type) { case SOF_IPC_STREAM_POSITION: From 11f3b3ad72758a98be91c269c67350223905d309 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Tue, 6 Jul 2021 16:48:51 +0300 Subject: [PATCH 10/32] ASoC: SOF: clients: Add support for IPC rx and firmware state change IPC rx is firmware initiated IPC message (notification/event). Add infrastructure for SOF clients to register their interest to a given SOF_GLB_TYPE() message or to receive notification about a firmware state change (not booted, booted and crashed state is broadcasted). For IPC rx one client can register for multiple types and multiple clients can register to receive notification for the same type as well. Add API to query the firmware state for clients which does not need to monitor the state, like IPC tests. Signed-off-by: Peter Ujfalusi --- sound/soc/sof/core.c | 23 +++++- sound/soc/sof/ipc.c | 24 +++--- sound/soc/sof/sof-client.c | 155 +++++++++++++++++++++++++++++++++++++ sound/soc/sof/sof-client.h | 21 +++++ sound/soc/sof/sof-priv.h | 32 ++++++-- 5 files changed, 239 insertions(+), 16 deletions(-) diff --git a/sound/soc/sof/core.c b/sound/soc/sof/core.c index 13e58b602cdba8..11d0bf4fe9bcde 100644 --- a/sound/soc/sof/core.c +++ b/sound/soc/sof/core.c @@ -95,6 +95,23 @@ void snd_sof_get_status(struct snd_sof_dev *sdev, u32 panic_code, } EXPORT_SYMBOL(snd_sof_get_status); +/* Helper to manage DSP state */ +void sof_set_fw_state(struct snd_sof_dev *sdev, enum snd_sof_fw_state new_state) +{ + sdev->fw_state = new_state; + + switch (new_state) { + case SOF_FW_BOOT_NOT_STARTED: + case SOF_FW_BOOT_COMPLETE: + case SOF_FW_CRASHED: + sof_client_fw_state_dispatcher(sdev); + fallthrough; + default: + break; + } +} +EXPORT_SYMBOL(sof_set_fw_state); + /* * FW Boot State Transition Diagram * @@ -308,7 +325,6 @@ int snd_sof_device_probe(struct device *dev, struct snd_sof_pdata *plat_data) sdev->pdata = plat_data; sdev->first_boot = true; - sof_set_fw_state(sdev, SOF_FW_BOOT_NOT_STARTED); #if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_PROBES) sdev->extractor_stream_tag = SOF_PROBE_INVALID_NODE_ID; #endif @@ -330,10 +346,13 @@ int snd_sof_device_probe(struct device *dev, struct snd_sof_pdata *plat_data) INIT_LIST_HEAD(&sdev->dai_list); INIT_LIST_HEAD(&sdev->route_list); INIT_LIST_HEAD(&sdev->ipc_client_list); + INIT_LIST_HEAD(&sdev->ipc_rx_handler_list); + INIT_LIST_HEAD(&sdev->fw_state_handler_list); spin_lock_init(&sdev->ipc_lock); spin_lock_init(&sdev->hw_lock); mutex_init(&sdev->power_state_access); mutex_init(&sdev->ipc_client_mutex); + mutex_init(&sdev->client_event_handler_mutex); /* set default timeouts if none provided */ if (plat_data->desc->ipc_timeout == 0) @@ -345,6 +364,8 @@ int snd_sof_device_probe(struct device *dev, struct snd_sof_pdata *plat_data) else sdev->boot_timeout = plat_data->desc->boot_timeout; + sof_set_fw_state(sdev, SOF_FW_BOOT_NOT_STARTED); + if (IS_ENABLED(CONFIG_SND_SOC_SOF_PROBE_WORK_QUEUE)) { INIT_WORK(&sdev->probe_work, sof_probe_work); schedule_work(&sdev->probe_work); diff --git a/sound/soc/sof/ipc.c b/sound/soc/sof/ipc.c index 647fb20d2701de..f35a7f3b05dd08 100644 --- a/sound/soc/sof/ipc.c +++ b/sound/soc/sof/ipc.c @@ -463,21 +463,25 @@ void snd_sof_ipc_msgs_rx(struct snd_sof_dev *sdev) break; } - if (rx_callback) { - /* read the full message as there we have rx handler for it */ - full_msg = kmalloc(hdr.size, GFP_KERNEL); - if (!full_msg) - return; + /* read the full message as there we have rx handler for it */ + full_msg = kmalloc(hdr.size, GFP_KERNEL); + if (!full_msg) + return; - err = snd_sof_ipc_msg_data(sdev, NULL, full_msg, hdr.size); - if (err < 0) - dev_err(sdev->dev, "failed to read message\n"); - else + err = snd_sof_ipc_msg_data(sdev, NULL, full_msg, hdr.size); + if (err < 0) { + dev_err(sdev->dev, "failed to read message\n"); + } else { + /* Call local handler for the message */ + if (rx_callback) rx_callback(sdev, full_msg); - kfree(full_msg); + /* Notify registered clients */ + sof_client_ipc_rx_dispatcher(sdev, full_msg); } + kfree(full_msg); + ipc_log_header(sdev->dev, "ipc rx done", hdr.cmd); } EXPORT_SYMBOL(snd_sof_ipc_msgs_rx); diff --git a/sound/soc/sof/sof-client.c b/sound/soc/sof/sof-client.c index 38ccbadf1851cf..1a360f7bf99211 100644 --- a/sound/soc/sof/sof-client.c +++ b/sound/soc/sof/sof-client.c @@ -15,6 +15,32 @@ #include "sof-client.h" #include "sof-priv.h" +/** + * struct sof_ipc_event_entry - IPC client event description + * @ipc_msg_type: IPC msg type of the event the client is interested + * @cdev: sof_client_dev of the requesting client + * @callback: Callback function of the client + * @list: item in SOF core client event list + */ +struct sof_ipc_event_entry { + u32 ipc_msg_type; + struct sof_client_dev *cdev; + sof_client_event_callback callback; + struct list_head list; +}; + +/** + * struct sof_state_event_entry - DSP panic event subscription entry + * @cdev: sof_client_dev of the requesting client + * @callback: Callback function of the client + * @list: item in SOF core client event list + */ +struct sof_state_event_entry { + struct sof_client_dev *cdev; + sof_client_fw_state_callback callback; + struct list_head list; +}; + static void sof_client_auxdev_release(struct device *dev) { struct auxiliary_device *auxdev = to_auxiliary_dev(dev); @@ -150,3 +176,132 @@ struct device *sof_client_get_dma_dev(struct sof_client_dev *cdev) return cdev->sdev->dev; } EXPORT_SYMBOL_NS_GPL(sof_client_get_dma_dev, SND_SOC_SOF_CLIENT); + +/* IPC event handling */ +void sof_client_ipc_rx_dispatcher(struct snd_sof_dev *sdev, void *full_msg) +{ + struct sof_ipc_cmd_hdr *hdr = full_msg; + u32 msg_type = hdr->cmd & SOF_GLB_TYPE_MASK; + struct sof_ipc_event_entry *event; + + mutex_lock(&sdev->client_event_handler_mutex); + + list_for_each_entry(event, &sdev->ipc_rx_handler_list, list) { + if (event->ipc_msg_type == msg_type) + event->callback(event->cdev, full_msg); + } + + mutex_unlock(&sdev->client_event_handler_mutex); +} + +int sof_client_register_ipc_rx_handler(struct sof_client_dev *cdev, + u32 ipc_msg_type, + sof_client_event_callback callback) +{ + struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev); + struct sof_ipc_event_entry *event; + + if (!callback || !(ipc_msg_type & SOF_GLB_TYPE_MASK)) + return -EINVAL; + + event = kmalloc(sizeof(*event), GFP_KERNEL); + if (!event) + return -ENOMEM; + + event->ipc_msg_type = ipc_msg_type; + event->cdev = cdev; + event->callback = callback; + + /* add to list of SOF client devices */ + mutex_lock(&sdev->client_event_handler_mutex); + list_add(&event->list, &sdev->ipc_rx_handler_list); + mutex_unlock(&sdev->client_event_handler_mutex); + + return 0; +} +EXPORT_SYMBOL_NS_GPL(sof_client_register_ipc_rx_handler, SND_SOC_SOF_CLIENT); + +void sof_client_unregister_ipc_rx_handler(struct sof_client_dev *cdev, + u32 ipc_msg_type) +{ + struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev); + struct sof_ipc_event_entry *event; + + mutex_lock(&sdev->client_event_handler_mutex); + + list_for_each_entry(event, &sdev->ipc_rx_handler_list, list) { + if (event->cdev == cdev && event->ipc_msg_type == ipc_msg_type) { + list_del(&event->list); + kfree(event); + break; + } + } + + mutex_unlock(&sdev->client_event_handler_mutex); +} +EXPORT_SYMBOL_NS_GPL(sof_client_unregister_ipc_rx_handler, SND_SOC_SOF_CLIENT); + +/*DSP state notification and query */ +void sof_client_fw_state_dispatcher(struct snd_sof_dev *sdev) +{ + struct sof_state_event_entry *event; + + mutex_lock(&sdev->client_event_handler_mutex); + + list_for_each_entry(event, &sdev->fw_state_handler_list, list) + event->callback(event->cdev, sdev->fw_state); + + mutex_unlock(&sdev->client_event_handler_mutex); +} + +int sof_client_register_fw_state_handler(struct sof_client_dev *cdev, + sof_client_fw_state_callback callback) +{ + struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev); + struct sof_state_event_entry *event; + + if (!callback) + return -EINVAL; + + event = kmalloc(sizeof(*event), GFP_KERNEL); + if (!event) + return -ENOMEM; + + event->cdev = cdev; + event->callback = callback; + + /* add to list of SOF client devices */ + mutex_lock(&sdev->client_event_handler_mutex); + list_add(&event->list, &sdev->fw_state_handler_list); + mutex_unlock(&sdev->client_event_handler_mutex); + + return 0; +} +EXPORT_SYMBOL_NS_GPL(sof_client_register_fw_state_handler, SND_SOC_SOF_CLIENT); + +void sof_client_unregister_fw_state_handler(struct sof_client_dev *cdev) +{ + struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev); + struct sof_state_event_entry *event; + + mutex_lock(&sdev->client_event_handler_mutex); + + list_for_each_entry(event, &sdev->fw_state_handler_list, list) { + if (event->cdev == cdev) { + list_del(&event->list); + kfree(event); + break; + } + } + + mutex_unlock(&sdev->client_event_handler_mutex); +} +EXPORT_SYMBOL_NS_GPL(sof_client_unregister_fw_state_handler, SND_SOC_SOF_CLIENT); + +enum snd_sof_fw_state sof_client_get_fw_state(struct sof_client_dev *cdev) +{ + struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev); + + return sdev->fw_state; +} +EXPORT_SYMBOL_NS_GPL(sof_client_get_fw_state, SND_SOC_SOF_CLIENT); diff --git a/sound/soc/sof/sof-client.h b/sound/soc/sof/sof-client.h index 1d92b8416ae7c5..374f2ebfefe812 100644 --- a/sound/soc/sof/sof-client.h +++ b/sound/soc/sof/sof-client.h @@ -6,7 +6,9 @@ #include #include #include +#include +struct sof_ipc_cmd_hdr; struct snd_sof_dev; struct dentry; @@ -38,4 +40,23 @@ int sof_client_ipc_tx_message(struct sof_client_dev *cdev, void *ipc_msg, struct dentry *sof_client_get_debugfs_root(struct sof_client_dev *cdev); struct device *sof_client_get_dma_dev(struct sof_client_dev *cdev); +/* IPC notification */ +typedef void (*sof_client_event_callback)(struct sof_client_dev *cdev, + void *full_msg); + +int sof_client_register_ipc_rx_handler(struct sof_client_dev *cdev, + u32 ipc_msg_type, + sof_client_event_callback callback); +void sof_client_unregister_ipc_rx_handler(struct sof_client_dev *cdev, + u32 ipc_msg_type); + +/* DSP state notification and query */ +typedef void (*sof_client_fw_state_callback)(struct sof_client_dev *cdev, + enum snd_sof_fw_state state); + +int sof_client_register_fw_state_handler(struct sof_client_dev *cdev, + sof_client_fw_state_callback callback); +void sof_client_unregister_fw_state_handler(struct sof_client_dev *cdev); +enum snd_sof_fw_state sof_client_get_fw_state(struct sof_client_dev *cdev); + #endif /* __SOC_SOF_CLIENT_H */ diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index b6cb247cc0c44a..d8d80fb04cf590 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -493,6 +493,21 @@ struct snd_sof_dev { /* mutex to protect client list */ struct mutex ipc_client_mutex; + /* + * Used for tracking the IPC client's RX registration for DSP initiated + * message handling. + */ + struct list_head ipc_rx_handler_list; + + /* + * Used for tracking the IPC client's registration for DSP state change + * notification + */ + struct list_head fw_state_handler_list; + + /* to protect the ipc_rx_handler_list and dsp_state_handler_list list */ + struct mutex client_event_handler_mutex; + void *private; /* core does not touch this */ }; @@ -592,11 +607,7 @@ extern const struct dsp_arch_ops sof_xtensa_arch_ops; /* * Firmware state tracking */ -static inline void sof_set_fw_state(struct snd_sof_dev *sdev, - enum snd_sof_fw_state new_state) -{ - sdev->fw_state = new_state; -} +void sof_set_fw_state(struct snd_sof_dev *sdev, enum snd_sof_fw_state new_state); /* * Utilities @@ -637,6 +648,8 @@ int sof_client_dev_register(struct snd_sof_dev *sdev, const char *name, u32 id, void sof_client_dev_unregister(struct snd_sof_dev *sdev, const char *name, u32 id); int sof_register_clients(struct snd_sof_dev *sdev); void sof_unregister_clients(struct snd_sof_dev *sdev); +void sof_client_ipc_rx_dispatcher(struct snd_sof_dev *sdev, void *full_msg); +void sof_client_fw_state_dispatcher(struct snd_sof_dev *sdev); #else /* CONFIG_SND_SOC_SOF_CLIENT */ static inline int sof_client_dev_register(struct snd_sof_dev *sdev, const char *name, u32 id, const void *data, size_t size) @@ -657,6 +670,15 @@ static inline int sof_register_clients(struct snd_sof_dev *sdev) static inline void sof_unregister_clients(struct snd_sof_dev *sdev) { } + +static inline void sof_client_ipc_rx_dispatcher(struct snd_sof_dev *sdev, + void *full_msg) +{ +} + +static inline void sof_client_fw_state_dispatcher(struct snd_sof_dev *sdev) +{ +} #endif /* CONFIG_SND_SOC_SOF_CLIENT */ #endif From 20c932354c27229f89ed0770e4bf76cf0206fcf8 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Fri, 9 Jul 2021 10:53:15 +0300 Subject: [PATCH 11/32] ASoC: SOF: clients: Add support for auxdev suspend/resume handling The auxiliary bus provides suspend and resume callbacks for auxbus devices. They are used to notify child devices about the real hardware suspend resume operation so they can act if there is a need for them in case the real device's state changes. Signed-off-by: Peter Ujfalusi --- sound/soc/sof/pm.c | 7 ++++++ sound/soc/sof/sof-client.c | 46 ++++++++++++++++++++++++++++++++++++++ sound/soc/sof/sof-priv.h | 12 ++++++++++ 3 files changed, 65 insertions(+) diff --git a/sound/soc/sof/pm.c b/sound/soc/sof/pm.c index b8712fbaaa9f6c..72c8e7443f8950 100644 --- a/sound/soc/sof/pm.c +++ b/sound/soc/sof/pm.c @@ -165,6 +165,9 @@ static int sof_resume(struct device *dev, bool runtime_resume) return ret; } + /* Notify clients about core resume */ + sof_resume_clients(sdev); + /* notify DSP of system resume */ ret = sof_send_pm_ctx_ipc(sdev, SOF_IPC_PM_CTX_RESTORE); if (ret < 0) @@ -178,6 +181,7 @@ static int sof_resume(struct device *dev, bool runtime_resume) static int sof_suspend(struct device *dev, bool runtime_suspend) { struct snd_sof_dev *sdev = dev_get_drvdata(dev); + pm_message_t pm_state = {}; u32 target_state = 0; int ret; @@ -216,6 +220,9 @@ static int sof_suspend(struct device *dev, bool runtime_suspend) /* release trace */ snd_sof_release_trace(sdev); + /* Notify clients about core suspend */ + sof_suspend_clients(sdev, pm_state); + #if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_ENABLE_DEBUGFS_CACHE) /* cache debugfs contents during runtime suspend */ if (runtime_suspend) diff --git a/sound/soc/sof/sof-client.c b/sound/soc/sof/sof-client.c index 1a360f7bf99211..315559cef22f53 100644 --- a/sound/soc/sof/sof-client.c +++ b/sound/soc/sof/sof-client.c @@ -164,6 +164,52 @@ int sof_client_ipc_tx_message(struct sof_client_dev *cdev, void *ipc_msg, } EXPORT_SYMBOL_NS_GPL(sof_client_ipc_tx_message, SND_SOC_SOF_CLIENT); +int sof_suspend_clients(struct snd_sof_dev *sdev, pm_message_t state) +{ + struct auxiliary_driver *adrv; + struct sof_client_dev *cdev; + + mutex_lock(&sdev->ipc_client_mutex); + + list_for_each_entry(cdev, &sdev->ipc_client_list, list) { + /* Skip devices without loaded driver */ + if (!cdev->auxdev.dev.driver) + continue; + + adrv = to_auxiliary_drv(cdev->auxdev.dev.driver); + if (adrv->suspend) + adrv->suspend(&cdev->auxdev, state); + } + + mutex_unlock(&sdev->ipc_client_mutex); + + return 0; +} +EXPORT_SYMBOL_NS_GPL(sof_suspend_clients, SND_SOC_SOF_CLIENT); + +int sof_resume_clients(struct snd_sof_dev *sdev) +{ + struct auxiliary_driver *adrv; + struct sof_client_dev *cdev; + + mutex_lock(&sdev->ipc_client_mutex); + + list_for_each_entry(cdev, &sdev->ipc_client_list, list) { + /* Skip devices without loaded driver */ + if (!cdev->auxdev.dev.driver) + continue; + + adrv = to_auxiliary_drv(cdev->auxdev.dev.driver); + if (adrv->resume) + adrv->resume(&cdev->auxdev); + } + + mutex_unlock(&sdev->ipc_client_mutex); + + return 0; +} +EXPORT_SYMBOL_NS_GPL(sof_resume_clients, SND_SOC_SOF_CLIENT); + struct dentry *sof_client_get_debugfs_root(struct sof_client_dev *cdev) { return cdev->sdev->debugfs_root; diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index d8d80fb04cf590..63479f4a9fe175 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -650,6 +650,8 @@ int sof_register_clients(struct snd_sof_dev *sdev); void sof_unregister_clients(struct snd_sof_dev *sdev); void sof_client_ipc_rx_dispatcher(struct snd_sof_dev *sdev, void *full_msg); void sof_client_fw_state_dispatcher(struct snd_sof_dev *sdev); +int sof_suspend_clients(struct snd_sof_dev *sdev, pm_message_t state); +int sof_resume_clients(struct snd_sof_dev *sdev); #else /* CONFIG_SND_SOC_SOF_CLIENT */ static inline int sof_client_dev_register(struct snd_sof_dev *sdev, const char *name, u32 id, const void *data, size_t size) @@ -679,6 +681,16 @@ static inline void sof_client_ipc_rx_dispatcher(struct snd_sof_dev *sdev, static inline void sof_client_fw_state_dispatcher(struct snd_sof_dev *sdev) { } + +static inline int sof_suspend_clients(struct snd_sof_dev *sdev, pm_message_t state) +{ + return 0; +} + +static inline int sof_resume_clients(struct snd_sof_dev *sdev) +{ + return 0; +} #endif /* CONFIG_SND_SOC_SOF_CLIENT */ #endif From 9e32894efb572b721a7da42c60e1d267e68389fc Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 7 Jul 2021 15:14:25 +0300 Subject: [PATCH 12/32] ASoC: SOF: clients: Add API to get the SOF firmware version Clients might need to be aware of the SOF firmware version in case the IPC message format differs between revisions. Signed-off-by: Peter Ujfalusi --- sound/soc/sof/sof-client.c | 8 ++++++++ sound/soc/sof/sof-client.h | 2 ++ 2 files changed, 10 insertions(+) diff --git a/sound/soc/sof/sof-client.c b/sound/soc/sof/sof-client.c index 315559cef22f53..715a85d3f04946 100644 --- a/sound/soc/sof/sof-client.c +++ b/sound/soc/sof/sof-client.c @@ -223,6 +223,14 @@ struct device *sof_client_get_dma_dev(struct sof_client_dev *cdev) } EXPORT_SYMBOL_NS_GPL(sof_client_get_dma_dev, SND_SOC_SOF_CLIENT); +const struct sof_ipc_fw_version *sof_client_get_fw_version(struct sof_client_dev *cdev) +{ + struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev); + + return &sdev->fw_ready.version; +} +EXPORT_SYMBOL_NS_GPL(sof_client_get_fw_version, SND_SOC_SOF_CLIENT); + /* IPC event handling */ void sof_client_ipc_rx_dispatcher(struct snd_sof_dev *sdev, void *full_msg) { diff --git a/sound/soc/sof/sof-client.h b/sound/soc/sof/sof-client.h index 374f2ebfefe812..68d9797119d9d9 100644 --- a/sound/soc/sof/sof-client.h +++ b/sound/soc/sof/sof-client.h @@ -8,6 +8,7 @@ #include #include +struct sof_ipc_fw_version; struct sof_ipc_cmd_hdr; struct snd_sof_dev; struct dentry; @@ -39,6 +40,7 @@ int sof_client_ipc_tx_message(struct sof_client_dev *cdev, void *ipc_msg, struct dentry *sof_client_get_debugfs_root(struct sof_client_dev *cdev); struct device *sof_client_get_dma_dev(struct sof_client_dev *cdev); +const struct sof_ipc_fw_version *sof_client_get_fw_version(struct sof_client_dev *cdev); /* IPC notification */ typedef void (*sof_client_event_callback)(struct sof_client_dev *cdev, From bcaad353eef97539f3087f46cfef400d0a8a0665 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 21 Jul 2021 15:10:01 +0300 Subject: [PATCH 13/32] ASoC: SOF: clients: Add API to manage the module refcount of SOF core The SOF core module (the device driver of the DSP) needs to be protected against removal when a client is active and needs the core to be present during it's active operation. sof_client_core_moduleget/put can be use by client drivers to manage the refcount on the core's module to prevent such a case. Signed-off-by: Peter Ujfalusi --- sound/soc/sof/sof-client.c | 20 ++++++++++++++++++++ sound/soc/sof/sof-client.h | 4 ++++ 2 files changed, 24 insertions(+) diff --git a/sound/soc/sof/sof-client.c b/sound/soc/sof/sof-client.c index 715a85d3f04946..6de5ebb38ae9b7 100644 --- a/sound/soc/sof/sof-client.c +++ b/sound/soc/sof/sof-client.c @@ -231,6 +231,26 @@ const struct sof_ipc_fw_version *sof_client_get_fw_version(struct sof_client_dev } EXPORT_SYMBOL_NS_GPL(sof_client_get_fw_version, SND_SOC_SOF_CLIENT); +/* module refcount management of SOF core */ +int sof_client_core_module_get(struct sof_client_dev *cdev) +{ + struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev); + + if (!try_module_get(sdev->dev->driver->owner)) + return -ENODEV; + + return 0; +} +EXPORT_SYMBOL_NS_GPL(sof_client_core_module_get, SND_SOC_SOF_CLIENT); + +void sof_client_core_module_put(struct sof_client_dev *cdev) +{ + struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev); + + module_put(sdev->dev->driver->owner); +} +EXPORT_SYMBOL_NS_GPL(sof_client_core_module_put, SND_SOC_SOF_CLIENT); + /* IPC event handling */ void sof_client_ipc_rx_dispatcher(struct snd_sof_dev *sdev, void *full_msg) { diff --git a/sound/soc/sof/sof-client.h b/sound/soc/sof/sof-client.h index 68d9797119d9d9..4cd4646d0aa51b 100644 --- a/sound/soc/sof/sof-client.h +++ b/sound/soc/sof/sof-client.h @@ -42,6 +42,10 @@ struct dentry *sof_client_get_debugfs_root(struct sof_client_dev *cdev); struct device *sof_client_get_dma_dev(struct sof_client_dev *cdev); const struct sof_ipc_fw_version *sof_client_get_fw_version(struct sof_client_dev *cdev); +/* module refcount management of SOF core */ +int sof_client_core_module_get(struct sof_client_dev *cdev); +void sof_client_core_module_put(struct sof_client_dev *cdev); + /* IPC notification */ typedef void (*sof_client_event_callback)(struct sof_client_dev *cdev, void *full_msg); From 92a48c257a86cc8c7818f7807ef5a17997e3889e Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Fri, 9 Jul 2021 11:30:59 +0300 Subject: [PATCH 14/32] ASoC: SOF: intel: hda-trace: Pass the dma buffer pointer to hda_dsp_trace_prepare Pass the snd_dma_buffer pointer as parameter to hda_dsp_trace_prepare() function. Signed-off-by: Peter Ujfalusi --- sound/soc/sof/intel/hda-trace.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/sound/soc/sof/intel/hda-trace.c b/sound/soc/sof/intel/hda-trace.c index 29e3da3c63db01..c5dc833b57b8fc 100644 --- a/sound/soc/sof/intel/hda-trace.c +++ b/sound/soc/sof/intel/hda-trace.c @@ -19,16 +19,15 @@ #include "../ops.h" #include "hda.h" -static int hda_dsp_trace_prepare(struct snd_sof_dev *sdev) +static int hda_dsp_trace_prepare(struct snd_sof_dev *sdev, struct snd_dma_buffer *dmab) { struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; struct hdac_ext_stream *stream = hda->dtrace_stream; struct hdac_stream *hstream = &stream->hstream; - struct snd_dma_buffer *dmab = &sdev->dmatb; int ret; hstream->period_bytes = 0;/* initialize period_bytes */ - hstream->bufsize = sdev->dmatb.bytes; + hstream->bufsize = dmab->bytes; ret = hda_dsp_stream_hw_params(sdev, stream, dmab, NULL); if (ret < 0) @@ -57,7 +56,7 @@ int hda_dsp_trace_init(struct snd_sof_dev *sdev, u32 *stream_tag) * initialize capture stream, set BDL address and return corresponding * stream tag which will be sent to the firmware by IPC message. */ - ret = hda_dsp_trace_prepare(sdev); + ret = hda_dsp_trace_prepare(sdev, &sdev->dmatb); if (ret < 0) { dev_err(sdev->dev, "error: hdac trace init failed: %d\n", ret); hda_dsp_stream_put(sdev, SNDRV_PCM_STREAM_CAPTURE, *stream_tag); From 9e675f230104afc924790cd0f53372191554d34f Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 15 Jul 2021 15:46:57 +0300 Subject: [PATCH 15/32] ASoC: SOF: Split up utils.c into sof-utils and iomem-utils The utils.c contains wrappers and implementation for accessing iomem mapped regions and a single unrelated function to create a compressed page table from snd_dma_buffer for firmware use. The later is used by the pcm and the trace code and it needs to be moved to a generic source/header for the client conversion to be possible. Signed-off-by: Peter Ujfalusi --- sound/soc/sof/Makefile | 5 +- sound/soc/sof/{utils.c => iomem-utils.c} | 61 +------------------ sound/soc/sof/pcm.c | 1 + sound/soc/sof/sof-priv.h | 4 -- sound/soc/sof/sof-utils.c | 77 ++++++++++++++++++++++++ sound/soc/sof/sof-utils.h | 19 ++++++ sound/soc/sof/trace.c | 1 + 7 files changed, 103 insertions(+), 65 deletions(-) rename sound/soc/sof/{utils.c => iomem-utils.c} (59%) create mode 100644 sound/soc/sof/sof-utils.c create mode 100644 sound/soc/sof/sof-utils.h diff --git a/sound/soc/sof/Makefile b/sound/soc/sof/Makefile index 67999db24b703c..1678c497f298ea 100644 --- a/sound/soc/sof/Makefile +++ b/sound/soc/sof/Makefile @@ -1,7 +1,7 @@ # SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) snd-sof-objs := core.o ops.o loader.o ipc.o pcm.o pm.o debug.o topology.o\ - control.o trace.o utils.o sof-audio.o stream-ipc.o + control.o trace.o iomem-utils.o sof-audio.o stream-ipc.o snd-sof-$(CONFIG_SND_SOC_SOF_CLIENT) += sof-client.o snd-sof-$(CONFIG_SND_SOC_SOF_DEBUG_PROBES) += sof-probes.o @@ -12,9 +12,12 @@ snd-sof-of-objs := sof-of-dev.o snd-sof-nocodec-objs := nocodec.o +snd-sof-utils-objs := sof-utils.o + obj-$(CONFIG_SND_SOC_SOF) += snd-sof.o obj-$(CONFIG_SND_SOC_SOF_NOCODEC) += snd-sof-nocodec.o +obj-$(CONFIG_SND_SOC_SOF) += snd-sof-utils.o obj-$(CONFIG_SND_SOC_SOF_ACPI_DEV) += snd-sof-acpi.o obj-$(CONFIG_SND_SOC_SOF_OF) += snd-sof-of.o diff --git a/sound/soc/sof/utils.c b/sound/soc/sof/iomem-utils.c similarity index 59% rename from sound/soc/sof/utils.c rename to sound/soc/sof/iomem-utils.c index 66fa6602fb67a3..7228a6e041c39f 100644 --- a/sound/soc/sof/utils.c +++ b/sound/soc/sof/iomem-utils.c @@ -3,7 +3,7 @@ // This file is provided under a dual BSD/GPLv2 license. When using or // redistributing this file, you may do so under either license. // -// Copyright(c) 2018 Intel Corporation. All rights reserved. +// Copyright(c) 2018-2021 Intel Corporation. All rights reserved. // // Author: Keyon Jie // @@ -125,62 +125,3 @@ int sof_block_read(struct snd_sof_dev *sdev, enum snd_sof_fw_blk_type blk_type, return 0; } EXPORT_SYMBOL(sof_block_read); - -/* - * Generic buffer page table creation. - * Take the each physical page address and drop the least significant unused - * bits from each (based on PAGE_SIZE). Then pack valid page address bits - * into compressed page table. - */ - -int snd_sof_create_page_table(struct device *dev, - struct snd_dma_buffer *dmab, - unsigned char *page_table, size_t size) -{ - int i, pages; - - pages = snd_sgbuf_aligned_pages(size); - - dev_dbg(dev, "generating page table for %p size 0x%zx pages %d\n", - dmab->area, size, pages); - - for (i = 0; i < pages; i++) { - /* - * The number of valid address bits for each page is 20. - * idx determines the byte position within page_table - * where the current page's address is stored - * in the compressed page_table. - * This can be calculated by multiplying the page number by 2.5. - */ - u32 idx = (5 * i) >> 1; - u32 pfn = snd_sgbuf_get_addr(dmab, i * PAGE_SIZE) >> PAGE_SHIFT; - u8 *pg_table; - - dev_vdbg(dev, "pfn i %i idx %d pfn %x\n", i, idx, pfn); - - pg_table = (u8 *)(page_table + idx); - - /* - * pagetable compression: - * byte 0 byte 1 byte 2 byte 3 byte 4 byte 5 - * ___________pfn 0__________ __________pfn 1___________ _pfn 2... - * .... .... .... .... .... .... .... .... .... .... .... - * It is created by: - * 1. set current location to 0, PFN index i to 0 - * 2. put pfn[i] at current location in Little Endian byte order - * 3. calculate an intermediate value as - * x = (pfn[i+1] << 4) | (pfn[i] & 0xf) - * 4. put x at offset (current location + 2) in LE byte order - * 5. increment current location by 5 bytes, increment i by 2 - * 6. continue to (2) - */ - if (i & 1) - put_unaligned_le32((pg_table[0] & 0xf) | pfn << 4, - pg_table); - else - put_unaligned_le32(pfn, pg_table); - } - - return pages; -} -EXPORT_SYMBOL(snd_sof_create_page_table); diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c index 172a2737eaacdf..24f0a74dfb90c6 100644 --- a/sound/soc/sof/pcm.c +++ b/sound/soc/sof/pcm.c @@ -19,6 +19,7 @@ #if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_PROBES) #include "sof-probes.h" #endif +#include "sof-utils.h" /* Create DMA buffer page table for DSP */ static int create_page_table(struct snd_soc_component *component, diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index 63479f4a9fe175..23e05de51bda0f 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -531,10 +531,6 @@ void snd_sof_complete(struct device *dev); void snd_sof_new_platform_drv(struct snd_sof_dev *sdev); -int snd_sof_create_page_table(struct device *dev, - struct snd_dma_buffer *dmab, - unsigned char *page_table, size_t size); - /* * Firmware loading. */ diff --git a/sound/soc/sof/sof-utils.c b/sound/soc/sof/sof-utils.c new file mode 100644 index 00000000000000..0d48f7174ec237 --- /dev/null +++ b/sound/soc/sof/sof-utils.c @@ -0,0 +1,77 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) +// +// This file is provided under a dual BSD/GPLv2 license. When using or +// redistributing this file, you may do so under either license. +// +// Copyright(c) 2018-2021 Intel Corporation. All rights reserved. +// +// Author: Keyon Jie +// + +#include +#include +#include +#include +#include +#include "sof-utils.h" + +/* + * Generic buffer page table creation. + * Take the each physical page address and drop the least significant unused + * bits from each (based on PAGE_SIZE). Then pack valid page address bits + * into compressed page table. + */ + +int snd_sof_create_page_table(struct device *dev, + struct snd_dma_buffer *dmab, + unsigned char *page_table, size_t size) +{ + int i, pages; + + pages = snd_sgbuf_aligned_pages(size); + + dev_dbg(dev, "generating page table for %p size 0x%zx pages %d\n", + dmab->area, size, pages); + + for (i = 0; i < pages; i++) { + /* + * The number of valid address bits for each page is 20. + * idx determines the byte position within page_table + * where the current page's address is stored + * in the compressed page_table. + * This can be calculated by multiplying the page number by 2.5. + */ + u32 idx = (5 * i) >> 1; + u32 pfn = snd_sgbuf_get_addr(dmab, i * PAGE_SIZE) >> PAGE_SHIFT; + u8 *pg_table; + + dev_vdbg(dev, "pfn i %i idx %d pfn %x\n", i, idx, pfn); + + pg_table = (u8 *)(page_table + idx); + + /* + * pagetable compression: + * byte 0 byte 1 byte 2 byte 3 byte 4 byte 5 + * ___________pfn 0__________ __________pfn 1___________ _pfn 2... + * .... .... .... .... .... .... .... .... .... .... .... + * It is created by: + * 1. set current location to 0, PFN index i to 0 + * 2. put pfn[i] at current location in Little Endian byte order + * 3. calculate an intermediate value as + * x = (pfn[i+1] << 4) | (pfn[i] & 0xf) + * 4. put x at offset (current location + 2) in LE byte order + * 5. increment current location by 5 bytes, increment i by 2 + * 6. continue to (2) + */ + if (i & 1) + put_unaligned_le32((pg_table[0] & 0xf) | pfn << 4, + pg_table); + else + put_unaligned_le32(pfn, pg_table); + } + + return pages; +} +EXPORT_SYMBOL(snd_sof_create_page_table); + +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/sound/soc/sof/sof-utils.h b/sound/soc/sof/sof-utils.h new file mode 100644 index 00000000000000..cf508ff7ff5ede --- /dev/null +++ b/sound/soc/sof/sof-utils.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2021 Intel Corporation. All rights reserved. + */ + +#ifndef __SOC_SOF_UTILS_H +#define __SOC_SOF_UTILS_H + +struct snd_dma_buffer; +struct device; + +int snd_sof_create_page_table(struct device *dev, + struct snd_dma_buffer *dmab, + unsigned char *page_table, size_t size); + +#endif diff --git a/sound/soc/sof/trace.c b/sound/soc/sof/trace.c index e3afc3dac7d17a..8862ef9eeb8a58 100644 --- a/sound/soc/sof/trace.c +++ b/sound/soc/sof/trace.c @@ -12,6 +12,7 @@ #include #include "sof-priv.h" #include "ops.h" +#include "sof-utils.h" #define TRACE_FILTER_ELEMENTS_PER_ENTRY 4 #define TRACE_FILTER_MAX_CONFIG_STRING_LENGTH 1024 From deb5a0c2cfe3aae245fd058ef1146651eaf9766e Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Thu, 22 Oct 2020 16:26:48 -0700 Subject: [PATCH 16/32] ASoC: SOF: Convert the generic IPC flood test into SOF client Move the IPC flood test code out from the debug file as separate SOF client driver. Based on the kernel configuration, the device registration for the new IPC flood test is going to happen in the core. With the separate client driver it is going to be possible to run multiple flood tests in parallel to increase the stress, the new Kconfig option can be used to select this (defaults to 1). In order to preserve backward compatibility with existing SW/scripts, the first IPC flood test's debugfs files have been linked to the old files. Signed-off-by: Ranjani Sridharan Co-developed-by: Fred Oh Signed-off-by: Fred Oh Signed-off-by: Peter Ujfalusi --- sound/soc/sof/Kconfig | 16 +- sound/soc/sof/Makefile | 4 + sound/soc/sof/core.c | 1 + sound/soc/sof/debug.c | 231 ----------------- sound/soc/sof/sof-client-ipc-test.c | 369 ++++++++++++++++++++++++++++ sound/soc/sof/sof-client.c | 56 ++++- sound/soc/sof/sof-priv.h | 6 +- 7 files changed, 442 insertions(+), 241 deletions(-) create mode 100644 sound/soc/sof/sof-client-ipc-test.c diff --git a/sound/soc/sof/Kconfig b/sound/soc/sof/Kconfig index 86cc0557a0de06..59e487843c1405 100644 --- a/sound/soc/sof/Kconfig +++ b/sound/soc/sof/Kconfig @@ -188,13 +188,23 @@ config SND_SOC_SOF_DEBUG_ENABLE_FIRMWARE_TRACE If unsure, select "N". config SND_SOC_SOF_DEBUG_IPC_FLOOD_TEST - bool "SOF enable IPC flood test" + tristate "SOF enable IPC flood test" + select SND_SOC_SOF_CLIENT help - This option enables the IPC flood test which can be used to flood - the DSP with test IPCs and gather stats about response times. + This option enables a separate client device for IPC flood test + which can be used to flood the DSP with test IPCs and gather stats + about response times. Say Y if you want to enable IPC flood test. If unsure, select "N". +config SND_SOC_SOF_DEBUG_IPC_FLOOD_TEST_NUM + int "Number of IPC flood test clients" + range 1 32 + default 2 + depends on SND_SOC_SOF_DEBUG_IPC_FLOOD_TEST + help + Select the number of IPC flood test clients to be created. + config SND_SOC_SOF_DEBUG_RETAIN_DSP_CONTEXT bool "SOF retain DSP context on any FW exceptions" help diff --git a/sound/soc/sof/Makefile b/sound/soc/sof/Makefile index 1678c497f298ea..eaca2fbddf4cc2 100644 --- a/sound/soc/sof/Makefile +++ b/sound/soc/sof/Makefile @@ -10,6 +10,8 @@ snd-sof-pci-objs := sof-pci-dev.o snd-sof-acpi-objs := sof-acpi-dev.o snd-sof-of-objs := sof-of-dev.o +snd-sof-ipc-test-objs := sof-client-ipc-test.o + snd-sof-nocodec-objs := nocodec.o snd-sof-utils-objs := sof-utils.o @@ -23,6 +25,8 @@ obj-$(CONFIG_SND_SOC_SOF_ACPI_DEV) += snd-sof-acpi.o obj-$(CONFIG_SND_SOC_SOF_OF) += snd-sof-of.o obj-$(CONFIG_SND_SOC_SOF_PCI_DEV) += snd-sof-pci.o +obj-$(CONFIG_SND_SOC_SOF_DEBUG_IPC_FLOOD_TEST) += snd-sof-ipc-test.o + obj-$(CONFIG_SND_SOC_SOF_INTEL_TOPLEVEL) += intel/ obj-$(CONFIG_SND_SOC_SOF_IMX_TOPLEVEL) += imx/ obj-$(CONFIG_SND_SOC_SOF_XTENSA) += xtensa/ diff --git a/sound/soc/sof/core.c b/sound/soc/sof/core.c index 11d0bf4fe9bcde..e26a6a7fce22cb 100644 --- a/sound/soc/sof/core.c +++ b/sound/soc/sof/core.c @@ -451,3 +451,4 @@ MODULE_AUTHOR("Liam Girdwood"); MODULE_DESCRIPTION("Sound Open Firmware (SOF) Core"); MODULE_LICENSE("Dual BSD/GPL"); MODULE_ALIAS("platform:sof-audio"); +MODULE_IMPORT_NS(SND_SOC_SOF_CLIENT); diff --git a/sound/soc/sof/debug.c b/sound/soc/sof/debug.c index 6d937ee7de910e..c290c37891b7a3 100644 --- a/sound/soc/sof/debug.c +++ b/sound/soc/sof/debug.c @@ -234,120 +234,10 @@ static int snd_sof_debugfs_probe_item(struct snd_sof_dev *sdev, } #endif -#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_IPC_FLOOD_TEST) -#define MAX_IPC_FLOOD_DURATION_MS 1000 -#define MAX_IPC_FLOOD_COUNT 10000 -#define IPC_FLOOD_TEST_RESULT_LEN 512 - -static int sof_debug_ipc_flood_test(struct snd_sof_dev *sdev, - struct snd_sof_dfsentry *dfse, - bool flood_duration_test, - unsigned long ipc_duration_ms, - unsigned long ipc_count) -{ - struct sof_ipc_cmd_hdr hdr; - struct sof_ipc_reply reply; - u64 min_response_time = U64_MAX; - ktime_t start, end, test_end; - u64 avg_response_time = 0; - u64 max_response_time = 0; - u64 ipc_response_time; - int i = 0; - int ret; - - /* configure test IPC */ - hdr.cmd = SOF_IPC_GLB_TEST_MSG | SOF_IPC_TEST_IPC_FLOOD; - hdr.size = sizeof(hdr); - - /* set test end time for duration flood test */ - if (flood_duration_test) - test_end = ktime_get_ns() + ipc_duration_ms * NSEC_PER_MSEC; - - /* send test IPC's */ - while (1) { - start = ktime_get(); - ret = sof_ipc_tx_message(sdev->ipc, hdr.cmd, &hdr, hdr.size, - &reply, sizeof(reply)); - end = ktime_get(); - - if (ret < 0) - break; - - /* compute min and max response times */ - ipc_response_time = ktime_to_ns(ktime_sub(end, start)); - min_response_time = min(min_response_time, ipc_response_time); - max_response_time = max(max_response_time, ipc_response_time); - - /* sum up response times */ - avg_response_time += ipc_response_time; - i++; - - /* test complete? */ - if (flood_duration_test) { - if (ktime_to_ns(end) >= test_end) - break; - } else { - if (i == ipc_count) - break; - } - } - - if (ret < 0) - dev_err(sdev->dev, - "error: ipc flood test failed at %d iterations\n", i); - - /* return if the first IPC fails */ - if (!i) - return ret; - - /* compute average response time */ - do_div(avg_response_time, i); - - /* clear previous test output */ - memset(dfse->cache_buf, 0, IPC_FLOOD_TEST_RESULT_LEN); - - if (flood_duration_test) { - dev_dbg(sdev->dev, "IPC Flood test duration: %lums\n", - ipc_duration_ms); - snprintf(dfse->cache_buf, IPC_FLOOD_TEST_RESULT_LEN, - "IPC Flood test duration: %lums\n", ipc_duration_ms); - } - - dev_dbg(sdev->dev, - "IPC Flood count: %d, Avg response time: %lluns\n", - i, avg_response_time); - dev_dbg(sdev->dev, "Max response time: %lluns\n", - max_response_time); - dev_dbg(sdev->dev, "Min response time: %lluns\n", - min_response_time); - - /* format output string */ - snprintf(dfse->cache_buf + strlen(dfse->cache_buf), - IPC_FLOOD_TEST_RESULT_LEN - strlen(dfse->cache_buf), - "IPC Flood count: %d\nAvg response time: %lluns\n", - i, avg_response_time); - - snprintf(dfse->cache_buf + strlen(dfse->cache_buf), - IPC_FLOOD_TEST_RESULT_LEN - strlen(dfse->cache_buf), - "Max response time: %lluns\nMin response time: %lluns\n", - max_response_time, min_response_time); - - return ret; -} -#endif static ssize_t sof_dfsentry_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos) { -#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_IPC_FLOOD_TEST) - struct snd_sof_dfsentry *dfse = file->private_data; - struct snd_sof_dev *sdev = dfse->sdev; - unsigned long ipc_duration_ms = 0; - bool flood_duration_test = false; - unsigned long ipc_count = 0; - struct dentry *dentry; - int err; -#endif size_t size; char *string; int ret; @@ -359,78 +249,6 @@ static ssize_t sof_dfsentry_write(struct file *file, const char __user *buffer, size = simple_write_to_buffer(string, count, ppos, buffer, count); ret = size; -#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_IPC_FLOOD_TEST) - /* - * write op is only supported for ipc_flood_count or - * ipc_flood_duration_ms debugfs entries atm. - * ipc_flood_count floods the DSP with the number of IPC's specified. - * ipc_duration_ms test floods the DSP for the time specified - * in the debugfs entry. - */ - dentry = file->f_path.dentry; - if (strcmp(dentry->d_name.name, "ipc_flood_count") && - strcmp(dentry->d_name.name, "ipc_flood_duration_ms")) { - ret = -EINVAL; - goto out; - } - - if (!strcmp(dentry->d_name.name, "ipc_flood_duration_ms")) - flood_duration_test = true; - - /* test completion criterion */ - if (flood_duration_test) - ret = kstrtoul(string, 0, &ipc_duration_ms); - else - ret = kstrtoul(string, 0, &ipc_count); - if (ret < 0) - goto out; - - /* limit max duration/ipc count for flood test */ - if (flood_duration_test) { - if (!ipc_duration_ms) { - ret = size; - goto out; - } - - /* find the minimum. min() is not used to avoid warnings */ - if (ipc_duration_ms > MAX_IPC_FLOOD_DURATION_MS) - ipc_duration_ms = MAX_IPC_FLOOD_DURATION_MS; - } else { - if (!ipc_count) { - ret = size; - goto out; - } - - /* find the minimum. min() is not used to avoid warnings */ - if (ipc_count > MAX_IPC_FLOOD_COUNT) - ipc_count = MAX_IPC_FLOOD_COUNT; - } - - ret = pm_runtime_get_sync(sdev->dev); - if (ret < 0 && ret != -EACCES) { - dev_err_ratelimited(sdev->dev, - "error: debugfs write failed to resume %d\n", - ret); - pm_runtime_put_noidle(sdev->dev); - goto out; - } - - /* flood test */ - ret = sof_debug_ipc_flood_test(sdev, dfse, flood_duration_test, - ipc_duration_ms, ipc_count); - - pm_runtime_mark_last_busy(sdev->dev); - err = pm_runtime_put_autosuspend(sdev->dev); - if (err < 0) - dev_err_ratelimited(sdev->dev, - "error: debugfs write failed to idle %d\n", - err); - - /* return size if test is successful */ - if (ret >= 0) - ret = size; -out: -#endif kfree(string); return ret; } @@ -446,24 +264,6 @@ static ssize_t sof_dfsentry_read(struct file *file, char __user *buffer, int size; u8 *buf; -#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_IPC_FLOOD_TEST) - struct dentry *dentry; - - dentry = file->f_path.dentry; - if ((!strcmp(dentry->d_name.name, "ipc_flood_count") || - !strcmp(dentry->d_name.name, "ipc_flood_duration_ms"))) { - if (*ppos) - return 0; - - count = strlen(dfse->cache_buf); - size_ret = copy_to_user(buffer, dfse->cache_buf, count); - if (size_ret) - return -EFAULT; - - *ppos += count; - return count; - } -#endif size = dfse->size; /* validate position & count */ @@ -621,19 +421,6 @@ int snd_sof_debugfs_buf_item(struct snd_sof_dev *sdev, dfse->size = size; dfse->sdev = sdev; -#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_IPC_FLOOD_TEST) - if (!strncmp(name, "ipc_flood", strlen("ipc_flood"))) { - /* - * cache_buf is unused for SOF_DFSENTRY_TYPE_BUF debugfs entries. - * So, use it to save the results of the last IPC flood test. - */ - dfse->cache_buf = devm_kzalloc(sdev->dev, IPC_FLOOD_TEST_RESULT_LEN, - GFP_KERNEL); - if (!dfse->cache_buf) - return -ENOMEM; - } -#endif - debugfs_create_file(name, mode, sdev->debugfs_root, dfse, &sof_dfs_fops); /* add to dfsentry list */ @@ -794,24 +581,6 @@ int snd_sof_dbg_init(struct snd_sof_dev *sdev) return err; #endif -#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_IPC_FLOOD_TEST) - /* create read-write ipc_flood_count debugfs entry */ - err = snd_sof_debugfs_buf_item(sdev, NULL, 0, - "ipc_flood_count", 0666); - - /* errors are only due to memory allocation, not debugfs */ - if (err < 0) - return err; - - /* create read-write ipc_flood_duration_ms debugfs entry */ - err = snd_sof_debugfs_buf_item(sdev, NULL, 0, - "ipc_flood_duration_ms", 0666); - - /* errors are only due to memory allocation, not debugfs */ - if (err < 0) - return err; -#endif - return 0; } EXPORT_SYMBOL_GPL(snd_sof_dbg_init); diff --git a/sound/soc/sof/sof-client-ipc-test.c b/sound/soc/sof/sof-client-ipc-test.c new file mode 100644 index 00000000000000..17f02307dd77c2 --- /dev/null +++ b/sound/soc/sof/sof-client-ipc-test.c @@ -0,0 +1,369 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// Copyright(c) 2021 Intel Corporation. All rights reserved. +// +// Author: Ranjani Sridharan +// + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "sof-client.h" + +#define MAX_IPC_FLOOD_DURATION_MS 1000 +#define MAX_IPC_FLOOD_COUNT 10000 +#define IPC_FLOOD_TEST_RESULT_LEN 512 +#define SOF_IPC_CLIENT_SUSPEND_DELAY_MS 3000 + +#define DEBUGFS_IPC_FLOOD_COUNT "ipc_flood_count" +#define DEBUGFS_IPC_FLOOD_DURATION "ipc_flood_duration_ms" + +struct sof_ipc_client_data { + struct dentry *dfs_root; + struct dentry *dfs_link[2]; + char *buf; +}; + +/* + * helper function to perform the flood test. Only one of the two params, ipc_duration_ms + * or ipc_count, will be non-zero and will determine the type of test + */ +static int sof_debug_ipc_flood_test(struct sof_client_dev *cdev, + bool flood_duration_test, + unsigned long ipc_duration_ms, + unsigned long ipc_count) +{ + struct sof_ipc_client_data *ipc_client_data = cdev->data; + struct device *dev = &cdev->auxdev.dev; + struct sof_ipc_cmd_hdr hdr; + struct sof_ipc_reply reply; + u64 min_response_time = U64_MAX; + ktime_t start, end, test_end; + u64 avg_response_time = 0; + u64 max_response_time = 0; + u64 ipc_response_time; + int i = 0; + int ret; + + /* configure test IPC */ + hdr.cmd = SOF_IPC_GLB_TEST_MSG | SOF_IPC_TEST_IPC_FLOOD; + hdr.size = sizeof(hdr); + + /* set test end time for duration flood test */ + if (flood_duration_test) + test_end = ktime_get_ns() + ipc_duration_ms * NSEC_PER_MSEC; + + /* send test IPC's */ + while (1) { + start = ktime_get(); + ret = sof_client_ipc_tx_message(cdev, &hdr, &reply, sizeof(reply)); + end = ktime_get(); + + if (ret < 0) + break; + + /* compute min and max response times */ + ipc_response_time = ktime_to_ns(ktime_sub(end, start)); + min_response_time = min(min_response_time, ipc_response_time); + max_response_time = max(max_response_time, ipc_response_time); + + /* sum up response times */ + avg_response_time += ipc_response_time; + i++; + + /* test complete? */ + if (flood_duration_test) { + if (ktime_to_ns(end) >= test_end) + break; + } else { + if (i == ipc_count) + break; + } + } + + if (ret < 0) + dev_err(dev, "error: ipc flood test failed at %d iterations\n", i); + + /* return if the first IPC fails */ + if (!i) + return ret; + + /* compute average response time */ + do_div(avg_response_time, i); + + /* clear previous test output */ + memset(ipc_client_data->buf, 0, IPC_FLOOD_TEST_RESULT_LEN); + + if (!ipc_count) { + dev_dbg(dev, "IPC Flood test duration: %lums\n", ipc_duration_ms); + snprintf(ipc_client_data->buf, IPC_FLOOD_TEST_RESULT_LEN, + "IPC Flood test duration: %lums\n", ipc_duration_ms); + } + + dev_dbg(dev, + "IPC Flood count: %d, Avg response time: %lluns\n", i, avg_response_time); + dev_dbg(dev, "Max response time: %lluns\n", max_response_time); + dev_dbg(dev, "Min response time: %lluns\n", min_response_time); + + /* format output string and save test results */ + snprintf(ipc_client_data->buf + strlen(ipc_client_data->buf), + IPC_FLOOD_TEST_RESULT_LEN - strlen(ipc_client_data->buf), + "IPC Flood count: %d\nAvg response time: %lluns\n", i, avg_response_time); + + snprintf(ipc_client_data->buf + strlen(ipc_client_data->buf), + IPC_FLOOD_TEST_RESULT_LEN - strlen(ipc_client_data->buf), + "Max response time: %lluns\nMin response time: %lluns\n", + max_response_time, min_response_time); + + return ret; +} + +/* + * Writing to the debugfs entry initiates the IPC flood test based on + * the IPC count or the duration specified by the user. + */ +static ssize_t sof_ipc_dfsentry_write(struct file *file, const char __user *buffer, + size_t count, loff_t *ppos) +{ + struct sof_client_dev *cdev = file->private_data; + struct device *dev = &cdev->auxdev.dev; + unsigned long ipc_duration_ms = 0; + bool flood_duration_test = false; + unsigned long ipc_count = 0; + struct dentry *dentry; + int err; + size_t size; + char *string; + int ret; + + string = kzalloc(count + 1, GFP_KERNEL); + if (!string) + return -ENOMEM; + + size = simple_write_to_buffer(string, count, ppos, buffer, count); + + /* + * write op is only supported for ipc_flood_count or + * ipc_flood_duration_ms debugfs entries atm. + * ipc_flood_count floods the DSP with the number of IPC's specified. + * ipc_duration_ms test floods the DSP for the time specified + * in the debugfs entry. + */ + dentry = file->f_path.dentry; + if (strcmp(dentry->d_name.name, DEBUGFS_IPC_FLOOD_COUNT) && + strcmp(dentry->d_name.name, DEBUGFS_IPC_FLOOD_DURATION)) { + ret = -EINVAL; + goto out; + } + + if (!strcmp(dentry->d_name.name, DEBUGFS_IPC_FLOOD_DURATION)) + flood_duration_test = true; + + /* test completion criterion */ + if (flood_duration_test) + ret = kstrtoul(string, 0, &ipc_duration_ms); + else + ret = kstrtoul(string, 0, &ipc_count); + if (ret < 0) + goto out; + + /* limit max duration/ipc count for flood test */ + if (flood_duration_test) { + if (!ipc_duration_ms) { + ret = size; + goto out; + } + + /* find the minimum. min() is not used to avoid warnings */ + if (ipc_duration_ms > MAX_IPC_FLOOD_DURATION_MS) + ipc_duration_ms = MAX_IPC_FLOOD_DURATION_MS; + } else { + if (!ipc_count) { + ret = size; + goto out; + } + + /* find the minimum. min() is not used to avoid warnings */ + if (ipc_count > MAX_IPC_FLOOD_COUNT) + ipc_count = MAX_IPC_FLOOD_COUNT; + } + + ret = pm_runtime_get_sync(dev); + if (ret < 0 && ret != -EACCES) { + dev_err_ratelimited(dev, + "error: debugfs write failed to resume %d\n", + ret); + pm_runtime_put_noidle(dev); + goto out; + } + + /* flood test */ + ret = sof_debug_ipc_flood_test(cdev, flood_duration_test, + ipc_duration_ms, ipc_count); + + pm_runtime_mark_last_busy(dev); + err = pm_runtime_put_autosuspend(dev); + if (err < 0) + dev_err_ratelimited(dev, + "error: debugfs write failed to idle %d\n", + err); + + /* return size if test is successful */ + if (ret >= 0) + ret = size; +out: + kfree(string); + return ret; +} + +/* return the result of the last IPC flood test */ +static ssize_t sof_ipc_dfsentry_read(struct file *file, char __user *buffer, + size_t count, loff_t *ppos) +{ + struct sof_client_dev *cdev = file->private_data; + struct sof_ipc_client_data *ipc_client_data = cdev->data; + size_t size_ret; + + struct dentry *dentry; + + dentry = file->f_path.dentry; + if (!strcmp(dentry->d_name.name, DEBUGFS_IPC_FLOOD_COUNT) || + !strcmp(dentry->d_name.name, DEBUGFS_IPC_FLOOD_DURATION)) { + if (*ppos) + return 0; + + count = min_t(size_t, count, strlen(ipc_client_data->buf)); + size_ret = copy_to_user(buffer, ipc_client_data->buf, count); + if (size_ret) + return -EFAULT; + + *ppos += count; + return count; + } + return count; +} + +static const struct file_operations sof_ipc_dfs_fops = { + .open = simple_open, + .read = sof_ipc_dfsentry_read, + .llseek = default_llseek, + .write = sof_ipc_dfsentry_write, + + .owner = THIS_MODULE, +}; + +/* + * The IPC test client creates a couple of debugfs entries that will be used + * flood tests. Users can write to these entries to execute the IPC flood test + * by specifying either the number of IPCs to flood the DSP with or the duration + * (in ms) for which the DSP should be flooded with test IPCs. At the + * end of each test, the average, min and max response times are reported back. + * The results of the last flood test can be accessed by reading the debugfs + * entries. + */ +static int sof_ipc_test_probe(struct auxiliary_device *auxdev, + const struct auxiliary_device_id *id) +{ + struct sof_client_dev *cdev = auxiliary_dev_to_sof_client_dev(auxdev); + struct sof_ipc_client_data *ipc_client_data; + struct dentry *debugfs_root = sof_client_get_debugfs_root(cdev); + + /* allocate memory for client data */ + ipc_client_data = devm_kzalloc(&auxdev->dev, sizeof(*ipc_client_data), GFP_KERNEL); + if (!ipc_client_data) + return -ENOMEM; + + ipc_client_data->buf = devm_kzalloc(&auxdev->dev, IPC_FLOOD_TEST_RESULT_LEN, GFP_KERNEL); + if (!ipc_client_data->buf) + return -ENOMEM; + + cdev->data = ipc_client_data; + + /* create debugfs root folder with device name under parent SOF dir */ + ipc_client_data->dfs_root = debugfs_create_dir(dev_name(&auxdev->dev), debugfs_root); + if (!IS_ERR_OR_NULL(ipc_client_data->dfs_root)) { + /* create read-write ipc_flood_count debugfs entry */ + debugfs_create_file(DEBUGFS_IPC_FLOOD_COUNT, 0644, + ipc_client_data->dfs_root, cdev, &sof_ipc_dfs_fops); + + /* create read-write ipc_flood_duration_ms debugfs entry */ + debugfs_create_file(DEBUGFS_IPC_FLOOD_DURATION, 0644, + ipc_client_data->dfs_root, cdev, &sof_ipc_dfs_fops); + + if (auxdev->id == 0) { + /* + * Create symlinks for backwards compatibility to the + * first IPC flood test instance + */ + char target[100]; + + snprintf(target, 100, "%s/" DEBUGFS_IPC_FLOOD_COUNT, + dev_name(&auxdev->dev)); + ipc_client_data->dfs_link[0] = + debugfs_create_symlink(DEBUGFS_IPC_FLOOD_COUNT, + debugfs_root, target); + + snprintf(target, 100, "%s/" DEBUGFS_IPC_FLOOD_DURATION, + dev_name(&auxdev->dev)); + ipc_client_data->dfs_link[1] = + debugfs_create_symlink(DEBUGFS_IPC_FLOOD_DURATION, + debugfs_root, target); + } + } + + /* enable runtime PM */ + pm_runtime_set_autosuspend_delay(&auxdev->dev, SOF_IPC_CLIENT_SUSPEND_DELAY_MS); + pm_runtime_use_autosuspend(&auxdev->dev); + pm_runtime_enable(&auxdev->dev); + pm_runtime_mark_last_busy(&auxdev->dev); + pm_runtime_idle(&auxdev->dev); + + return 0; +} + +static void sof_ipc_test_remove(struct auxiliary_device *auxdev) +{ + struct sof_client_dev *cdev = auxiliary_dev_to_sof_client_dev(auxdev); + struct sof_ipc_client_data *ipc_client_data = cdev->data; + + pm_runtime_disable(&auxdev->dev); + + if (auxdev->id == 0) { + debugfs_remove(ipc_client_data->dfs_link[0]); + debugfs_remove(ipc_client_data->dfs_link[1]); + } + + debugfs_remove_recursive(ipc_client_data->dfs_root); +} + +static const struct auxiliary_device_id sof_ipc_test_client_id_table[] = { + { .name = "snd_sof.ipc_test" }, + {}, +}; +MODULE_DEVICE_TABLE(auxiliary, sof_ipc_test_client_id_table); + +/* + * No need for driver pm_ops as the generic pm callbacks in the auxiliary bus + * type are enough to ensure that the parent SOF device resumes to bring the DSP + * back to D0. + * Driver name will be set based on KBUILD_MODNAME. + */ +static struct auxiliary_driver sof_ipc_test_client_drv = { + .probe = sof_ipc_test_probe, + .remove = sof_ipc_test_remove, + + .id_table = sof_ipc_test_client_id_table, +}; + +module_auxiliary_driver(sof_ipc_test_client_drv); + +MODULE_DESCRIPTION("SOF IPC Test Client Driver"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(SND_SOC_SOF_CLIENT); diff --git a/sound/soc/sof/sof-client.c b/sound/soc/sof/sof-client.c index 6de5ebb38ae9b7..33f2113e9df3b3 100644 --- a/sound/soc/sof/sof-client.c +++ b/sound/soc/sof/sof-client.c @@ -65,18 +65,70 @@ static int sof_client_dev_add_data(struct sof_client_dev *cdev, const void *data return 0; } +#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_IPC_FLOOD_TEST) +static int sof_register_ipcflood_test(struct snd_sof_dev *sdev) +{ + int ret = 0; + int i; + + for (i = 0; i < CONFIG_SND_SOC_SOF_DEBUG_IPC_FLOOD_TEST_NUM; i++) { + ret = sof_client_dev_register(sdev, "ipc_test", i, NULL, 0); + if (ret < 0) + break; + } + + if (ret) { + for (; i >= 0; --i) + sof_client_dev_unregister(sdev, "ipc_test", i); + } + + return ret; +} + +static void sof_unregister_ipcflood_test(struct snd_sof_dev *sdev) +{ + int i; + + for (i = 0; i < CONFIG_SND_SOC_SOF_DEBUG_IPC_FLOOD_TEST_NUM; i++) + sof_client_dev_unregister(sdev, "ipc_test", i); +} +#else +static inline int sof_register_ipcflood_test(struct snd_sof_dev *sdev) +{ + return 0; +} + +static inline void sof_unregister_ipcflood_test(struct snd_sof_dev *sdev) {} +#endif /* CONFIG_SND_SOC_SOF_DEBUG_IPC_FLOOD_TEST */ + int sof_register_clients(struct snd_sof_dev *sdev) { + int ret; + + /* Register platform independent client devices */ + ret = sof_register_ipcflood_test(sdev); + if (ret) { + dev_err(sdev->dev, "error: IPC flood test client registration failed\n"); + return ret; + } + + /* Platform depndent client device registration */ + if (sof_ops(sdev) && sof_ops(sdev)->register_ipc_clients) - return sof_ops(sdev)->register_ipc_clients(sdev); + ret = sof_ops(sdev)->register_ipc_clients(sdev); - return 0; + if (ret) + sof_unregister_ipcflood_test(sdev); + + return ret; } void sof_unregister_clients(struct snd_sof_dev *sdev) { if (sof_ops(sdev) && sof_ops(sdev)->unregister_ipc_clients) sof_ops(sdev)->unregister_ipc_clients(sdev); + + sof_unregister_ipcflood_test(sdev); } int sof_client_dev_register(struct snd_sof_dev *sdev, const char *name, u32 id, diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index 23e05de51bda0f..2258b42524bd8e 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -64,10 +64,6 @@ extern int sof_core_debug; #define SOF_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | \ SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_FLOAT) -#define ENABLE_DEBUGFS_CACHEBUF \ - (IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_ENABLE_DEBUGFS_CACHE) || \ - IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_IPC_FLOOD_TEST)) - /* So far the primary core on all DSPs has ID 0 */ #define SOF_DSP_PRIMARY_CORE 0 @@ -331,7 +327,7 @@ struct snd_sof_dfsentry { * or if it is accessible only when the DSP is in D0. */ enum sof_debugfs_access_type access_type; -#if ENABLE_DEBUGFS_CACHEBUF +#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_ENABLE_DEBUGFS_CACHE) char *cache_buf; /* buffer to cache the contents of debugfs memory */ #endif struct snd_sof_dev *sdev; From cba8df9c9b1ac52b03032b7ab938fd39abc0eb1d Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 22 Jul 2021 10:25:24 +0300 Subject: [PATCH 17/32] ASoC: SOF: sof-client-ipc-test: Protection against removal while in use Add additional protection for the debugfs file to make sure that it can not be removed while it is open. Signed-off-by: Peter Ujfalusi --- sound/soc/sof/sof-client-ipc-test.c | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/sound/soc/sof/sof-client-ipc-test.c b/sound/soc/sof/sof-client-ipc-test.c index 17f02307dd77c2..e54cd150a39547 100644 --- a/sound/soc/sof/sof-client-ipc-test.c +++ b/sound/soc/sof/sof-client-ipc-test.c @@ -31,6 +31,21 @@ struct sof_ipc_client_data { char *buf; }; +static int sof_ipc_dfsentry_open(struct inode *inode, struct file *file) +{ + int ret; + + ret = debugfs_file_get(file->f_path.dentry); + if (unlikely(ret)) + return ret; + + ret = simple_open(inode, file); + if (ret) + debugfs_file_put(file->f_path.dentry); + + return ret; +} + /* * helper function to perform the flood test. Only one of the two params, ipc_duration_ms * or ipc_count, will be non-zero and will determine the type of test @@ -250,11 +265,19 @@ static ssize_t sof_ipc_dfsentry_read(struct file *file, char __user *buffer, return count; } +static int sof_ipc_dfsentry_release(struct inode *inode, struct file *file) +{ + debugfs_file_put(file->f_path.dentry); + + return 0; +} + static const struct file_operations sof_ipc_dfs_fops = { - .open = simple_open, + .open = sof_ipc_dfsentry_open, .read = sof_ipc_dfsentry_read, .llseek = default_llseek, .write = sof_ipc_dfsentry_write, + .release = sof_ipc_dfsentry_release, .owner = THIS_MODULE, }; From 64a91b8ab2a38a9db27b2022d292eb74063c02fe Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Fri, 20 Aug 2021 14:04:02 +0300 Subject: [PATCH 18/32] ASoC: SOF: sof-client-ipc-test: Block the test if the firmware has crashed Do not allow the test to be started if the firmware is in crashed state. Signed-off-by: Peter Ujfalusi --- sound/soc/sof/sof-client-ipc-test.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sound/soc/sof/sof-client-ipc-test.c b/sound/soc/sof/sof-client-ipc-test.c index e54cd150a39547..9509d7e40f844c 100644 --- a/sound/soc/sof/sof-client-ipc-test.c +++ b/sound/soc/sof/sof-client-ipc-test.c @@ -33,8 +33,12 @@ struct sof_ipc_client_data { static int sof_ipc_dfsentry_open(struct inode *inode, struct file *file) { + struct sof_client_dev *cdev = inode->i_private; int ret; + if (sof_client_get_fw_state(cdev) == SOF_FW_CRASHED) + return -ENODEV; + ret = debugfs_file_get(file->f_path.dentry); if (unlikely(ret)) return ret; From 5f585a82911755a230e2d2e85ec2374b1cdaa53f Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 9 Jun 2021 09:48:28 +0300 Subject: [PATCH 19/32] ASoC: SOF: Convert the generic probe support to SOF client Add a new client driver for probes support and move all the probes-related code from the core to the client driver. The probes client driver registers a component driver with one CPU DAI driver for extraction and creates a new sound card with one DUMMY DAI link with a dummy codec that will be used for extracting audio data from specific points in the audio pipeline. The probes debugfs ops are based on the initial implementation by Cezary Rojewski and have been moved out of the SOF core into the client driver making it easier to maintain. This change will make it easier for the probes functionality to be added for all platforms without having the need to modify the existing(15+) machine drivers. Signed-off-by: Ranjani Sridharan Signed-off-by: Peter Ujfalusi --- sound/soc/sof/Kconfig | 7 +- sound/soc/sof/Makefile | 4 +- sound/soc/sof/core.c | 6 - sound/soc/sof/debug.c | 227 --------- sound/soc/sof/intel/Kconfig | 19 +- sound/soc/sof/intel/apl.c | 23 +- sound/soc/sof/intel/cnl.c | 23 +- sound/soc/sof/intel/hda-dai.c | 19 - sound/soc/sof/intel/hda-probes.c | 71 ++- sound/soc/sof/intel/hda.h | 45 +- sound/soc/sof/intel/icl.c | 23 +- sound/soc/sof/intel/tgl.c | 23 +- sound/soc/sof/ops.h | 43 -- sound/soc/sof/pcm.c | 8 +- sound/soc/sof/sof-client-probes.c | 797 ++++++++++++++++++++++++++++++ sound/soc/sof/sof-client-probes.h | 31 ++ sound/soc/sof/sof-priv.h | 25 - sound/soc/sof/sof-probes.c | 364 -------------- sound/soc/sof/sof-probes.h | 38 -- 19 files changed, 969 insertions(+), 827 deletions(-) create mode 100644 sound/soc/sof/sof-client-probes.c create mode 100644 sound/soc/sof/sof-client-probes.h delete mode 100644 sound/soc/sof/sof-probes.c delete mode 100644 sound/soc/sof/sof-probes.h diff --git a/sound/soc/sof/Kconfig b/sound/soc/sof/Kconfig index 59e487843c1405..51b0ad815f964e 100644 --- a/sound/soc/sof/Kconfig +++ b/sound/soc/sof/Kconfig @@ -47,13 +47,14 @@ config SND_SOC_SOF_OF Say Y if you need this option. If unsure select "N". config SND_SOC_SOF_DEBUG_PROBES - bool "SOF enable data probing" + tristate + select SND_SOC_SOF_CLIENT select SND_SOC_COMPRESS help This option enables the data probing feature that can be used to gather data directly from specific points of the audio pipeline. - Say Y if you want to enable probes. - If unsure, select "N". + This option is not user-selectable but automagically handled by + 'select' statements at a higher level. config SND_SOC_SOF_CLIENT tristate diff --git a/sound/soc/sof/Makefile b/sound/soc/sof/Makefile index eaca2fbddf4cc2..95b41b181b3cea 100644 --- a/sound/soc/sof/Makefile +++ b/sound/soc/sof/Makefile @@ -4,13 +4,12 @@ snd-sof-objs := core.o ops.o loader.o ipc.o pcm.o pm.o debug.o topology.o\ control.o trace.o iomem-utils.o sof-audio.o stream-ipc.o snd-sof-$(CONFIG_SND_SOC_SOF_CLIENT) += sof-client.o -snd-sof-$(CONFIG_SND_SOC_SOF_DEBUG_PROBES) += sof-probes.o - snd-sof-pci-objs := sof-pci-dev.o snd-sof-acpi-objs := sof-acpi-dev.o snd-sof-of-objs := sof-of-dev.o snd-sof-ipc-test-objs := sof-client-ipc-test.o +snd-sof-probes-objs := sof-client-probes.o snd-sof-nocodec-objs := nocodec.o @@ -26,6 +25,7 @@ obj-$(CONFIG_SND_SOC_SOF_OF) += snd-sof-of.o obj-$(CONFIG_SND_SOC_SOF_PCI_DEV) += snd-sof-pci.o obj-$(CONFIG_SND_SOC_SOF_DEBUG_IPC_FLOOD_TEST) += snd-sof-ipc-test.o +obj-$(CONFIG_SND_SOC_SOF_DEBUG_PROBES) += snd-sof-probes.o obj-$(CONFIG_SND_SOC_SOF_INTEL_TOPLEVEL) += intel/ obj-$(CONFIG_SND_SOC_SOF_IMX_TOPLEVEL) += imx/ diff --git a/sound/soc/sof/core.c b/sound/soc/sof/core.c index e26a6a7fce22cb..96d79fc4a9352d 100644 --- a/sound/soc/sof/core.c +++ b/sound/soc/sof/core.c @@ -14,9 +14,6 @@ #include #include "sof-priv.h" #include "ops.h" -#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_PROBES) -#include "sof-probes.h" -#endif /* see SOF_DBG_ flags */ int sof_core_debug = IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_ENABLE_FIRMWARE_TRACE); @@ -325,9 +322,6 @@ int snd_sof_device_probe(struct device *dev, struct snd_sof_pdata *plat_data) sdev->pdata = plat_data; sdev->first_boot = true; -#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_PROBES) - sdev->extractor_stream_tag = SOF_PROBE_INVALID_NODE_ID; -#endif dev_set_drvdata(dev, sdev); /* check all mandatory ops */ diff --git a/sound/soc/sof/debug.c b/sound/soc/sof/debug.c index c290c37891b7a3..aa5804a9042495 100644 --- a/sound/soc/sof/debug.c +++ b/sound/soc/sof/debug.c @@ -19,222 +19,6 @@ #include "sof-priv.h" #include "ops.h" -#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_PROBES) -#include "sof-probes.h" - -/** - * strsplit_u32 - Split string into sequence of u32 tokens - * @buf: String to split into tokens. - * @delim: String containing delimiter characters. - * @tkns: Returned u32 sequence pointer. - * @num_tkns: Returned number of tokens obtained. - */ -static int -strsplit_u32(char **buf, const char *delim, u32 **tkns, size_t *num_tkns) -{ - char *s; - u32 *data, *tmp; - size_t count = 0; - size_t cap = 32; - int ret = 0; - - *tkns = NULL; - *num_tkns = 0; - data = kcalloc(cap, sizeof(*data), GFP_KERNEL); - if (!data) - return -ENOMEM; - - while ((s = strsep(buf, delim)) != NULL) { - ret = kstrtouint(s, 0, data + count); - if (ret) - goto exit; - if (++count >= cap) { - cap *= 2; - tmp = krealloc(data, cap * sizeof(*data), GFP_KERNEL); - if (!tmp) { - ret = -ENOMEM; - goto exit; - } - data = tmp; - } - } - - if (!count) - goto exit; - *tkns = kmemdup(data, count * sizeof(*data), GFP_KERNEL); - if (*tkns == NULL) { - ret = -ENOMEM; - goto exit; - } - *num_tkns = count; - -exit: - kfree(data); - return ret; -} - -static int tokenize_input(const char __user *from, size_t count, - loff_t *ppos, u32 **tkns, size_t *num_tkns) -{ - char *buf; - int ret; - - buf = kmalloc(count + 1, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - ret = simple_write_to_buffer(buf, count, ppos, from, count); - if (ret != count) { - ret = ret >= 0 ? -EIO : ret; - goto exit; - } - - buf[count] = '\0'; - ret = strsplit_u32((char **)&buf, ",", tkns, num_tkns); -exit: - kfree(buf); - return ret; -} - -static ssize_t probe_points_read(struct file *file, - char __user *to, size_t count, loff_t *ppos) -{ - struct snd_sof_dfsentry *dfse = file->private_data; - struct snd_sof_dev *sdev = dfse->sdev; - struct sof_probe_point_desc *desc; - size_t num_desc, len = 0; - char *buf; - int i, ret; - - if (sdev->extractor_stream_tag == SOF_PROBE_INVALID_NODE_ID) { - dev_warn(sdev->dev, "no extractor stream running\n"); - return -ENOENT; - } - - buf = kzalloc(PAGE_SIZE, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - ret = sof_ipc_probe_points_info(sdev, &desc, &num_desc); - if (ret < 0) - 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].buffer_id, desc[i].purpose, desc[i].stream_tag); - 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 snd_sof_dfsentry *dfse = file->private_data; - struct snd_sof_dev *sdev = dfse->sdev; - struct sof_probe_point_desc *desc; - size_t num_tkns, bytes; - u32 *tkns; - int ret; - - if (sdev->extractor_stream_tag == SOF_PROBE_INVALID_NODE_ID) { - dev_warn(sdev->dev, "no extractor stream running\n"); - return -ENOENT; - } - - ret = tokenize_input(from, count, ppos, &tkns, &num_tkns); - if (ret < 0) - return ret; - bytes = sizeof(*tkns) * num_tkns; - if (!num_tkns || (bytes % sizeof(*desc))) { - ret = -EINVAL; - goto exit; - } - - desc = (struct sof_probe_point_desc *)tkns; - ret = sof_ipc_probe_points_add(sdev, - desc, bytes / sizeof(*desc)); - if (!ret) - ret = count; -exit: - kfree(tkns); - return ret; -} - -static const struct file_operations probe_points_fops = { - .open = simple_open, - .read = probe_points_read, - .write = probe_points_write, - .llseek = default_llseek, -}; - -static ssize_t probe_points_remove_write(struct file *file, - const char __user *from, size_t count, loff_t *ppos) -{ - struct snd_sof_dfsentry *dfse = file->private_data; - struct snd_sof_dev *sdev = dfse->sdev; - size_t num_tkns; - u32 *tkns; - int ret; - - if (sdev->extractor_stream_tag == SOF_PROBE_INVALID_NODE_ID) { - dev_warn(sdev->dev, "no extractor stream running\n"); - return -ENOENT; - } - - ret = tokenize_input(from, count, ppos, &tkns, &num_tkns); - if (ret < 0) - return ret; - if (!num_tkns) { - ret = -EINVAL; - goto exit; - } - - ret = sof_ipc_probe_points_remove(sdev, tkns, num_tkns); - if (!ret) - ret = count; -exit: - kfree(tkns); - return ret; -} - -static const struct file_operations probe_points_remove_fops = { - .open = simple_open, - .write = probe_points_remove_write, - .llseek = default_llseek, -}; - -static int snd_sof_debugfs_probe_item(struct snd_sof_dev *sdev, - const char *name, mode_t mode, - const struct file_operations *fops) -{ - struct snd_sof_dfsentry *dfse; - - dfse = devm_kzalloc(sdev->dev, sizeof(*dfse), GFP_KERNEL); - if (!dfse) - return -ENOMEM; - - dfse->type = SOF_DFSENTRY_TYPE_BUF; - dfse->sdev = sdev; - - debugfs_create_file(name, mode, sdev->debugfs_root, dfse, fops); - /* add to dfsentry list */ - list_add(&dfse->list, &sdev->dfsentry_list); - - return 0; -} -#endif - - static ssize_t sof_dfsentry_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos) { @@ -570,17 +354,6 @@ int snd_sof_dbg_init(struct snd_sof_dev *sdev) return err; } -#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_PROBES) - err = snd_sof_debugfs_probe_item(sdev, "probe_points", - 0644, &probe_points_fops); - if (err < 0) - return err; - err = snd_sof_debugfs_probe_item(sdev, "probe_points_remove", - 0200, &probe_points_remove_fops); - if (err < 0) - return err; -#endif - return 0; } EXPORT_SYMBOL_GPL(snd_sof_dbg_init); diff --git a/sound/soc/sof/intel/Kconfig b/sound/soc/sof/intel/Kconfig index 88b6176af021c1..b53f216d4ecc32 100644 --- a/sound/soc/sof/intel/Kconfig +++ b/sound/soc/sof/intel/Kconfig @@ -215,6 +215,7 @@ config SND_SOC_SOF_HDA_COMMON select SND_SOC_SOF_PCI_DEV select SND_INTEL_DSP_CONFIG select SND_SOC_SOF_HDA_LINK_BASELINE + select SND_SOC_SOF_HDA_PROBES help This option is not user-selectable but automagically handled by 'select' statements at a higher level. @@ -240,15 +241,6 @@ config SND_SOC_SOF_HDA_AUDIO_CODEC Say Y if you want to enable HDAudio codecs with SOF. If unsure select "N". -config SND_SOC_SOF_HDA_PROBES - bool "SOF enable probes over HDA" - depends on SND_SOC_SOF_DEBUG_PROBES - help - This option enables the data probing for Intel(R) - Skylake and newer platforms. - Say Y if you want to enable probes. - If unsure, select "N". - endif ## SND_SOC_SOF_HDA_COMMON config SND_SOC_SOF_HDA_LINK_BASELINE @@ -266,6 +258,15 @@ config SND_SOC_SOF_HDA This option is not user-selectable but automagically handled by 'select' statements at a higher level. +config SND_SOC_SOF_HDA_PROBES + bool + select SND_SOC_SOF_DEBUG_PROBES + help + The option enables the data probing for Intel(R) Skylake and newer + (HDA) platforms. + This option is not user-selectable but automagically handled by + 'select' statements at a higher level. + config SND_SOC_SOF_INTEL_SOUNDWIRE_LINK_BASELINE tristate select SOUNDWIRE_INTEL if SND_SOC_SOF_INTEL_SOUNDWIRE diff --git a/sound/soc/sof/intel/apl.c b/sound/soc/sof/intel/apl.c index 6ff5e09055d9eb..5630d9b7fb66be 100644 --- a/sound/soc/sof/intel/apl.c +++ b/sound/soc/sof/intel/apl.c @@ -25,6 +25,16 @@ static const struct snd_sof_debugfs_map apl_dsp_debugfs[] = { {"dsp", HDA_DSP_BAR, 0, 0x10000, SOF_DEBUGFS_ACCESS_ALWAYS}, }; +static int apl_register_clients(struct snd_sof_dev *sdev) +{ + return hda_probes_register(sdev); +} + +static void apl_unregister_clients(struct snd_sof_dev *sdev) +{ + hda_probes_unregister(sdev); +} + /* apollolake ops */ const struct snd_sof_dsp_ops sof_apl_ops = { /* probe/remove/shutdown */ @@ -80,15 +90,6 @@ const struct snd_sof_dsp_ops sof_apl_ops = { .pcm_pointer = hda_dsp_pcm_pointer, .pcm_ack = hda_dsp_pcm_ack, -#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_PROBES) - /* probe callbacks */ - .probe_assign = hda_probe_compr_assign, - .probe_free = hda_probe_compr_free, - .probe_set_params = hda_probe_compr_set_params, - .probe_trigger = hda_probe_compr_trigger, - .probe_pointer = hda_probe_compr_pointer, -#endif - /* firmware loading */ .load_firmware = snd_sof_load_firmware_raw, @@ -111,6 +112,10 @@ const struct snd_sof_dsp_ops sof_apl_ops = { .trace_release = hda_dsp_trace_release, .trace_trigger = hda_dsp_trace_trigger, + /* client ops */ + .register_ipc_clients = apl_register_clients, + .unregister_ipc_clients = apl_unregister_clients, + /* DAI drivers */ .drv = skl_dai, .num_drv = SOF_SKL_NUM_DAIS, diff --git a/sound/soc/sof/intel/cnl.c b/sound/soc/sof/intel/cnl.c index 643506fa7c572a..aeb4a14c738032 100644 --- a/sound/soc/sof/intel/cnl.c +++ b/sound/soc/sof/intel/cnl.c @@ -230,6 +230,16 @@ void cnl_ipc_dump(struct snd_sof_dev *sdev) hipcida, hipctdr, hipcctl); } +static int cnl_register_clients(struct snd_sof_dev *sdev) +{ + return hda_probes_register(sdev); +} + +static void cnl_unregister_clients(struct snd_sof_dev *sdev) +{ + hda_probes_unregister(sdev); +} + /* cannonlake ops */ const struct snd_sof_dsp_ops sof_cnl_ops = { /* probe/remove/shutdown */ @@ -285,15 +295,6 @@ const struct snd_sof_dsp_ops sof_cnl_ops = { .pcm_pointer = hda_dsp_pcm_pointer, .pcm_ack = hda_dsp_pcm_ack, -#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_PROBES) - /* probe callbacks */ - .probe_assign = hda_probe_compr_assign, - .probe_free = hda_probe_compr_free, - .probe_set_params = hda_probe_compr_set_params, - .probe_trigger = hda_probe_compr_trigger, - .probe_pointer = hda_probe_compr_pointer, -#endif - /* firmware loading */ .load_firmware = snd_sof_load_firmware_raw, @@ -316,6 +317,10 @@ const struct snd_sof_dsp_ops sof_cnl_ops = { .trace_release = hda_dsp_trace_release, .trace_trigger = hda_dsp_trace_trigger, + /* client ops */ + .register_ipc_clients = cnl_register_clients, + .unregister_ipc_clients = cnl_unregister_clients, + /* DAI drivers */ .drv = skl_dai, .num_drv = SOF_SKL_NUM_DAIS, diff --git a/sound/soc/sof/intel/hda-dai.c b/sound/soc/sof/intel/hda-dai.c index d71165fb6fad38..60cc265847c7bd 100644 --- a/sound/soc/sof/intel/hda-dai.c +++ b/sound/soc/sof/intel/hda-dai.c @@ -16,10 +16,6 @@ #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) -#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_PROBES) -#include "../sof-probes.h" -#endif - struct hda_pipe_params { u8 host_dma_id; u8 link_dma_id; @@ -709,20 +705,5 @@ struct snd_soc_dai_driver skl_dai[] = { .channels_max = 16, }, }, -#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_PROBES) -{ - .name = "Probe Extraction CPU DAI", - .compress_new = snd_soc_new_compress, - .cops = &sof_probe_compr_ops, - .capture = { - .stream_name = "Probe Extraction", - .channels_min = 1, - .channels_max = 8, - .rates = SNDRV_PCM_RATE_48000, - .rate_min = 48000, - .rate_max = 48000, - }, -}, -#endif #endif }; diff --git a/sound/soc/sof/intel/hda-probes.c b/sound/soc/sof/intel/hda-probes.c index fe2f3f7d236b10..8d8979c3ecb1f2 100644 --- a/sound/soc/sof/intel/hda-probes.c +++ b/sound/soc/sof/intel/hda-probes.c @@ -3,14 +3,19 @@ // This file is provided under a dual BSD/GPLv2 license. When using or // redistributing this file, you may do so under either license. // -// Copyright(c) 2019-2020 Intel Corporation. All rights reserved. +// Copyright(c) 2019-2021 Intel Corporation. All rights reserved. // // Author: Cezary Rojewski +// Converted to SOF client: +// Ranjani Sridharan +// Peter Ujfalusi // #include #include #include "../sof-priv.h" +#include "../sof-client-probes.h" +#include "../sof-client.h" #include "hda.h" static inline struct hdac_ext_stream * @@ -19,10 +24,11 @@ hda_compr_get_stream(struct snd_compr_stream *cstream) return cstream->runtime->private_data; } -int hda_probe_compr_assign(struct snd_sof_dev *sdev, - struct snd_compr_stream *cstream, - struct snd_soc_dai *dai) +static int hda_probes_compr_assign(struct sof_client_dev *cdev, + struct snd_compr_stream *cstream, + struct snd_soc_dai *dai, u32 *stream_id) { + struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev); struct hdac_ext_stream *stream; stream = hda_dsp_stream_get(sdev, cstream->direction, 0); @@ -33,14 +39,17 @@ int hda_probe_compr_assign(struct snd_sof_dev *sdev, hdac_stream(stream)->cstream = cstream; cstream->runtime->private_data = stream; - return hdac_stream(stream)->stream_tag; + *stream_id = hdac_stream(stream)->stream_tag; + + return 0; } -int hda_probe_compr_free(struct snd_sof_dev *sdev, - struct snd_compr_stream *cstream, - struct snd_soc_dai *dai) +static int hda_probes_compr_free(struct sof_client_dev *cdev, + struct snd_compr_stream *cstream, + struct snd_soc_dai *dai) { struct hdac_ext_stream *stream = hda_compr_get_stream(cstream); + struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev); int ret; ret = hda_dsp_stream_put(sdev, cstream->direction, @@ -56,12 +65,13 @@ int hda_probe_compr_free(struct snd_sof_dev *sdev, return 0; } -int hda_probe_compr_set_params(struct snd_sof_dev *sdev, - struct snd_compr_stream *cstream, - struct snd_compr_params *params, - struct snd_soc_dai *dai) +static int hda_probes_compr_set_params(struct sof_client_dev *cdev, + struct snd_compr_stream *cstream, + struct snd_compr_params *params, + struct snd_soc_dai *dai) { struct hdac_ext_stream *stream = hda_compr_get_stream(cstream); + struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev); struct hdac_stream *hstream = hdac_stream(stream); struct snd_dma_buffer *dmab; u32 bits, rate; @@ -89,19 +99,20 @@ int hda_probe_compr_set_params(struct snd_sof_dev *sdev, return 0; } -int hda_probe_compr_trigger(struct snd_sof_dev *sdev, - struct snd_compr_stream *cstream, int cmd, - struct snd_soc_dai *dai) +static int hda_probes_compr_trigger(struct sof_client_dev *cdev, + struct snd_compr_stream *cstream, + int cmd, struct snd_soc_dai *dai) { struct hdac_ext_stream *stream = hda_compr_get_stream(cstream); + struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev); return hda_dsp_stream_trigger(sdev, stream, cmd); } -int hda_probe_compr_pointer(struct snd_sof_dev *sdev, - struct snd_compr_stream *cstream, - struct snd_compr_tstamp *tstamp, - struct snd_soc_dai *dai) +static int hda_probes_compr_pointer(struct sof_client_dev *cdev, + struct snd_compr_stream *cstream, + struct snd_compr_tstamp *tstamp, + struct snd_soc_dai *dai) { struct hdac_ext_stream *stream = hda_compr_get_stream(cstream); struct snd_soc_pcm_stream *pstream; @@ -112,3 +123,25 @@ int hda_probe_compr_pointer(struct snd_sof_dev *sdev, return 0; } + +/* SOF client implementation */ +static const struct sof_probes_host_ops hda_probes_ops = { + .assign = hda_probes_compr_assign, + .free = hda_probes_compr_free, + .set_params = hda_probes_compr_set_params, + .trigger = hda_probes_compr_trigger, + .pointer = hda_probes_compr_pointer, +}; + +int hda_probes_register(struct snd_sof_dev *sdev) +{ + return sof_client_dev_register(sdev, "hda-probes", 0, &hda_probes_ops, + sizeof(hda_probes_ops)); +} + +void hda_probes_unregister(struct snd_sof_dev *sdev) +{ + sof_client_dev_unregister(sdev, "hda-probes", 0); +} + +MODULE_IMPORT_NS(SND_SOC_SOF_CLIENT); diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h index 0fff8fcb8503d6..07bdf716a37cc3 100644 --- a/sound/soc/sof/intel/hda.h +++ b/sound/soc/sof/intel/hda.h @@ -16,6 +16,7 @@ #include #include #include +#include "../sof-client-probes.h" #include "shim.h" /* PCI registers */ @@ -351,13 +352,7 @@ /* Number of DAIs */ #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) - -#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_PROBES) -#define SOF_SKL_NUM_DAIS 16 -#else #define SOF_SKL_NUM_DAIS 15 -#endif - #else #define SOF_SKL_NUM_DAIS 8 #endif @@ -573,29 +568,6 @@ int hda_ipc_pcm_params(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream, const struct sof_ipc_pcm_params_reply *reply); -#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_PROBES) -/* - * Probe Compress Operations. - */ -int hda_probe_compr_assign(struct snd_sof_dev *sdev, - struct snd_compr_stream *cstream, - struct snd_soc_dai *dai); -int hda_probe_compr_free(struct snd_sof_dev *sdev, - struct snd_compr_stream *cstream, - struct snd_soc_dai *dai); -int hda_probe_compr_set_params(struct snd_sof_dev *sdev, - struct snd_compr_stream *cstream, - struct snd_compr_params *params, - struct snd_soc_dai *dai); -int hda_probe_compr_trigger(struct snd_sof_dev *sdev, - struct snd_compr_stream *cstream, int cmd, - struct snd_soc_dai *dai); -int hda_probe_compr_pointer(struct snd_sof_dev *sdev, - struct snd_compr_stream *cstream, - struct snd_compr_tstamp *tstamp, - struct snd_soc_dai *dai); -#endif - /* * DSP IPC Operations. */ @@ -726,6 +698,21 @@ extern const struct sof_intel_dsp_desc ehl_chip_info; extern const struct sof_intel_dsp_desc jsl_chip_info; extern const struct sof_intel_dsp_desc adls_chip_info; +/* Probes support */ +#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_PROBES) +int hda_probes_register(struct snd_sof_dev *sdev); +void hda_probes_unregister(struct snd_sof_dev *sdev); +#else +static inline int hda_probes_register(struct snd_sof_dev *sdev) +{ + return 0; +} + +static inline void hda_probes_unregister(struct snd_sof_dev *sdev) +{ +} +#endif /* CONFIG_SND_SOC_SOF_HDA_PROBES */ + /* machine driver select */ void hda_machine_select(struct snd_sof_dev *sdev); void hda_set_mach_params(const struct snd_soc_acpi_mach *mach, diff --git a/sound/soc/sof/intel/icl.c b/sound/soc/sof/intel/icl.c index a8758dddd86563..04c3f9b98b3b41 100644 --- a/sound/soc/sof/intel/icl.c +++ b/sound/soc/sof/intel/icl.c @@ -88,6 +88,16 @@ static int icl_dsp_post_fw_run(struct snd_sof_dev *sdev) return hda_dsp_ctrl_clock_power_gating(sdev, true); } +static int icl_register_clients(struct snd_sof_dev *sdev) +{ + return hda_probes_register(sdev); +} + +static void icl_unregister_clients(struct snd_sof_dev *sdev) +{ + hda_probes_unregister(sdev); +} + /* Icelake ops */ const struct snd_sof_dsp_ops sof_icl_ops = { /* probe/remove/shutdown */ @@ -142,15 +152,6 @@ const struct snd_sof_dsp_ops sof_icl_ops = { .pcm_trigger = hda_dsp_pcm_trigger, .pcm_pointer = hda_dsp_pcm_pointer, -#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_PROBES) - /* probe callbacks */ - .probe_assign = hda_probe_compr_assign, - .probe_free = hda_probe_compr_free, - .probe_set_params = hda_probe_compr_set_params, - .probe_trigger = hda_probe_compr_trigger, - .probe_pointer = hda_probe_compr_pointer, -#endif - /* firmware loading */ .load_firmware = snd_sof_load_firmware_raw, @@ -174,6 +175,10 @@ const struct snd_sof_dsp_ops sof_icl_ops = { .trace_release = hda_dsp_trace_release, .trace_trigger = hda_dsp_trace_trigger, + /* client ops */ + .register_ipc_clients = icl_register_clients, + .unregister_ipc_clients = icl_unregister_clients, + /* DAI drivers */ .drv = skl_dai, .num_drv = SOF_SKL_NUM_DAIS, diff --git a/sound/soc/sof/intel/tgl.c b/sound/soc/sof/intel/tgl.c index 7f7929c5cb88fd..9924a1c474fd4f 100644 --- a/sound/soc/sof/intel/tgl.c +++ b/sound/soc/sof/intel/tgl.c @@ -60,6 +60,16 @@ static int tgl_dsp_core_put(struct snd_sof_dev *sdev, int core) &pm_core_config, sizeof(pm_core_config)); } +static int tgl_register_clients(struct snd_sof_dev *sdev) +{ + return hda_probes_register(sdev); +} + +static void tgl_unregister_clients(struct snd_sof_dev *sdev) +{ + hda_probes_unregister(sdev); +} + /* Tigerlake ops */ const struct snd_sof_dsp_ops sof_tgl_ops = { /* probe/remove/shutdown */ @@ -115,15 +125,6 @@ const struct snd_sof_dsp_ops sof_tgl_ops = { .pcm_pointer = hda_dsp_pcm_pointer, .pcm_ack = hda_dsp_pcm_ack, -#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_PROBES) - /* probe callbacks */ - .probe_assign = hda_probe_compr_assign, - .probe_free = hda_probe_compr_free, - .probe_set_params = hda_probe_compr_set_params, - .probe_trigger = hda_probe_compr_trigger, - .probe_pointer = hda_probe_compr_pointer, -#endif - /* firmware loading */ .load_firmware = snd_sof_load_firmware_raw, @@ -146,6 +147,10 @@ const struct snd_sof_dsp_ops sof_tgl_ops = { .trace_release = hda_dsp_trace_release, .trace_trigger = hda_dsp_trace_trigger, + /* client ops */ + .register_ipc_clients = tgl_register_clients, + .unregister_ipc_clients = tgl_unregister_clients, + /* DAI drivers */ .drv = skl_dai, .num_drv = SOF_SKL_NUM_DAIS, diff --git a/sound/soc/sof/ops.h b/sound/soc/sof/ops.h index 9cf24d72032661..2398b4e19ac996 100644 --- a/sound/soc/sof/ops.h +++ b/sound/soc/sof/ops.h @@ -493,49 +493,6 @@ static inline int snd_sof_pcm_platform_ack(struct snd_sof_dev *sdev, return 0; } -#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_PROBES) -static inline int -snd_sof_probe_compr_assign(struct snd_sof_dev *sdev, - struct snd_compr_stream *cstream, struct snd_soc_dai *dai) -{ - return sof_ops(sdev)->probe_assign(sdev, cstream, dai); -} - -static inline int -snd_sof_probe_compr_free(struct snd_sof_dev *sdev, - struct snd_compr_stream *cstream, struct snd_soc_dai *dai) -{ - return sof_ops(sdev)->probe_free(sdev, cstream, dai); -} - -static inline int -snd_sof_probe_compr_set_params(struct snd_sof_dev *sdev, - struct snd_compr_stream *cstream, - struct snd_compr_params *params, struct snd_soc_dai *dai) -{ - return sof_ops(sdev)->probe_set_params(sdev, cstream, params, dai); -} - -static inline int -snd_sof_probe_compr_trigger(struct snd_sof_dev *sdev, - struct snd_compr_stream *cstream, int cmd, - struct snd_soc_dai *dai) -{ - return sof_ops(sdev)->probe_trigger(sdev, cstream, cmd, dai); -} - -static inline int -snd_sof_probe_compr_pointer(struct snd_sof_dev *sdev, - struct snd_compr_stream *cstream, - struct snd_compr_tstamp *tstamp, struct snd_soc_dai *dai) -{ - if (sof_ops(sdev) && sof_ops(sdev)->probe_pointer) - return sof_ops(sdev)->probe_pointer(sdev, cstream, tstamp, dai); - - return 0; -} -#endif - /* machine driver */ static inline int snd_sof_machine_register(struct snd_sof_dev *sdev, void *pdata) diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c index 24f0a74dfb90c6..58d3696d2883a2 100644 --- a/sound/soc/sof/pcm.c +++ b/sound/soc/sof/pcm.c @@ -15,11 +15,8 @@ #include #include "sof-priv.h" #include "sof-audio.h" -#include "ops.h" -#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_PROBES) -#include "sof-probes.h" -#endif #include "sof-utils.h" +#include "ops.h" /* Create DMA buffer page table for DSP */ static int create_page_table(struct snd_soc_component *component, @@ -893,9 +890,6 @@ void snd_sof_new_platform_drv(struct snd_sof_dev *sdev) pd->pointer = sof_pcm_pointer; pd->ack = sof_pcm_ack; -#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_PROBES) - pd->compress_ops = &sof_probe_compressed_ops; -#endif pd->pcm_construct = sof_pcm_new; pd->ignore_machine = drv_name; pd->be_hw_params_fixup = sof_pcm_dai_link_fixup; diff --git a/sound/soc/sof/sof-client-probes.c b/sound/soc/sof/sof-client-probes.c new file mode 100644 index 00000000000000..04b0e028ac5c53 --- /dev/null +++ b/sound/soc/sof/sof-client-probes.c @@ -0,0 +1,797 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// Copyright(c) 2019-2021 Intel Corporation. All rights reserved. +// +// Author: Cezary Rojewski +// +// SOF client support: +// Ranjani Sridharan +// Peter Ujfalusi +// + +#include +#include +#include +#include +#include +#include "sof-client.h" +#include "sof-client-probes.h" + +#define SOF_PROBES_SUSPEND_DELAY_MS 3000 +/* only extraction supported for now */ +#define SOF_PROBES_NUM_DAI_LINKS 1 + +#define SOF_PROBES_INVALID_NODE_ID UINT_MAX + +struct sof_probes_priv { + struct dentry *dfs_points; + struct dentry *dfs_points_remove; + u32 extractor_stream_tag; + struct snd_soc_card card; + + const struct sof_probes_host_ops *host_ops; +}; + +struct sof_probe_point_desc { + unsigned int buffer_id; + unsigned int purpose; + unsigned int stream_tag; +} __packed; + +struct sof_probe_dma { + unsigned int stream_tag; + unsigned int dma_buffer_size; +} __packed; + +struct sof_ipc_probe_dma_add_params { + struct sof_ipc_cmd_hdr hdr; + unsigned int num_elems; + struct sof_probe_dma dma[]; +} __packed; + +struct sof_ipc_probe_info_params { + struct sof_ipc_reply rhdr; + unsigned int num_elems; + union { + struct sof_probe_dma dma[0]; + struct sof_probe_point_desc desc[0]; + }; +} __packed; + +struct sof_ipc_probe_point_add_params { + struct sof_ipc_cmd_hdr hdr; + unsigned int num_elems; + struct sof_probe_point_desc desc[]; +} __packed; + +struct sof_ipc_probe_point_remove_params { + struct sof_ipc_cmd_hdr hdr; + unsigned int num_elems; + unsigned int buffer_id[]; +} __packed; + +/** + * sof_probes_init - initialize data probing + * @cdev: SOF client device + * @stream_tag: Extractor stream tag + * @buffer_size: DMA buffer size to set for extractor + * + * Host chooses whether extraction is supported or not by providing + * valid stream tag to DSP. Once specified, stream described by that + * tag will be tied to DSP for extraction for the entire lifetime of + * probe. + * + * Probing is initialized only once and each INIT request must be + * matched by DEINIT call. + */ +static int sof_probes_init(struct sof_client_dev *cdev, u32 stream_tag, + size_t buffer_size) +{ + struct sof_ipc_probe_dma_add_params *msg; + size_t size = struct_size(msg, dma, 1); + struct sof_ipc_reply reply; + int ret; + + msg = kmalloc(size, GFP_KERNEL); + if (!msg) + return -ENOMEM; + msg->hdr.size = size; + msg->hdr.cmd = SOF_IPC_GLB_PROBE | SOF_IPC_PROBE_INIT; + msg->num_elems = 1; + msg->dma[0].stream_tag = stream_tag; + msg->dma[0].dma_buffer_size = buffer_size; + + ret = sof_client_ipc_tx_message(cdev, msg, &reply, sizeof(reply)); + kfree(msg); + return ret; +} + +/** + * sof_probes_deinit - cleanup after data probing + * @cdev: SOF client device + * + * Host sends DEINIT request to free previously initialized probe + * on DSP side once it is no longer needed. DEINIT only when there + * are no probes connected and with all injectors detached. + */ +static int sof_probes_deinit(struct sof_client_dev *cdev) +{ + struct sof_ipc_cmd_hdr msg; + struct sof_ipc_reply reply; + + msg.size = sizeof(msg); + msg.cmd = SOF_IPC_GLB_PROBE | SOF_IPC_PROBE_DEINIT; + + return sof_client_ipc_tx_message(cdev, &msg, &reply, sizeof(reply)); +} + +static int sof_probes_info(struct sof_client_dev *cdev, unsigned int cmd, + void **params, size_t *num_params) +{ + struct sof_ipc_probe_info_params msg = {{{0}}}; + struct sof_ipc_probe_info_params *reply; + size_t bytes; + int ret; + + *params = NULL; + *num_params = 0; + + reply = kzalloc(SOF_IPC_MSG_MAX_SIZE, GFP_KERNEL); + if (!reply) + return -ENOMEM; + msg.rhdr.hdr.size = sizeof(msg); + msg.rhdr.hdr.cmd = SOF_IPC_GLB_PROBE | cmd; + + ret = sof_client_ipc_tx_message(cdev, &msg, reply, SOF_IPC_MSG_MAX_SIZE); + if (ret < 0 || reply->rhdr.error < 0) + goto exit; + + if (!reply->num_elems) + goto exit; + + if (cmd == SOF_IPC_PROBE_DMA_INFO) + bytes = sizeof(reply->dma[0]); + else + bytes = sizeof(reply->desc[0]); + bytes *= reply->num_elems; + *params = kmemdup(&reply->dma[0], bytes, GFP_KERNEL); + if (!*params) { + ret = -ENOMEM; + goto exit; + } + *num_params = reply->num_elems; + +exit: + kfree(reply); + return ret; +} + +/** + * sof_probes_points_info - retrieve list of active probe points + * @cdev: SOF client device + * @desc: Returned list of active probes + * @num_desc: Returned count of active probes + * + * Host sends PROBE_POINT_INFO request to obtain list of active probe + * points, valid for disconnection when given probe is no longer + * required. + */ +static int sof_probes_points_info(struct sof_client_dev *cdev, + struct sof_probe_point_desc **desc, + size_t *num_desc) +{ + return sof_probes_info(cdev, SOF_IPC_PROBE_POINT_INFO, + (void **)desc, num_desc); +} + +/** + * sof_probes_points_add - connect specified probes + * @cdev: SOF client device + * @desc: List of probe points to connect + * @num_desc: Number of elements in @desc + * + * Dynamically connects to provided set of endpoints. Immediately + * after connection is established, host must be prepared to + * transfer data from or to target stream given the probing purpose. + * + * Each probe point should be removed using PROBE_POINT_REMOVE + * request when no longer needed. + */ +static int sof_probes_points_add(struct sof_client_dev *cdev, + struct sof_probe_point_desc *desc, + size_t num_desc) +{ + struct sof_ipc_probe_point_add_params *msg; + size_t size = struct_size(msg, desc, num_desc); + struct sof_ipc_reply reply; + int ret; + + msg = kmalloc(size, GFP_KERNEL); + if (!msg) + return -ENOMEM; + msg->hdr.size = size; + msg->num_elems = num_desc; + msg->hdr.cmd = SOF_IPC_GLB_PROBE | SOF_IPC_PROBE_POINT_ADD; + memcpy(&msg->desc[0], desc, size - sizeof(*msg)); + + ret = sof_client_ipc_tx_message(cdev, msg, &reply, sizeof(reply)); + kfree(msg); + return ret; +} + +/** + * sof_probes_points_remove - disconnect specified probes + * @cdev: SOF client device + * @buffer_id: List of probe points to disconnect + * @num_buffer_id: Number of elements in @desc + * + * Removes previously connected probes from list of active probe + * points and frees all resources on DSP side. + */ +static int sof_probes_points_remove(struct sof_client_dev *cdev, + unsigned int *buffer_id, size_t num_buffer_id) +{ + struct sof_ipc_probe_point_remove_params *msg; + size_t size = struct_size(msg, buffer_id, num_buffer_id); + struct sof_ipc_reply reply; + int ret; + + msg = kmalloc(size, GFP_KERNEL); + if (!msg) + return -ENOMEM; + msg->hdr.size = size; + msg->num_elems = num_buffer_id; + msg->hdr.cmd = SOF_IPC_GLB_PROBE | SOF_IPC_PROBE_POINT_REMOVE; + memcpy(&msg->buffer_id[0], buffer_id, size - sizeof(*msg)); + + ret = sof_client_ipc_tx_message(cdev, msg, &reply, sizeof(reply)); + kfree(msg); + return ret; +} + +static int sof_probes_compr_startup(struct snd_compr_stream *cstream, + struct snd_soc_dai *dai) +{ + struct snd_soc_card *card = snd_soc_component_get_drvdata(dai->component); + struct sof_client_dev *cdev = snd_soc_card_get_drvdata(card); + struct sof_probes_priv *priv = cdev->data; + const struct sof_probes_host_ops *ops = priv->host_ops; + int ret; + + ret = ops->assign(cdev, cstream, dai, &priv->extractor_stream_tag); + if (ret) { + dev_err(dai->dev, "Failed to assign probe stream: %d\n", ret); + priv->extractor_stream_tag = SOF_PROBES_INVALID_NODE_ID; + } + + return ret; +} + +static int sof_probes_compr_shutdown(struct snd_compr_stream *cstream, + struct snd_soc_dai *dai) +{ + struct snd_soc_card *card = snd_soc_component_get_drvdata(dai->component); + struct sof_client_dev *cdev = snd_soc_card_get_drvdata(card); + struct sof_probes_priv *priv = cdev->data; + const struct sof_probes_host_ops *ops = priv->host_ops; + struct sof_probe_point_desc *desc; + size_t num_desc; + int i, ret; + + /* disconnect all probe points */ + ret = sof_probes_points_info(cdev, &desc, &num_desc); + if (ret < 0) { + dev_err(dai->dev, "Failed to get probe points: %d\n", ret); + goto exit; + } + + for (i = 0; i < num_desc; i++) + sof_probes_points_remove(cdev, &desc[i].buffer_id, 1); + kfree(desc); + +exit: + ret = sof_probes_deinit(cdev); + if (ret < 0) + dev_err(dai->dev, "Failed to deinit probe: %d\n", ret); + + priv->extractor_stream_tag = SOF_PROBES_INVALID_NODE_ID; + snd_compr_free_pages(cstream); + + return ops->free(cdev, cstream, dai); +} + +static int sof_probes_compr_set_params(struct snd_compr_stream *cstream, + struct snd_compr_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_card *card = snd_soc_component_get_drvdata(dai->component); + struct sof_client_dev *cdev = snd_soc_card_get_drvdata(card); + struct snd_compr_runtime *rtd = cstream->runtime; + struct sof_probes_priv *priv = cdev->data; + const struct sof_probes_host_ops *ops = priv->host_ops; + int ret; + + cstream->dma_buffer.dev.type = SNDRV_DMA_TYPE_DEV_SG; + cstream->dma_buffer.dev.dev = sof_client_get_dma_dev(cdev); + ret = snd_compr_malloc_pages(cstream, rtd->buffer_size); + if (ret < 0) + return ret; + + ret = ops->set_params(cdev, cstream, params, dai); + if (ret) + return ret; + + ret = sof_probes_init(cdev, priv->extractor_stream_tag, rtd->dma_bytes); + if (ret < 0) { + dev_err(dai->dev, "Failed to init probe: %d\n", ret); + return ret; + } + + return 0; +} + +static int sof_probes_compr_trigger(struct snd_compr_stream *cstream, int cmd, + struct snd_soc_dai *dai) +{ + struct snd_soc_card *card = snd_soc_component_get_drvdata(dai->component); + struct sof_client_dev *cdev = snd_soc_card_get_drvdata(card); + struct sof_probes_priv *priv = cdev->data; + const struct sof_probes_host_ops *ops = priv->host_ops; + + return ops->trigger(cdev, cstream, cmd, dai); +} + +static int sof_probes_compr_pointer(struct snd_compr_stream *cstream, + struct snd_compr_tstamp *tstamp, + struct snd_soc_dai *dai) +{ + struct snd_soc_card *card = snd_soc_component_get_drvdata(dai->component); + struct sof_client_dev *cdev = snd_soc_card_get_drvdata(card); + struct sof_probes_priv *priv = cdev->data; + const struct sof_probes_host_ops *ops = priv->host_ops; + + return ops->pointer(cdev, cstream, tstamp, dai); +} + +static struct snd_soc_cdai_ops sof_probes_compr_ops = { + .startup = sof_probes_compr_startup, + .shutdown = sof_probes_compr_shutdown, + .set_params = sof_probes_compr_set_params, + .trigger = sof_probes_compr_trigger, + .pointer = sof_probes_compr_pointer, +}; + +static int sof_probes_compr_copy(struct snd_soc_component *component, + 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_compress_ops sof_probes_compressed_ops = { + .copy = sof_probes_compr_copy, +}; + +/** + * strsplit_u32 - Split string into sequence of u32 tokens + * @buf: String to split into tokens. + * @delim: String containing delimiter characters. + * @tkns: Returned u32 sequence pointer. + * @num_tkns: Returned number of tokens obtained. + */ +static int strsplit_u32(char *buf, const char *delim, u32 **tkns, size_t *num_tkns) +{ + char *s; + u32 *data, *tmp; + size_t count = 0; + size_t cap = 32; + int ret = 0; + + *tkns = NULL; + *num_tkns = 0; + data = kcalloc(cap, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + while ((s = strsep(&buf, delim)) != NULL) { + ret = kstrtouint(s, 0, data + count); + if (ret) + goto exit; + if (++count >= cap) { + cap *= 2; + tmp = krealloc(data, cap * sizeof(*data), GFP_KERNEL); + if (!tmp) { + ret = -ENOMEM; + goto exit; + } + data = tmp; + } + } + + if (!count) + goto exit; + *tkns = kmemdup(data, count * sizeof(*data), GFP_KERNEL); + if (!(*tkns)) { + ret = -ENOMEM; + goto exit; + } + *num_tkns = count; + +exit: + kfree(data); + return ret; +} + +static int tokenize_input(const char __user *from, size_t count, + loff_t *ppos, u32 **tkns, size_t *num_tkns) +{ + char *buf; + int ret; + + buf = kmalloc(count + 1, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + ret = simple_write_to_buffer(buf, count, ppos, from, count); + if (ret != count) { + ret = ret >= 0 ? -EIO : ret; + goto exit; + } + + buf[count] = '\0'; + ret = strsplit_u32(buf, ",", tkns, num_tkns); +exit: + kfree(buf); + return ret; +} + +static ssize_t probe_points_read(struct file *file, char __user *to, + size_t count, loff_t *ppos) +{ + struct sof_client_dev *cdev = file->private_data; + struct sof_probes_priv *priv = cdev->data; + struct device *dev = &cdev->auxdev.dev; + struct sof_probe_point_desc *desc; + size_t num_desc; + int remaining; + char *buf; + int i, ret, err; + + if (priv->extractor_stream_tag == SOF_PROBES_INVALID_NODE_ID) { + dev_warn(dev, "no extractor stream running\n"); + return -ENOENT; + } + + buf = kzalloc(PAGE_SIZE, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + ret = pm_runtime_get_sync(dev); + if (ret < 0 && ret != -EACCES) { + dev_err_ratelimited(dev, "error: debugfs read failed to resume %d\n", ret); + pm_runtime_put_noidle(dev); + goto exit; + } + + ret = sof_probes_points_info(cdev, &desc, &num_desc); + if (ret < 0) + goto exit; + + pm_runtime_mark_last_busy(dev); + err = pm_runtime_put_autosuspend(dev); + if (err < 0) + dev_err_ratelimited(dev, "error: debugfs read failed to idle %d\n", err); + + for (i = 0; i < num_desc; i++) { + remaining = PAGE_SIZE - strlen(buf); + if (remaining > 0) { + ret = snprintf(buf + strlen(buf), remaining, + "Id: %#010x Purpose: %u Node id: %#x\n", + desc[i].buffer_id, desc[i].purpose, desc[i].stream_tag); + if (ret < 0) + goto free_desc; + } else { + break; + } + } + + ret = simple_read_from_buffer(to, count, ppos, buf, strlen(buf)); +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 sof_client_dev *cdev = file->private_data; + struct sof_probes_priv *priv = cdev->data; + struct device *dev = &cdev->auxdev.dev; + struct sof_probe_point_desc *desc; + size_t num_tkns, bytes; + u32 *tkns; + int ret, err; + + if (priv->extractor_stream_tag == SOF_PROBES_INVALID_NODE_ID) { + dev_warn(dev, "no extractor stream running\n"); + return -ENOENT; + } + + ret = tokenize_input(from, count, ppos, &tkns, &num_tkns); + if (ret < 0) + return ret; + bytes = sizeof(*tkns) * num_tkns; + if (!num_tkns || (bytes % sizeof(*desc))) { + ret = -EINVAL; + goto exit; + } + + desc = (struct sof_probe_point_desc *)tkns; + + ret = pm_runtime_get_sync(dev); + if (ret < 0 && ret != -EACCES) { + dev_err_ratelimited(dev, "error: debugfs write failed to resume %d\n", ret); + pm_runtime_put_noidle(dev); + goto exit; + } + + ret = sof_probes_points_add(cdev, desc, bytes / sizeof(*desc)); + if (!ret) + ret = count; + + pm_runtime_mark_last_busy(dev); + err = pm_runtime_put_autosuspend(dev); + if (err < 0) + dev_err_ratelimited(dev, "error: debugfs write failed to idle %d\n", err); +exit: + kfree(tkns); + return ret; +} + +static const struct file_operations probe_points_fops = { + .open = simple_open, + .read = probe_points_read, + .write = probe_points_write, + .llseek = default_llseek, + + .owner = THIS_MODULE, +}; + +static ssize_t +probe_points_remove_write(struct file *file, const char __user *from, + size_t count, loff_t *ppos) +{ + struct sof_client_dev *cdev = file->private_data; + struct sof_probes_priv *priv = cdev->data; + struct device *dev = &cdev->auxdev.dev; + size_t num_tkns; + u32 *tkns; + int ret, err; + + if (priv->extractor_stream_tag == SOF_PROBES_INVALID_NODE_ID) { + dev_warn(dev, "no extractor stream running\n"); + return -ENOENT; + } + + ret = tokenize_input(from, count, ppos, &tkns, &num_tkns); + if (ret < 0) + return ret; + if (!num_tkns) { + ret = -EINVAL; + goto exit; + } + + ret = pm_runtime_get_sync(dev); + if (ret < 0) { + dev_err_ratelimited(dev, "error: debugfs write failed to resume %d\n", ret); + pm_runtime_put_noidle(dev); + goto exit; + } + + ret = sof_probes_points_remove(cdev, tkns, num_tkns); + if (!ret) + ret = count; + + pm_runtime_mark_last_busy(dev); + err = pm_runtime_put_autosuspend(dev); + if (err < 0) + dev_err_ratelimited(dev, "error: debugfs write failed to idle %d\n", err); +exit: + kfree(tkns); + return ret; +} + +static const struct file_operations probe_points_remove_fops = { + .open = simple_open, + .write = probe_points_remove_write, + .llseek = default_llseek, + + .owner = THIS_MODULE, +}; + +static struct snd_soc_dai_driver sof_probes_dai_drv[] = { +{ + .name = "Probe Extraction CPU DAI", + .compress_new = snd_soc_new_compress, + .cops = &sof_probes_compr_ops, + .capture = { + .stream_name = "Probe Extraction", + .channels_min = 1, + .channels_max = 8, + .rates = SNDRV_PCM_RATE_48000, + .rate_min = 48000, + .rate_max = 48000, + }, +}, +}; + +static const struct snd_soc_component_driver sof_probes_component = { + .name = "sof-probes-component", + .compress_ops = &sof_probes_compressed_ops, + .module_get_upon_open = 1, +}; + +SND_SOC_DAILINK_DEF(dummy, DAILINK_COMP_ARRAY(COMP_DUMMY())); + +static int sof_probes_client_probe(struct auxiliary_device *auxdev, + const struct auxiliary_device_id *id) +{ + struct sof_client_dev *cdev = auxiliary_dev_to_sof_client_dev(auxdev); + struct dentry *dfsroot = sof_client_get_debugfs_root(cdev); + struct device *dev = &auxdev->dev; + struct snd_soc_dai_link_component platform_component[] = { + { + .name = dev_name(dev), + } + }; + struct snd_soc_card *card; + struct sof_probes_priv *priv; + struct snd_soc_dai_link_component *cpus; + struct sof_probes_host_ops *ops; + struct snd_soc_dai_link *links; + int ret; + + if (!dev->platform_data) { + dev_err(dev, "error: missing platform data\n"); + return -ENODEV; + } + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + ops = dev->platform_data; + + if (!ops->assign || !ops->free || !ops->set_params || !ops->trigger || + !ops->pointer) { + dev_err(dev, "error: missing platform callback(s)\n"); + return -ENODEV; + } + + priv->host_ops = ops; + cdev->data = priv; + + /* register probes component driver and dai */ + ret = devm_snd_soc_register_component(dev, &sof_probes_component, + sof_probes_dai_drv, + ARRAY_SIZE(sof_probes_dai_drv)); + if (ret < 0) { + dev_err(dev, "error: failed to register SOF probes DAI driver %d\n", ret); + return ret; + } + + /* set client data */ + priv->extractor_stream_tag = SOF_PROBES_INVALID_NODE_ID; + + /* create read-write probes_points debugfs entry */ + priv->dfs_points = debugfs_create_file("probe_points", 0644, dfsroot, + cdev, &probe_points_fops); + + /* create read-write probe_points_remove debugfs entry */ + priv->dfs_points_remove = debugfs_create_file("probe_points_remove", 0644, + dfsroot, cdev, + &probe_points_remove_fops); + + links = devm_kcalloc(dev, SOF_PROBES_NUM_DAI_LINKS, sizeof(*links), GFP_KERNEL); + cpus = devm_kcalloc(dev, SOF_PROBES_NUM_DAI_LINKS, sizeof(*cpus), GFP_KERNEL); + if (!links || !cpus) { + debugfs_remove(priv->dfs_points); + debugfs_remove(priv->dfs_points_remove); + return -ENOMEM; + } + + /* extraction DAI link */ + links[0].name = "Compress Probe Capture"; + links[0].id = 0; + links[0].cpus = &cpus[0]; + links[0].num_cpus = 1; + links[0].cpus->dai_name = "Probe Extraction CPU DAI"; + links[0].codecs = dummy; + links[0].num_codecs = 1; + links[0].platforms = platform_component; + links[0].num_platforms = ARRAY_SIZE(platform_component); + links[0].nonatomic = 1; + + card = &priv->card; + + card->dev = dev; + card->name = "sof-probes"; + card->owner = THIS_MODULE; + card->num_links = SOF_PROBES_NUM_DAI_LINKS; + card->dai_link = links; + + /* set idle_bias_off to prevent the core from resuming the card->dev */ + card->dapm.idle_bias_off = true; + + snd_soc_card_set_drvdata(card, cdev); + + ret = devm_snd_soc_register_card(dev, card); + if (ret < 0) { + debugfs_remove(priv->dfs_points); + debugfs_remove(priv->dfs_points_remove); + dev_err(dev, "error: Probes card register failed %d\n", ret); + return ret; + } + + /* enable runtime PM */ + pm_runtime_set_autosuspend_delay(dev, SOF_PROBES_SUSPEND_DELAY_MS); + pm_runtime_use_autosuspend(dev); + pm_runtime_enable(dev); + pm_runtime_mark_last_busy(dev); + pm_runtime_idle(dev); + + return 0; +} + +static void sof_probes_client_remove(struct auxiliary_device *auxdev) +{ + struct sof_client_dev *cdev = auxiliary_dev_to_sof_client_dev(auxdev); + struct sof_probes_priv *priv = cdev->data; + + pm_runtime_disable(&auxdev->dev); + debugfs_remove(priv->dfs_points); + debugfs_remove(priv->dfs_points_remove); +} + +static const struct auxiliary_device_id sof_probes_client_id_table[] = { + { .name = "snd_sof.hda-probes", }, + {}, +}; +MODULE_DEVICE_TABLE(auxiliary, sof_probes_client_id_table); + +/* driver name will be set based on KBUILD_MODNAME */ +static struct auxiliary_driver sof_probes_client_drv = { + .probe = sof_probes_client_probe, + .remove = sof_probes_client_remove, + + .id_table = sof_probes_client_id_table, +}; + +module_auxiliary_driver(sof_probes_client_drv); + +MODULE_DESCRIPTION("SOF Probes Client Driver"); +MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS(SND_SOC_SOF_CLIENT); diff --git a/sound/soc/sof/sof-client-probes.h b/sound/soc/sof/sof-client-probes.h new file mode 100644 index 00000000000000..0f9ed4569fd3bb --- /dev/null +++ b/sound/soc/sof/sof-client-probes.h @@ -0,0 +1,31 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef __SOF_CLIENT_PROBES_H +#define __SOF_CLIENT_PROBES_H + +struct snd_compr_stream; +struct snd_compr_tstamp; +struct snd_compr_params; +struct sof_client_dev; +struct snd_soc_dai; + +/* + * Callbacks used on platforms where the control for audio is split between + * DSP and host, like HDA. + */ +struct sof_probes_host_ops { + int (*assign)(struct sof_client_dev *cdev, struct snd_compr_stream *cstream, + struct snd_soc_dai *dai, u32 *stream_id); + int (*free)(struct sof_client_dev *cdev, struct snd_compr_stream *cstream, + struct snd_soc_dai *dai); + int (*set_params)(struct sof_client_dev *cdev, struct snd_compr_stream *cstream, + struct snd_compr_params *params, + struct snd_soc_dai *dai); + int (*trigger)(struct sof_client_dev *cdev, struct snd_compr_stream *cstream, + int cmd, struct snd_soc_dai *dai); + int (*pointer)(struct sof_client_dev *cdev, struct snd_compr_stream *cstream, + struct snd_compr_tstamp *tstamp, + struct snd_soc_dai *dai); +}; + +#endif diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index 2258b42524bd8e..e3570c93fae747 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -206,27 +206,6 @@ struct snd_sof_dsp_ops { /* pcm ack */ int (*pcm_ack)(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream); /* optional */ -#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_PROBES) - /* Except for probe_pointer, all probe ops are mandatory */ - int (*probe_assign)(struct snd_sof_dev *sdev, - struct snd_compr_stream *cstream, - struct snd_soc_dai *dai); /* mandatory */ - int (*probe_free)(struct snd_sof_dev *sdev, - struct snd_compr_stream *cstream, - struct snd_soc_dai *dai); /* mandatory */ - int (*probe_set_params)(struct snd_sof_dev *sdev, - struct snd_compr_stream *cstream, - struct snd_compr_params *params, - struct snd_soc_dai *dai); /* mandatory */ - int (*probe_trigger)(struct snd_sof_dev *sdev, - struct snd_compr_stream *cstream, int cmd, - struct snd_soc_dai *dai); /* mandatory */ - int (*probe_pointer)(struct snd_sof_dev *sdev, - struct snd_compr_stream *cstream, - struct snd_compr_tstamp *tstamp, - struct snd_soc_dai *dai); /* optional */ -#endif - /* host read DSP stream data */ int (*ipc_msg_data)(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream, @@ -451,10 +430,6 @@ struct snd_sof_dev { int ipc_timeout; int boot_timeout; -#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_PROBES) - unsigned int extractor_stream_tag; -#endif - /* DMA for Trace */ struct snd_dma_buffer dmatb; struct snd_dma_buffer dmatp; diff --git a/sound/soc/sof/sof-probes.c b/sound/soc/sof/sof-probes.c deleted file mode 100644 index 5586af9f1a251c..00000000000000 --- a/sound/soc/sof/sof-probes.c +++ /dev/null @@ -1,364 +0,0 @@ -// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) -// -// This file is provided under a dual BSD/GPLv2 license. When using or -// redistributing this file, you may do so under either license. -// -// Copyright(c) 2019-2021 Intel Corporation. All rights reserved. -// Author: Cezary Rojewski -// - -#include -#include "ops.h" -#include "sof-priv.h" -#include "sof-probes.h" - -struct sof_probe_dma { - unsigned int stream_tag; - unsigned int dma_buffer_size; -} __packed; - -struct sof_ipc_probe_dma_add_params { - struct sof_ipc_cmd_hdr hdr; - unsigned int num_elems; - struct sof_probe_dma dma[]; -} __packed; - -struct sof_ipc_probe_info_params { - struct sof_ipc_reply rhdr; - unsigned int num_elems; - union { - struct sof_probe_dma dma[0]; - struct sof_probe_point_desc desc[0]; - }; -} __packed; - -struct sof_ipc_probe_point_add_params { - struct sof_ipc_cmd_hdr hdr; - unsigned int num_elems; - struct sof_probe_point_desc desc[]; -} __packed; - -struct sof_ipc_probe_point_remove_params { - struct sof_ipc_cmd_hdr hdr; - unsigned int num_elems; - unsigned int buffer_id[]; -} __packed; - -/** - * sof_ipc_probe_init - initialize data probing - * @sdev: SOF sound device - * @stream_tag: Extractor stream tag - * @buffer_size: DMA buffer size to set for extractor - * - * Host chooses whether extraction is supported or not by providing - * valid stream tag to DSP. Once specified, stream described by that - * tag will be tied to DSP for extraction for the entire lifetime of - * probe. - * - * Probing is initialized only once and each INIT request must be - * matched by DEINIT call. - */ -static int sof_ipc_probe_init(struct snd_sof_dev *sdev, u32 stream_tag, - size_t buffer_size) -{ - struct sof_ipc_probe_dma_add_params *msg; - struct sof_ipc_reply reply; - size_t size = struct_size(msg, dma, 1); - int ret; - - msg = kmalloc(size, GFP_KERNEL); - if (!msg) - return -ENOMEM; - msg->hdr.size = size; - msg->hdr.cmd = SOF_IPC_GLB_PROBE | SOF_IPC_PROBE_INIT; - msg->num_elems = 1; - msg->dma[0].stream_tag = stream_tag; - msg->dma[0].dma_buffer_size = buffer_size; - - ret = sof_ipc_tx_message(sdev->ipc, msg->hdr.cmd, msg, msg->hdr.size, - &reply, sizeof(reply)); - kfree(msg); - return ret; -} - -/** - * sof_ipc_probe_deinit - cleanup after data probing - * @sdev: SOF sound device - * - * Host sends DEINIT request to free previously initialized probe - * on DSP side once it is no longer needed. DEINIT only when there - * are no probes connected and with all injectors detached. - */ -static int sof_ipc_probe_deinit(struct snd_sof_dev *sdev) -{ - struct sof_ipc_cmd_hdr msg; - struct sof_ipc_reply reply; - - msg.size = sizeof(msg); - msg.cmd = SOF_IPC_GLB_PROBE | SOF_IPC_PROBE_DEINIT; - - return sof_ipc_tx_message(sdev->ipc, msg.cmd, &msg, msg.size, - &reply, sizeof(reply)); -} - -static int sof_ipc_probe_info(struct snd_sof_dev *sdev, unsigned int cmd, - void **params, size_t *num_params) -{ - struct sof_ipc_probe_info_params msg = {{{0}}}; - struct sof_ipc_probe_info_params *reply; - size_t bytes; - int ret; - - *params = NULL; - *num_params = 0; - - reply = kzalloc(SOF_IPC_MSG_MAX_SIZE, GFP_KERNEL); - if (!reply) - return -ENOMEM; - msg.rhdr.hdr.size = sizeof(msg); - msg.rhdr.hdr.cmd = SOF_IPC_GLB_PROBE | cmd; - - ret = sof_ipc_tx_message(sdev->ipc, msg.rhdr.hdr.cmd, &msg, - msg.rhdr.hdr.size, reply, SOF_IPC_MSG_MAX_SIZE); - if (ret < 0 || reply->rhdr.error < 0) - goto exit; - - if (!reply->num_elems) - goto exit; - - if (cmd == SOF_IPC_PROBE_DMA_INFO) - bytes = sizeof(reply->dma[0]); - else - bytes = sizeof(reply->desc[0]); - bytes *= reply->num_elems; - *params = kmemdup(&reply->dma[0], bytes, GFP_KERNEL); - if (!*params) { - ret = -ENOMEM; - goto exit; - } - *num_params = reply->num_elems; - -exit: - kfree(reply); - return ret; -} - -/** - * sof_ipc_probe_points_info - retrieve list of active probe points - * @sdev: SOF sound device - * @desc: Returned list of active probes - * @num_desc: Returned count of active probes - * - * Host sends PROBE_POINT_INFO request to obtain list of active probe - * points, valid for disconnection when given probe is no longer - * required. - */ -int sof_ipc_probe_points_info(struct snd_sof_dev *sdev, - struct sof_probe_point_desc **desc, - size_t *num_desc) -{ - return sof_ipc_probe_info(sdev, SOF_IPC_PROBE_POINT_INFO, - (void **)desc, num_desc); -} -EXPORT_SYMBOL(sof_ipc_probe_points_info); - -/** - * sof_ipc_probe_points_add - connect specified probes - * @sdev: SOF sound device - * @desc: List of probe points to connect - * @num_desc: Number of elements in @desc - * - * Dynamically connects to provided set of endpoints. Immediately - * after connection is established, host must be prepared to - * transfer data from or to target stream given the probing purpose. - * - * Each probe point should be removed using PROBE_POINT_REMOVE - * request when no longer needed. - */ -int sof_ipc_probe_points_add(struct snd_sof_dev *sdev, - struct sof_probe_point_desc *desc, size_t num_desc) -{ - struct sof_ipc_probe_point_add_params *msg; - struct sof_ipc_reply reply; - size_t size = struct_size(msg, desc, num_desc); - int ret; - - msg = kmalloc(size, GFP_KERNEL); - if (!msg) - return -ENOMEM; - msg->hdr.size = size; - msg->num_elems = num_desc; - msg->hdr.cmd = SOF_IPC_GLB_PROBE | SOF_IPC_PROBE_POINT_ADD; - memcpy(&msg->desc[0], desc, size - sizeof(*msg)); - - ret = sof_ipc_tx_message(sdev->ipc, msg->hdr.cmd, msg, msg->hdr.size, - &reply, sizeof(reply)); - kfree(msg); - return ret; -} -EXPORT_SYMBOL(sof_ipc_probe_points_add); - -/** - * sof_ipc_probe_points_remove - disconnect specified probes - * @sdev: SOF sound device - * @buffer_id: List of probe points to disconnect - * @num_buffer_id: Number of elements in @desc - * - * Removes previously connected probes from list of active probe - * points and frees all resources on DSP side. - */ -int sof_ipc_probe_points_remove(struct snd_sof_dev *sdev, - unsigned int *buffer_id, size_t num_buffer_id) -{ - struct sof_ipc_probe_point_remove_params *msg; - struct sof_ipc_reply reply; - size_t size = struct_size(msg, buffer_id, num_buffer_id); - int ret; - - msg = kmalloc(size, GFP_KERNEL); - if (!msg) - return -ENOMEM; - msg->hdr.size = size; - msg->num_elems = num_buffer_id; - msg->hdr.cmd = SOF_IPC_GLB_PROBE | SOF_IPC_PROBE_POINT_REMOVE; - memcpy(&msg->buffer_id[0], buffer_id, size - sizeof(*msg)); - - ret = sof_ipc_tx_message(sdev->ipc, msg->hdr.cmd, msg, msg->hdr.size, - &reply, sizeof(reply)); - kfree(msg); - return ret; -} -EXPORT_SYMBOL(sof_ipc_probe_points_remove); - -static int sof_probe_compr_startup(struct snd_compr_stream *cstream, - struct snd_soc_dai *dai) -{ - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(dai->component); - int ret; - - ret = snd_sof_probe_compr_assign(sdev, cstream, dai); - if (ret < 0) { - dev_err(dai->dev, "Failed to assign probe stream: %d\n", ret); - return ret; - } - - sdev->extractor_stream_tag = ret; - return 0; -} - -static int sof_probe_compr_shutdown(struct snd_compr_stream *cstream, - struct snd_soc_dai *dai) -{ - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(dai->component); - struct sof_probe_point_desc *desc; - size_t num_desc; - int i, ret; - - /* disconnect all probe points */ - ret = sof_ipc_probe_points_info(sdev, &desc, &num_desc); - if (ret < 0) { - dev_err(dai->dev, "Failed to get probe points: %d\n", ret); - goto exit; - } - - for (i = 0; i < num_desc; i++) - sof_ipc_probe_points_remove(sdev, &desc[i].buffer_id, 1); - kfree(desc); - -exit: - ret = sof_ipc_probe_deinit(sdev); - if (ret < 0) - dev_err(dai->dev, "Failed to deinit probe: %d\n", ret); - - sdev->extractor_stream_tag = SOF_PROBE_INVALID_NODE_ID; - snd_compr_free_pages(cstream); - - return snd_sof_probe_compr_free(sdev, cstream, dai); -} - -static int sof_probe_compr_set_params(struct snd_compr_stream *cstream, - struct snd_compr_params *params, - struct snd_soc_dai *dai) -{ - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(dai->component); - struct snd_compr_runtime *rtd = cstream->runtime; - int ret; - - cstream->dma_buffer.dev.type = SNDRV_DMA_TYPE_DEV_SG; - cstream->dma_buffer.dev.dev = sdev->dev; - ret = snd_compr_malloc_pages(cstream, rtd->buffer_size); - if (ret < 0) - return ret; - - ret = snd_sof_probe_compr_set_params(sdev, cstream, params, dai); - if (ret < 0) - return ret; - - ret = sof_ipc_probe_init(sdev, sdev->extractor_stream_tag, - rtd->dma_bytes); - if (ret < 0) { - dev_err(dai->dev, "Failed to init probe: %d\n", ret); - return ret; - } - - return 0; -} - -static int sof_probe_compr_trigger(struct snd_compr_stream *cstream, int cmd, - struct snd_soc_dai *dai) -{ - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(dai->component); - - return snd_sof_probe_compr_trigger(sdev, cstream, cmd, dai); -} - -static int sof_probe_compr_pointer(struct snd_compr_stream *cstream, - struct snd_compr_tstamp *tstamp, - struct snd_soc_dai *dai) -{ - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(dai->component); - - return snd_sof_probe_compr_pointer(sdev, cstream, tstamp, dai); -} - -struct snd_soc_cdai_ops sof_probe_compr_ops = { - .startup = sof_probe_compr_startup, - .shutdown = sof_probe_compr_shutdown, - .set_params = sof_probe_compr_set_params, - .trigger = sof_probe_compr_trigger, - .pointer = sof_probe_compr_pointer, -}; -EXPORT_SYMBOL(sof_probe_compr_ops); - -static int sof_probe_compr_copy(struct snd_soc_component *component, - 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; -} - -const struct snd_compress_ops sof_probe_compressed_ops = { - .copy = sof_probe_compr_copy, -}; -EXPORT_SYMBOL(sof_probe_compressed_ops); diff --git a/sound/soc/sof/sof-probes.h b/sound/soc/sof/sof-probes.h deleted file mode 100644 index 35e1dd8d9e0383..00000000000000 --- a/sound/soc/sof/sof-probes.h +++ /dev/null @@ -1,38 +0,0 @@ -/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */ -/* - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * Copyright(c) 2019-2021 Intel Corporation. All rights reserved. - * Author: Cezary Rojewski - */ - -#ifndef __SOF_PROBES_H -#define __SOF_PROBES_H - -#include -#include - -struct snd_sof_dev; - -#define SOF_PROBE_INVALID_NODE_ID UINT_MAX - -struct sof_probe_point_desc { - unsigned int buffer_id; - unsigned int purpose; - unsigned int stream_tag; -} __packed; - -int sof_ipc_probe_points_info(struct snd_sof_dev *sdev, - struct sof_probe_point_desc **desc, - size_t *num_desc); -int sof_ipc_probe_points_add(struct snd_sof_dev *sdev, - struct sof_probe_point_desc *desc, - size_t num_desc); -int sof_ipc_probe_points_remove(struct snd_sof_dev *sdev, - unsigned int *buffer_id, size_t num_buffer_id); - -extern struct snd_soc_cdai_ops sof_probe_compr_ops; -extern const struct snd_compress_ops sof_probe_compressed_ops; - -#endif From d6a888d245d38a04f847059ea8e05b59ef2fde7a Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Mon, 9 Aug 2021 13:31:45 +0300 Subject: [PATCH 20/32] ASoC: SOF: sof-client-probes: Add module parameter to enable probes support Keep the probes support disabled by default and require module parameter to enable it explicitly as the probes usage is meant for developers only. This will avoid confusing users with the existence of a probes card with a compressed capture. Signed-off-by: Peter Ujfalusi --- sound/soc/sof/sof-client-probes.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/sound/soc/sof/sof-client-probes.c b/sound/soc/sof/sof-client-probes.c index 04b0e028ac5c53..20561e7bd032c9 100644 --- a/sound/soc/sof/sof-client-probes.c +++ b/sound/soc/sof/sof-client-probes.c @@ -23,6 +23,10 @@ #define SOF_PROBES_INVALID_NODE_ID UINT_MAX +static bool __read_mostly sof_probes_enabled; +module_param_named(enable, sof_probes_enabled, bool, 0444); +MODULE_PARM_DESC(enable, "Enable SOF probes support"); + struct sof_probes_priv { struct dentry *dfs_points; struct dentry *dfs_points_remove; @@ -674,6 +678,10 @@ static int sof_probes_client_probe(struct auxiliary_device *auxdev, struct snd_soc_dai_link *links; int ret; + /* do not set up the probes support if it is not enabled */ + if (!sof_probes_enabled) + return -ENXIO; + if (!dev->platform_data) { dev_err(dev, "error: missing platform data\n"); return -ENODEV; @@ -771,6 +779,9 @@ static void sof_probes_client_remove(struct auxiliary_device *auxdev) struct sof_client_dev *cdev = auxiliary_dev_to_sof_client_dev(auxdev); struct sof_probes_priv *priv = cdev->data; + if (!sof_probes_enabled) + return; + pm_runtime_disable(&auxdev->dev); debugfs_remove(priv->dfs_points); debugfs_remove(priv->dfs_points_remove); From 6677eec0bf4059ada5293b57bf9933ee6a58de8c Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 22 Jul 2021 10:22:38 +0300 Subject: [PATCH 21/32] ASoC: SOF: sof-client-probes: Protection against removal while in use Make sure that the core (the device driver for the DSP) can not be removed while the compress stream is running. Signed-off-by: Peter Ujfalusi --- sound/soc/sof/sof-client-probes.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/sound/soc/sof/sof-client-probes.c b/sound/soc/sof/sof-client-probes.c index 20561e7bd032c9..02ec888cbb2331 100644 --- a/sound/soc/sof/sof-client-probes.c +++ b/sound/soc/sof/sof-client-probes.c @@ -262,10 +262,15 @@ static int sof_probes_compr_startup(struct snd_compr_stream *cstream, const struct sof_probes_host_ops *ops = priv->host_ops; int ret; + ret = sof_client_core_module_get(cdev); + if (ret) + return ret; + ret = ops->assign(cdev, cstream, dai, &priv->extractor_stream_tag); if (ret) { dev_err(dai->dev, "Failed to assign probe stream: %d\n", ret); priv->extractor_stream_tag = SOF_PROBES_INVALID_NODE_ID; + sof_client_core_module_put(cdev); } return ret; @@ -301,7 +306,11 @@ static int sof_probes_compr_shutdown(struct snd_compr_stream *cstream, priv->extractor_stream_tag = SOF_PROBES_INVALID_NODE_ID; snd_compr_free_pages(cstream); - return ops->free(cdev, cstream, dai); + ret = ops->free(cdev, cstream, dai); + + sof_client_core_module_put(cdev); + + return ret; } static int sof_probes_compr_set_params(struct snd_compr_stream *cstream, From 68d6c635c21d8e672e14cd09086b3294ab319f2c Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Fri, 20 Aug 2021 14:04:13 +0300 Subject: [PATCH 22/32] ASoC: SOF: sof-client-probes: Block the capture if the firmware is crashed Do not allow the compressed stream to be started if the firmware is in crashed state. Signed-off-by: Peter Ujfalusi --- sound/soc/sof/sof-client-probes.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sound/soc/sof/sof-client-probes.c b/sound/soc/sof/sof-client-probes.c index 02ec888cbb2331..a071a28ef2f943 100644 --- a/sound/soc/sof/sof-client-probes.c +++ b/sound/soc/sof/sof-client-probes.c @@ -262,6 +262,9 @@ static int sof_probes_compr_startup(struct snd_compr_stream *cstream, const struct sof_probes_host_ops *ops = priv->host_ops; int ret; + if (sof_client_get_fw_state(cdev) == SOF_FW_CRASHED) + return -ENODEV; + ret = sof_client_core_module_get(cdev); if (ret) return ret; From 39459f79e1bda76ffbc02b2add2e5adfc946d1f6 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Tue, 6 Jul 2021 16:55:11 +0300 Subject: [PATCH 23/32] ASoC: SOF: Add optional SOF client for dma-trace support Add a new client driver to implement the dma-trace functionality outside of the SOF core. This is optional for now, if the dma-trace client is enabled via the SND_SOC_SOF_HDA_DMA_TRACE kconfig option then the core's built in implementation is force disabled. Signed-off-by: Peter Ujfalusi --- sound/soc/sof/Kconfig | 10 + sound/soc/sof/Makefile | 2 + sound/soc/sof/core.c | 5 + sound/soc/sof/intel/Kconfig | 15 + sound/soc/sof/intel/Makefile | 9 +- sound/soc/sof/intel/apl.c | 9 +- sound/soc/sof/intel/atom.c | 14 + sound/soc/sof/intel/atom.h | 3 + sound/soc/sof/intel/bdw.c | 16 + sound/soc/sof/intel/byt.c | 8 + sound/soc/sof/intel/cnl.c | 9 +- sound/soc/sof/intel/hda-trace.c | 78 ++++ sound/soc/sof/intel/hda.h | 14 + sound/soc/sof/intel/icl.c | 9 +- sound/soc/sof/intel/tgl.c | 9 +- sound/soc/sof/sof-client-dma-trace.c | 647 +++++++++++++++++++++++++++ sound/soc/sof/sof-client-dma-trace.h | 21 + 17 files changed, 870 insertions(+), 8 deletions(-) create mode 100644 sound/soc/sof/sof-client-dma-trace.c create mode 100644 sound/soc/sof/sof-client-dma-trace.h diff --git a/sound/soc/sof/Kconfig b/sound/soc/sof/Kconfig index 51b0ad815f964e..4639caedebe9a7 100644 --- a/sound/soc/sof/Kconfig +++ b/sound/soc/sof/Kconfig @@ -56,6 +56,16 @@ config SND_SOC_SOF_DEBUG_PROBES This option is not user-selectable but automagically handled by 'select' statements at a higher level. +config SND_SOC_SOF_DEBUG_DMA_TRACE + tristate + select SND_SOC_SOF_CLIENT + help + This option enables the dma-trace feature that can be used to + gather trace information in close to real time from firmware, backed + by DMA. + This option is not user-selectable but automagically handled by + 'select' statements at a higher level. + config SND_SOC_SOF_CLIENT tristate select AUXILIARY_BUS diff --git a/sound/soc/sof/Makefile b/sound/soc/sof/Makefile index 95b41b181b3cea..db411405f3f0c3 100644 --- a/sound/soc/sof/Makefile +++ b/sound/soc/sof/Makefile @@ -10,6 +10,7 @@ snd-sof-of-objs := sof-of-dev.o snd-sof-ipc-test-objs := sof-client-ipc-test.o snd-sof-probes-objs := sof-client-probes.o +snd-sof-dma-trace-objs := sof-client-dma-trace.o snd-sof-nocodec-objs := nocodec.o @@ -26,6 +27,7 @@ obj-$(CONFIG_SND_SOC_SOF_PCI_DEV) += snd-sof-pci.o obj-$(CONFIG_SND_SOC_SOF_DEBUG_IPC_FLOOD_TEST) += snd-sof-ipc-test.o obj-$(CONFIG_SND_SOC_SOF_DEBUG_PROBES) += snd-sof-probes.o +obj-$(CONFIG_SND_SOC_SOF_DEBUG_DMA_TRACE) += snd-sof-dma-trace.o obj-$(CONFIG_SND_SOC_SOF_INTEL_TOPLEVEL) += intel/ obj-$(CONFIG_SND_SOC_SOF_IMX_TOPLEVEL) += imx/ diff --git a/sound/soc/sof/core.c b/sound/soc/sof/core.c index 96d79fc4a9352d..877a60ddfcf49c 100644 --- a/sound/soc/sof/core.c +++ b/sound/soc/sof/core.c @@ -216,6 +216,10 @@ static int sof_probe_continue(struct snd_sof_dev *sdev) goto fw_run_err; } +#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_DMA_TRACE) + /* trace support via SOF client */ + sdev->dtrace_is_supported = false; +#else if (sof_core_debug & SOF_DBG_ENABLE_TRACE) { sdev->dtrace_is_supported = true; @@ -230,6 +234,7 @@ static int sof_probe_continue(struct snd_sof_dev *sdev) } else { dev_dbg(sdev->dev, "SOF firmware trace disabled\n"); } +#endif /* hereafter all FW boot flows are for PM reasons */ sdev->first_boot = false; diff --git a/sound/soc/sof/intel/Kconfig b/sound/soc/sof/intel/Kconfig index b53f216d4ecc32..b1f1b22be7a232 100644 --- a/sound/soc/sof/intel/Kconfig +++ b/sound/soc/sof/intel/Kconfig @@ -15,6 +15,20 @@ config SND_SOC_SOF_INTEL_HIFI_EP_IPC This option is not user-selectable but automagically handled by 'select' statements at a higher level. +#config SND_SOC_SOF_HDA_DMA_TRACE +# tristate +# select SND_SOC_SOF_DEBUG_DMA_TRACE +# help +# This option is not user-selectable but automagically handled by +# 'select' statements at a higher level. + +config SND_SOC_SOF_HDA_DMA_TRACE + bool "Enable dma-trace for HDA" + select SND_SOC_SOF_DEBUG_DMA_TRACE + help + This option is not user-selectable but automagically handled by + 'select' statements at a higher level. + config SND_SOC_SOF_INTEL_ATOM_HIFI_EP tristate select SND_SOC_SOF_INTEL_COMMON @@ -31,6 +45,7 @@ config SND_SOC_SOF_INTEL_COMMON select SND_SOC_INTEL_MACH select SND_SOC_ACPI if ACPI select SND_INTEL_DSP_CONFIG +# select SND_SOC_SOF_HDA_DMA_TRACE help This option is not user-selectable but automagically handled by 'select' statements at a higher level. diff --git a/sound/soc/sof/intel/Makefile b/sound/soc/sof/intel/Makefile index 1f473d4d8416e4..6be605973ccb7d 100644 --- a/sound/soc/sof/intel/Makefile +++ b/sound/soc/sof/intel/Makefile @@ -3,11 +3,12 @@ snd-sof-acpi-intel-byt-objs := byt.o snd-sof-acpi-intel-bdw-objs := bdw.o -snd-sof-intel-hda-common-objs := hda.o hda-loader.o hda-stream.o hda-trace.o \ - hda-dsp.o hda-ipc.o hda-ctrl.o hda-pcm.o \ - hda-dai.o hda-bus.o \ - apl.o cnl.o tgl.o icl.o +snd-sof-intel-hda-common-objs := hda.o hda-loader.o hda-stream.o hda-dsp.o \ + hda-ipc.o hda-ctrl.o hda-pcm.o hda-dai.o \ + hda-bus.o apl.o cnl.o tgl.o icl.o snd-sof-intel-hda-common-$(CONFIG_SND_SOC_SOF_HDA_PROBES) += hda-probes.o +# snd-sof-intel-hda-common-$(CONFIG_SND_SOC_SOF_HDA_DMA_TRACE) += hda-trace.o +snd-sof-intel-hda-common-objs += hda-trace.o snd-sof-intel-hda-objs := hda-codec.o diff --git a/sound/soc/sof/intel/apl.c b/sound/soc/sof/intel/apl.c index 5630d9b7fb66be..2b403a92c936d3 100644 --- a/sound/soc/sof/intel/apl.c +++ b/sound/soc/sof/intel/apl.c @@ -27,12 +27,19 @@ static const struct snd_sof_debugfs_map apl_dsp_debugfs[] = { static int apl_register_clients(struct snd_sof_dev *sdev) { - return hda_probes_register(sdev); + int ret; + + ret = hda_probes_register(sdev); + if (ret) + return ret; + + return hda_dma_trace_register(sdev); } static void apl_unregister_clients(struct snd_sof_dev *sdev) { hda_probes_unregister(sdev); + hda_dma_trace_unregister(sdev); } /* apollolake ops */ diff --git a/sound/soc/sof/intel/atom.c b/sound/soc/sof/intel/atom.c index 74c630bb984718..88f7d448e3de2b 100644 --- a/sound/soc/sof/intel/atom.c +++ b/sound/soc/sof/intel/atom.c @@ -23,6 +23,7 @@ #include "atom.h" #include "../sof-acpi-dev.h" #include "../sof-audio.h" +#include "../sof-client-dma-trace.h" #include "../../intel/common/soc-intel-quirks.h" static void atom_host_done(struct snd_sof_dev *sdev); @@ -457,4 +458,17 @@ void atom_set_mach_params(const struct snd_soc_acpi_mach *mach, } EXPORT_SYMBOL_NS(atom_set_mach_params, SND_SOC_SOF_INTEL_ATOM_HIFI_EP); +int atom_dma_trace_register(struct snd_sof_dev *sdev) +{ + return sof_client_dev_register(sdev, "atom-dma-trace", 0, NULL, 0); +} +EXPORT_SYMBOL_NS(atom_dma_trace_register, SND_SOC_SOF_INTEL_ATOM_HIFI_EP); + +void atom_dma_trace_unregister(struct snd_sof_dev *sdev) +{ + sof_client_dev_unregister(sdev, "atom-dma-trace", 0); +} +EXPORT_SYMBOL_NS(atom_dma_trace_unregister, SND_SOC_SOF_INTEL_ATOM_HIFI_EP); + +MODULE_IMPORT_NS(SND_SOC_SOF_CLIENT); MODULE_LICENSE("Dual BSD/GPL"); diff --git a/sound/soc/sof/intel/atom.h b/sound/soc/sof/intel/atom.h index 96a462c7a2e551..07f6f7cdb54f61 100644 --- a/sound/soc/sof/intel/atom.h +++ b/sound/soc/sof/intel/atom.h @@ -71,4 +71,7 @@ void atom_set_mach_params(const struct snd_soc_acpi_mach *mach, extern struct snd_soc_dai_driver atom_dai[]; +int atom_dma_trace_register(struct snd_sof_dev *sdev); +void atom_dma_trace_unregister(struct snd_sof_dev *sdev); + #endif diff --git a/sound/soc/sof/intel/bdw.c b/sound/soc/sof/intel/bdw.c index 839f0efbab64f6..8e7f76b643ae72 100644 --- a/sound/soc/sof/intel/bdw.c +++ b/sound/soc/sof/intel/bdw.c @@ -22,6 +22,7 @@ #include "shim.h" #include "../sof-acpi-dev.h" #include "../sof-audio.h" +#include "../sof-client-dma-trace.h" /* BARs */ #define BDW_DSP_BAR 0 @@ -580,6 +581,16 @@ static void bdw_set_mach_params(const struct snd_soc_acpi_mach *mach, mach_params->dai_drivers = desc->ops->drv; } +static int bdw_dma_trace_register(struct snd_sof_dev *sdev) +{ + return sof_client_dev_register(sdev, "bdw-dma-trace", 0, NULL, 0); +} + +static void bdw_dma_trace_unregister(struct snd_sof_dev *sdev) +{ + sof_client_dev_unregister(sdev, "bdw-dma-trace", 0); +} + /* Broadwell DAIs */ static struct snd_soc_dai_driver bdw_dai[] = { { @@ -660,6 +671,10 @@ static const struct snd_sof_dsp_ops sof_bdw_ops = { /*Firmware loading */ .load_firmware = snd_sof_load_firmware_memcpy, + /* client ops */ + .register_ipc_clients = bdw_dma_trace_register, + .unregister_ipc_clients = bdw_dma_trace_unregister, + /* DAI drivers */ .drv = bdw_dai, .num_drv = ARRAY_SIZE(bdw_dai), @@ -739,3 +754,4 @@ MODULE_LICENSE("Dual BSD/GPL"); MODULE_IMPORT_NS(SND_SOC_SOF_INTEL_HIFI_EP_IPC); MODULE_IMPORT_NS(SND_SOC_SOF_XTENSA); MODULE_IMPORT_NS(SND_SOC_SOF_ACPI_DEV); +MODULE_IMPORT_NS(SND_SOC_SOF_CLIENT); diff --git a/sound/soc/sof/intel/byt.c b/sound/soc/sof/intel/byt.c index dcfeaedb8fd5ff..f66b31e973de61 100644 --- a/sound/soc/sof/intel/byt.c +++ b/sound/soc/sof/intel/byt.c @@ -278,6 +278,10 @@ static const struct snd_sof_dsp_ops sof_byt_ops = { .suspend = byt_suspend, .resume = byt_resume, + /* client ops */ + .register_ipc_clients = atom_dma_trace_register, + .unregister_ipc_clients = atom_dma_trace_unregister, + /* DAI drivers */ .drv = atom_dai, .num_drv = 3, /* we have only 3 SSPs on byt*/ @@ -360,6 +364,10 @@ static const struct snd_sof_dsp_ops sof_cht_ops = { .suspend = byt_suspend, .resume = byt_resume, + /* client ops */ + .register_ipc_clients = atom_dma_trace_register, + .unregister_ipc_clients = atom_dma_trace_unregister, + /* DAI drivers */ .drv = atom_dai, /* all 6 SSPs may be available for cherrytrail */ diff --git a/sound/soc/sof/intel/cnl.c b/sound/soc/sof/intel/cnl.c index aeb4a14c738032..468380efc79000 100644 --- a/sound/soc/sof/intel/cnl.c +++ b/sound/soc/sof/intel/cnl.c @@ -232,12 +232,19 @@ void cnl_ipc_dump(struct snd_sof_dev *sdev) static int cnl_register_clients(struct snd_sof_dev *sdev) { - return hda_probes_register(sdev); + int ret; + + ret = hda_probes_register(sdev); + if (ret) + return ret; + + return hda_dma_trace_register(sdev); } static void cnl_unregister_clients(struct snd_sof_dev *sdev) { hda_probes_unregister(sdev); + hda_dma_trace_unregister(sdev); } /* cannonlake ops */ diff --git a/sound/soc/sof/intel/hda-trace.c b/sound/soc/sof/intel/hda-trace.c index c5dc833b57b8fc..af723ff3b8076b 100644 --- a/sound/soc/sof/intel/hda-trace.c +++ b/sound/soc/sof/intel/hda-trace.c @@ -17,6 +17,8 @@ #include #include "../ops.h" +#include "../sof-client-dma-trace.h" +#include "../sof-client.h" #include "hda.h" static int hda_dsp_trace_prepare(struct snd_sof_dev *sdev, struct snd_dma_buffer *dmab) @@ -91,3 +93,79 @@ int hda_dsp_trace_trigger(struct snd_sof_dev *sdev, int cmd) return hda_dsp_stream_trigger(sdev, hda->dtrace_stream, cmd); } + +#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_DMA_TRACE) +static int hda_dma_trace_init(struct sof_client_dev *cdev, + struct snd_dma_buffer *dmab, u32 *stream_tag) +{ + struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev); + struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; + int ret; + + hda->dtrace_stream = hda_dsp_stream_get(sdev, SNDRV_PCM_STREAM_CAPTURE, + SOF_HDA_STREAM_DMI_L1_COMPATIBLE); + + if (!hda->dtrace_stream) { + dev_err(sdev->dev, + "error: no available capture stream for DMA trace\n"); + return -ENODEV; + } + + *stream_tag = hda->dtrace_stream->hstream.stream_tag; + + /* + * initialize capture stream, set BDL address and return corresponding + * stream tag which will be sent to the firmware by IPC message. + */ + ret = hda_dsp_trace_prepare(sdev, dmab); + if (ret < 0) { + dev_err(sdev->dev, "error: hdac trace init failed: %d\n", ret); + hda_dsp_stream_put(sdev, SNDRV_PCM_STREAM_CAPTURE, *stream_tag); + hda->dtrace_stream = NULL; + *stream_tag = 0; + } + + return ret; +} + +static int hda_dma_trace_release(struct sof_client_dev *cdev) +{ + struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev); + + return hda_dsp_trace_release(sdev); +} + +static int hda_dma_trace_start(struct sof_client_dev *cdev) +{ + struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev); + + return hda_dsp_trace_trigger(sdev, SNDRV_PCM_TRIGGER_START); +} + +static int hda_dma_trace_stop(struct sof_client_dev *cdev) +{ + struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev); + + return hda_dsp_trace_trigger(sdev, SNDRV_PCM_TRIGGER_STOP); +} + +static const struct sof_dma_trace_host_ops hda_dma_trace_ops = { + .init = hda_dma_trace_init, + .release = hda_dma_trace_release, + .start = hda_dma_trace_start, + .stop = hda_dma_trace_stop, +}; + +int hda_dma_trace_register(struct snd_sof_dev *sdev) +{ + return sof_client_dev_register(sdev, "hda-dma-trace", 0, &hda_dma_trace_ops, + sizeof(hda_dma_trace_ops)); +} + +void hda_dma_trace_unregister(struct snd_sof_dev *sdev) +{ + sof_client_dev_unregister(sdev, "hda-dma-trace", 0); +} + +MODULE_IMPORT_NS(SND_SOC_SOF_CLIENT); +#endif diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h index 07bdf716a37cc3..0e7ebcec5c2d42 100644 --- a/sound/soc/sof/intel/hda.h +++ b/sound/soc/sof/intel/hda.h @@ -713,6 +713,20 @@ static inline void hda_probes_unregister(struct snd_sof_dev *sdev) } #endif /* CONFIG_SND_SOC_SOF_HDA_PROBES */ +#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_DMA_TRACE) +int hda_dma_trace_register(struct snd_sof_dev *sdev); +void hda_dma_trace_unregister(struct snd_sof_dev *sdev); +#else +static inline int hda_dma_trace_register(struct snd_sof_dev *sdev) +{ + return 0; +} + +static inline void hda_dma_trace_unregister(struct snd_sof_dev *sdev) +{ +} +#endif /* CONFIG_SND_SOC_SOF_HDA_DMA_TRACE */ + /* machine driver select */ void hda_machine_select(struct snd_sof_dev *sdev); void hda_set_mach_params(const struct snd_soc_acpi_mach *mach, diff --git a/sound/soc/sof/intel/icl.c b/sound/soc/sof/intel/icl.c index 04c3f9b98b3b41..713e6d4a3f3234 100644 --- a/sound/soc/sof/intel/icl.c +++ b/sound/soc/sof/intel/icl.c @@ -90,12 +90,19 @@ static int icl_dsp_post_fw_run(struct snd_sof_dev *sdev) static int icl_register_clients(struct snd_sof_dev *sdev) { - return hda_probes_register(sdev); + int ret; + + ret = hda_probes_register(sdev); + if (ret) + return ret; + + return hda_dma_trace_register(sdev); } static void icl_unregister_clients(struct snd_sof_dev *sdev) { hda_probes_unregister(sdev); + hda_dma_trace_unregister(sdev); } /* Icelake ops */ diff --git a/sound/soc/sof/intel/tgl.c b/sound/soc/sof/intel/tgl.c index 9924a1c474fd4f..25421730972806 100644 --- a/sound/soc/sof/intel/tgl.c +++ b/sound/soc/sof/intel/tgl.c @@ -62,12 +62,19 @@ static int tgl_dsp_core_put(struct snd_sof_dev *sdev, int core) static int tgl_register_clients(struct snd_sof_dev *sdev) { - return hda_probes_register(sdev); + int ret; + + ret = hda_probes_register(sdev); + if (ret) + return ret; + + return hda_dma_trace_register(sdev); } static void tgl_unregister_clients(struct snd_sof_dev *sdev) { hda_probes_unregister(sdev); + hda_dma_trace_unregister(sdev); } /* Tigerlake ops */ diff --git a/sound/soc/sof/sof-client-dma-trace.c b/sound/soc/sof/sof-client-dma-trace.c new file mode 100644 index 00000000000000..f906c3b9e8c454 --- /dev/null +++ b/sound/soc/sof/sof-client-dma-trace.c @@ -0,0 +1,647 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// Copyright(c) 2018-2021 Intel Corporation. All rights reserved. +// +// Author: Liam Girdwood +// +// SOF client version: +// Peter Ujfalusi +// + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "sof-client.h" +#include "sof-client-dma-trace.h" +#include "sof-utils.h" + +/* DMA buffer size for trace */ +#define SOF_DTRACE_BUF_SIZE (PAGE_SIZE * 16) +#define SOF_DTRACE_SUSPEND_DELAY_MS 3000 + +#define TRACE_FILTER_ELEMENTS_PER_ENTRY 4 +#define TRACE_FILTER_MAX_CONFIG_STRING_LENGTH 1024 + +struct sof_dtrace_priv { + const struct sof_dma_trace_host_ops *host_ops; + wait_queue_head_t dtrace_sleep; + struct snd_dma_buffer dmatb; + struct snd_dma_buffer dmatp; + struct dentry *dfs_trace; + struct dentry *dfs_filter; + struct device *dev; + + int dtrace_pages; + u32 host_offset; + bool dtrace_is_enabled; + bool dtrace_error; + bool dtrace_draining; +}; + +static int trace_filter_append_elem(u32 key, u32 value, + struct sof_ipc_trace_filter_elem *elem_list, + int capacity, int *counter) +{ + if (*counter >= capacity) + return -ENOMEM; + + elem_list[*counter].key = key; + elem_list[*counter].value = value; + ++*counter; + + return 0; +} + +static int trace_filter_parse_entry(struct sof_client_dev *cdev, const char *line, + struct sof_ipc_trace_filter_elem *elem, + int capacity, int *counter) +{ + int log_level, pipe_id, comp_id, read, ret; + struct sof_dtrace_priv *priv = cdev->data; + int len = strlen(line); + int cnt = *counter; + u32 uuid_id; + + /* ignore empty content */ + ret = sscanf(line, " %n", &read); + if (!ret && read == len) + return len; + + ret = sscanf(line, " %d %x %d %d %n", &log_level, &uuid_id, &pipe_id, &comp_id, &read); + if (ret != TRACE_FILTER_ELEMENTS_PER_ENTRY || read != len) { + dev_err(priv->dev, "invalid trace filter entry '%s'\n", line); + return -EINVAL; + } + + if (uuid_id > 0) { + ret = trace_filter_append_elem(SOF_IPC_TRACE_FILTER_ELEM_BY_UUID, + uuid_id, elem, capacity, &cnt); + if (ret) + return ret; + } + if (pipe_id >= 0) { + ret = trace_filter_append_elem(SOF_IPC_TRACE_FILTER_ELEM_BY_PIPE, + pipe_id, elem, capacity, &cnt); + if (ret) + return ret; + } + if (comp_id >= 0) { + ret = trace_filter_append_elem(SOF_IPC_TRACE_FILTER_ELEM_BY_COMP, + comp_id, elem, capacity, &cnt); + if (ret) + return ret; + } + + ret = trace_filter_append_elem(SOF_IPC_TRACE_FILTER_ELEM_SET_LEVEL | + SOF_IPC_TRACE_FILTER_ELEM_FIN, + log_level, elem, capacity, &cnt); + if (ret) + return ret; + + /* update counter only when parsing whole entry passed */ + *counter = cnt; + + return len; +} + +static int trace_filter_parse(struct sof_client_dev *cdev, char *string, + int *out_elem_cnt, + struct sof_ipc_trace_filter_elem **out) +{ + struct sof_dtrace_priv *priv = cdev->data; + static const char entry_delimiter[] = ";"; + char *entry = string; + int capacity = 0; + int entry_len; + int cnt = 0; + + /* + * Each entry contains at least 1, up to TRACE_FILTER_ELEMENTS_PER_ENTRY + * IPC elements, depending on content. Calculate IPC elements capacity + * for the input string where each element is set. + */ + while (entry) { + capacity += TRACE_FILTER_ELEMENTS_PER_ENTRY; + entry = strchr(entry + 1, entry_delimiter[0]); + } + *out = kmalloc(capacity * sizeof(**out), GFP_KERNEL); + if (!*out) + return -ENOMEM; + + /* split input string by ';', and parse each entry separately in trace_filter_parse_entry */ + while ((entry = strsep(&string, entry_delimiter))) { + entry_len = trace_filter_parse_entry(cdev, entry, *out, capacity, &cnt); + if (entry_len < 0) { + dev_err(priv->dev, "%s failed for '%s', %d\n", __func__, entry, + entry_len); + return -EINVAL; + } + } + + *out_elem_cnt = cnt; + + return 0; +} + +static int sof_ipc_trace_update_filter(struct sof_client_dev *cdev, int num_elems, + struct sof_ipc_trace_filter_elem *elems) +{ + struct sof_ipc_trace_filter *msg; + struct sof_ipc_reply reply; + size_t size; + int ret; + + size = struct_size(msg, elems, num_elems); + if (size > SOF_IPC_MSG_MAX_SIZE) + return -EINVAL; + + msg = kmalloc(size, GFP_KERNEL); + if (!msg) + return -ENOMEM; + + msg->hdr.size = size; + msg->hdr.cmd = SOF_IPC_GLB_TRACE_MSG | SOF_IPC_TRACE_FILTER_UPDATE; + msg->elem_cnt = num_elems; + memcpy(&msg->elems[0], elems, num_elems * sizeof(*elems)); + + ret = sof_client_ipc_tx_message(cdev, msg, &reply, sizeof(reply)); + + kfree(msg); + + return ret ? ret : reply.error; +} + +static ssize_t sof_dfsentry_trace_filter_write(struct file *file, const char __user *from, + size_t count, loff_t *ppos) +{ + struct sof_client_dev *cdev = file->private_data; + struct sof_ipc_trace_filter_elem *elems = NULL; + struct sof_dtrace_priv *priv = cdev->data; + int num_elems, ret; + loff_t pos = 0; + char *string; + + if (!priv->dtrace_is_enabled) { + dev_info(priv->dev, "filter can not be updated while suspended\n"); + return -EBUSY; + } + + if (count > TRACE_FILTER_MAX_CONFIG_STRING_LENGTH) { + dev_err(priv->dev, "%s too long input, %zu > %d\n", __func__, count, + TRACE_FILTER_MAX_CONFIG_STRING_LENGTH); + return -EINVAL; + } + + string = kmalloc(count + 1, GFP_KERNEL); + if (!string) + return -ENOMEM; + + /* assert null termination */ + string[count] = 0; + ret = simple_write_to_buffer(string, count, &pos, from, count); + if (ret < 0) + goto error; + + ret = trace_filter_parse(cdev, string, &num_elems, &elems); + if (ret < 0) + goto error; + + if (num_elems) { + ret = sof_ipc_trace_update_filter(cdev, num_elems, elems); + if (ret < 0) { + dev_err(priv->dev, "filter update failed: %d\n", ret); + goto error; + } + } + ret = count; + +error: + kfree(string); + kfree(elems); + return ret; +} + +static const struct file_operations sof_dtrace_filter_fops = { + .open = simple_open, + .write = sof_dfsentry_trace_filter_write, + .llseek = default_llseek, + + .owner = THIS_MODULE, +}; + +static size_t sof_trace_avail(struct sof_client_dev *cdev, + loff_t pos, size_t buffer_size) +{ + struct sof_dtrace_priv *priv = cdev->data; + loff_t host_offset = READ_ONCE(priv->host_offset); + + /* + * If host offset is less than local pos, it means write pointer of + * host DMA buffer has been wrapped. We should output the trace data + * at the end of host DMA buffer at first. + */ + if (host_offset < pos) + return buffer_size - pos; + + /* If there is available trace data now, it is unnecessary to wait. */ + if (host_offset > pos) + return host_offset - pos; + + return 0; +} + +static size_t sof_wait_trace_avail(struct sof_client_dev *cdev, + loff_t pos, size_t buffer_size) +{ + size_t ret = sof_trace_avail(cdev, pos, buffer_size); + struct sof_dtrace_priv *priv = cdev->data; + wait_queue_entry_t wait; + + /* data immediately available */ + if (ret) + return ret; + + if (!priv->dtrace_is_enabled && priv->dtrace_draining) { + /* + * tracing has ended and all traces have been + * read by client, return EOF + */ + priv->dtrace_draining = false; + return 0; + } + + /* wait for available trace data from FW */ + init_waitqueue_entry(&wait, current); + set_current_state(TASK_INTERRUPTIBLE); + add_wait_queue(&priv->dtrace_sleep, &wait); + + if (!signal_pending(current)) { + /* set timeout to max value, no error code */ + schedule_timeout(MAX_SCHEDULE_TIMEOUT); + } + remove_wait_queue(&priv->dtrace_sleep, &wait); + + return sof_trace_avail(cdev, pos, buffer_size); +} + +static ssize_t sof_dfsentry_trace_read(struct file *file, char __user *buffer, + size_t count, loff_t *ppos) +{ + struct sof_client_dev *cdev = file->private_data; + struct sof_dtrace_priv *priv = cdev->data; + size_t buffer_size = priv->dmatb.bytes; + unsigned long rem; + loff_t lpos = *ppos; + size_t avail; + u64 lpos_64; + + /* make sure we know about any failures on the DSP side */ + priv->dtrace_error = false; + + /* check pos and count */ + if (lpos < 0) + return -EINVAL; + if (!count) + return 0; + + /* check for buffer wrap and count overflow */ + lpos_64 = lpos; + lpos = do_div(lpos_64, buffer_size); + + if (count > buffer_size - lpos) /* min() not used to avoid sparse warnings */ + count = buffer_size - lpos; + + /* get available count based on current host offset */ + avail = sof_wait_trace_avail(cdev, lpos, buffer_size); + if (priv->dtrace_error) { + dev_err(priv->dev, "trace IO error\n"); + return -EIO; + } + + /* make sure count is <= avail */ + count = avail > count ? count : avail; + + /* copy available trace data to debugfs */ + rem = copy_to_user(buffer, ((u8 *)(priv->dmatb.area) + lpos), count); + if (rem) + return -EFAULT; + + *ppos += count; + + /* move debugfs reading position */ + return count; +} + +static int sof_dfsentry_trace_release(struct inode *inode, struct file *file) +{ + struct sof_client_dev *cdev = inode->i_private; + struct sof_dtrace_priv *priv = cdev->data; + + /* avoid duplicate traces at next open */ + if (!priv->dtrace_is_enabled) + priv->host_offset = 0; + + return 0; +} + +static const struct file_operations sof_dtrace_trace_fops = { + .open = simple_open, + .read = sof_dfsentry_trace_read, + .llseek = default_llseek, + .release = sof_dfsentry_trace_release, + + .owner = THIS_MODULE, +}; + +static void snd_sof_dtrace_update_pos(struct sof_client_dev *cdev, void *full_msg) +{ + struct sof_ipc_dma_trace_posn *posn = full_msg; + u32 msg_type = posn->rhdr.hdr.cmd & SOF_CMD_TYPE_MASK; + struct sof_dtrace_priv *priv = cdev->data; + + if (msg_type != SOF_IPC_TRACE_DMA_POSITION) + dev_info(priv->dev, "unhandled trace message %#x\n", msg_type); + + if (priv->dtrace_is_enabled && priv->host_offset != posn->host_offset) { + priv->host_offset = posn->host_offset; + wake_up(&priv->dtrace_sleep); + } + + if (posn->overflow != 0) + dev_err(priv->dev, + "DSP trace buffer overflow %u bytes. Total messages %d\n", + posn->overflow, posn->messages); +} + +/* an error has occurred within the DSP that prevents further trace */ +static void sof_dtrace_fw_state(struct sof_client_dev *cdev, + enum snd_sof_fw_state state) +{ + struct sof_dtrace_priv *priv = cdev->data; + + if (priv->dtrace_is_enabled && state == SOF_FW_CRASHED) { + priv->dtrace_error = true; + wake_up(&priv->dtrace_sleep); + } +} + +static void sof_dtrace_release(struct sof_client_dev *cdev) +{ + struct sof_dtrace_priv *priv = cdev->data; + const struct sof_dma_trace_host_ops *ops = priv->host_ops; + + if (!priv->dtrace_is_enabled) + return; + + if (ops) { + int ret = ops->stop(cdev); + + if (ret < 0) + dev_err(priv->dev, "host stop failed: %d\n", ret); + + ret = ops->release(cdev); + if (ret < 0) + dev_err(priv->dev, "host release failed: %d\n", ret); + } + + priv->dtrace_is_enabled = false; + priv->dtrace_draining = true; + wake_up(&priv->dtrace_sleep); +} + +static int sof_dtrace_init_ipc(struct sof_client_dev *cdev) +{ + const struct sof_ipc_fw_version *v = sof_client_get_fw_version(cdev); + struct sof_dtrace_priv *priv = cdev->data; + const struct sof_dma_trace_host_ops *ops = priv->host_ops; + struct sof_ipc_dma_trace_params_ext params; + struct sof_ipc_reply ipc_reply; + int ret; + + if (priv->dtrace_is_enabled) + return 0; + + /* set IPC parameters */ + params.hdr.cmd = SOF_IPC_GLB_TRACE_MSG; + /* PARAMS_EXT is only supported from ABI 3.7.0 onwards */ + if (v->abi_version >= SOF_ABI_VER(3, 7, 0)) { + params.hdr.size = sizeof(struct sof_ipc_dma_trace_params_ext); + params.hdr.cmd |= SOF_IPC_TRACE_DMA_PARAMS_EXT; + params.timestamp_ns = ktime_get(); /* in nanosecond */ + } else { + params.hdr.size = sizeof(struct sof_ipc_dma_trace_params); + params.hdr.cmd |= SOF_IPC_TRACE_DMA_PARAMS; + } + params.buffer.phy_addr = priv->dmatp.addr; + params.buffer.size = priv->dmatb.bytes; + params.buffer.pages = priv->dtrace_pages; + params.stream_tag = 0; + + priv->host_offset = 0; + priv->dtrace_draining = false; + + if (ops) { + ret = ops->init(cdev, &priv->dmatb, ¶ms.stream_tag); + if (ret < 0) { + dev_err(priv->dev, "host init failed: %d\n", ret); + return ret; + } + } + + dev_dbg(priv->dev, "stream_tag: %d\n", params.stream_tag); + + /* send IPC to the DSP */ + ret = sof_client_ipc_tx_message(cdev, ¶ms, &ipc_reply, sizeof(ipc_reply)); + if (ret < 0) { + dev_err(priv->dev, "can't set params for DMA for trace %d\n", ret); + goto trace_release; + } + + if (ops) { + ret = ops->start(cdev); + if (ret < 0) { + dev_err(priv->dev, "host start failed: %d\n", ret); + goto trace_release; + } + } + + priv->dtrace_is_enabled = true; + + return 0; + +trace_release: + if (ops) + ops->release(cdev); + + return ret; +} + +static int sof_dtrace_client_probe(struct auxiliary_device *auxdev, + const struct auxiliary_device_id *id) +{ + struct sof_client_dev *cdev = auxiliary_dev_to_sof_client_dev(auxdev); + struct dentry *dfsroot = sof_client_get_debugfs_root(cdev); + struct device *dma_dev = sof_client_get_dma_dev(cdev); + struct sof_dma_trace_host_ops *ops; + struct device *dev = &auxdev->dev; + struct sof_dtrace_priv *priv; + int ret; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + ops = dev->platform_data; + + if (ops && (!ops->init || !ops->release || !ops->start || !ops->stop)) { + dev_err(dev, "missing platform callback(s)\n"); + return -ENODEV; + } + + /* + * dma-trace is power managed via auxdev suspend/resume callbacks by + * SOF core + */ + pm_runtime_no_callbacks(dev); + + priv->host_ops = ops; + priv->dev = dev; + cdev->data = priv; + + /* allocate trace page table buffer */ + ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, dma_dev, PAGE_SIZE, + &priv->dmatp); + if (ret < 0) { + dev_err(dev, "can't alloc page table for trace %d\n", ret); + return ret; + } + + /* allocate trace data buffer */ + ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV_SG, dma_dev, + SOF_DTRACE_BUF_SIZE, &priv->dmatb); + if (ret < 0) { + dev_err(dev, "can't alloc buffer for trace %d\n", ret); + goto page_err; + } + + /* create compressed page table for audio firmware */ + ret = snd_sof_create_page_table(dma_dev, &priv->dmatb, priv->dmatp.area, + priv->dmatb.bytes); + if (ret < 0) + goto table_err; + + priv->dtrace_pages = ret; + dev_dbg(dev, "dtrace_pages: %d\n", priv->dtrace_pages); + + priv->dfs_trace = debugfs_create_file("trace", 0444, dfsroot, cdev, + &sof_dtrace_trace_fops); + priv->dfs_filter = debugfs_create_file("filter", 0200, dfsroot, cdev, + &sof_dtrace_filter_fops); + + init_waitqueue_head(&priv->dtrace_sleep); + + ret = sof_client_register_ipc_rx_handler(cdev, SOF_IPC_GLB_TRACE_MSG, + snd_sof_dtrace_update_pos); + if (ret) + goto register_rx_err; + + ret = sof_client_register_fw_state_handler(cdev, sof_dtrace_fw_state); + if (ret) + goto register_fw_state_err; + + ret = sof_dtrace_init_ipc(cdev); + if (ret < 0) { + sof_client_unregister_ipc_rx_handler(cdev, SOF_IPC_GLB_TRACE_MSG); + goto ipc_err; + } + + if (ops && ops->available) + ops->available(cdev, true); + + return 0; + +ipc_err: + sof_client_unregister_fw_state_handler(cdev); +register_fw_state_err: + sof_client_unregister_ipc_rx_handler(cdev, SOF_IPC_GLB_TRACE_MSG); +register_rx_err: + debugfs_remove(priv->dfs_trace); + debugfs_remove(priv->dfs_filter); +table_err: + snd_dma_free_pages(&priv->dmatb); +page_err: + snd_dma_free_pages(&priv->dmatp); + + return ret; +} + +static void sof_dtrace_client_remove(struct auxiliary_device *auxdev) +{ + struct sof_client_dev *cdev = auxiliary_dev_to_sof_client_dev(auxdev); + struct sof_dtrace_priv *priv = cdev->data; + const struct sof_dma_trace_host_ops *ops = priv->host_ops; + + sof_dtrace_release(cdev); + + debugfs_remove(priv->dfs_filter); + debugfs_remove(priv->dfs_trace); + + sof_client_unregister_fw_state_handler(cdev); + sof_client_unregister_ipc_rx_handler(cdev, SOF_IPC_GLB_TRACE_MSG); + + if (ops && ops->available) + ops->available(cdev, false); + + snd_dma_free_pages(&priv->dmatb); + snd_dma_free_pages(&priv->dmatp); +} + +static int sof_dtrace_client_resume(struct auxiliary_device *auxdev) +{ + struct sof_client_dev *cdev = auxiliary_dev_to_sof_client_dev(auxdev); + + sof_dtrace_init_ipc(cdev); + + return 0; +} + +static int sof_dtrace_client_suspend(struct auxiliary_device *auxdev, + pm_message_t state) +{ + struct sof_client_dev *cdev = auxiliary_dev_to_sof_client_dev(auxdev); + + sof_dtrace_release(cdev); + + return 0; +} + +static const struct auxiliary_device_id sof_dtrace_client_id_table[] = { + { .name = "snd_sof.atom-dma-trace", }, + { .name = "snd_sof.bdw-dma-trace", }, + { .name = "snd_sof.hda-dma-trace", }, + {}, +}; +MODULE_DEVICE_TABLE(auxiliary, sof_dtrace_client_id_table); + +/* driver name will be set based on KBUILD_MODNAME */ +static struct auxiliary_driver sof_dtrace_client_drv = { + .probe = sof_dtrace_client_probe, + .remove = sof_dtrace_client_remove, + .suspend = sof_dtrace_client_suspend, + .resume = sof_dtrace_client_resume, + + .id_table = sof_dtrace_client_id_table, +}; + +module_auxiliary_driver(sof_dtrace_client_drv); + +MODULE_DESCRIPTION("SOF DMA Trace Client Driver"); +MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS(SND_SOC_SOF_CLIENT); diff --git a/sound/soc/sof/sof-client-dma-trace.h b/sound/soc/sof/sof-client-dma-trace.h new file mode 100644 index 00000000000000..ce83e6998d2902 --- /dev/null +++ b/sound/soc/sof/sof-client-dma-trace.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef __SOF_CLIENT_DMA_TRACE_H +#define __SOF_CLIENT_DMA_TRACE_H + +struct snd_dma_buffer; +struct sof_client_dev; + +/* Platform callbacks */ +struct sof_dma_trace_host_ops { + int (*init)(struct sof_client_dev *cdev, struct snd_dma_buffer *dmab, + u32 *stream_tag); + int (*release)(struct sof_client_dev *cdev); + int (*start)(struct sof_client_dev *cdev); + int (*stop)(struct sof_client_dev *cdev); + + /* Optional */ + void (*available)(struct sof_client_dev *cdev, bool available); +}; + +#endif From 9e6b9845185037cef9eb8a5fbf35ca7b12f0f851 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 18 Aug 2021 12:06:48 +0300 Subject: [PATCH 24/32] ASoC: SOF: imx: Enable SOF client version of dma-trace i.MX is using dma-trace currently, make sure that the move to SOF client is not going to cause regression regarding to trace functionality. Signed-off-by: Peter Ujfalusi --- sound/soc/sof/imx/imx-common.c | 14 ++++++++++++++ sound/soc/sof/imx/imx-common.h | 3 +++ sound/soc/sof/imx/imx8.c | 8 ++++++++ sound/soc/sof/imx/imx8m.c | 4 ++++ sound/soc/sof/sof-client-dma-trace.c | 1 + 5 files changed, 30 insertions(+) diff --git a/sound/soc/sof/imx/imx-common.c b/sound/soc/sof/imx/imx-common.c index 8826ef94f04a33..26bf2b0e0cd0bf 100644 --- a/sound/soc/sof/imx/imx-common.c +++ b/sound/soc/sof/imx/imx-common.c @@ -7,6 +7,7 @@ #include #include #include "../ops.h" +#include "../sof-client-dma-trace.h" #include "imx-common.h" @@ -74,4 +75,17 @@ void imx8_dump(struct snd_sof_dev *sdev, u32 flags) } EXPORT_SYMBOL(imx8_dump); +int imx8_dma_trace_register(struct snd_sof_dev *sdev) +{ + return sof_client_dev_register(sdev, "imx8-dma-trace", 0, NULL, 0); +} +EXPORT_SYMBOL(imx8_dma_trace_register); + +void imx8_dma_trace_unregister(struct snd_sof_dev *sdev) +{ + sof_client_dev_unregister(sdev, "imx8-dma-trace", 0); +} +EXPORT_SYMBOL(imx8_dma_trace_unregister); + +MODULE_IMPORT_NS(SND_SOC_SOF_CLIENT); MODULE_LICENSE("Dual BSD/GPL"); diff --git a/sound/soc/sof/imx/imx-common.h b/sound/soc/sof/imx/imx-common.h index 1cc7d670418250..2c9bf3520e5079 100644 --- a/sound/soc/sof/imx/imx-common.h +++ b/sound/soc/sof/imx/imx-common.h @@ -13,4 +13,7 @@ void imx8_get_registers(struct snd_sof_dev *sdev, void imx8_dump(struct snd_sof_dev *sdev, u32 flags); +int imx8_dma_trace_register(struct snd_sof_dev *sdev); +void imx8_dma_trace_unregister(struct snd_sof_dev *sdev); + #endif diff --git a/sound/soc/sof/imx/imx8.c b/sound/soc/sof/imx/imx8.c index fc1720c211a3ea..5af512491abb67 100644 --- a/sound/soc/sof/imx/imx8.c +++ b/sound/soc/sof/imx/imx8.c @@ -433,6 +433,10 @@ struct snd_sof_dsp_ops sof_imx8_ops = { /* firmware loading */ .load_firmware = snd_sof_load_firmware_memcpy, + /* client ops */ + .register_ipc_clients = imx8_dma_trace_register, + .unregister_ipc_clients = imx8_dma_trace_unregister, + /* Debug information */ .dbg_dump = imx8_dump, .debugfs_add_region_item = snd_sof_debugfs_add_region_item_iomem, @@ -488,6 +492,10 @@ struct snd_sof_dsp_ops sof_imx8x_ops = { /* firmware loading */ .load_firmware = snd_sof_load_firmware_memcpy, + /* client ops */ + .register_ipc_clients = imx8_dma_trace_register, + .unregister_ipc_clients = imx8_dma_trace_unregister, + /* Debug information */ .dbg_dump = imx8_dump, .debugfs_add_region_item = snd_sof_debugfs_add_region_item_iomem, diff --git a/sound/soc/sof/imx/imx8m.c b/sound/soc/sof/imx/imx8m.c index 30624fafc632cd..cb8bdc3aa1d572 100644 --- a/sound/soc/sof/imx/imx8m.c +++ b/sound/soc/sof/imx/imx8m.c @@ -296,6 +296,10 @@ struct snd_sof_dsp_ops sof_imx8m_ops = { /* firmware loading */ .load_firmware = snd_sof_load_firmware_memcpy, + /* client ops */ + .register_ipc_clients = imx8_dma_trace_register, + .unregister_ipc_clients = imx8_dma_trace_unregister, + /* Debug information */ .dbg_dump = imx8_dump, .debugfs_add_region_item = snd_sof_debugfs_add_region_item_iomem, diff --git a/sound/soc/sof/sof-client-dma-trace.c b/sound/soc/sof/sof-client-dma-trace.c index f906c3b9e8c454..22ba21d143f722 100644 --- a/sound/soc/sof/sof-client-dma-trace.c +++ b/sound/soc/sof/sof-client-dma-trace.c @@ -626,6 +626,7 @@ static const struct auxiliary_device_id sof_dtrace_client_id_table[] = { { .name = "snd_sof.atom-dma-trace", }, { .name = "snd_sof.bdw-dma-trace", }, { .name = "snd_sof.hda-dma-trace", }, + { .name = "snd_sof.imx8-dma-trace", }, {}, }; MODULE_DEVICE_TABLE(auxiliary, sof_dtrace_client_id_table); From e8bab418e24882f4b2e24e1def55df88245607ab Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Mon, 19 Jul 2021 14:18:54 +0300 Subject: [PATCH 25/32] ASoC: SOF: Switch to the client driver for dma-trace support Switch to the SOF client version of the dma-trace. In order to not introduce regression the SND_SOC_SOF_DEBUG_ENABLE_FIRMWARE_TRACE is used to enable the new driver. Signed-off-by: Peter Ujfalusi --- sound/soc/sof/Makefile | 2 +- sound/soc/sof/core.c | 28 +- sound/soc/sof/debug.c | 1 - sound/soc/sof/intel/Kconfig | 15 +- sound/soc/sof/intel/Makefile | 3 +- sound/soc/sof/intel/apl.c | 5 - sound/soc/sof/intel/cnl.c | 5 - sound/soc/sof/intel/hda-dsp.c | 2 +- sound/soc/sof/intel/hda-trace.c | 88 ++--- sound/soc/sof/intel/hda.h | 7 - sound/soc/sof/intel/icl.c | 5 - sound/soc/sof/intel/tgl.c | 5 - sound/soc/sof/ipc.c | 24 +- sound/soc/sof/ops.c | 1 - sound/soc/sof/ops.h | 26 -- sound/soc/sof/pm.c | 12 - sound/soc/sof/sof-priv.h | 29 +- sound/soc/sof/trace.c | 577 -------------------------------- 18 files changed, 41 insertions(+), 794 deletions(-) delete mode 100644 sound/soc/sof/trace.c diff --git a/sound/soc/sof/Makefile b/sound/soc/sof/Makefile index db411405f3f0c3..3ef1cc0bd5b606 100644 --- a/sound/soc/sof/Makefile +++ b/sound/soc/sof/Makefile @@ -1,7 +1,7 @@ # SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) snd-sof-objs := core.o ops.o loader.o ipc.o pcm.o pm.o debug.o topology.o\ - control.o trace.o iomem-utils.o sof-audio.o stream-ipc.o + control.o iomem-utils.o sof-audio.o stream-ipc.o snd-sof-$(CONFIG_SND_SOC_SOF_CLIENT) += sof-client.o snd-sof-pci-objs := sof-pci-dev.o diff --git a/sound/soc/sof/core.c b/sound/soc/sof/core.c index 877a60ddfcf49c..0947c1e8b8dd22 100644 --- a/sound/soc/sof/core.c +++ b/sound/soc/sof/core.c @@ -216,25 +216,8 @@ static int sof_probe_continue(struct snd_sof_dev *sdev) goto fw_run_err; } -#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_DMA_TRACE) - /* trace support via SOF client */ - sdev->dtrace_is_supported = false; -#else - if (sof_core_debug & SOF_DBG_ENABLE_TRACE) { - sdev->dtrace_is_supported = true; - - /* init DMA trace */ - ret = snd_sof_init_trace(sdev); - if (ret < 0) { - /* non fatal */ - dev_warn(sdev->dev, - "warning: failed to initialize trace %d\n", - ret); - } - } else { - dev_dbg(sdev->dev, "SOF firmware trace disabled\n"); - } -#endif + if (sof_core_debug & SOF_DBG_ENABLE_TRACE) + dev_dbg(sdev->dev, "SOF_DBG_ENABLE_TRACE is no longer used\n"); /* hereafter all FW boot flows are for PM reasons */ sdev->first_boot = false; @@ -246,14 +229,14 @@ static int sof_probe_continue(struct snd_sof_dev *sdev) if (ret < 0) { dev_err(sdev->dev, "error: failed to register DSP DAI driver %d\n", ret); - goto fw_trace_err; + goto fw_run_err; } ret = snd_sof_machine_register(sdev, plat_data); if (ret < 0) { dev_err(sdev->dev, "error: failed to register machine driver %d\n", ret); - goto fw_trace_err; + goto fw_run_err; } ret = sof_register_clients(sdev); @@ -279,8 +262,6 @@ static int sof_probe_continue(struct snd_sof_dev *sdev) sof_machine_err: snd_sof_machine_unregister(sdev, plat_data); -fw_trace_err: - snd_sof_free_trace(sdev); fw_run_err: snd_sof_fw_unload(sdev); fw_load_err: @@ -413,7 +394,6 @@ int snd_sof_device_remove(struct device *dev) snd_sof_ipc_free(sdev); snd_sof_free_debug(sdev); - snd_sof_free_trace(sdev); } /* diff --git a/sound/soc/sof/debug.c b/sound/soc/sof/debug.c index aa5804a9042495..8d9515a83587df 100644 --- a/sound/soc/sof/debug.c +++ b/sound/soc/sof/debug.c @@ -408,6 +408,5 @@ void snd_sof_handle_fw_exception(struct snd_sof_dev *sdev) snd_sof_ipc_dump(sdev); snd_sof_dsp_dbg_dump(sdev, SOF_DBG_DUMP_REGS | SOF_DBG_DUMP_MBOX); sof_set_fw_state(sdev, SOF_FW_CRASHED); - snd_sof_trace_notify_for_error(sdev); } EXPORT_SYMBOL(snd_sof_handle_fw_exception); diff --git a/sound/soc/sof/intel/Kconfig b/sound/soc/sof/intel/Kconfig index b1f1b22be7a232..9fbdbba34a5541 100644 --- a/sound/soc/sof/intel/Kconfig +++ b/sound/soc/sof/intel/Kconfig @@ -15,19 +15,13 @@ config SND_SOC_SOF_INTEL_HIFI_EP_IPC This option is not user-selectable but automagically handled by 'select' statements at a higher level. -#config SND_SOC_SOF_HDA_DMA_TRACE -# tristate -# select SND_SOC_SOF_DEBUG_DMA_TRACE -# help -# This option is not user-selectable but automagically handled by -# 'select' statements at a higher level. - config SND_SOC_SOF_HDA_DMA_TRACE - bool "Enable dma-trace for HDA" + bool + default SND_SOC_SOF_DEBUG_ENABLE_FIRMWARE_TRACE select SND_SOC_SOF_DEBUG_DMA_TRACE help - This option is not user-selectable but automagically handled by - 'select' statements at a higher level. + This option is not user-selectable but following the higher level + SND_SOC_SOF_DEBUG_ENABLE_FIRMWARE_TRACE option. config SND_SOC_SOF_INTEL_ATOM_HIFI_EP tristate @@ -45,7 +39,6 @@ config SND_SOC_SOF_INTEL_COMMON select SND_SOC_INTEL_MACH select SND_SOC_ACPI if ACPI select SND_INTEL_DSP_CONFIG -# select SND_SOC_SOF_HDA_DMA_TRACE help This option is not user-selectable but automagically handled by 'select' statements at a higher level. diff --git a/sound/soc/sof/intel/Makefile b/sound/soc/sof/intel/Makefile index 6be605973ccb7d..26037ed634e22f 100644 --- a/sound/soc/sof/intel/Makefile +++ b/sound/soc/sof/intel/Makefile @@ -7,8 +7,7 @@ snd-sof-intel-hda-common-objs := hda.o hda-loader.o hda-stream.o hda-dsp.o \ hda-ipc.o hda-ctrl.o hda-pcm.o hda-dai.o \ hda-bus.o apl.o cnl.o tgl.o icl.o snd-sof-intel-hda-common-$(CONFIG_SND_SOC_SOF_HDA_PROBES) += hda-probes.o -# snd-sof-intel-hda-common-$(CONFIG_SND_SOC_SOF_HDA_DMA_TRACE) += hda-trace.o -snd-sof-intel-hda-common-objs += hda-trace.o +snd-sof-intel-hda-common-$(CONFIG_SND_SOC_SOF_HDA_DMA_TRACE) += hda-trace.o snd-sof-intel-hda-objs := hda-codec.o diff --git a/sound/soc/sof/intel/apl.c b/sound/soc/sof/intel/apl.c index 2b403a92c936d3..1ad0b771ed7921 100644 --- a/sound/soc/sof/intel/apl.c +++ b/sound/soc/sof/intel/apl.c @@ -114,11 +114,6 @@ const struct snd_sof_dsp_ops sof_apl_ops = { .core_get = hda_dsp_core_get, .core_put = hda_dsp_core_put, - /* trace callback */ - .trace_init = hda_dsp_trace_init, - .trace_release = hda_dsp_trace_release, - .trace_trigger = hda_dsp_trace_trigger, - /* client ops */ .register_ipc_clients = apl_register_clients, .unregister_ipc_clients = apl_unregister_clients, diff --git a/sound/soc/sof/intel/cnl.c b/sound/soc/sof/intel/cnl.c index 468380efc79000..ab3ffd594850c3 100644 --- a/sound/soc/sof/intel/cnl.c +++ b/sound/soc/sof/intel/cnl.c @@ -319,11 +319,6 @@ const struct snd_sof_dsp_ops sof_cnl_ops = { /* firmware run */ .run = hda_dsp_cl_boot_firmware, - /* trace callback */ - .trace_init = hda_dsp_trace_init, - .trace_release = hda_dsp_trace_release, - .trace_trigger = hda_dsp_trace_trigger, - /* client ops */ .register_ipc_clients = cnl_register_clients, .unregister_ipc_clients = cnl_unregister_clients, diff --git a/sound/soc/sof/intel/hda-dsp.c b/sound/soc/sof/intel/hda-dsp.c index e03bb455f82a16..7c3a402bff020a 100644 --- a/sound/soc/sof/intel/hda-dsp.c +++ b/sound/soc/sof/intel/hda-dsp.c @@ -433,7 +433,7 @@ static int hda_dsp_set_D0_state(struct snd_sof_dev *sdev, * when the DSP enters D0I3 while the system is in S0 * for debug purpose. */ - if (!sdev->dtrace_is_supported || + if (!sdev->dtrace_is_available || !hda_enable_trace_D0I3_S0 || sdev->system_suspend_target != SOF_SUSPEND_NONE) flags = HDA_PM_NO_DMA_TRACE; diff --git a/sound/soc/sof/intel/hda-trace.c b/sound/soc/sof/intel/hda-trace.c index af723ff3b8076b..697d8e85a3cede 100644 --- a/sound/soc/sof/intel/hda-trace.c +++ b/sound/soc/sof/intel/hda-trace.c @@ -33,13 +33,15 @@ static int hda_dsp_trace_prepare(struct snd_sof_dev *sdev, struct snd_dma_buffer ret = hda_dsp_stream_hw_params(sdev, stream, dmab, NULL); if (ret < 0) - dev_err(sdev->dev, "error: hdac prepare failed: %d\n", ret); + dev_err(sdev->dev, "hdac prepare failed: %d\n", ret); return ret; } -int hda_dsp_trace_init(struct snd_sof_dev *sdev, u32 *stream_tag) +static int hda_dma_trace_init(struct sof_client_dev *cdev, + struct snd_dma_buffer *dmab, u32 *stream_tag) { + struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev); struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; int ret; @@ -47,8 +49,7 @@ int hda_dsp_trace_init(struct snd_sof_dev *sdev, u32 *stream_tag) SOF_HDA_STREAM_DMI_L1_COMPATIBLE); if (!hda->dtrace_stream) { - dev_err(sdev->dev, - "error: no available capture stream for DMA trace\n"); + dev_err(sdev->dev, "no available capture stream for DMA trace\n"); return -ENODEV; } @@ -58,9 +59,9 @@ int hda_dsp_trace_init(struct snd_sof_dev *sdev, u32 *stream_tag) * initialize capture stream, set BDL address and return corresponding * stream tag which will be sent to the firmware by IPC message. */ - ret = hda_dsp_trace_prepare(sdev, &sdev->dmatb); + ret = hda_dsp_trace_prepare(sdev, dmab); if (ret < 0) { - dev_err(sdev->dev, "error: hdac trace init failed: %d\n", ret); + dev_err(sdev->dev, "hdac trace init failed: %d\n", ret); hda_dsp_stream_put(sdev, SNDRV_PCM_STREAM_CAPTURE, *stream_tag); hda->dtrace_stream = NULL; *stream_tag = 0; @@ -69,84 +70,48 @@ int hda_dsp_trace_init(struct snd_sof_dev *sdev, u32 *stream_tag) return ret; } -int hda_dsp_trace_release(struct snd_sof_dev *sdev) -{ - struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; - struct hdac_stream *hstream; - - if (hda->dtrace_stream) { - hstream = &hda->dtrace_stream->hstream; - hda_dsp_stream_put(sdev, - SNDRV_PCM_STREAM_CAPTURE, - hstream->stream_tag); - hda->dtrace_stream = NULL; - return 0; - } - - dev_dbg(sdev->dev, "DMA trace stream is not opened!\n"); - return -ENODEV; -} - -int hda_dsp_trace_trigger(struct snd_sof_dev *sdev, int cmd) -{ - struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; - - return hda_dsp_stream_trigger(sdev, hda->dtrace_stream, cmd); -} - -#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_DMA_TRACE) -static int hda_dma_trace_init(struct sof_client_dev *cdev, - struct snd_dma_buffer *dmab, u32 *stream_tag) +static int hda_dma_trace_release(struct sof_client_dev *cdev) { struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev); struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; - int ret; - - hda->dtrace_stream = hda_dsp_stream_get(sdev, SNDRV_PCM_STREAM_CAPTURE, - SOF_HDA_STREAM_DMI_L1_COMPATIBLE); + struct hdac_stream *hstream; if (!hda->dtrace_stream) { - dev_err(sdev->dev, - "error: no available capture stream for DMA trace\n"); + dev_dbg(sdev->dev, "DMA trace stream is not opened!\n"); return -ENODEV; } - *stream_tag = hda->dtrace_stream->hstream.stream_tag; - - /* - * initialize capture stream, set BDL address and return corresponding - * stream tag which will be sent to the firmware by IPC message. - */ - ret = hda_dsp_trace_prepare(sdev, dmab); - if (ret < 0) { - dev_err(sdev->dev, "error: hdac trace init failed: %d\n", ret); - hda_dsp_stream_put(sdev, SNDRV_PCM_STREAM_CAPTURE, *stream_tag); - hda->dtrace_stream = NULL; - *stream_tag = 0; - } + hstream = &hda->dtrace_stream->hstream; + hda_dsp_stream_put(sdev, SNDRV_PCM_STREAM_CAPTURE, hstream->stream_tag); + hda->dtrace_stream = NULL; - return ret; + return 0; } -static int hda_dma_trace_release(struct sof_client_dev *cdev) +static int hda_dsp_trace_trigger(struct sof_client_dev *cdev, int cmd) { struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev); + struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; - return hda_dsp_trace_release(sdev); + return hda_dsp_stream_trigger(sdev, hda->dtrace_stream, cmd); } static int hda_dma_trace_start(struct sof_client_dev *cdev) { - struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev); - - return hda_dsp_trace_trigger(sdev, SNDRV_PCM_TRIGGER_START); + return hda_dsp_trace_trigger(cdev, SNDRV_PCM_TRIGGER_START); } static int hda_dma_trace_stop(struct sof_client_dev *cdev) +{ + return hda_dsp_trace_trigger(cdev, SNDRV_PCM_TRIGGER_STOP); +} + +static void hda_dma_trace_is_available(struct sof_client_dev *cdev, + bool available) { struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev); - return hda_dsp_trace_trigger(sdev, SNDRV_PCM_TRIGGER_STOP); + sdev->dtrace_is_available = available; } static const struct sof_dma_trace_host_ops hda_dma_trace_ops = { @@ -154,6 +119,8 @@ static const struct sof_dma_trace_host_ops hda_dma_trace_ops = { .release = hda_dma_trace_release, .start = hda_dma_trace_start, .stop = hda_dma_trace_stop, + + .available = hda_dma_trace_is_available, }; int hda_dma_trace_register(struct snd_sof_dev *sdev) @@ -168,4 +135,3 @@ void hda_dma_trace_unregister(struct snd_sof_dev *sdev) } MODULE_IMPORT_NS(SND_SOC_SOF_CLIENT); -#endif diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h index 0e7ebcec5c2d42..deab247f9b473d 100644 --- a/sound/soc/sof/intel/hda.h +++ b/sound/soc/sof/intel/hda.h @@ -638,13 +638,6 @@ static inline int hda_codec_i915_exit(struct snd_sof_dev *sdev) { return 0; } #endif -/* - * Trace Control. - */ -int hda_dsp_trace_init(struct snd_sof_dev *sdev, u32 *stream_tag); -int hda_dsp_trace_release(struct snd_sof_dev *sdev); -int hda_dsp_trace_trigger(struct snd_sof_dev *sdev, int cmd); - /* * SoundWire support */ diff --git a/sound/soc/sof/intel/icl.c b/sound/soc/sof/intel/icl.c index 713e6d4a3f3234..3a310530730626 100644 --- a/sound/soc/sof/intel/icl.c +++ b/sound/soc/sof/intel/icl.c @@ -177,11 +177,6 @@ const struct snd_sof_dsp_ops sof_icl_ops = { .run = hda_dsp_cl_boot_firmware_iccmax, .stall = icl_dsp_core_stall, - /* trace callback */ - .trace_init = hda_dsp_trace_init, - .trace_release = hda_dsp_trace_release, - .trace_trigger = hda_dsp_trace_trigger, - /* client ops */ .register_ipc_clients = icl_register_clients, .unregister_ipc_clients = icl_unregister_clients, diff --git a/sound/soc/sof/intel/tgl.c b/sound/soc/sof/intel/tgl.c index 25421730972806..602d2f35b37754 100644 --- a/sound/soc/sof/intel/tgl.c +++ b/sound/soc/sof/intel/tgl.c @@ -149,11 +149,6 @@ const struct snd_sof_dsp_ops sof_tgl_ops = { /* firmware run */ .run = hda_dsp_cl_boot_firmware_iccmax, - /* trace callback */ - .trace_init = hda_dsp_trace_init, - .trace_release = hda_dsp_trace_release, - .trace_trigger = hda_dsp_trace_trigger, - /* client ops */ .register_ipc_clients = tgl_register_clients, .unregister_ipc_clients = tgl_unregister_clients, diff --git a/sound/soc/sof/ipc.c b/sound/soc/sof/ipc.c index f35a7f3b05dd08..1c0c802a442b07 100644 --- a/sound/soc/sof/ipc.c +++ b/sound/soc/sof/ipc.c @@ -20,7 +20,6 @@ typedef void (*ipc_rx_callback)(struct snd_sof_dev *sdev, void *full_msg); -static void ipc_trace_message(struct snd_sof_dev *sdev, void *full_msg); static void ipc_stream_message(struct snd_sof_dev *sdev, void *full_msg); /* @@ -445,6 +444,7 @@ void snd_sof_ipc_msgs_rx(struct snd_sof_dev *sdev) wake_up(&sdev->boot_wait); } break; + case SOF_IPC_GLB_TRACE_MSG: case SOF_IPC_GLB_COMPOUND: case SOF_IPC_GLB_TPLG_MSG: case SOF_IPC_GLB_PM_MSG: @@ -455,9 +455,6 @@ void snd_sof_ipc_msgs_rx(struct snd_sof_dev *sdev) case SOF_IPC_GLB_STREAM_MSG: rx_callback = ipc_stream_message; break; - case SOF_IPC_GLB_TRACE_MSG: - rx_callback = ipc_trace_message; - break; default: dev_err(sdev->dev, "error: unknown DSP message 0x%x\n", cmd); break; @@ -486,25 +483,6 @@ void snd_sof_ipc_msgs_rx(struct snd_sof_dev *sdev) } EXPORT_SYMBOL(snd_sof_ipc_msgs_rx); -/* - * IPC trace mechanism. - */ - -static void ipc_trace_message(struct snd_sof_dev *sdev, void *full_msg) -{ - struct sof_ipc_cmd_hdr *hdr = full_msg; - u32 msg_type = hdr->cmd & SOF_CMD_TYPE_MASK; - - switch (msg_type) { - case SOF_IPC_TRACE_DMA_POSITION: - snd_sof_trace_update_pos(sdev, full_msg); - break; - default: - dev_err(sdev->dev, "error: unhandled trace message %#x\n", msg_type); - break; - } -} - /* * IPC stream position. */ diff --git a/sound/soc/sof/ops.c b/sound/soc/sof/ops.c index 92a64114bfd0ac..7c02ab686c0afb 100644 --- a/sound/soc/sof/ops.c +++ b/sound/soc/sof/ops.c @@ -159,6 +159,5 @@ void snd_sof_dsp_panic(struct snd_sof_dev *sdev, u32 offset) snd_sof_dsp_dbg_dump(sdev, SOF_DBG_DUMP_REGS | SOF_DBG_DUMP_MBOX); sof_set_fw_state(sdev, SOF_FW_CRASHED); - snd_sof_trace_notify_for_error(sdev); } EXPORT_SYMBOL(snd_sof_dsp_panic); diff --git a/sound/soc/sof/ops.h b/sound/soc/sof/ops.h index 2398b4e19ac996..9a43575b7d764a 100644 --- a/sound/soc/sof/ops.h +++ b/sound/soc/sof/ops.h @@ -363,32 +363,6 @@ static inline int snd_sof_dsp_send_msg(struct snd_sof_dev *sdev, return sof_ops(sdev)->send_msg(sdev, msg); } -/* host DMA trace */ -static inline int snd_sof_dma_trace_init(struct snd_sof_dev *sdev, - u32 *stream_tag) -{ - if (sof_ops(sdev)->trace_init) - return sof_ops(sdev)->trace_init(sdev, stream_tag); - - return 0; -} - -static inline int snd_sof_dma_trace_release(struct snd_sof_dev *sdev) -{ - if (sof_ops(sdev)->trace_release) - return sof_ops(sdev)->trace_release(sdev); - - return 0; -} - -static inline int snd_sof_dma_trace_trigger(struct snd_sof_dev *sdev, int cmd) -{ - if (sof_ops(sdev)->trace_trigger) - return sof_ops(sdev)->trace_trigger(sdev, cmd); - - return 0; -} - /* host PCM ops */ static inline int snd_sof_pcm_platform_open(struct snd_sof_dev *sdev, diff --git a/sound/soc/sof/pm.c b/sound/soc/sof/pm.c index 72c8e7443f8950..fb2575c324e483 100644 --- a/sound/soc/sof/pm.c +++ b/sound/soc/sof/pm.c @@ -147,15 +147,6 @@ static int sof_resume(struct device *dev, bool runtime_resume) return ret; } - /* resume DMA trace, only need send ipc */ - ret = snd_sof_init_trace_ipc(sdev); - if (ret < 0) { - /* non fatal */ - dev_warn(sdev->dev, - "warning: failed to init trace after resume %d\n", - ret); - } - /* restore pipelines */ ret = sof_set_up_pipelines(sdev, false); if (ret < 0) { @@ -217,9 +208,6 @@ static int sof_suspend(struct device *dev, bool runtime_suspend) sof_tear_down_pipelines(sdev, false); - /* release trace */ - snd_sof_release_trace(sdev); - /* Notify clients about core suspend */ sof_suspend_clients(sdev, pm_state); diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index e3570c93fae747..745da6e370a83f 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -50,9 +50,6 @@ extern int sof_core_debug; /* time in ms for runtime suspend delay */ #define SND_SOF_SUSPEND_DELAY_MS 2000 -/* DMA buffer size for trace */ -#define DMA_BUF_SIZE_FOR_TRACE (PAGE_SIZE * 16) - #define SOF_IPC_DSP_REPLY 0 #define SOF_IPC_HOST_REPLY 1 @@ -249,13 +246,6 @@ struct snd_sof_dsp_ops { size_t size, const char *name, enum sof_debugfs_access_type access_type); /* optional */ - /* host DMA trace initialization */ - int (*trace_init)(struct snd_sof_dev *sdev, - u32 *stream_tag); /* optional */ - int (*trace_release)(struct snd_sof_dev *sdev); /* optional */ - int (*trace_trigger)(struct snd_sof_dev *sdev, - int cmd); /* optional */ - /* misc */ int (*get_bar_index)(struct snd_sof_dev *sdev, u32 type); /* optional */ @@ -430,16 +420,8 @@ struct snd_sof_dev { int ipc_timeout; int boot_timeout; - /* DMA for Trace */ - struct snd_dma_buffer dmatb; - struct snd_dma_buffer dmatp; - int dma_trace_pages; - wait_queue_head_t trace_sleep; - u32 host_offset; - bool dtrace_is_supported; /* set with Kconfig or module parameter */ - bool dtrace_is_enabled; - bool dtrace_error; - bool dtrace_draining; + /* dtrace client is available */ + bool dtrace_is_available; bool msi_enabled; @@ -532,22 +514,15 @@ int sof_ipc_tx_message_no_pm(struct snd_sof_ipc *ipc, u32 header, /* * Trace/debug */ -int snd_sof_init_trace(struct snd_sof_dev *sdev); -void snd_sof_release_trace(struct snd_sof_dev *sdev); -void snd_sof_free_trace(struct snd_sof_dev *sdev); int snd_sof_dbg_init(struct snd_sof_dev *sdev); void snd_sof_free_debug(struct snd_sof_dev *sdev); int snd_sof_debugfs_buf_item(struct snd_sof_dev *sdev, void *base, size_t size, const char *name, mode_t mode); -int snd_sof_trace_update_pos(struct snd_sof_dev *sdev, - struct sof_ipc_dma_trace_posn *posn); -void snd_sof_trace_notify_for_error(struct snd_sof_dev *sdev); void snd_sof_get_status(struct snd_sof_dev *sdev, u32 panic_code, u32 tracep_code, void *oops, struct sof_ipc_panic_info *panic_info, void *stack, size_t stack_words); -int snd_sof_init_trace_ipc(struct snd_sof_dev *sdev); void snd_sof_handle_fw_exception(struct snd_sof_dev *sdev); int snd_sof_dbg_memory_info_init(struct snd_sof_dev *sdev); int snd_sof_debugfs_add_region_item_iomem(struct snd_sof_dev *sdev, diff --git a/sound/soc/sof/trace.c b/sound/soc/sof/trace.c deleted file mode 100644 index 8862ef9eeb8a58..00000000000000 --- a/sound/soc/sof/trace.c +++ /dev/null @@ -1,577 +0,0 @@ -// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) -// -// This file is provided under a dual BSD/GPLv2 license. When using or -// redistributing this file, you may do so under either license. -// -// Copyright(c) 2018 Intel Corporation. All rights reserved. -// -// Author: Liam Girdwood -// - -#include -#include -#include "sof-priv.h" -#include "ops.h" -#include "sof-utils.h" - -#define TRACE_FILTER_ELEMENTS_PER_ENTRY 4 -#define TRACE_FILTER_MAX_CONFIG_STRING_LENGTH 1024 - -static int trace_filter_append_elem(struct snd_sof_dev *sdev, uint32_t key, uint32_t value, - struct sof_ipc_trace_filter_elem *elem_list, - int capacity, int *counter) -{ - if (*counter >= capacity) - return -ENOMEM; - - elem_list[*counter].key = key; - elem_list[*counter].value = value; - ++*counter; - - return 0; -} - -static int trace_filter_parse_entry(struct snd_sof_dev *sdev, const char *line, - struct sof_ipc_trace_filter_elem *elem, - int capacity, int *counter) -{ - int len = strlen(line); - int cnt = *counter; - uint32_t uuid_id; - int log_level; - int pipe_id; - int comp_id; - int read; - int ret; - - /* ignore empty content */ - ret = sscanf(line, " %n", &read); - if (!ret && read == len) - return len; - - ret = sscanf(line, " %d %x %d %d %n", &log_level, &uuid_id, &pipe_id, &comp_id, &read); - if (ret != TRACE_FILTER_ELEMENTS_PER_ENTRY || read != len) { - dev_err(sdev->dev, "error: invalid trace filter entry '%s'\n", line); - return -EINVAL; - } - - if (uuid_id > 0) { - ret = trace_filter_append_elem(sdev, SOF_IPC_TRACE_FILTER_ELEM_BY_UUID, - uuid_id, elem, capacity, &cnt); - if (ret) - return ret; - } - if (pipe_id >= 0) { - ret = trace_filter_append_elem(sdev, SOF_IPC_TRACE_FILTER_ELEM_BY_PIPE, - pipe_id, elem, capacity, &cnt); - if (ret) - return ret; - } - if (comp_id >= 0) { - ret = trace_filter_append_elem(sdev, SOF_IPC_TRACE_FILTER_ELEM_BY_COMP, - comp_id, elem, capacity, &cnt); - if (ret) - return ret; - } - - ret = trace_filter_append_elem(sdev, SOF_IPC_TRACE_FILTER_ELEM_SET_LEVEL | - SOF_IPC_TRACE_FILTER_ELEM_FIN, - log_level, elem, capacity, &cnt); - if (ret) - return ret; - - /* update counter only when parsing whole entry passed */ - *counter = cnt; - - return len; -} - -static int trace_filter_parse(struct snd_sof_dev *sdev, char *string, - int *out_elem_cnt, - struct sof_ipc_trace_filter_elem **out) -{ - static const char entry_delimiter[] = ";"; - char *entry = string; - int capacity = 0; - int entry_len; - int cnt = 0; - - /* - * Each entry contains at least 1, up to TRACE_FILTER_ELEMENTS_PER_ENTRY - * IPC elements, depending on content. Calculate IPC elements capacity - * for the input string where each element is set. - */ - while (entry) { - capacity += TRACE_FILTER_ELEMENTS_PER_ENTRY; - entry = strchr(entry + 1, entry_delimiter[0]); - } - *out = kmalloc(capacity * sizeof(**out), GFP_KERNEL); - if (!*out) - return -ENOMEM; - - /* split input string by ';', and parse each entry separately in trace_filter_parse_entry */ - while ((entry = strsep(&string, entry_delimiter))) { - entry_len = trace_filter_parse_entry(sdev, entry, *out, capacity, &cnt); - if (entry_len < 0) { - dev_err(sdev->dev, "error: %s failed for '%s', %d\n", __func__, entry, - entry_len); - return -EINVAL; - } - } - - *out_elem_cnt = cnt; - - return 0; -} - -static int sof_ipc_trace_update_filter(struct snd_sof_dev *sdev, int num_elems, - struct sof_ipc_trace_filter_elem *elems) -{ - struct sof_ipc_trace_filter *msg; - struct sof_ipc_reply reply; - size_t size; - int ret; - - size = struct_size(msg, elems, num_elems); - if (size > SOF_IPC_MSG_MAX_SIZE) - return -EINVAL; - - msg = kmalloc(size, GFP_KERNEL); - if (!msg) - return -ENOMEM; - - msg->hdr.size = size; - msg->hdr.cmd = SOF_IPC_GLB_TRACE_MSG | SOF_IPC_TRACE_FILTER_UPDATE; - msg->elem_cnt = num_elems; - memcpy(&msg->elems[0], elems, num_elems * sizeof(*elems)); - - ret = pm_runtime_get_sync(sdev->dev); - if (ret < 0 && ret != -EACCES) { - pm_runtime_put_noidle(sdev->dev); - dev_err(sdev->dev, "error: enabling device failed: %d\n", ret); - goto error; - } - ret = sof_ipc_tx_message(sdev->ipc, msg->hdr.cmd, msg, msg->hdr.size, - &reply, sizeof(reply)); - pm_runtime_mark_last_busy(sdev->dev); - pm_runtime_put_autosuspend(sdev->dev); - -error: - kfree(msg); - return ret ? ret : reply.error; -} - -static ssize_t sof_dfsentry_trace_filter_write(struct file *file, const char __user *from, - size_t count, loff_t *ppos) -{ - struct snd_sof_dfsentry *dfse = file->private_data; - struct sof_ipc_trace_filter_elem *elems = NULL; - struct snd_sof_dev *sdev = dfse->sdev; - loff_t pos = 0; - int num_elems; - char *string; - int ret; - - if (count > TRACE_FILTER_MAX_CONFIG_STRING_LENGTH) { - dev_err(sdev->dev, "%s too long input, %zu > %d\n", __func__, count, - TRACE_FILTER_MAX_CONFIG_STRING_LENGTH); - return -EINVAL; - } - - string = kmalloc(count + 1, GFP_KERNEL); - if (!string) - return -ENOMEM; - - /* assert null termination */ - string[count] = 0; - ret = simple_write_to_buffer(string, count, &pos, from, count); - if (ret < 0) - goto error; - - ret = trace_filter_parse(sdev, string, &num_elems, &elems); - if (ret < 0) { - dev_err(sdev->dev, "error: fail in trace_filter_parse, %d\n", ret); - goto error; - } - - if (num_elems) { - ret = sof_ipc_trace_update_filter(sdev, num_elems, elems); - if (ret < 0) { - dev_err(sdev->dev, "error: fail in sof_ipc_trace_update_filter %d\n", ret); - goto error; - } - } - ret = count; -error: - kfree(string); - kfree(elems); - return ret; -} - -static const struct file_operations sof_dfs_trace_filter_fops = { - .open = simple_open, - .write = sof_dfsentry_trace_filter_write, - .llseek = default_llseek, -}; - -static int trace_debugfs_filter_create(struct snd_sof_dev *sdev) -{ - struct snd_sof_dfsentry *dfse; - - dfse = devm_kzalloc(sdev->dev, sizeof(*dfse), GFP_KERNEL); - if (!dfse) - return -ENOMEM; - - dfse->sdev = sdev; - dfse->type = SOF_DFSENTRY_TYPE_BUF; - - debugfs_create_file("filter", 0200, sdev->debugfs_root, dfse, - &sof_dfs_trace_filter_fops); - /* add to dfsentry list */ - list_add(&dfse->list, &sdev->dfsentry_list); - - return 0; -} - -static size_t sof_trace_avail(struct snd_sof_dev *sdev, - loff_t pos, size_t buffer_size) -{ - loff_t host_offset = READ_ONCE(sdev->host_offset); - - /* - * If host offset is less than local pos, it means write pointer of - * host DMA buffer has been wrapped. We should output the trace data - * at the end of host DMA buffer at first. - */ - if (host_offset < pos) - return buffer_size - pos; - - /* If there is available trace data now, it is unnecessary to wait. */ - if (host_offset > pos) - return host_offset - pos; - - return 0; -} - -static size_t sof_wait_trace_avail(struct snd_sof_dev *sdev, - loff_t pos, size_t buffer_size) -{ - wait_queue_entry_t wait; - size_t ret = sof_trace_avail(sdev, pos, buffer_size); - - /* data immediately available */ - if (ret) - return ret; - - if (!sdev->dtrace_is_enabled && sdev->dtrace_draining) { - /* - * tracing has ended and all traces have been - * read by client, return EOF - */ - sdev->dtrace_draining = false; - return 0; - } - - /* wait for available trace data from FW */ - init_waitqueue_entry(&wait, current); - set_current_state(TASK_INTERRUPTIBLE); - add_wait_queue(&sdev->trace_sleep, &wait); - - if (!signal_pending(current)) { - /* set timeout to max value, no error code */ - schedule_timeout(MAX_SCHEDULE_TIMEOUT); - } - remove_wait_queue(&sdev->trace_sleep, &wait); - - return sof_trace_avail(sdev, pos, buffer_size); -} - -static ssize_t sof_dfsentry_trace_read(struct file *file, char __user *buffer, - size_t count, loff_t *ppos) -{ - struct snd_sof_dfsentry *dfse = file->private_data; - struct snd_sof_dev *sdev = dfse->sdev; - unsigned long rem; - loff_t lpos = *ppos; - size_t avail, buffer_size = dfse->size; - u64 lpos_64; - - /* make sure we know about any failures on the DSP side */ - sdev->dtrace_error = false; - - /* check pos and count */ - if (lpos < 0) - return -EINVAL; - if (!count) - return 0; - - /* check for buffer wrap and count overflow */ - lpos_64 = lpos; - lpos = do_div(lpos_64, buffer_size); - - if (count > buffer_size - lpos) /* min() not used to avoid sparse warnings */ - count = buffer_size - lpos; - - /* get available count based on current host offset */ - avail = sof_wait_trace_avail(sdev, lpos, buffer_size); - if (sdev->dtrace_error) { - dev_err(sdev->dev, "error: trace IO error\n"); - return -EIO; - } - - /* make sure count is <= avail */ - count = avail > count ? count : avail; - - /* copy available trace data to debugfs */ - rem = copy_to_user(buffer, ((u8 *)(dfse->buf) + lpos), count); - if (rem) - return -EFAULT; - - *ppos += count; - - /* move debugfs reading position */ - return count; -} - -static int sof_dfsentry_trace_release(struct inode *inode, struct file *file) -{ - struct snd_sof_dfsentry *dfse = inode->i_private; - struct snd_sof_dev *sdev = dfse->sdev; - - /* avoid duplicate traces at next open */ - if (!sdev->dtrace_is_enabled) - sdev->host_offset = 0; - - return 0; -} - -static const struct file_operations sof_dfs_trace_fops = { - .open = simple_open, - .read = sof_dfsentry_trace_read, - .llseek = default_llseek, - .release = sof_dfsentry_trace_release, -}; - -static int trace_debugfs_create(struct snd_sof_dev *sdev) -{ - struct snd_sof_dfsentry *dfse; - int ret; - - if (!sdev) - return -EINVAL; - - ret = trace_debugfs_filter_create(sdev); - if (ret < 0) - dev_err(sdev->dev, "error: fail in %s, %d", __func__, ret); - - dfse = devm_kzalloc(sdev->dev, sizeof(*dfse), GFP_KERNEL); - if (!dfse) - return -ENOMEM; - - dfse->type = SOF_DFSENTRY_TYPE_BUF; - dfse->buf = sdev->dmatb.area; - dfse->size = sdev->dmatb.bytes; - dfse->sdev = sdev; - - debugfs_create_file("trace", 0444, sdev->debugfs_root, dfse, - &sof_dfs_trace_fops); - - return 0; -} - -int snd_sof_init_trace_ipc(struct snd_sof_dev *sdev) -{ - struct sof_ipc_fw_ready *ready = &sdev->fw_ready; - struct sof_ipc_fw_version *v = &ready->version; - struct sof_ipc_dma_trace_params_ext params; - struct sof_ipc_reply ipc_reply; - int ret; - - if (!sdev->dtrace_is_supported) - return 0; - - if (sdev->dtrace_is_enabled || !sdev->dma_trace_pages) - return -EINVAL; - - /* set IPC parameters */ - params.hdr.cmd = SOF_IPC_GLB_TRACE_MSG; - /* PARAMS_EXT is only supported from ABI 3.7.0 onwards */ - if (v->abi_version >= SOF_ABI_VER(3, 7, 0)) { - params.hdr.size = sizeof(struct sof_ipc_dma_trace_params_ext); - params.hdr.cmd |= SOF_IPC_TRACE_DMA_PARAMS_EXT; - params.timestamp_ns = ktime_get(); /* in nanosecond */ - } else { - params.hdr.size = sizeof(struct sof_ipc_dma_trace_params); - params.hdr.cmd |= SOF_IPC_TRACE_DMA_PARAMS; - } - params.buffer.phy_addr = sdev->dmatp.addr; - params.buffer.size = sdev->dmatb.bytes; - params.buffer.pages = sdev->dma_trace_pages; - params.stream_tag = 0; - - sdev->host_offset = 0; - sdev->dtrace_draining = false; - - ret = snd_sof_dma_trace_init(sdev, ¶ms.stream_tag); - if (ret < 0) { - dev_err(sdev->dev, - "error: fail in snd_sof_dma_trace_init %d\n", ret); - return ret; - } - dev_dbg(sdev->dev, "%s: stream_tag: %d\n", __func__, params.stream_tag); - - /* send IPC to the DSP */ - ret = sof_ipc_tx_message(sdev->ipc, - params.hdr.cmd, ¶ms, sizeof(params), - &ipc_reply, sizeof(ipc_reply)); - if (ret < 0) { - dev_err(sdev->dev, - "error: can't set params for DMA for trace %d\n", ret); - goto trace_release; - } - - ret = snd_sof_dma_trace_trigger(sdev, SNDRV_PCM_TRIGGER_START); - if (ret < 0) { - dev_err(sdev->dev, - "error: snd_sof_dma_trace_trigger: start: %d\n", ret); - goto trace_release; - } - - sdev->dtrace_is_enabled = true; - - return 0; - -trace_release: - snd_sof_dma_trace_release(sdev); - return ret; -} - -int snd_sof_init_trace(struct snd_sof_dev *sdev) -{ - int ret; - - if (!sdev->dtrace_is_supported) - return 0; - - /* set false before start initialization */ - sdev->dtrace_is_enabled = false; - - /* allocate trace page table buffer */ - ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, sdev->dev, - PAGE_SIZE, &sdev->dmatp); - if (ret < 0) { - dev_err(sdev->dev, - "error: can't alloc page table for trace %d\n", ret); - return ret; - } - - /* allocate trace data buffer */ - ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV_SG, sdev->dev, - DMA_BUF_SIZE_FOR_TRACE, &sdev->dmatb); - if (ret < 0) { - dev_err(sdev->dev, - "error: can't alloc buffer for trace %d\n", ret); - goto page_err; - } - - /* create compressed page table for audio firmware */ - ret = snd_sof_create_page_table(sdev->dev, &sdev->dmatb, - sdev->dmatp.area, sdev->dmatb.bytes); - if (ret < 0) - goto table_err; - - sdev->dma_trace_pages = ret; - dev_dbg(sdev->dev, "%s: dma_trace_pages: %d\n", - __func__, sdev->dma_trace_pages); - - if (sdev->first_boot) { - ret = trace_debugfs_create(sdev); - if (ret < 0) - goto table_err; - } - - init_waitqueue_head(&sdev->trace_sleep); - - ret = snd_sof_init_trace_ipc(sdev); - if (ret < 0) - goto table_err; - - return 0; -table_err: - sdev->dma_trace_pages = 0; - snd_dma_free_pages(&sdev->dmatb); -page_err: - snd_dma_free_pages(&sdev->dmatp); - return ret; -} -EXPORT_SYMBOL(snd_sof_init_trace); - -int snd_sof_trace_update_pos(struct snd_sof_dev *sdev, - struct sof_ipc_dma_trace_posn *posn) -{ - if (!sdev->dtrace_is_supported) - return 0; - - if (sdev->dtrace_is_enabled && sdev->host_offset != posn->host_offset) { - sdev->host_offset = posn->host_offset; - wake_up(&sdev->trace_sleep); - } - - if (posn->overflow != 0) - dev_err(sdev->dev, - "error: DSP trace buffer overflow %u bytes. Total messages %d\n", - posn->overflow, posn->messages); - - return 0; -} - -/* an error has occurred within the DSP that prevents further trace */ -void snd_sof_trace_notify_for_error(struct snd_sof_dev *sdev) -{ - if (!sdev->dtrace_is_supported) - return; - - if (sdev->dtrace_is_enabled) { - sdev->dtrace_error = true; - wake_up(&sdev->trace_sleep); - } -} -EXPORT_SYMBOL(snd_sof_trace_notify_for_error); - -void snd_sof_release_trace(struct snd_sof_dev *sdev) -{ - int ret; - - if (!sdev->dtrace_is_supported || !sdev->dtrace_is_enabled) - return; - - ret = snd_sof_dma_trace_trigger(sdev, SNDRV_PCM_TRIGGER_STOP); - if (ret < 0) - dev_err(sdev->dev, - "error: snd_sof_dma_trace_trigger: stop: %d\n", ret); - - ret = snd_sof_dma_trace_release(sdev); - if (ret < 0) - dev_err(sdev->dev, - "error: fail in snd_sof_dma_trace_release %d\n", ret); - - sdev->dtrace_is_enabled = false; - sdev->dtrace_draining = true; - wake_up(&sdev->trace_sleep); -} -EXPORT_SYMBOL(snd_sof_release_trace); - -void snd_sof_free_trace(struct snd_sof_dev *sdev) -{ - if (!sdev->dtrace_is_supported) - return; - - snd_sof_release_trace(sdev); - - if (sdev->dma_trace_pages) { - snd_dma_free_pages(&sdev->dmatb); - snd_dma_free_pages(&sdev->dmatp); - sdev->dma_trace_pages = 0; - } -} -EXPORT_SYMBOL(snd_sof_free_trace); From 5c75bc5bda4f66b80f9a79ccf50a1ef60ada3362 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 21 Jul 2021 16:17:06 +0300 Subject: [PATCH 26/32] ASoC: SOF: sof-client-dma-trace: Add protection against file/module removal Based on debugging the following issues were identified: if the snd_sof_dma_trace module is removed while the sof-logger keeps the trace file open: The module's remove will block on the debugfs_remove() until the sof-logger is stopped (w/ C-c). The sof_dfsentry_trace_read() will return and the debugfs file is removed, the module remove completes. Then the debugfs file's release callback got called but the file itself was already removed! The second issue is that while the client driver is loaded and sof-logger keeps the trace file open, the user can rmmod the parent driver for the DSP itself which will removes the SOF client devices and the same chain of events will happen. With this patch we protect the debugfs file from removal with the debugfs_file_get/put and we protect the parent device against removal while the module is in active use (the debugfs file is open). Signed-off-by: Peter Ujfalusi --- sound/soc/sof/sof-client-dma-trace.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/sound/soc/sof/sof-client-dma-trace.c b/sound/soc/sof/sof-client-dma-trace.c index 22ba21d143f722..576f76547b6cad 100644 --- a/sound/soc/sof/sof-client-dma-trace.c +++ b/sound/soc/sof/sof-client-dma-trace.c @@ -291,6 +291,21 @@ static size_t sof_wait_trace_avail(struct sof_client_dev *cdev, return sof_trace_avail(cdev, pos, buffer_size); } +static int sof_dfsentry_trace_open(struct inode *inode, struct file *file) +{ + int ret; + + ret = debugfs_file_get(file->f_path.dentry); + if (unlikely(ret)) + return ret; + + ret = simple_open(inode, file); + if (ret) + debugfs_file_put(file->f_path.dentry); + + return ret; +} + static ssize_t sof_dfsentry_trace_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos) { @@ -348,11 +363,12 @@ static int sof_dfsentry_trace_release(struct inode *inode, struct file *file) if (!priv->dtrace_is_enabled) priv->host_offset = 0; + debugfs_file_put(file->f_path.dentry); return 0; } static const struct file_operations sof_dtrace_trace_fops = { - .open = simple_open, + .open = sof_dfsentry_trace_open, .read = sof_dfsentry_trace_read, .llseek = default_llseek, .release = sof_dfsentry_trace_release, From b3089afd3eb62f8e59618c6b46005911d408b40b Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Fri, 20 Aug 2021 14:07:41 +0300 Subject: [PATCH 27/32] ASoC: SOF: sof-client-dma-trace: Block the dtrace if the firmware has crashed Do not allow the dma trace to be started if the firmware is in crashed state. Signed-off-by: Peter Ujfalusi --- sound/soc/sof/sof-client-dma-trace.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sound/soc/sof/sof-client-dma-trace.c b/sound/soc/sof/sof-client-dma-trace.c index 576f76547b6cad..d23e4c0a427106 100644 --- a/sound/soc/sof/sof-client-dma-trace.c +++ b/sound/soc/sof/sof-client-dma-trace.c @@ -293,8 +293,12 @@ static size_t sof_wait_trace_avail(struct sof_client_dev *cdev, static int sof_dfsentry_trace_open(struct inode *inode, struct file *file) { + struct sof_client_dev *cdev = inode->i_private; int ret; + if (sof_client_get_fw_state(cdev) == SOF_FW_CRASHED) + return -ENODEV; + ret = debugfs_file_get(file->f_path.dentry); if (unlikely(ret)) return ret; From b269552ed8152b3242d260f1e45003ed131355d9 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Fri, 16 Jul 2021 12:19:55 +0300 Subject: [PATCH 28/32] ASoC: SOF: sof-client-dma-trace: Simplify count adjustment in trace_read The first count check and fixup against "buffer - lpos" can be removed as we will do the adjustment later against the "avail" in sof_dfsentry_trace_read() Signed-off-by: Peter Ujfalusi --- sound/soc/sof/sof-client-dma-trace.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/sound/soc/sof/sof-client-dma-trace.c b/sound/soc/sof/sof-client-dma-trace.c index d23e4c0a427106..e08fcf5023c046 100644 --- a/sound/soc/sof/sof-client-dma-trace.c +++ b/sound/soc/sof/sof-client-dma-trace.c @@ -334,9 +334,6 @@ static ssize_t sof_dfsentry_trace_read(struct file *file, char __user *buffer, lpos_64 = lpos; lpos = do_div(lpos_64, buffer_size); - if (count > buffer_size - lpos) /* min() not used to avoid sparse warnings */ - count = buffer_size - lpos; - /* get available count based on current host offset */ avail = sof_wait_trace_avail(cdev, lpos, buffer_size); if (priv->dtrace_error) { @@ -345,7 +342,8 @@ static ssize_t sof_dfsentry_trace_read(struct file *file, char __user *buffer, } /* make sure count is <= avail */ - count = avail > count ? count : avail; + if (count > avail) + count = avail; /* copy available trace data to debugfs */ rem = copy_to_user(buffer, ((u8 *)(priv->dmatb.area) + lpos), count); From fd4a66c80d01ed0636c3e16283390b2e0cd43aa4 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Fri, 16 Jul 2021 12:42:21 +0300 Subject: [PATCH 29/32] ASoC: SOF: sof-client-dma-trace: Coding style cleanups Reduce the number of long lines where it can be done in a clean way. Signed-off-by: Peter Ujfalusi --- sound/soc/sof/sof-client-dma-trace.c | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/sound/soc/sof/sof-client-dma-trace.c b/sound/soc/sof/sof-client-dma-trace.c index e08fcf5023c046..ffb779634e185c 100644 --- a/sound/soc/sof/sof-client-dma-trace.c +++ b/sound/soc/sof/sof-client-dma-trace.c @@ -74,7 +74,8 @@ static int trace_filter_parse_entry(struct sof_client_dev *cdev, const char *lin if (!ret && read == len) return len; - ret = sscanf(line, " %d %x %d %d %n", &log_level, &uuid_id, &pipe_id, &comp_id, &read); + ret = sscanf(line, " %d %x %d %d %n", + &log_level, &uuid_id, &pipe_id, &comp_id, &read); if (ret != TRACE_FILTER_ELEMENTS_PER_ENTRY || read != len) { dev_err(priv->dev, "invalid trace filter entry '%s'\n", line); return -EINVAL; @@ -135,12 +136,12 @@ static int trace_filter_parse(struct sof_client_dev *cdev, char *string, if (!*out) return -ENOMEM; - /* split input string by ';', and parse each entry separately in trace_filter_parse_entry */ + /* split input string by ';', and parse each entry separately */ while ((entry = strsep(&string, entry_delimiter))) { entry_len = trace_filter_parse_entry(cdev, entry, *out, capacity, &cnt); if (entry_len < 0) { - dev_err(priv->dev, "%s failed for '%s', %d\n", __func__, entry, - entry_len); + dev_err(priv->dev, "%s failed for '%s', %d\n", __func__, + entry, entry_len); return -EINVAL; } } @@ -178,7 +179,8 @@ static int sof_ipc_trace_update_filter(struct sof_client_dev *cdev, int num_elem return ret ? ret : reply.error; } -static ssize_t sof_dfsentry_trace_filter_write(struct file *file, const char __user *from, +static ssize_t sof_dfsentry_trace_filter_write(struct file *file, + const char __user *from, size_t count, loff_t *ppos) { struct sof_client_dev *cdev = file->private_data; @@ -194,8 +196,8 @@ static ssize_t sof_dfsentry_trace_filter_write(struct file *file, const char __u } if (count > TRACE_FILTER_MAX_CONFIG_STRING_LENGTH) { - dev_err(priv->dev, "%s too long input, %zu > %d\n", __func__, count, - TRACE_FILTER_MAX_CONFIG_STRING_LENGTH); + dev_err(priv->dev, "%s too long input, %zu > %d\n", __func__, + count, TRACE_FILTER_MAX_CONFIG_STRING_LENGTH); return -EINVAL; } @@ -236,8 +238,8 @@ static const struct file_operations sof_dtrace_filter_fops = { .owner = THIS_MODULE, }; -static size_t sof_trace_avail(struct sof_client_dev *cdev, - loff_t pos, size_t buffer_size) +static size_t sof_trace_avail(struct sof_client_dev *cdev, loff_t pos, + size_t buffer_size) { struct sof_dtrace_priv *priv = cdev->data; loff_t host_offset = READ_ONCE(priv->host_offset); @@ -257,8 +259,8 @@ static size_t sof_trace_avail(struct sof_client_dev *cdev, return 0; } -static size_t sof_wait_trace_avail(struct sof_client_dev *cdev, - loff_t pos, size_t buffer_size) +static size_t sof_wait_trace_avail(struct sof_client_dev *cdev, loff_t pos, + size_t buffer_size) { size_t ret = sof_trace_avail(cdev, pos, buffer_size); struct sof_dtrace_priv *priv = cdev->data; From 5e3f49213c808cebf949cd7f50a06094dad3c7ce Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 22 Jul 2021 15:26:12 +0300 Subject: [PATCH 30/32] ASoC: SOF: sof-client-ipc-test: Code cleanup for consistency Rename the "struct sof_ipc_client_data" to "struct sof_ipc_test_priv" Remove "error: " prefixing from error prints Rename the debugfs callbacks from sof_ipc_dfsentry_* to sof_ipc_test_dfs_* Long line wrapping cleanups Signed-off-by: Peter Ujfalusi --- sound/soc/sof/sof-client-ipc-test.c | 92 ++++++++++++++--------------- 1 file changed, 45 insertions(+), 47 deletions(-) diff --git a/sound/soc/sof/sof-client-ipc-test.c b/sound/soc/sof/sof-client-ipc-test.c index 9509d7e40f844c..332321741a8b2f 100644 --- a/sound/soc/sof/sof-client-ipc-test.c +++ b/sound/soc/sof/sof-client-ipc-test.c @@ -25,13 +25,13 @@ #define DEBUGFS_IPC_FLOOD_COUNT "ipc_flood_count" #define DEBUGFS_IPC_FLOOD_DURATION "ipc_flood_duration_ms" -struct sof_ipc_client_data { +struct sof_ipc_test_priv { struct dentry *dfs_root; struct dentry *dfs_link[2]; char *buf; }; -static int sof_ipc_dfsentry_open(struct inode *inode, struct file *file) +static int sof_ipc_test_dfs_open(struct inode *inode, struct file *file) { struct sof_client_dev *cdev = inode->i_private; int ret; @@ -59,7 +59,7 @@ static int sof_debug_ipc_flood_test(struct sof_client_dev *cdev, unsigned long ipc_duration_ms, unsigned long ipc_count) { - struct sof_ipc_client_data *ipc_client_data = cdev->data; + struct sof_ipc_test_priv *priv = cdev->data; struct device *dev = &cdev->auxdev.dev; struct sof_ipc_cmd_hdr hdr; struct sof_ipc_reply reply; @@ -108,7 +108,7 @@ static int sof_debug_ipc_flood_test(struct sof_client_dev *cdev, } if (ret < 0) - dev_err(dev, "error: ipc flood test failed at %d iterations\n", i); + dev_err(dev, "ipc flood test failed at %d iterations\n", i); /* return if the first IPC fails */ if (!i) @@ -118,26 +118,27 @@ static int sof_debug_ipc_flood_test(struct sof_client_dev *cdev, do_div(avg_response_time, i); /* clear previous test output */ - memset(ipc_client_data->buf, 0, IPC_FLOOD_TEST_RESULT_LEN); + memset(priv->buf, 0, IPC_FLOOD_TEST_RESULT_LEN); if (!ipc_count) { dev_dbg(dev, "IPC Flood test duration: %lums\n", ipc_duration_ms); - snprintf(ipc_client_data->buf, IPC_FLOOD_TEST_RESULT_LEN, + snprintf(priv->buf, IPC_FLOOD_TEST_RESULT_LEN, "IPC Flood test duration: %lums\n", ipc_duration_ms); } - dev_dbg(dev, - "IPC Flood count: %d, Avg response time: %lluns\n", i, avg_response_time); + dev_dbg(dev, "IPC Flood count: %d, Avg response time: %lluns\n", + i, avg_response_time); dev_dbg(dev, "Max response time: %lluns\n", max_response_time); dev_dbg(dev, "Min response time: %lluns\n", min_response_time); /* format output string and save test results */ - snprintf(ipc_client_data->buf + strlen(ipc_client_data->buf), - IPC_FLOOD_TEST_RESULT_LEN - strlen(ipc_client_data->buf), - "IPC Flood count: %d\nAvg response time: %lluns\n", i, avg_response_time); + snprintf(priv->buf + strlen(priv->buf), + IPC_FLOOD_TEST_RESULT_LEN - strlen(priv->buf), + "IPC Flood count: %d\nAvg response time: %lluns\n", + i, avg_response_time); - snprintf(ipc_client_data->buf + strlen(ipc_client_data->buf), - IPC_FLOOD_TEST_RESULT_LEN - strlen(ipc_client_data->buf), + snprintf(priv->buf + strlen(priv->buf), + IPC_FLOOD_TEST_RESULT_LEN - strlen(priv->buf), "Max response time: %lluns\nMin response time: %lluns\n", max_response_time, min_response_time); @@ -148,7 +149,7 @@ static int sof_debug_ipc_flood_test(struct sof_client_dev *cdev, * Writing to the debugfs entry initiates the IPC flood test based on * the IPC count or the duration specified by the user. */ -static ssize_t sof_ipc_dfsentry_write(struct file *file, const char __user *buffer, +static ssize_t sof_ipc_test_dfs_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos) { struct sof_client_dev *cdev = file->private_data; @@ -216,9 +217,7 @@ static ssize_t sof_ipc_dfsentry_write(struct file *file, const char __user *buff ret = pm_runtime_get_sync(dev); if (ret < 0 && ret != -EACCES) { - dev_err_ratelimited(dev, - "error: debugfs write failed to resume %d\n", - ret); + dev_err_ratelimited(dev, "debugfs write failed to resume %d\n", ret); pm_runtime_put_noidle(dev); goto out; } @@ -230,9 +229,7 @@ static ssize_t sof_ipc_dfsentry_write(struct file *file, const char __user *buff pm_runtime_mark_last_busy(dev); err = pm_runtime_put_autosuspend(dev); if (err < 0) - dev_err_ratelimited(dev, - "error: debugfs write failed to idle %d\n", - err); + dev_err_ratelimited(dev, "debugfs write failed to idle %d\n", err); /* return size if test is successful */ if (ret >= 0) @@ -243,11 +240,11 @@ static ssize_t sof_ipc_dfsentry_write(struct file *file, const char __user *buff } /* return the result of the last IPC flood test */ -static ssize_t sof_ipc_dfsentry_read(struct file *file, char __user *buffer, +static ssize_t sof_ipc_test_dfs_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos) { struct sof_client_dev *cdev = file->private_data; - struct sof_ipc_client_data *ipc_client_data = cdev->data; + struct sof_ipc_test_priv *priv = cdev->data; size_t size_ret; struct dentry *dentry; @@ -258,8 +255,8 @@ static ssize_t sof_ipc_dfsentry_read(struct file *file, char __user *buffer, if (*ppos) return 0; - count = min_t(size_t, count, strlen(ipc_client_data->buf)); - size_ret = copy_to_user(buffer, ipc_client_data->buf, count); + count = min_t(size_t, count, strlen(priv->buf)); + size_ret = copy_to_user(buffer, priv->buf, count); if (size_ret) return -EFAULT; @@ -269,19 +266,19 @@ static ssize_t sof_ipc_dfsentry_read(struct file *file, char __user *buffer, return count; } -static int sof_ipc_dfsentry_release(struct inode *inode, struct file *file) +static int sof_ipc_test_dfs_release(struct inode *inode, struct file *file) { debugfs_file_put(file->f_path.dentry); return 0; } -static const struct file_operations sof_ipc_dfs_fops = { - .open = sof_ipc_dfsentry_open, - .read = sof_ipc_dfsentry_read, +static const struct file_operations sof_ipc_test_fops = { + .open = sof_ipc_test_dfs_open, + .read = sof_ipc_test_dfs_read, .llseek = default_llseek, - .write = sof_ipc_dfsentry_write, - .release = sof_ipc_dfsentry_release, + .write = sof_ipc_test_dfs_write, + .release = sof_ipc_test_dfs_release, .owner = THIS_MODULE, }; @@ -299,30 +296,31 @@ static int sof_ipc_test_probe(struct auxiliary_device *auxdev, const struct auxiliary_device_id *id) { struct sof_client_dev *cdev = auxiliary_dev_to_sof_client_dev(auxdev); - struct sof_ipc_client_data *ipc_client_data; struct dentry *debugfs_root = sof_client_get_debugfs_root(cdev); + struct sof_ipc_test_priv *priv; /* allocate memory for client data */ - ipc_client_data = devm_kzalloc(&auxdev->dev, sizeof(*ipc_client_data), GFP_KERNEL); - if (!ipc_client_data) + priv = devm_kzalloc(&auxdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) return -ENOMEM; - ipc_client_data->buf = devm_kzalloc(&auxdev->dev, IPC_FLOOD_TEST_RESULT_LEN, GFP_KERNEL); - if (!ipc_client_data->buf) + priv->buf = devm_kzalloc(&auxdev->dev, IPC_FLOOD_TEST_RESULT_LEN, + GFP_KERNEL); + if (!priv->buf) return -ENOMEM; - cdev->data = ipc_client_data; + cdev->data = priv; /* create debugfs root folder with device name under parent SOF dir */ - ipc_client_data->dfs_root = debugfs_create_dir(dev_name(&auxdev->dev), debugfs_root); - if (!IS_ERR_OR_NULL(ipc_client_data->dfs_root)) { + priv->dfs_root = debugfs_create_dir(dev_name(&auxdev->dev), debugfs_root); + if (!IS_ERR_OR_NULL(priv->dfs_root)) { /* create read-write ipc_flood_count debugfs entry */ - debugfs_create_file(DEBUGFS_IPC_FLOOD_COUNT, 0644, - ipc_client_data->dfs_root, cdev, &sof_ipc_dfs_fops); + debugfs_create_file(DEBUGFS_IPC_FLOOD_COUNT, 0644, priv->dfs_root, + cdev, &sof_ipc_test_fops); /* create read-write ipc_flood_duration_ms debugfs entry */ debugfs_create_file(DEBUGFS_IPC_FLOOD_DURATION, 0644, - ipc_client_data->dfs_root, cdev, &sof_ipc_dfs_fops); + priv->dfs_root, cdev, &sof_ipc_test_fops); if (auxdev->id == 0) { /* @@ -333,13 +331,13 @@ static int sof_ipc_test_probe(struct auxiliary_device *auxdev, snprintf(target, 100, "%s/" DEBUGFS_IPC_FLOOD_COUNT, dev_name(&auxdev->dev)); - ipc_client_data->dfs_link[0] = + priv->dfs_link[0] = debugfs_create_symlink(DEBUGFS_IPC_FLOOD_COUNT, debugfs_root, target); snprintf(target, 100, "%s/" DEBUGFS_IPC_FLOOD_DURATION, dev_name(&auxdev->dev)); - ipc_client_data->dfs_link[1] = + priv->dfs_link[1] = debugfs_create_symlink(DEBUGFS_IPC_FLOOD_DURATION, debugfs_root, target); } @@ -358,16 +356,16 @@ static int sof_ipc_test_probe(struct auxiliary_device *auxdev, static void sof_ipc_test_remove(struct auxiliary_device *auxdev) { struct sof_client_dev *cdev = auxiliary_dev_to_sof_client_dev(auxdev); - struct sof_ipc_client_data *ipc_client_data = cdev->data; + struct sof_ipc_test_priv *priv = cdev->data; pm_runtime_disable(&auxdev->dev); if (auxdev->id == 0) { - debugfs_remove(ipc_client_data->dfs_link[0]); - debugfs_remove(ipc_client_data->dfs_link[1]); + debugfs_remove(priv->dfs_link[0]); + debugfs_remove(priv->dfs_link[1]); } - debugfs_remove_recursive(ipc_client_data->dfs_root); + debugfs_remove_recursive(priv->dfs_root); } static const struct auxiliary_device_id sof_ipc_test_client_id_table[] = { From f763eeafe676814171b556cdb3ae0facf3dfae0e Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 22 Jul 2021 15:28:26 +0300 Subject: [PATCH 31/32] ASoC: SOF: sof-client-probes: Code cleanup for consistency Rename the debugfs callbacks to be somehow consistent Remove the "error: " prefix from error prints Signed-off-by: Peter Ujfalusi --- sound/soc/sof/sof-client-probes.c | 47 ++++++++++++++++--------------- 1 file changed, 24 insertions(+), 23 deletions(-) diff --git a/sound/soc/sof/sof-client-probes.c b/sound/soc/sof/sof-client-probes.c index a071a28ef2f943..727aa25ca1fec9 100644 --- a/sound/soc/sof/sof-client-probes.c +++ b/sound/soc/sof/sof-client-probes.c @@ -482,8 +482,8 @@ static int tokenize_input(const char __user *from, size_t count, return ret; } -static ssize_t probe_points_read(struct file *file, char __user *to, - size_t count, loff_t *ppos) +static ssize_t sof_probes_dfs_points_read(struct file *file, char __user *to, + size_t count, loff_t *ppos) { struct sof_client_dev *cdev = file->private_data; struct sof_probes_priv *priv = cdev->data; @@ -505,7 +505,7 @@ static ssize_t probe_points_read(struct file *file, char __user *to, ret = pm_runtime_get_sync(dev); if (ret < 0 && ret != -EACCES) { - dev_err_ratelimited(dev, "error: debugfs read failed to resume %d\n", ret); + dev_err_ratelimited(dev, "debugfs read failed to resume %d\n", ret); pm_runtime_put_noidle(dev); goto exit; } @@ -517,7 +517,7 @@ static ssize_t probe_points_read(struct file *file, char __user *to, pm_runtime_mark_last_busy(dev); err = pm_runtime_put_autosuspend(dev); if (err < 0) - dev_err_ratelimited(dev, "error: debugfs read failed to idle %d\n", err); + dev_err_ratelimited(dev, "debugfs read failed to idle %d\n", err); for (i = 0; i < num_desc; i++) { remaining = PAGE_SIZE - strlen(buf); @@ -540,8 +540,9 @@ static ssize_t probe_points_read(struct file *file, char __user *to, return ret; } -static ssize_t probe_points_write(struct file *file, const char __user *from, - size_t count, loff_t *ppos) +static ssize_t +sof_probes_dfs_points_write(struct file *file, const char __user *from, + size_t count, loff_t *ppos) { struct sof_client_dev *cdev = file->private_data; struct sof_probes_priv *priv = cdev->data; @@ -569,7 +570,7 @@ static ssize_t probe_points_write(struct file *file, const char __user *from, ret = pm_runtime_get_sync(dev); if (ret < 0 && ret != -EACCES) { - dev_err_ratelimited(dev, "error: debugfs write failed to resume %d\n", ret); + dev_err_ratelimited(dev, "debugfs write failed to resume %d\n", ret); pm_runtime_put_noidle(dev); goto exit; } @@ -581,24 +582,24 @@ static ssize_t probe_points_write(struct file *file, const char __user *from, pm_runtime_mark_last_busy(dev); err = pm_runtime_put_autosuspend(dev); if (err < 0) - dev_err_ratelimited(dev, "error: debugfs write failed to idle %d\n", err); + dev_err_ratelimited(dev, "debugfs write failed to idle %d\n", err); exit: kfree(tkns); return ret; } -static const struct file_operations probe_points_fops = { +static const struct file_operations sof_probes_points_fops = { .open = simple_open, - .read = probe_points_read, - .write = probe_points_write, + .read = sof_probes_dfs_points_read, + .write = sof_probes_dfs_points_write, .llseek = default_llseek, .owner = THIS_MODULE, }; static ssize_t -probe_points_remove_write(struct file *file, const char __user *from, - size_t count, loff_t *ppos) +sof_probes_dfs_points_remove_write(struct file *file, const char __user *from, + size_t count, loff_t *ppos) { struct sof_client_dev *cdev = file->private_data; struct sof_probes_priv *priv = cdev->data; @@ -622,7 +623,7 @@ probe_points_remove_write(struct file *file, const char __user *from, ret = pm_runtime_get_sync(dev); if (ret < 0) { - dev_err_ratelimited(dev, "error: debugfs write failed to resume %d\n", ret); + dev_err_ratelimited(dev, "debugfs write failed to resume %d\n", ret); pm_runtime_put_noidle(dev); goto exit; } @@ -634,15 +635,15 @@ probe_points_remove_write(struct file *file, const char __user *from, pm_runtime_mark_last_busy(dev); err = pm_runtime_put_autosuspend(dev); if (err < 0) - dev_err_ratelimited(dev, "error: debugfs write failed to idle %d\n", err); + dev_err_ratelimited(dev, "debugfs write failed to idle %d\n", err); exit: kfree(tkns); return ret; } -static const struct file_operations probe_points_remove_fops = { +static const struct file_operations sof_probes_points_remove_fops = { .open = simple_open, - .write = probe_points_remove_write, + .write = sof_probes_dfs_points_remove_write, .llseek = default_llseek, .owner = THIS_MODULE, @@ -695,7 +696,7 @@ static int sof_probes_client_probe(struct auxiliary_device *auxdev, return -ENXIO; if (!dev->platform_data) { - dev_err(dev, "error: missing platform data\n"); + dev_err(dev, "missing platform data\n"); return -ENODEV; } @@ -707,7 +708,7 @@ static int sof_probes_client_probe(struct auxiliary_device *auxdev, if (!ops->assign || !ops->free || !ops->set_params || !ops->trigger || !ops->pointer) { - dev_err(dev, "error: missing platform callback(s)\n"); + dev_err(dev, "missing platform callback(s)\n"); return -ENODEV; } @@ -719,7 +720,7 @@ static int sof_probes_client_probe(struct auxiliary_device *auxdev, sof_probes_dai_drv, ARRAY_SIZE(sof_probes_dai_drv)); if (ret < 0) { - dev_err(dev, "error: failed to register SOF probes DAI driver %d\n", ret); + dev_err(dev, "failed to register SOF probes DAI driver %d\n", ret); return ret; } @@ -728,12 +729,12 @@ static int sof_probes_client_probe(struct auxiliary_device *auxdev, /* create read-write probes_points debugfs entry */ priv->dfs_points = debugfs_create_file("probe_points", 0644, dfsroot, - cdev, &probe_points_fops); + cdev, &sof_probes_points_fops); /* create read-write probe_points_remove debugfs entry */ priv->dfs_points_remove = debugfs_create_file("probe_points_remove", 0644, dfsroot, cdev, - &probe_points_remove_fops); + &sof_probes_points_remove_fops); links = devm_kcalloc(dev, SOF_PROBES_NUM_DAI_LINKS, sizeof(*links), GFP_KERNEL); cpus = devm_kcalloc(dev, SOF_PROBES_NUM_DAI_LINKS, sizeof(*cpus), GFP_KERNEL); @@ -772,7 +773,7 @@ static int sof_probes_client_probe(struct auxiliary_device *auxdev, if (ret < 0) { debugfs_remove(priv->dfs_points); debugfs_remove(priv->dfs_points_remove); - dev_err(dev, "error: Probes card register failed %d\n", ret); + dev_err(dev, "Probes card register failed %d\n", ret); return ret; } From 3bd20bb83d5696c51f1e768af2d51fc106b39bad Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 22 Jul 2021 15:29:27 +0300 Subject: [PATCH 32/32] ASoC: SOF: sof-client-dma-trace: Code cleanup for consistency Rename the debugfs callbacks to be somehow consistent Signed-off-by: Peter Ujfalusi --- sound/soc/sof/sof-client-dma-trace.c | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/sound/soc/sof/sof-client-dma-trace.c b/sound/soc/sof/sof-client-dma-trace.c index ffb779634e185c..081509b8848a91 100644 --- a/sound/soc/sof/sof-client-dma-trace.c +++ b/sound/soc/sof/sof-client-dma-trace.c @@ -179,9 +179,8 @@ static int sof_ipc_trace_update_filter(struct sof_client_dev *cdev, int num_elem return ret ? ret : reply.error; } -static ssize_t sof_dfsentry_trace_filter_write(struct file *file, - const char __user *from, - size_t count, loff_t *ppos) +static ssize_t sof_dtrace_dfs_filter_write(struct file *file, const char __user *from, + size_t count, loff_t *ppos) { struct sof_client_dev *cdev = file->private_data; struct sof_ipc_trace_filter_elem *elems = NULL; @@ -232,7 +231,7 @@ static ssize_t sof_dfsentry_trace_filter_write(struct file *file, static const struct file_operations sof_dtrace_filter_fops = { .open = simple_open, - .write = sof_dfsentry_trace_filter_write, + .write = sof_dtrace_dfs_filter_write, .llseek = default_llseek, .owner = THIS_MODULE, @@ -293,7 +292,7 @@ static size_t sof_wait_trace_avail(struct sof_client_dev *cdev, loff_t pos, return sof_trace_avail(cdev, pos, buffer_size); } -static int sof_dfsentry_trace_open(struct inode *inode, struct file *file) +static int sof_dtrace_dfs_trace_open(struct inode *inode, struct file *file) { struct sof_client_dev *cdev = inode->i_private; int ret; @@ -312,8 +311,8 @@ static int sof_dfsentry_trace_open(struct inode *inode, struct file *file) return ret; } -static ssize_t sof_dfsentry_trace_read(struct file *file, char __user *buffer, - size_t count, loff_t *ppos) +static ssize_t sof_dtrace_dfs_trace_read(struct file *file, char __user *buffer, + size_t count, loff_t *ppos) { struct sof_client_dev *cdev = file->private_data; struct sof_dtrace_priv *priv = cdev->data; @@ -358,7 +357,7 @@ static ssize_t sof_dfsentry_trace_read(struct file *file, char __user *buffer, return count; } -static int sof_dfsentry_trace_release(struct inode *inode, struct file *file) +static int sof_dtrace_dfs_trace_release(struct inode *inode, struct file *file) { struct sof_client_dev *cdev = inode->i_private; struct sof_dtrace_priv *priv = cdev->data; @@ -372,10 +371,10 @@ static int sof_dfsentry_trace_release(struct inode *inode, struct file *file) } static const struct file_operations sof_dtrace_trace_fops = { - .open = sof_dfsentry_trace_open, - .read = sof_dfsentry_trace_read, + .open = sof_dtrace_dfs_trace_open, + .read = sof_dtrace_dfs_trace_read, .llseek = default_llseek, - .release = sof_dfsentry_trace_release, + .release = sof_dtrace_dfs_trace_release, .owner = THIS_MODULE, };