From 8398e9ae9c0f54e1d29d16ce477a5af3fd8931ec Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Fri, 13 Jul 2018 00:25:48 -0700 Subject: [PATCH 01/10] ASoC: SOF: add suspend/resume callbacks for APL Add suspend/resume callbacks for APL. Signed-off-by: Ranjani Sridharan --- sound/soc/sof/intel/apl.c | 4 ++++ sound/soc/sof/intel/hda-dsp.c | 17 ++++++++++++++++- sound/soc/sof/intel/hda.h | 2 ++ 3 files changed, 22 insertions(+), 1 deletion(-) 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); /* From 644e4bfc7a556f70b5bb4e1dcbed71a674f486c6 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Mon, 16 Jul 2018 15:21:10 -0700 Subject: [PATCH 02/10] ASoC: SOF: store ipc comp data as private data Save the ipc comp data so that the pipeline components can be restored during PM resume. This patch also handles deleting the DAPM routes before the pipeline widgets are unloaded and the associated data is freed. Signed-off-by: Ranjani Sridharan --- sound/soc/sof/topology.c | 278 +++++++++++++++++++++++++-------------- 1 file changed, 177 insertions(+), 101 deletions(-) diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index 79071990fd221e..23ca7f9c3c1f36 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 */ @@ -1080,45 +1115,51 @@ static int sof_widget_load_pga(struct snd_soc_component *scomp, int index, /* 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 +1173,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 +1231,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 +1434,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; } @@ -2023,10 +2085,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) From 88808880b982b815515db9fb4b77e6ba805ae425 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Mon, 16 Jul 2018 17:21:26 -0700 Subject: [PATCH 03/10] ASoC: SOF: create route list in snd_sof_dev to store pipeline connections Create an route_list in snd_sof_dev that will be used to store the dapm routes while parsing topology. This list will be used for restoring the pipeline connections during suspend/resume. Signed-off-by: Ranjani Sridharan --- sound/soc/sof/core.c | 1 + sound/soc/sof/sof-priv.h | 11 +++++++++++ sound/soc/sof/topology.c | 36 ++++++++++++++++++++++++++++-------- 3 files changed, 40 insertions(+), 8 deletions(-) diff --git a/sound/soc/sof/core.c b/sound/soc/sof/core.c index fe010440c8f5e9..76169c84bdf587 100644 --- a/sound/soc/sof/core.c +++ b/sound/soc/sof/core.c @@ -249,6 +249,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); diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index 11f3b1575f8a24..e8aed718cf61e0 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -261,6 +261,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 +334,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 */ diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index 23ca7f9c3c1f36..64382a07363c45 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -1852,15 +1852,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", @@ -1879,7 +1892,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); @@ -1894,7 +1907,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 @@ -1908,8 +1921,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 */ @@ -1931,6 +1944,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; } From 9bc525181a6ce51c408bea8f984c8b77822fc0cb Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Mon, 16 Jul 2018 23:02:46 -0700 Subject: [PATCH 04/10] ASoC: SOF: move ipc for initializing trace into a separate function Move the code to send ipc for initializing trace into a separate function that can be called during suspend/resume. Signed-off-by: Ranjani Sridharan --- sound/soc/sof/sof-priv.h | 1 + sound/soc/sof/trace.c | 85 +++++++++++++++++++++++----------------- 2 files changed, 49 insertions(+), 37 deletions(-) diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index e8aed718cf61e0..45d9bd390c719c 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -473,6 +473,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/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: From 47d23a61406fa133df77cea2a8a321588a01e009 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Mon, 16 Jul 2018 23:05:39 -0700 Subject: [PATCH 05/10] ASoC: SOF: make sof_complete_pipeline non static This will be called during resume to send ipc for pipeline completion. Signed-off-by: Ranjani Sridharan --- sound/soc/sof/sof-priv.h | 2 ++ sound/soc/sof/topology.c | 7 +++---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index 45d9bd390c719c..65a9074c833b2c 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -456,6 +456,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 diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index 64382a07363c45..287bff84339c19 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -1961,10 +1961,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; @@ -1999,7 +1998,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; From e1658fee04deb1de017073dda160f20cc2184eee Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Fri, 13 Jul 2018 00:32:47 -0700 Subject: [PATCH 06/10] ASoC: SOF: suspend/resume and runtime PM implementation This is the initial implementation for PM and runtime PM callbacks in the SOF driver. The suspend callback includes: suspend all pcm's stream that are running, send CTX_SAVE ipc, drop all ipc's, release trace dma and then power off the DSP. And the resume callback performs the following steps: load FW, run FW, re-initialize trace, restore pipeline, restore the kcontrol values and finally send the ctx restore ipc to the dsp. The streams that are suspended are resumed by the ALSA resume trigger. If the streams are paused during system suspend, they are marked explicitly so they can be restored during PAUSE_RELEASE. Signed-off-by: Ranjani Sridharan --- sound/soc/sof/pm.c | 284 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 281 insertions(+), 3 deletions(-) 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); From 7f21cdb0628122d2772848edaf7efa6fbf8f6c83 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Wed, 18 Jul 2018 00:32:15 -0700 Subject: [PATCH 07/10] ASoC: SOF: implement resume trigger flow This patch adds the changes required to save the pcm hw_params that will be used to restart streams during PM resume. It also implements the flow for pcm resume trigger and handles the stop/pause_release triggersafter resume, There are 3 possible situations when the system resumes from sleep: 1. If the stream was running at suspend, the hw_params is restored and the stream started from the last know host dma position. 2. If the stream was paused at suspend and the user undoes pause after resume, the SNDRV_PCM_TRIGGER_RESUME does not get invoked for such streams. So these streams need to marked for hw_params to be restored at resume and started from the paused host dma position. 3. If the stream was paused at suspend and the user stops playback after resume, the trigger callback method should return without any further action because the stream has not been set up after resume anyway. Signed-off-by: Ranjani Sridharan --- sound/soc/sof/pcm.c | 82 ++++++++++++++++++++++++++++++++++++---- sound/soc/sof/sof-priv.h | 2 + 2 files changed, 76 insertions(+), 8 deletions(-) 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/sof-priv.h b/sound/soc/sof/sof-priv.h index 65a9074c833b2c..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 */ From 2824587a88dfe89311b65fe1a6435b73f3e0f5be Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Fri, 20 Jul 2018 00:12:15 -0700 Subject: [PATCH 08/10] ASoC: SOF: invoke runtime_put after booting firmware Without this change the runtime_usage count never reaches 0 and the device never suspends even when it is idle. Signed-off-by: Ranjani Sridharan --- sound/soc/sof/core.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sound/soc/sof/core.c b/sound/soc/sof/core.c index 76169c84bdf587..78f2d6109a93b2 100644 --- a/sound/soc/sof/core.c +++ b/sound/soc/sof/core.c @@ -8,6 +8,7 @@ * Author: Liam Girdwood */ +#include #include #include #include @@ -331,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: From a1e21c698e46a924d8b8f9d4ff0da5cd9e275df1 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Fri, 20 Jul 2018 22:24:46 -0700 Subject: [PATCH 09/10] ASoC: SOF: set kcontrol cmd for pga widget set the kcontrol cmd which will be used to send the correct ipc command to restore volume control value during resume. Signed-off-by: Ranjani Sridharan --- sound/soc/sof/topology.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index 287bff84339c19..767f6ec06b2ba8 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -1109,6 +1109,9 @@ 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; From aa8cb23da447320a9fb91f69064fd029f3e24bcb Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Thu, 19 Jul 2018 23:05:09 -0700 Subject: [PATCH 10/10] ASoC: SOF: move PM callbacks to the SOF platform device Set the PM callbacks for the SOF device instead of the acpi/pci/spi device. pm_runtime_enable() is called in the pcm_probe() method and it is enabled for the platform device. Signed-off-by: Ranjani Sridharan --- sound/soc/sof/core.c | 8 ++++++++ sound/soc/sof/sof-acpi-dev.c | 8 -------- sound/soc/sof/sof-pci-dev.c | 10 ---------- sound/soc/sof/sof-spi-dev.c | 7 ------- 4 files changed, 8 insertions(+), 25 deletions(-) diff --git a/sound/soc/sof/core.c b/sound/soc/sof/core.c index 78f2d6109a93b2..1c1af8ba0f824a 100644 --- a/sound/soc/sof/core.c +++ b/sound/soc/sof/core.c @@ -371,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/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-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;