diff --git a/sound/soc/sof/core.c b/sound/soc/sof/core.c index fe010440c8f5e9..1c1af8ba0f824a 100644 --- a/sound/soc/sof/core.c +++ b/sound/soc/sof/core.c @@ -8,6 +8,7 @@ * Author: Liam Girdwood */ +#include #include #include #include @@ -249,6 +250,7 @@ static int sof_probe(struct platform_device *pdev) INIT_LIST_HEAD(&sdev->kcontrol_list); 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); @@ -330,6 +332,9 @@ static int sof_probe(struct platform_device *pdev) "warning: failed to initialize trace %d\n", ret); } + pm_runtime_mark_last_busy(sdev->dev); + pm_runtime_put_autosuspend(sdev->dev); + return 0; comp_err: @@ -366,9 +371,17 @@ void snd_sof_shutdown(struct device *dev) } EXPORT_SYMBOL(snd_sof_shutdown); +static const struct dev_pm_ops sof_platform_pm = { + SET_SYSTEM_SLEEP_PM_OPS(snd_sof_suspend, snd_sof_resume) + SET_RUNTIME_PM_OPS(snd_sof_runtime_suspend, snd_sof_runtime_resume, + NULL) + .suspend_late = snd_sof_suspend_late, +}; + static struct platform_driver sof_driver = { .driver = { .name = "sof-audio", + .pm = &sof_platform_pm, }, .probe = sof_probe, diff --git a/sound/soc/sof/intel/apl.c b/sound/soc/sof/intel/apl.c index 3ff19d94f43937..d3816b99498584 100644 --- a/sound/soc/sof/intel/apl.c +++ b/sound/soc/sof/intel/apl.c @@ -96,5 +96,9 @@ struct snd_sof_dsp_ops sof_apl_ops = { /* DAI drivers */ .drv = skl_dai, .num_drv = SOF_SKL_NUM_DAIS, + + /* PM */ + .suspend = hda_dsp_suspend, + .resume = hda_dsp_resume, }; EXPORT_SYMBOL(sof_apl_ops); diff --git a/sound/soc/sof/intel/hda-dsp.c b/sound/soc/sof/intel/hda-dsp.c index 82f9ce897137c5..86d474c083a920 100644 --- a/sound/soc/sof/intel/hda-dsp.c +++ b/sound/soc/sof/intel/hda-dsp.c @@ -29,7 +29,7 @@ #include #include #include - +#include #include "../sof-priv.h" #include "../ops.h" #include "hda.h" @@ -239,3 +239,18 @@ int hda_dsp_core_reset_power_down(struct snd_sof_dev *sdev, return ret; } +int hda_dsp_suspend(struct snd_sof_dev *sdev, int state) +{ + const struct sof_intel_dsp_desc *chip = sdev->hda->desc; + + /* power down DSP */ + return hda_dsp_core_reset_power_down(sdev, chip->cores_mask); +} + +int hda_dsp_resume(struct snd_sof_dev *sdev) +{ + const struct sof_intel_dsp_desc *chip = sdev->hda->desc; + + /* power up the DSP */ + return hda_dsp_core_power_up(sdev, chip->cores_mask); +} diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h index 7f63b454e07844..dcba0b585abd9b 100644 --- a/sound/soc/sof/intel/hda.h +++ b/sound/soc/sof/intel/hda.h @@ -405,6 +405,8 @@ bool hda_dsp_core_is_enabled(struct snd_sof_dev *sdev, unsigned int core_mask); int hda_dsp_core_reset_power_down(struct snd_sof_dev *sdev, unsigned int core_mask); +int hda_dsp_suspend(struct snd_sof_dev *sdev, int state); +int hda_dsp_resume(struct snd_sof_dev *sdev); void hda_dsp_dump(struct snd_sof_dev *sdev, u32 flags); /* diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c index e4048f129e0d9e..b746e470f584d5 100644 --- a/sound/soc/sof/pcm.c +++ b/sound/soc/sof/pcm.c @@ -158,6 +158,9 @@ static int sof_pcm_hw_params(struct snd_pcm_substream *substream, spcm->posn_offset[substream->stream] = sdev->stream_box.offset + posn_offset; + /* save pcm hw_params */ + memcpy(&spcm->params[substream->stream], params, sizeof(*params)); + return ret; } @@ -190,6 +193,33 @@ static int sof_pcm_hw_free(struct snd_pcm_substream *substream) return ret; } +static int sof_restore_hw_params(struct snd_pcm_substream *substream, + struct snd_sof_pcm *spcm, + struct snd_sof_dev *sdev) +{ + snd_pcm_uframes_t host = 0; + u64 host_posn; + int ret = 0; + + /* resume stream */ + host_posn = spcm->stream[substream->stream].posn.host_posn; + host = bytes_to_frames(substream->runtime, host_posn); + dev_dbg(sdev->dev, + "PCM: resume stream %d dir %d DMA position %lu\n", + spcm->pcm.pcm_id, substream->stream, host); + + /* set hw_params */ + ret = sof_pcm_hw_params(substream, + &spcm->params[substream->stream]); + if (ret < 0) { + dev_err(sdev->dev, + "error: set pcm hw_params after resume\n"); + return ret; + } + + return 0; +} + static int sof_pcm_trigger(struct snd_pcm_substream *substream, int cmd) { struct snd_soc_pcm_runtime *rtd = substream->private_data; @@ -198,6 +228,8 @@ static int sof_pcm_trigger(struct snd_pcm_substream *substream, int cmd) struct snd_sof_pcm *spcm = rtd->sof; struct sof_ipc_stream stream; struct sof_ipc_reply reply; + snd_pcm_uframes_t host = 0; + u64 host_posn; int ret = 0; /* nothing todo for BE */ @@ -216,15 +248,56 @@ static int sof_pcm_trigger(struct snd_pcm_substream *substream, int cmd) stream.hdr.cmd |= SOF_IPC_STREAM_TRIG_START; break; case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - stream.hdr.cmd |= SOF_IPC_STREAM_TRIG_RELEASE; + + /* check if the stream hw_params needs to be restored */ + if (spcm->restore_stream[substream->stream]) { + + /* restore hw_params */ + ret = sof_restore_hw_params(substream, spcm, sdev); + if (ret < 0) + return ret; + + /* unset restore_stream */ + spcm->restore_stream[substream->stream] = 0; + + /* trigger start */ + stream.hdr.cmd |= SOF_IPC_STREAM_TRIG_START; + } else { + + /* trigger pause release */ + stream.hdr.cmd |= SOF_IPC_STREAM_TRIG_RELEASE; + } break; case SNDRV_PCM_TRIGGER_STOP: + + /* + * Check if stream was marked for restore before suspend + */ + if (spcm->restore_stream[substream->stream]) { + + /* unset restore_stream */ + spcm->restore_stream[substream->stream] = 0; + + /* do not send ipc as the stream hasn't been set up */ + return 0; + } + stream.hdr.cmd |= SOF_IPC_STREAM_TRIG_STOP; break; case SNDRV_PCM_TRIGGER_PAUSE_PUSH: stream.hdr.cmd |= SOF_IPC_STREAM_TRIG_PAUSE; break; case SNDRV_PCM_TRIGGER_RESUME: + + /* restore hw_params */ + ret = sof_restore_hw_params(substream, spcm, sdev); + if (ret < 0) + return ret; + + /* trigger stream */ + stream.hdr.cmd |= SOF_IPC_STREAM_TRIG_START; + + break; case SNDRV_PCM_TRIGGER_SUSPEND: break; default: @@ -580,13 +653,6 @@ static int sof_pcm_probe(struct snd_soc_platform *platform) goto err; } - /* enable runtime PM with auto suspend */ - pm_runtime_set_autosuspend_delay(platform->dev, - SND_SOF_SUSPEND_DELAY); - pm_runtime_use_autosuspend(platform->dev); - pm_runtime_enable(platform->dev); - pm_runtime_idle(platform->dev); - err: return ret; } diff --git a/sound/soc/sof/pm.c b/sound/soc/sof/pm.c index 56c60423e85c84..93d096434bc2a0 100644 --- a/sound/soc/sof/pm.c +++ b/sound/soc/sof/pm.c @@ -9,6 +9,7 @@ */ #include +#include #include #include #include @@ -16,28 +17,305 @@ #include #include #include +#include +#include "ops.h" #include "sof-priv.h" -int snd_sof_runtime_suspend(struct device *dev) +static int sof_restore_pipelines(struct snd_sof_dev *sdev) { + struct snd_sof_widget *swidget = NULL; + struct snd_sof_route *sroute = NULL; + struct snd_sof_dai *dai; + struct sof_ipc_comp_dai *comp_dai; + struct sof_ipc_hdr *hdr; + struct snd_sof_control *scontrol = NULL; + int ret = 0; + + /* restore pipeline components */ + list_for_each_entry_reverse(swidget, &sdev->widget_list, list) { + struct sof_ipc_comp_reply r; + + switch (swidget->id) { + case snd_soc_dapm_dai_in: + /* fallthrough */ + case snd_soc_dapm_dai_out: + dai = (struct snd_sof_dai *)swidget->private; + comp_dai = &dai->comp_dai; + ret = sof_ipc_tx_message(sdev->ipc, + comp_dai->comp.hdr.cmd, + comp_dai, sizeof(*comp_dai), + &r, sizeof(r)); + break; + default: + hdr = (struct sof_ipc_hdr *)swidget->private; + ret = sof_ipc_tx_message(sdev->ipc, hdr->cmd, + swidget->private, hdr->size, + &r, sizeof(r)); + break; + } + if (ret < 0) { + dev_err(sdev->dev, + "error: failed to load widget type %d with ID: %d\n", + swidget->widget->id, swidget->comp_id); + + return ret; + } + } + + /* restore pipeline connections */ + list_for_each_entry_reverse(sroute, &sdev->route_list, list) { + struct sof_ipc_pipe_comp_connect *connect = sroute->private; + struct sof_ipc_reply reply; + + ret = sof_ipc_tx_message(sdev->ipc, + connect->hdr.cmd, + connect, sizeof(*connect), + &reply, sizeof(reply)); + + if (ret < 0) { + dev_err(sdev->dev, + "error: failed to load route sink %s control %s source %s\n", + sroute->route->sink, + sroute->route->control ? sroute->route->control + : "none", + sroute->route->source); + + return ret; + } + } + + /* restore dai links */ + list_for_each_entry_reverse(dai, &sdev->dai_list, list) { + struct sof_ipc_reply reply; + struct sof_ipc_dai_config *config = &dai->dai_config; + + ret = sof_ipc_tx_message(sdev->ipc, + config->hdr.cmd, config, + sizeof(*config), + &reply, sizeof(reply)); + + if (ret < 0) { + dev_err(sdev->dev, + "error: failed to set dai config for %s\n", + dai->name); + + return ret; + } + } + + /* complete pipeline */ + list_for_each_entry(swidget, &sdev->widget_list, list) { + switch (swidget->id) { + case snd_soc_dapm_scheduler: + swidget->complete = + sof_complete_pipeline(sdev, swidget); + break; + default: + break; + } + } + + /* restore kcontrol values */ + list_for_each_entry(scontrol, &sdev->kcontrol_list, list) { + + /* notify DSP of kcontrol values */ + ret = snd_sof_ipc_set_comp_data(sdev->ipc, scontrol, + SOF_IPC_COMP_SET_VALUE, + SOF_CTRL_TYPE_VALUE_CHAN_SET, + scontrol->cmd); + if (ret < 0) { + dev_err(sdev->dev, + "error: failed kcontrol value set for widget: %d\n", + scontrol->comp_id); + + return ret; + } + } + return 0; } + +static int sof_send_ctx_ipc(struct snd_sof_dev *sdev, int cmd) +{ + struct sof_ipc_pm_ctx pm_ctx; + + memset(&pm_ctx, 0, sizeof(pm_ctx)); + + /* configure ctx save ipc message */ + pm_ctx.hdr.size = sizeof(pm_ctx); + pm_ctx.hdr.cmd = SOF_IPC_GLB_PM_MSG | cmd; + + /* send ctx save ipc to dsp */ + return sof_ipc_tx_message(sdev->ipc, pm_ctx.hdr.cmd, &pm_ctx, + sizeof(pm_ctx), &pm_ctx, sizeof(pm_ctx)); +} + +static void sof_suspend_streams(struct snd_sof_dev *sdev) +{ + struct snd_sof_pcm *spcm; + struct snd_pcm_substream *substream; + int state, dir; + + /* suspend all running streams */ + list_for_each_entry(spcm, &sdev->pcm_list, list) { + + /* suspend running playback stream */ + dir = SNDRV_PCM_STREAM_PLAYBACK; + substream = spcm->stream[dir].substream; + + if (substream && substream->runtime) { + state = substream->runtime->status->state; + snd_pcm_suspend(substream); + + /* + * SNDRV_PCM_TRIGGER_RESUME is not called for streams + * that are paused during suspend. + * So mark them explicitly so hw_params is restored. + */ + if (state == SNDRV_PCM_STATE_PAUSED) + spcm->restore_stream[dir] = 1; + } + + /* suspend running capture stream */ + dir = SNDRV_PCM_STREAM_CAPTURE; + substream = spcm->stream[dir].substream; + + if (substream && substream->runtime) { + state = substream->runtime->status->state; + snd_pcm_suspend(substream); + + /* + * SNDRV_PCM_TRIGGER_RESUME is not called for streams + * that are paused during suspend. + * So mark them explicitly so hw_params is restored. + */ + if (state == SNDRV_PCM_STATE_PAUSED) + spcm->restore_stream[dir] = 1; + } + } +} + +static int sof_resume(struct device *dev) +{ + struct snd_sof_dev *sdev = dev_get_drvdata(dev); + int ret = 0; + + /* power up DSP */ + ret = snd_sof_dsp_resume(sdev); + if (ret < 0) { + dev_err(sdev->dev, + "error: failed to power up DSP after resume\n"); + return ret; + } + + /* load the firmware */ + ret = snd_sof_load_firmware(sdev, sdev->pdata->fw); + if (ret < 0) { + dev_err(sdev->dev, + "error: failed to load DSP firmware after resume %d\n", + ret); + return ret; + } + + /* boot the firmware */ + ret = snd_sof_run_firmware(sdev); + if (ret < 0) { + dev_err(sdev->dev, + "error: failed to boot DSP firmware after resume %d\n", + ret); + return ret; + } + + /* init DMA trace */ + ret = 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_restore_pipelines(sdev); + if (ret < 0) { + dev_err(sdev->dev, + "error: failed to restore pipeline after resume %d\n", + ret); + return ret; + } + + /* notify DSP to restore context */ + ret = sof_send_ctx_ipc(sdev, SOF_IPC_PM_CTX_RESTORE); + if (ret < 0) + dev_err(sdev->dev, + "error: ctx_restore ipc error during resume %d\n", + ret); + + return ret; +} + +static int sof_suspend(struct device *dev) +{ + struct snd_sof_dev *sdev = dev_get_drvdata(dev); + int ret = 0; + + /* + * Suspend running pcm streams. + * They will be restarted by ALSA resume trigger call. + */ + sof_suspend_streams(sdev); + + /* notify DSP to save context */ + ret = sof_send_ctx_ipc(sdev, SOF_IPC_PM_CTX_SAVE); + if (ret < 0) { + dev_err(sdev->dev, + "error: ctx_save ipc error during suspend %d\n", + ret); + return ret; + } + + /* drop all ipc */ + sof_ipc_drop_all(sdev->ipc); + + /* release trace */ + snd_sof_dma_trace_release(sdev); + + /* power down DSP */ + ret = snd_sof_dsp_suspend(sdev, 0); + if (ret < 0) + dev_err(sdev->dev, + "error: failed to power down DSP during suspend %d\n", + ret); + + return ret; +} + +int snd_sof_runtime_suspend(struct device *dev) +{ + return sof_suspend(dev); +} EXPORT_SYMBOL(snd_sof_runtime_suspend); int snd_sof_runtime_resume(struct device *dev) { - return 0; + return sof_resume(dev); } EXPORT_SYMBOL(snd_sof_runtime_resume); int snd_sof_resume(struct device *dev) { - return 0; + return sof_resume(dev); } EXPORT_SYMBOL(snd_sof_resume); int snd_sof_suspend(struct device *dev) { + struct snd_sof_dev *sdev = dev_get_drvdata(dev); + + /* suspend if dev is runtime_active */ + if (pm_runtime_active(sdev->dev)) + return sof_suspend(dev); + return 0; } EXPORT_SYMBOL(snd_sof_suspend); diff --git a/sound/soc/sof/sof-acpi-dev.c b/sound/soc/sof/sof-acpi-dev.c index dd268c1c1225d6..c3e8adbc1fda16 100644 --- a/sound/soc/sof/sof-acpi-dev.c +++ b/sound/soc/sof/sof-acpi-dev.c @@ -158,13 +158,6 @@ static void sof_acpi_fw_cb(const struct firmware *fw, void *context) } } -static const struct dev_pm_ops sof_acpi_pm = { - SET_SYSTEM_SLEEP_PM_OPS(snd_sof_suspend, snd_sof_resume) - SET_RUNTIME_PM_OPS(snd_sof_runtime_suspend, snd_sof_runtime_resume, - NULL) - .suspend_late = snd_sof_suspend_late, -}; - static const struct sof_ops_table mach_ops[] = { #if IS_ENABLED(CONFIG_SND_SOC_SOF_HASWELL) {&sof_acpi_haswell_desc, &sof_hsw_ops}, @@ -344,7 +337,6 @@ static struct platform_driver snd_sof_acpi_driver = { .shutdown = sof_acpi_shutdown, .driver = { .name = "sof-audio-acpi", - .pm = &sof_acpi_pm, .acpi_match_table = ACPI_PTR(sof_acpi_match), }, }; diff --git a/sound/soc/sof/sof-pci-dev.c b/sound/soc/sof/sof-pci-dev.c index 5445d8007afb77..b4d716ae6d1793 100644 --- a/sound/soc/sof/sof-pci-dev.c +++ b/sound/soc/sof/sof-pci-dev.c @@ -151,13 +151,6 @@ static void sof_pci_fw_cb(const struct firmware *fw, void *context) } } -static const struct dev_pm_ops sof_pci_pm = { - SET_SYSTEM_SLEEP_PM_OPS(snd_sof_suspend, snd_sof_resume) - SET_RUNTIME_PM_OPS(snd_sof_runtime_suspend, snd_sof_runtime_resume, - NULL) - .suspend_late = snd_sof_suspend_late, -}; - static const struct sof_ops_table mach_ops[] = { #if IS_ENABLED(CONFIG_SND_SOC_SOF_APOLLOLAKE) {&bxt_desc, &sof_apl_ops}, @@ -368,9 +361,6 @@ static struct pci_driver snd_sof_pci_driver = { .probe = sof_pci_probe, .remove = sof_pci_remove, .shutdown = sof_pci_shutdown, - .driver = { - .pm = &sof_pci_pm, - }, }; module_pci_driver(snd_sof_pci_driver); diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index 11f3b1575f8a24..ba98308d7ce236 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -229,6 +229,8 @@ struct snd_sof_pcm { u32 posn_offset[2]; struct mutex mutex; /* access mutex */ struct list_head list; /* list in sdev pcm list */ + struct snd_pcm_hw_params params[2]; + int restore_stream[2]; /* restore hw_params for paused stream */ }; /* ALSA SOF Kcontrol device */ @@ -261,6 +263,16 @@ struct snd_sof_widget { void *private; /* core does not touch this */ }; +/* ASoC SOF DAPM route */ +struct snd_sof_route { + struct snd_sof_dev *sdev; + + struct snd_soc_dapm_route *route; + struct list_head list; /* list in sdev route list */ + + void *private; +}; + /* ASoC DAI device */ struct snd_sof_dai { struct snd_sof_dev *sdev; @@ -324,6 +336,7 @@ struct snd_sof_dev { struct list_head kcontrol_list; struct list_head widget_list; struct list_head dai_list; + struct list_head route_list; struct snd_soc_component *component; /* FW configuration */ @@ -445,6 +458,8 @@ int snd_sof_init_topology(struct snd_sof_dev *sdev, struct snd_soc_tplg_ops *ops); int snd_sof_load_topology(struct snd_sof_dev *sdev, const char *file); void snd_sof_free_topology(struct snd_sof_dev *sdev); +int sof_complete_pipeline(struct snd_sof_dev *sdev, + struct snd_sof_widget *swidget); /* * Trace/debug @@ -462,6 +477,7 @@ void snd_sof_trace_notify_for_error(struct snd_sof_dev *sdev); int snd_sof_get_status(struct snd_sof_dev *sdev, u32 panic_code, u32 tracep_code, void *oops, void *stack, size_t stack_size); +int sof_init_trace_ipc(struct snd_sof_dev *sdev); /* * Platform specific ops. diff --git a/sound/soc/sof/sof-spi-dev.c b/sound/soc/sof/sof-spi-dev.c index 57928d1442c96b..de11f7d68c01b4 100644 --- a/sound/soc/sof/sof-spi-dev.c +++ b/sound/soc/sof/sof-spi-dev.c @@ -43,13 +43,6 @@ static void sof_spi_fw_cb(const struct firmware *fw, void *context) } } -static const struct dev_pm_ops sof_spi_pm = { - SET_SYSTEM_SLEEP_PM_OPS(snd_sof_suspend, snd_sof_resume) - SET_RUNTIME_PM_OPS(snd_sof_runtime_suspend, snd_sof_runtime_resume, - NULL) - .suspend_late = snd_sof_suspend_late, -}; - static int sof_spi_probe(struct spi_device *spi) { struct device *dev = &spi->dev; diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index 79071990fd221e..767f6ec06b2ba8 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -874,31 +874,37 @@ static int sof_widget_load_buffer(struct snd_soc_component *scomp, int index, { struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); struct snd_soc_tplg_private *private = &tw->priv; - struct sof_ipc_buffer buffer; + struct sof_ipc_buffer *buffer; int ret; + buffer = kzalloc(sizeof(*buffer), GFP_KERNEL); + if (!buffer) + return -ENOMEM; + /* configure dai IPC message */ - memset(&buffer, 0, sizeof(buffer)); - buffer.comp.hdr.size = sizeof(buffer); - buffer.comp.hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_BUFFER_NEW; - buffer.comp.id = swidget->comp_id; - buffer.comp.type = SOF_COMP_BUFFER; - buffer.comp.pipeline_id = index; - - ret = sof_parse_tokens(scomp, &buffer, buffer_tokens, + buffer->comp.hdr.size = sizeof(*buffer); + buffer->comp.hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_BUFFER_NEW; + buffer->comp.id = swidget->comp_id; + buffer->comp.type = SOF_COMP_BUFFER; + buffer->comp.pipeline_id = index; + + ret = sof_parse_tokens(scomp, buffer, buffer_tokens, ARRAY_SIZE(buffer_tokens), private->array, le32_to_cpu(private->size)); if (ret != 0) { dev_err(sdev->dev, "error: parse buffer tokens failed %d\n", private->size); + kfree(buffer); return ret; } dev_dbg(sdev->dev, "buffer %s: size %d caps 0x%x\n", - swidget->widget->name, buffer.size, buffer.caps); + swidget->widget->name, buffer->size, buffer->caps); + + swidget->private = (void *)buffer; return sof_ipc_tx_message(sdev->ipc, - buffer.comp.hdr.cmd, &buffer, sizeof(buffer), + buffer->comp.hdr.cmd, buffer, sizeof(*buffer), r, sizeof(*r)); } @@ -914,42 +920,51 @@ static int sof_widget_load_pcm(struct snd_soc_component *scomp, int index, { struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); struct snd_soc_tplg_private *private = &tw->priv; - struct sof_ipc_comp_host host; + struct sof_ipc_comp_host *host; int ret; + host = kzalloc(sizeof(*host), GFP_KERNEL); + if (!host) + return -ENOMEM; + /* configure mixer IPC message */ - memset(&host, 0, sizeof(host)); - host.comp.hdr.size = sizeof(host); - host.comp.hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_COMP_NEW; - host.comp.id = swidget->comp_id; - host.comp.type = SOF_COMP_HOST; - host.comp.pipeline_id = index; - host.direction = dir; - - ret = sof_parse_tokens(scomp, &host, pcm_tokens, + host->comp.hdr.size = sizeof(*host); + host->comp.hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_COMP_NEW; + host->comp.id = swidget->comp_id; + host->comp.type = SOF_COMP_HOST; + host->comp.pipeline_id = index; + host->direction = dir; + + ret = sof_parse_tokens(scomp, host, pcm_tokens, ARRAY_SIZE(pcm_tokens), private->array, le32_to_cpu(private->size)); if (ret != 0) { dev_err(sdev->dev, "error: parse host tokens failed %d\n", private->size); - return ret; + goto err; } - ret = sof_parse_tokens(scomp, &host.config, comp_tokens, + ret = sof_parse_tokens(scomp, &host->config, comp_tokens, ARRAY_SIZE(comp_tokens), private->array, le32_to_cpu(private->size)); if (ret != 0) { dev_err(sdev->dev, "error: parse host.cfg tokens failed %d\n", le32_to_cpu(private->size)); - return ret; + goto err; } dev_dbg(sdev->dev, "loaded host %s\n", swidget->widget->name); - sof_dbg_comp_config(scomp, &host.config); + sof_dbg_comp_config(scomp, &host->config); + + swidget->private = (void *)host; return sof_ipc_tx_message(sdev->ipc, - host.comp.hdr.cmd, &host, sizeof(host), r, + host->comp.hdr.cmd, host, sizeof(*host), r, sizeof(*r)); + +err: + kfree(host); + return ret; } /* @@ -963,46 +978,54 @@ static int sof_widget_load_pipeline(struct snd_soc_component *scomp, { struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); struct snd_soc_tplg_private *private = &tw->priv; - struct sof_ipc_pipe_new pipeline; + struct sof_ipc_pipe_new *pipeline; struct snd_sof_widget *comp_swidget; int ret; + pipeline = kzalloc(sizeof(*pipeline), GFP_KERNEL); + if (!pipeline) + return -ENOMEM; + /* configure dai IPC message */ - memset(&pipeline, 0, sizeof(pipeline)); - pipeline.hdr.size = sizeof(pipeline); - pipeline.hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_PIPE_NEW; - pipeline.pipeline_id = index; - pipeline.comp_id = swidget->comp_id; + pipeline->hdr.size = sizeof(*pipeline); + pipeline->hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_PIPE_NEW; + pipeline->pipeline_id = index; + pipeline->comp_id = swidget->comp_id; /* component at start of pipeline is our stream id */ comp_swidget = snd_sof_find_swidget(sdev, tw->sname); if (!comp_swidget) { dev_err(sdev->dev, "error: widget %s refers to non existent widget %s\n", tw->name, tw->sname); - return -EINVAL; + ret = -EINVAL; + goto err; } - pipeline.sched_id = comp_swidget->comp_id; + pipeline->sched_id = comp_swidget->comp_id; dev_dbg(sdev->dev, "tplg: pipeline id %d comp %d scheduling comp id %d\n", - pipeline.pipeline_id, pipeline.comp_id, pipeline.sched_id); + pipeline->pipeline_id, pipeline->comp_id, pipeline->sched_id); - ret = sof_parse_tokens(scomp, &pipeline, sched_tokens, + ret = sof_parse_tokens(scomp, pipeline, sched_tokens, ARRAY_SIZE(sched_tokens), private->array, le32_to_cpu(private->size)); if (ret != 0) { dev_err(sdev->dev, "error: parse pipeline tokens failed %d\n", private->size); - return ret; + goto err; } dev_dbg(sdev->dev, "pipeline %s: deadline %d pri %d mips %d core %d frames %d\n", - swidget->widget->name, pipeline.deadline, pipeline.priority, - pipeline.mips, pipeline.core, pipeline.frames_per_sched); + swidget->widget->name, pipeline->deadline, pipeline->priority, + pipeline->mips, pipeline->core, pipeline->frames_per_sched); - return sof_ipc_tx_message(sdev->ipc, - pipeline.hdr.cmd, &pipeline, sizeof(pipeline), - r, sizeof(*r)); + swidget->private = (void *)pipeline; + + return sof_ipc_tx_message(sdev->ipc, pipeline->hdr.cmd, pipeline, + sizeof(*pipeline), r, sizeof(*r)); +err: + kfree(pipeline); + return ret; } /* @@ -1016,29 +1039,36 @@ static int sof_widget_load_mixer(struct snd_soc_component *scomp, int index, { struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); struct snd_soc_tplg_private *private = &tw->priv; - struct sof_ipc_comp_mixer mixer; + struct sof_ipc_comp_mixer *mixer; int ret; + mixer = kzalloc(sizeof(*mixer), GFP_KERNEL); + if (!mixer) + return -ENOMEM; + /* configure mixer IPC message */ - memset(&mixer, 0, sizeof(mixer)); - mixer.comp.hdr.size = sizeof(mixer); - mixer.comp.hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_COMP_NEW; - mixer.comp.id = swidget->comp_id; - mixer.comp.type = SOF_COMP_MIXER; - mixer.comp.pipeline_id = index; - - ret = sof_parse_tokens(scomp, &mixer.config, comp_tokens, + mixer->comp.hdr.size = sizeof(*mixer); + mixer->comp.hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_COMP_NEW; + mixer->comp.id = swidget->comp_id; + mixer->comp.type = SOF_COMP_MIXER; + mixer->comp.pipeline_id = index; + + ret = sof_parse_tokens(scomp, &mixer->config, comp_tokens, ARRAY_SIZE(comp_tokens), private->array, le32_to_cpu(private->size)); if (ret != 0) { dev_err(sdev->dev, "error: parse mixer.cfg tokens failed %d\n", private->size); + kfree(mixer); return ret; } - sof_dbg_comp_config(scomp, &mixer.config); + sof_dbg_comp_config(scomp, &mixer->config); + + swidget->private = (void *)mixer; + return sof_ipc_tx_message(sdev->ipc, - mixer.comp.hdr.cmd, &mixer, sizeof(mixer), r, + mixer->comp.hdr.cmd, mixer, sizeof(*mixer), r, sizeof(*r)); } @@ -1053,7 +1083,7 @@ static int sof_widget_load_pga(struct snd_soc_component *scomp, int index, { struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); struct snd_soc_tplg_private *private = &tw->priv; - struct sof_ipc_comp_volume volume; + struct sof_ipc_comp_volume *volume; struct snd_soc_dapm_widget *widget = swidget->widget; const struct snd_kcontrol_new *kc = NULL; struct soc_mixer_control *sm; @@ -1061,10 +1091,15 @@ static int sof_widget_load_pga(struct snd_soc_component *scomp, int index, const unsigned int *p; int ret, tlv[TLV_ITEMS]; + volume = kzalloc(sizeof(*volume), GFP_KERNEL); + if (!volume) + return -ENOMEM; + if (le32_to_cpu(tw->num_kcontrols) != 1) { dev_err(sdev->dev, "error: invalid kcontrol count %d for volume\n", tw->num_kcontrols); - return -EINVAL; + ret = -EINVAL; + goto err; } /* set up volume gain tables for kcontrol */ @@ -1074,51 +1109,60 @@ static int sof_widget_load_pga(struct snd_soc_component *scomp, int index, /* get volume control */ scontrol = sm->dobj.private; + /* set cmd for pga kcontrol */ + scontrol->cmd = SOF_CTRL_CMD_VOLUME; + /* get topology tlv data */ p = kc->tlv.p; /* extract tlv data */ if (get_tlv_data(p, tlv) < 0) { dev_err(sdev->dev, "error: invalid TLV data\n"); - return -EINVAL; + ret = -EINVAL; + goto err; } /* set up volume table */ if (set_up_volume_table(scontrol, tlv, sm->max + 1) < 0) { dev_err(sdev->dev, "error: setting up volume table\n"); - return -ENOMEM; + goto err; } /* configure dai IPC message */ - memset(&volume, 0, sizeof(volume)); - volume.comp.hdr.size = sizeof(volume); - volume.comp.hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_COMP_NEW; - volume.comp.id = swidget->comp_id; - volume.comp.type = SOF_COMP_VOLUME; - volume.comp.pipeline_id = index; - - ret = sof_parse_tokens(scomp, &volume, volume_tokens, + volume->comp.hdr.size = sizeof(*volume); + volume->comp.hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_COMP_NEW; + volume->comp.id = swidget->comp_id; + volume->comp.type = SOF_COMP_VOLUME; + volume->comp.pipeline_id = index; + + ret = sof_parse_tokens(scomp, volume, volume_tokens, ARRAY_SIZE(volume_tokens), private->array, le32_to_cpu(private->size)); if (ret != 0) { dev_err(sdev->dev, "error: parse volume tokens failed %d\n", private->size); - return ret; + goto err; } - ret = sof_parse_tokens(scomp, &volume.config, comp_tokens, + ret = sof_parse_tokens(scomp, &volume->config, comp_tokens, ARRAY_SIZE(comp_tokens), private->array, le32_to_cpu(private->size)); if (ret != 0) { dev_err(sdev->dev, "error: parse volume.cfg tokens failed %d\n", le32_to_cpu(private->size)); - return ret; + goto err; } - sof_dbg_comp_config(scomp, &volume.config); + sof_dbg_comp_config(scomp, &volume->config); + + swidget->private = (void *)volume; return sof_ipc_tx_message(sdev->ipc, - volume.comp.hdr.cmd, &volume, sizeof(volume), + volume->comp.hdr.cmd, volume, sizeof(*volume), r, sizeof(*r)); + +err: + kfree(volume); + return ret; } /* @@ -1132,42 +1176,51 @@ static int sof_widget_load_src(struct snd_soc_component *scomp, int index, { struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); struct snd_soc_tplg_private *private = &tw->priv; - struct sof_ipc_comp_src src; + struct sof_ipc_comp_src *src; int ret; + src = kzalloc(sizeof(*src), GFP_KERNEL); + if (!src) + return -ENOMEM; + /* configure mixer IPC message */ - memset(&src, 0, sizeof(src)); - src.comp.hdr.size = sizeof(src); - src.comp.hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_COMP_NEW; - src.comp.id = swidget->comp_id; - src.comp.type = SOF_COMP_SRC; - src.comp.pipeline_id = index; - - ret = sof_parse_tokens(scomp, &src, src_tokens, + src->comp.hdr.size = sizeof(*src); + src->comp.hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_COMP_NEW; + src->comp.id = swidget->comp_id; + src->comp.type = SOF_COMP_SRC; + src->comp.pipeline_id = index; + + ret = sof_parse_tokens(scomp, src, src_tokens, ARRAY_SIZE(src_tokens), private->array, le32_to_cpu(private->size)); if (ret != 0) { dev_err(sdev->dev, "error: parse src tokens failed %d\n", private->size); - return ret; + goto err; } - ret = sof_parse_tokens(scomp, &src.config, comp_tokens, + ret = sof_parse_tokens(scomp, &src->config, comp_tokens, ARRAY_SIZE(comp_tokens), private->array, le32_to_cpu(private->size)); if (ret != 0) { dev_err(sdev->dev, "error: parse src.cfg tokens failed %d\n", le32_to_cpu(private->size)); - return ret; + goto err; } dev_dbg(sdev->dev, "src %s: source rate %d sink rate %d\n", - swidget->widget->name, src.source_rate, src.sink_rate); - sof_dbg_comp_config(scomp, &src.config); + swidget->widget->name, src->source_rate, src->sink_rate); + sof_dbg_comp_config(scomp, &src->config); + + swidget->private = (void *)src; return sof_ipc_tx_message(sdev->ipc, - src.comp.hdr.cmd, &src, sizeof(src), r, + src->comp.hdr.cmd, src, sizeof(*src), r, sizeof(*r)); + +err: + kfree(src); + return ret; } /* @@ -1181,42 +1234,51 @@ static int sof_widget_load_siggen(struct snd_soc_component *scomp, int index, { struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); struct snd_soc_tplg_private *private = &tw->priv; - struct sof_ipc_comp_tone tone; + struct sof_ipc_comp_tone *tone; int ret; + tone = kzalloc(sizeof(*tone), GFP_KERNEL); + if (!tone) + return -ENOMEM; + /* configure mixer IPC message */ - memset(&tone, 0, sizeof(tone)); - tone.comp.hdr.size = sizeof(tone); - tone.comp.hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_COMP_NEW; - tone.comp.id = swidget->comp_id; - tone.comp.type = SOF_COMP_TONE; - tone.comp.pipeline_id = index; - - ret = sof_parse_tokens(scomp, &tone, tone_tokens, + tone->comp.hdr.size = sizeof(*tone); + tone->comp.hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_COMP_NEW; + tone->comp.id = swidget->comp_id; + tone->comp.type = SOF_COMP_TONE; + tone->comp.pipeline_id = index; + + ret = sof_parse_tokens(scomp, tone, tone_tokens, ARRAY_SIZE(tone_tokens), private->array, le32_to_cpu(private->size)); if (ret != 0) { dev_err(sdev->dev, "error: parse tone tokens failed %d\n", le32_to_cpu(private->size)); - return ret; + goto err; } - ret = sof_parse_tokens(scomp, &tone.config, comp_tokens, + ret = sof_parse_tokens(scomp, &tone->config, comp_tokens, ARRAY_SIZE(comp_tokens), private->array, le32_to_cpu(private->size)); if (ret != 0) { dev_err(sdev->dev, "error: parse tone.cfg tokens failed %d\n", le32_to_cpu(private->size)); - return ret; + goto err; } dev_dbg(sdev->dev, "tone %s: frequency %d amplitude %d\n", - swidget->widget->name, tone.frequency, tone.amplitude); - sof_dbg_comp_config(scomp, &tone.config); + swidget->widget->name, tone->frequency, tone->amplitude); + sof_dbg_comp_config(scomp, &tone->config); + + swidget->private = (void *)tone; return sof_ipc_tx_message(sdev->ipc, - tone.comp.hdr.cmd, &tone, sizeof(tone), r, + tone->comp.hdr.cmd, tone, sizeof(*tone), r, sizeof(*r)); + +err: + kfree(tone); + return ret; } /* @@ -1375,8 +1437,11 @@ static int sof_widget_unload(struct snd_soc_component *scomp, /* free volume table */ kfree(scontrol->volume_table); - break; + + /* fallthrough */ default: + /* free private value */ + kfree(swidget->private); break; } @@ -1790,15 +1855,28 @@ static int sof_route_load(struct snd_soc_component *scomp, int index, struct snd_soc_dapm_route *route) { struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); - struct sof_ipc_pipe_comp_connect connect; + struct sof_ipc_pipe_comp_connect *connect; struct snd_sof_widget *source_swidget, *sink_swidget; struct snd_sof_pcm *spcm; + struct snd_sof_route *sroute; struct sof_ipc_reply reply; int ret = 0; - memset(&connect, 0, sizeof(connect)); - connect.hdr.size = sizeof(connect); - connect.hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_COMP_CONNECT; + /* allocate memory for sroute and connect */ + sroute = kzalloc(sizeof(*sroute), GFP_KERNEL); + if (!sroute) + return -ENOMEM; + + sroute->sdev = sdev; + + connect = kzalloc(sizeof(*connect), GFP_KERNEL); + if (!connect) { + kfree(sroute); + return -ENOMEM; + } + + connect->hdr.size = sizeof(*connect); + connect->hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_COMP_CONNECT; dev_dbg(sdev->dev, "sink %s control %s source %s\n", route->sink, route->control ? route->control : "none", @@ -1817,7 +1895,7 @@ static int sof_route_load(struct snd_soc_component *scomp, int index, return -EINVAL; } - connect.source_id = source_swidget->comp_id; + connect->source_id = source_swidget->comp_id; /* sink component */ sink_swidget = snd_sof_find_swidget(sdev, (char *)route->sink); @@ -1832,7 +1910,7 @@ static int sof_route_load(struct snd_soc_component *scomp, int index, return -EINVAL; } - connect.sink_id = sink_swidget->comp_id; + connect->sink_id = sink_swidget->comp_id; /* Some virtual routes and widgets may been added in topology for * compatibility. For virtual routes, both sink and source are not @@ -1846,8 +1924,8 @@ static int sof_route_load(struct snd_soc_component *scomp, int index, route->source, route->sink); } else { ret = sof_ipc_tx_message(sdev->ipc, - connect.hdr.cmd, - &connect, sizeof(connect), + connect->hdr.cmd, + connect, sizeof(*connect), &reply, sizeof(reply)); /* check IPC return value */ @@ -1869,6 +1947,13 @@ static int sof_route_load(struct snd_soc_component *scomp, int index, } } + /* TODO: free sroute/connect when unloading route */ + sroute->route = route; + sroute->private = connect; + + /* add route to route list */ + list_add(&sroute->list, &sdev->route_list); + return ret; } @@ -1879,10 +1964,9 @@ static int sof_route_unload(struct snd_soc_component *scomp, return 0; } -static int sof_complete_pipeline(struct snd_soc_component *scomp, - struct snd_sof_widget *swidget) +int sof_complete_pipeline(struct snd_sof_dev *sdev, + struct snd_sof_widget *swidget) { - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); struct sof_ipc_pipe_ready ready; struct sof_ipc_reply reply; int ret; @@ -1917,7 +2001,7 @@ static void sof_complete(struct snd_soc_component *scomp) switch (swidget->id) { case snd_soc_dapm_scheduler: swidget->complete = - sof_complete_pipeline(scomp, swidget); + sof_complete_pipeline(sdev, swidget); break; default: break; @@ -2023,10 +2107,24 @@ EXPORT_SYMBOL(snd_sof_load_topology); void snd_sof_free_topology(struct snd_sof_dev *sdev) { + struct snd_soc_dapm_context *dapm = + snd_soc_component_get_dapm(sdev->component); + struct snd_sof_route *sroute, *temp; int ret; dev_dbg(sdev->dev, "free topology...\n"); + /* remove routes */ + list_for_each_entry_safe(sroute, temp, &sdev->route_list, list) { + + /* delete dapm route */ + snd_soc_dapm_del_routes(dapm, sroute->route, 1); + + /* free sroute and its private data */ + kfree(sroute->private); + kfree(sroute); + } + ret = snd_soc_tplg_component_remove(sdev->component, SND_SOC_TPLG_INDEX_ALL); if (ret < 0) diff --git a/sound/soc/sof/trace.c b/sound/soc/sof/trace.c index 04acd78f13be1a..311c7407f241e4 100644 --- a/sound/soc/sof/trace.c +++ b/sound/soc/sof/trace.c @@ -142,12 +142,57 @@ static int trace_debugfs_create(struct snd_sof_dev *sdev) return 0; } -int snd_sof_init_trace(struct snd_sof_dev *sdev) +int sof_init_trace_ipc(struct snd_sof_dev *sdev) { struct sof_ipc_dma_trace_params params; struct sof_ipc_reply ipc_reply; int ret; + /* set IPC parameters */ + params.hdr.size = sizeof(params); + params.hdr.cmd = SOF_IPC_GLB_TRACE_MSG | SOF_IPC_TRACE_DMA_PARAMS; + params.buffer.phy_addr = sdev->dmatp.addr; + params.buffer.size = sdev->dmatb.bytes; + params.buffer.offset = 0; + params.buffer.pages = sdev->dma_trace_pages; + + init_waitqueue_head(&sdev->trace_sleep); + sdev->host_offset = 0; + + 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, "stream_tag: %d\n", 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); + return ret; + } + + 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); + return ret; + } + + sdev->dtrace_is_enabled = true; + + return 0; +} + +int snd_sof_init_trace(struct snd_sof_dev *sdev) +{ + int ret; + /* set false before start initialization */ sdev->dtrace_is_enabled = false; @@ -182,45 +227,11 @@ int snd_sof_init_trace(struct snd_sof_dev *sdev) if (ret < 0) goto table_err; - /* set IPC parameters */ - params.hdr.size = sizeof(params); - params.hdr.cmd = SOF_IPC_GLB_TRACE_MSG | SOF_IPC_TRACE_DMA_PARAMS; - params.buffer.phy_addr = sdev->dmatp.addr; - params.buffer.size = sdev->dmatb.bytes; - params.buffer.offset = 0; - params.buffer.pages = sdev->dma_trace_pages; - - init_waitqueue_head(&sdev->trace_sleep); - sdev->host_offset = 0; - - 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); - goto table_err; - } - dev_dbg(sdev->dev, "stream_tag: %d\n", 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 table_err; - } - - 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); + ret = sof_init_trace_ipc(sdev); + if (ret < 0) goto table_err; - } - sdev->dtrace_is_enabled = true; return 0; - table_err: snd_dma_free_pages(&sdev->dmatb); page_err: