diff --git a/sound/soc/sof/Kconfig b/sound/soc/sof/Kconfig index e8382eb8e1bb35..06b237f726e17d 100644 --- a/sound/soc/sof/Kconfig +++ b/sound/soc/sof/Kconfig @@ -144,6 +144,14 @@ config SND_SOC_SOF module dependencies but since the module or built-in type is decided at the top level it doesn't matter. +config SND_SOC_SOF_PROBE_WORK_QUEUE + bool + help + This option is not user-selectable but automagically handled by + 'select' statements at a higher level + When selected, the probe is handled in two steps, for example to + avoid lockdeps if request_module is used in the probe. + source "sound/soc/sof/intel/Kconfig" source "sound/soc/sof/xtensa/Kconfig" diff --git a/sound/soc/sof/core.c b/sound/soc/sof/core.c index e5b5cdce4df18a..af5dadee9c399d 100644 --- a/sound/soc/sof/core.c +++ b/sound/soc/sof/core.c @@ -255,47 +255,14 @@ static int sof_machine_check(struct snd_sof_dev *sdev) return 0; } -int snd_sof_device_probe(struct device *dev, struct snd_sof_pdata *plat_data) +static int sof_probe_continue(struct snd_sof_dev *sdev) { - struct snd_sof_dev *sdev; + struct snd_sof_pdata *plat_data = sdev->pdata; const char *drv_name; const void *mach; int size; int ret; - sdev = devm_kzalloc(dev, sizeof(*sdev), GFP_KERNEL); - if (!sdev) - return -ENOMEM; - - dev_dbg(dev, "probing SOF DSP device....\n"); - - /* initialize sof device */ - sdev->dev = dev; - if dev_is_pci(plat_data->dev) - sdev->pci = to_pci_dev(plat_data->dev); - - sdev->pdata = plat_data; - sdev->first_boot = true; - dev_set_drvdata(dev, sdev); - - INIT_LIST_HEAD(&sdev->pcm_list); - INIT_LIST_HEAD(&sdev->kcontrol_list); - INIT_LIST_HEAD(&sdev->widget_list); - INIT_LIST_HEAD(&sdev->dai_list); - INIT_LIST_HEAD(&sdev->route_list); - spin_lock_init(&sdev->ipc_lock); - spin_lock_init(&sdev->hw_lock); - - /* set default timeouts if none provided */ - if (plat_data->desc->ipc_timeout == 0) - sdev->ipc_timeout = TIMEOUT_DEFAULT_IPC_MS; - else - sdev->ipc_timeout = plat_data->desc->ipc_timeout; - if (plat_data->desc->boot_timeout == 0) - sdev->boot_timeout = TIMEOUT_DEFAULT_BOOT_MS; - else - sdev->boot_timeout = plat_data->desc->boot_timeout; - /* probe the DSP hardware */ ret = snd_sof_probe(sdev); if (ret < 0) { @@ -362,7 +329,7 @@ int snd_sof_device_probe(struct device *dev, struct snd_sof_pdata *plat_data) sdev->first_boot = false; /* now register audio DSP platform driver and dai */ - ret = snd_soc_register_component(dev, &sdev->plat_drv, + ret = snd_soc_register_component(sdev->dev, &sdev->plat_drv, sof_ops(sdev)->drv, sof_ops(sdev)->num_drv); if (ret < 0) { @@ -388,10 +355,13 @@ int snd_sof_device_probe(struct device *dev, struct snd_sof_pdata *plat_data) dev_dbg(sdev->dev, "created machine %s\n", dev_name(&plat_data->pdev_mach->dev)); + if (plat_data->sof_probe_complete) + plat_data->sof_probe_complete(sdev->dev); + return 0; comp_err: - snd_soc_unregister_component(dev); + snd_soc_unregister_component(sdev->dev); fw_run_err: snd_sof_fw_unload(sdev); fw_load_err: @@ -403,6 +373,71 @@ int snd_sof_device_probe(struct device *dev, struct snd_sof_pdata *plat_data) return ret; } + +#if IS_ENABLED(CONFIG_SND_SOC_SOF_PROBE_WORK_QUEUE) +static void sof_probe_work(struct work_struct *work) +{ + struct snd_sof_dev *sdev = + container_of(work, struct snd_sof_dev, probe_work); + int ret; + + dev_dbg(sdev->dev, "%s entry\n", __func__); + ret = sof_probe_continue(sdev); + if (ret < 0) { + /* errors cannot be propagated, log */ + dev_err(sdev->dev, "error: %s failed err: %d\n", __func__, ret); + } +} +#endif + +int snd_sof_device_probe(struct device *dev, struct snd_sof_pdata *plat_data) +{ + struct snd_sof_dev *sdev; + + sdev = devm_kzalloc(dev, sizeof(*sdev), GFP_KERNEL); + if (!sdev) + return -ENOMEM; + + dev_dbg(dev, "probing SOF DSP device....\n"); + + /* initialize sof device */ + sdev->dev = dev; + if dev_is_pci(plat_data->dev) + sdev->pci = to_pci_dev(plat_data->dev); + + sdev->pdata = plat_data; + sdev->first_boot = true; + dev_set_drvdata(dev, sdev); + + INIT_LIST_HEAD(&sdev->pcm_list); + INIT_LIST_HEAD(&sdev->kcontrol_list); + INIT_LIST_HEAD(&sdev->widget_list); + INIT_LIST_HEAD(&sdev->dai_list); + INIT_LIST_HEAD(&sdev->route_list); + spin_lock_init(&sdev->ipc_lock); + spin_lock_init(&sdev->hw_lock); + +#if IS_ENABLED(CONFIG_SND_SOC_SOF_PROBE_WORK_QUEUE) + INIT_WORK(&sdev->probe_work, sof_probe_work); +#endif + + /* set default timeouts if none provided */ + if (plat_data->desc->ipc_timeout == 0) + sdev->ipc_timeout = TIMEOUT_DEFAULT_IPC_MS; + else + sdev->ipc_timeout = plat_data->desc->ipc_timeout; + if (plat_data->desc->boot_timeout == 0) + sdev->boot_timeout = TIMEOUT_DEFAULT_BOOT_MS; + else + sdev->boot_timeout = plat_data->desc->boot_timeout; + + if (IS_ENABLED(CONFIG_SND_SOC_SOF_PROBE_WORK_QUEUE)) { + schedule_work(&sdev->probe_work); + return 0; + } + + return sof_probe_continue(sdev); +} EXPORT_SYMBOL(snd_sof_device_probe); int snd_sof_device_remove(struct device *dev) @@ -410,6 +445,9 @@ int snd_sof_device_remove(struct device *dev) struct snd_sof_dev *sdev = dev_get_drvdata(dev); struct snd_sof_pdata *pdata = sdev->pdata; + if (IS_ENABLED(CONFIG_SND_SOC_SOF_PROBE_WORK_QUEUE)) + cancel_work_sync(&sdev->probe_work); + snd_soc_unregister_component(dev); snd_sof_fw_unload(sdev); snd_sof_ipc_free(sdev); diff --git a/sound/soc/sof/intel/Kconfig b/sound/soc/sof/intel/Kconfig index a5b8b47b9deb2e..50cb06b160a87d 100644 --- a/sound/soc/sof/intel/Kconfig +++ b/sound/soc/sof/intel/Kconfig @@ -215,6 +215,7 @@ if SND_SOC_SOF_HDA_COMMON config SND_SOC_SOF_HDA_LINK bool "SOF support for HDA Links(HDA/HDMI)" + select SND_SOC_SOF_PROBE_WORK_QUEUE help This adds support for HDA links(HDA/HDMI) with Sound Open Firmware for Intel(R) platforms. diff --git a/sound/soc/sof/sof-pci-dev.c b/sound/soc/sof/sof-pci-dev.c index 3e383d867464bf..9fcb8087898e90 100644 --- a/sound/soc/sof/sof-pci-dev.c +++ b/sound/soc/sof/sof-pci-dev.c @@ -150,6 +150,24 @@ static const struct dev_pm_ops sof_pci_pm = { NULL) }; +static void sof_pci_probe_complete(struct device *dev) +{ + dev_dbg(dev, "Completing SOF PCI probe"); + + /* allow runtime_pm */ + pm_runtime_set_autosuspend_delay(dev, SND_SOF_SUSPEND_DELAY_MS); + pm_runtime_use_autosuspend(dev); + + /* + * runtime pm for pci device is "forbidden" by default. + * so call pm_runtime_allow() to enable it. + */ + pm_runtime_allow(dev); + + /* follow recommendation in pci-driver.c to decrement usage counter */ + pm_runtime_put_noidle(dev); +} + static int sof_pci_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) { @@ -209,6 +227,10 @@ static int sof_pci_probe(struct pci_dev *pci, sof_pdata->dev = dev; sof_pdata->platform = dev_name(dev); +#if IS_ENABLED(CONFIG_SND_SOC_SOF_PROBE_WORK_QUEUE) + /* set callback to enable runtime_pm */ + sof_pdata->sof_probe_complete = sof_pci_probe_complete; +#endif /* call sof helper for DSP hardware probe */ ret = snd_sof_device_probe(dev, sof_pdata); if (ret) { @@ -216,18 +238,9 @@ static int sof_pci_probe(struct pci_dev *pci, goto release_regions; } - /* allow runtime_pm */ - pm_runtime_set_autosuspend_delay(dev, SND_SOF_SUSPEND_DELAY_MS); - pm_runtime_use_autosuspend(dev); - - /* - * runtime pm for pci device is "forbidden" by default. - * so call pm_runtime_allow() to enable it. - */ - pm_runtime_allow(dev); - - /* follow recommendation in pci-driver.c to decrement usage counter */ - pm_runtime_put_noidle(dev); +#if !IS_ENABLED(CONFIG_SND_SOC_SOF_PROBE_WORK_QUEUE) + sof_pci_probe_complete(dev); +#endif return ret; diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index b229949a286ec7..ed865c794306ab 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -314,6 +314,9 @@ struct snd_sof_dev { u32 boot_complete; u32 first_boot; + /* work queue in case the probe is implemented in two steps */ + struct work_struct probe_work; + /* DSP HW differentiation */ struct snd_sof_pdata *pdata;