diff --git a/include/sound/sof.h b/include/sound/sof.h index 7c11d16207e478..251103f46fe51a 100644 --- a/include/sound/sof.h +++ b/include/sound/sof.h @@ -30,6 +30,16 @@ struct snd_sof_pdata { /* parent device */ struct device *dev; + /* + * notification callback used if the hardware initialization + * can take time or is handled in a workqueue. This callback + * can be used by the parent device to e.g. enable runtime_pm + * or limit functionality until all low-level inits are + * complete. The device argument refers to the parent, not the + * "sof-audio" device. + */ + void (*sof_probe_complete)(struct device *dev); + /* descriptor */ const struct sof_dev_desc *desc; diff --git a/sound/soc/sof/core.c b/sound/soc/sof/core.c index 1eff06acb9f057..78af9987053040 100644 --- a/sound/soc/sof/core.c +++ b/sound/soc/sof/core.c @@ -282,7 +282,6 @@ static int sof_probe(struct platform_device *pdev) INIT_LIST_HEAD(&sdev->widget_list); INIT_LIST_HEAD(&sdev->dai_list); INIT_LIST_HEAD(&sdev->route_list); - dev_set_drvdata(&pdev->dev, sdev); spin_lock_init(&sdev->ipc_lock); spin_lock_init(&sdev->hw_lock); @@ -388,6 +387,14 @@ static int sof_probe(struct platform_device *pdev) dev_dbg(sdev->dev, "created machine %s\n", dev_name(&plat_data->pdev_mach->dev)); + /* + * so far so good, set the drvdata which can be used to test probe + * success. Also let the parent device know the probe completed + */ + dev_set_drvdata(&pdev->dev, sdev); + if (plat_data->sof_probe_complete) + plat_data->sof_probe_complete(sdev->parent); + return 0; comp_err: diff --git a/sound/soc/sof/pm.c b/sound/soc/sof/pm.c index 93102993b508dc..47f30ba84ecd35 100644 --- a/sound/soc/sof/pm.c +++ b/sound/soc/sof/pm.c @@ -234,6 +234,10 @@ static int sof_resume(struct device *dev, bool runtime_resume) struct snd_sof_dev *sdev = dev_get_drvdata(&priv->pdev_pcm->dev); int ret = 0; + /* do nothing if the probe failed somehow */ + if (!sdev) + return 0; + /* do nothing if dsp resume callbacks are not set */ if (!sof_ops(sdev)->resume || !sof_ops(sdev)->runtime_resume) return 0; @@ -304,6 +308,10 @@ static int sof_suspend(struct device *dev, bool runtime_suspend) struct snd_sof_dev *sdev = dev_get_drvdata(&priv->pdev_pcm->dev); int ret = 0; + /* do nothing if the probe failed somehow */ + if (!sdev) + return 0; + /* do nothing if dsp suspend callback is not set */ if (!sof_ops(sdev)->suspend) return 0; diff --git a/sound/soc/sof/sof-pci-dev.c b/sound/soc/sof/sof-pci-dev.c index 8893ce224b0ecb..f4833e4d0ce007 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) { @@ -217,6 +235,9 @@ static int sof_pci_probe(struct pci_dev *pci, sof_pdata->dev = &pci->dev; sof_pdata->platform = "sof-audio"; + /* set callback to enable runtime_pm */ + sof_pdata->sof_probe_complete = sof_pci_probe_complete; + /* register sof-audio platform driver */ ret = sof_create_platform_device(priv); if (ret) { @@ -224,19 +245,6 @@ 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); - return ret; release_regions: