diff --git a/sound/soc/sof/core.c b/sound/soc/sof/core.c index 1f38fe03e49395..b275c091245785 100644 --- a/sound/soc/sof/core.c +++ b/sound/soc/sof/core.c @@ -699,6 +699,7 @@ int snd_sof_device_probe(struct device *dev, struct snd_sof_pdata *plat_data) mutex_init(&sdev->ipc_client_mutex); mutex_init(&sdev->client_event_handler_mutex); mutex_init(&sdev->dsp_fw_boot_mutex); + mutex_init(&sdev->loaded_tplg_names_mutex); /* set default timeouts if none provided */ if (plat_data->desc->ipc_timeout == 0) diff --git a/sound/soc/sof/debug.c b/sound/soc/sof/debug.c index 9801c875dd7018..1ac13ddc5d1306 100644 --- a/sound/soc/sof/debug.c +++ b/sound/soc/sof/debug.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include "sof-priv.h" @@ -313,6 +314,28 @@ int snd_sof_dbg_memory_info_init(struct snd_sof_dev *sdev) } EXPORT_SYMBOL_GPL(snd_sof_dbg_memory_info_init); +static int sof_dbg_tplg_name_show(struct seq_file *m, void *v) +{ + struct snd_sof_dev *sdev = m->private; + + /* fall back to the selected topology name while no topology is loaded */ + mutex_lock(&sdev->loaded_tplg_names_mutex); + seq_printf(m, "%s\n", sdev->loaded_tplg_names ?: sdev->pdata->tplg_filename); + mutex_unlock(&sdev->loaded_tplg_names_mutex); + + return 0; +} +DEFINE_SHOW_ATTRIBUTE(sof_dbg_tplg_name); + +/* Takes ownership of the devm allocated tplg_names, NULL clears the list */ +void sof_debug_set_tplg_name(struct snd_sof_dev *sdev, char *tplg_names) +{ + mutex_lock(&sdev->loaded_tplg_names_mutex); + devm_kfree(sdev->dev, sdev->loaded_tplg_names); + sdev->loaded_tplg_names = tplg_names; + mutex_unlock(&sdev->loaded_tplg_names_mutex); +} + int snd_sof_dbg_init(struct snd_sof_dev *sdev) { const struct snd_sof_dsp_ops *ops = sof_ops(sdev); @@ -355,8 +378,8 @@ int snd_sof_dbg_init(struct snd_sof_dev *sdev) (char **)&plat_data->tplg_filename_prefix); debugfs_create_str("fw_name", 0444, fw_profile, (char **)&plat_data->fw_filename); - debugfs_create_str("tplg_name", 0444, fw_profile, - (char **)&plat_data->tplg_filename); + debugfs_create_file("tplg_name", 0444, fw_profile, sdev, + &sof_dbg_tplg_name_fops); debugfs_create_u32("ipc_type", 0444, fw_profile, (u32 *)&plat_data->ipc_type); diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c index 0161c0ae8bbdb4..47e0d312946835 100644 --- a/sound/soc/sof/pcm.c +++ b/sound/soc/sof/pcm.c @@ -808,8 +808,11 @@ static int sof_pcm_probe(struct snd_soc_component *component) static void sof_pcm_remove(struct snd_soc_component *component) { + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); + /* remove topology */ snd_soc_tplg_component_remove(component); + sof_debug_set_tplg_name(sdev, NULL); } static int sof_pcm_ack(struct snd_soc_component *component, diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index 83e4d83cc27cbb..fb5d25454233ef 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -656,6 +656,9 @@ struct snd_sof_dev { struct snd_soc_component *component; u32 enabled_cores_mask; /* keep track of enabled cores */ bool led_present; + /* newline separated list of the loaded topology files for debugfs */ + char *loaded_tplg_names; + struct mutex loaded_tplg_names_mutex; /* protects loaded_tplg_names */ /* FW configuration */ struct sof_ipc_window *info_window; @@ -798,6 +801,7 @@ static inline void snd_sof_ipc_process_reply(struct snd_sof_dev *sdev, u32 msg_i */ int snd_sof_dbg_init(struct snd_sof_dev *sdev); void snd_sof_free_debug(struct snd_sof_dev *sdev); +void sof_debug_set_tplg_name(struct snd_sof_dev *sdev, char *tplg_names); int snd_sof_debugfs_buf_item(struct snd_sof_dev *sdev, void *base, size_t size, const char *name, mode_t mode); diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index 750916b57def74..3de24acc45e7f9 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -2509,11 +2509,40 @@ static const struct snd_soc_tplg_ops sof_dspless_tplg_ops = { .bytes_ext_ops_count = ARRAY_SIZE(sof_dspless_bytes_ext_ops), }; +/* + * Append the name of a loaded topology file to the list exposed via debugfs. + * The topology path prefix is dropped as it is exposed separately. + */ +static int sof_tplg_names_append(struct snd_sof_dev *sdev, char **names, + const char *file) +{ + const char *prefix = sdev->pdata->tplg_filename_prefix; + size_t prefix_len = strlen(prefix); + char *new_names; + + if (!strncmp(file, prefix, prefix_len) && file[prefix_len] == '/') + file += prefix_len + 1; + + if (*names) + new_names = devm_kasprintf(sdev->dev, GFP_KERNEL, "%s\n%s", + *names, file); + else + new_names = devm_kstrdup(sdev->dev, file, GFP_KERNEL); + if (!new_names) + return -ENOMEM; + + devm_kfree(sdev->dev, *names); + *names = new_names; + + return 0; +} + int snd_sof_load_topology(struct snd_soc_component *scomp, const char *file) { struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); struct snd_sof_pdata *sof_pdata = sdev->pdata; const char *tplg_filename_prefix = sof_pdata->tplg_filename_prefix; + char *tplg_names = NULL; int tplg_cnt = 0; int ret; int i; @@ -2575,12 +2604,6 @@ int snd_sof_load_topology(struct snd_soc_component *scomp, const char *file) const struct firmware *fw __free(firmware) = NULL; ret = request_firmware(&fw, tplg_files[i], scomp->dev); if (ret < 0) { - /* - * snd_soc_tplg_component_remove(scomp) will be called - * if snd_soc_tplg_component_load(scomp) failed and all - * objects in the scomp will be removed. No need to call - * snd_soc_tplg_component_remove(scomp) here. - */ dev_err(scomp->dev, "tplg request firmware %s failed err: %d\n", tplg_files[i], ret); goto out; @@ -2596,6 +2619,10 @@ int snd_sof_load_topology(struct snd_soc_component *scomp, const char *file) tplg_files[i], ret); goto out; } + + ret = sof_tplg_names_append(sdev, &tplg_names, tplg_files[i]); + if (ret < 0) + goto out; } /* Loading user defined topologies */ @@ -2613,12 +2640,6 @@ int snd_sof_load_topology(struct snd_soc_component *scomp, const char *file) const struct firmware *fw __free(firmware) = NULL; ret = request_firmware(&fw, feature_topology, scomp->dev); if (ret < 0) { - /* - * snd_soc_tplg_component_remove(scomp) will be called - * if snd_soc_tplg_component_load(scomp) failed and all - * objects in the scomp will be removed. No need to call - * snd_soc_tplg_component_remove(scomp) here. - */ dev_warn(scomp->dev, "feature tplg request firmware %s failed err: %d\n", feature_topologies[i], ret); /* @@ -2644,15 +2665,37 @@ int snd_sof_load_topology(struct snd_soc_component *scomp, const char *file) */ goto out; } + + ret = sof_tplg_names_append(sdev, &tplg_names, feature_topology); + if (ret < 0) + goto out; } /* call sof_complete when topologies are loaded successfully */ ret = sof_complete(scomp); + if (ret < 0) + goto out; + + /* Update the list of loaded topologies exposed via debugfs */ + sof_debug_set_tplg_name(sdev, tplg_names); + tplg_names = NULL; out: + devm_kfree(sdev->dev, tplg_names); + if (ret >= 0 && sdev->led_present) ret = snd_ctl_led_request(); + /* + * Remove the topology on any error as .remove is not called when the + * component probe fails. If snd_soc_tplg_component_load() failed, it + * has removed it already and this call is a no-op. + */ + if (ret < 0) { + snd_soc_tplg_component_remove(scomp); + sof_debug_set_tplg_name(sdev, NULL); + } + return ret; } EXPORT_SYMBOL(snd_sof_load_topology);