diff options
Diffstat (limited to 'sound/soc/intel/avs/pcm.c')
-rw-r--r-- | sound/soc/intel/avs/pcm.c | 416 |
1 files changed, 360 insertions, 56 deletions
diff --git a/sound/soc/intel/avs/pcm.c b/sound/soc/intel/avs/pcm.c index 8fe5917b1e26..70d687fa9923 100644 --- a/sound/soc/intel/avs/pcm.c +++ b/sound/soc/intel/avs/pcm.c @@ -28,6 +28,8 @@ struct avs_dma_data { * host stream assigned */ struct hdac_ext_stream *host_stream; + + struct snd_pcm_substream *substream; }; static struct avs_tplg_path_template * @@ -55,8 +57,11 @@ avs_dai_find_path_template(struct snd_soc_dai *dai, bool is_fe, int direction) return dw->priv; } -static int avs_dai_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai, bool is_fe) +static int avs_dai_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai, bool is_fe, + const struct snd_soc_dai_ops *ops) { + struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream); + struct avs_dev *adev = to_avs_dev(dai->dev); struct avs_tplg_path_template *template; struct avs_dma_data *data; @@ -71,9 +76,13 @@ static int avs_dai_startup(struct snd_pcm_substream *substream, struct snd_soc_d if (!data) return -ENOMEM; + data->substream = substream; data->template = template; snd_soc_dai_set_dma_data(dai, substream, data); + if (rtd->dai_link->ignore_suspend) + adev->num_lp_paths++; + return 0; } @@ -151,15 +160,22 @@ static int avs_dai_prepare(struct avs_dev *adev, struct snd_pcm_substream *subst return ret; } +static const struct snd_soc_dai_ops avs_dai_nonhda_be_ops; + static int avs_dai_nonhda_be_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { - return avs_dai_startup(substream, dai, false); + return avs_dai_startup(substream, dai, false, &avs_dai_nonhda_be_ops); } static void avs_dai_nonhda_be_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { + struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream); + struct avs_dev *adev = to_avs_dev(dai->dev); struct avs_dma_data *data; + if (rtd->dai_link->ignore_suspend) + adev->num_lp_paths--; + data = snd_soc_dai_get_dma_data(dai, substream); snd_soc_dai_set_dma_data(dai, substream, NULL); @@ -202,30 +218,43 @@ static int avs_dai_nonhda_be_prepare(struct snd_pcm_substream *substream, struct static int avs_dai_nonhda_be_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai) { + struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream); struct avs_dma_data *data; int ret = 0; data = snd_soc_dai_get_dma_data(dai, substream); switch (cmd) { + case SNDRV_PCM_TRIGGER_RESUME: + if (rtd->dai_link->ignore_suspend) + break; + fallthrough; case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + ret = avs_path_pause(data->path); + if (ret < 0) { + dev_err(dai->dev, "pause BE path failed: %d\n", ret); + break; + } + ret = avs_path_run(data->path, AVS_TPLG_TRIGGER_AUTO); if (ret < 0) dev_err(dai->dev, "run BE path failed: %d\n", ret); break; + case SNDRV_PCM_TRIGGER_SUSPEND: + if (rtd->dai_link->ignore_suspend) + break; + fallthrough; case SNDRV_PCM_TRIGGER_PAUSE_PUSH: case SNDRV_PCM_TRIGGER_STOP: ret = avs_path_pause(data->path); if (ret < 0) dev_err(dai->dev, "pause BE path failed: %d\n", ret); - if (cmd == SNDRV_PCM_TRIGGER_STOP) { - ret = avs_path_reset(data->path); - if (ret < 0) - dev_err(dai->dev, "reset BE path failed: %d\n", ret); - } + ret = avs_path_reset(data->path); + if (ret < 0) + dev_err(dai->dev, "reset BE path failed: %d\n", ret); break; default: @@ -245,9 +274,11 @@ static const struct snd_soc_dai_ops avs_dai_nonhda_be_ops = { .trigger = avs_dai_nonhda_be_trigger, }; +static const struct snd_soc_dai_ops avs_dai_hda_be_ops; + static int avs_dai_hda_be_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { - return avs_dai_startup(substream, dai, false); + return avs_dai_startup(substream, dai, false, &avs_dai_hda_be_ops); } static void avs_dai_hda_be_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) @@ -292,12 +323,12 @@ static int avs_dai_hda_be_hw_free(struct snd_pcm_substream *substream, struct sn /* clear link <-> stream mapping */ codec = dev_to_hda_codec(asoc_rtd_to_codec(rtd, 0)->dev); - link = snd_hdac_ext_bus_link_at(&codec->bus->core, codec->core.addr); + link = snd_hdac_ext_bus_get_hlink_by_addr(&codec->bus->core, codec->core.addr); if (!link) return -EINVAL; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - snd_hdac_ext_link_clear_stream_id(link, hdac_stream(link_stream)->stream_tag); + snd_hdac_ext_bus_link_clear_stream_id(link, hdac_stream(link_stream)->stream_tag); return 0; } @@ -322,15 +353,15 @@ static int avs_dai_hda_be_prepare(struct snd_pcm_substream *substream, struct sn runtime->sample_bits, 0); snd_hdac_ext_stream_decouple(bus, link_stream, true); - snd_hdac_ext_link_stream_reset(link_stream); - snd_hdac_ext_link_stream_setup(link_stream, format_val); + snd_hdac_ext_stream_reset(link_stream); + snd_hdac_ext_stream_setup(link_stream, format_val); - link = snd_hdac_ext_bus_link_at(bus, codec->core.addr); + link = snd_hdac_ext_bus_get_hlink_by_addr(bus, codec->core.addr); if (!link) return -EINVAL; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - snd_hdac_ext_link_set_stream_id(link, hdac_stream(link_stream)->stream_tag); + snd_hdac_ext_bus_link_set_stream_id(link, hdac_stream(link_stream)->stream_tag); ret = avs_dai_prepare(to_avs_dev(dai->dev), substream, dai); if (ret) @@ -343,6 +374,7 @@ static int avs_dai_hda_be_prepare(struct snd_pcm_substream *substream, struct sn static int avs_dai_hda_be_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai) { + struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream); struct hdac_ext_stream *link_stream; struct avs_dma_data *data; int ret = 0; @@ -353,28 +385,40 @@ static int avs_dai_hda_be_trigger(struct snd_pcm_substream *substream, int cmd, link_stream = substream->runtime->private_data; switch (cmd) { + case SNDRV_PCM_TRIGGER_RESUME: + if (rtd->dai_link->ignore_suspend) + break; + fallthrough; case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - snd_hdac_ext_link_stream_start(link_stream); + snd_hdac_ext_stream_start(link_stream); + + ret = avs_path_pause(data->path); + if (ret < 0) { + dev_err(dai->dev, "pause BE path failed: %d\n", ret); + break; + } ret = avs_path_run(data->path, AVS_TPLG_TRIGGER_AUTO); if (ret < 0) dev_err(dai->dev, "run BE path failed: %d\n", ret); break; + case SNDRV_PCM_TRIGGER_SUSPEND: + if (rtd->dai_link->ignore_suspend) + break; + fallthrough; case SNDRV_PCM_TRIGGER_PAUSE_PUSH: case SNDRV_PCM_TRIGGER_STOP: ret = avs_path_pause(data->path); if (ret < 0) dev_err(dai->dev, "pause BE path failed: %d\n", ret); - snd_hdac_ext_link_stream_clear(link_stream); + snd_hdac_ext_stream_clear(link_stream); - if (cmd == SNDRV_PCM_TRIGGER_STOP) { - ret = avs_path_reset(data->path); - if (ret < 0) - dev_err(dai->dev, "reset BE path failed: %d\n", ret); - } + ret = avs_path_reset(data->path); + if (ret < 0) + dev_err(dai->dev, "reset BE path failed: %d\n", ret); break; default: @@ -407,6 +451,8 @@ static const struct snd_pcm_hw_constraint_list hw_rates = { .mask = 0, }; +const struct snd_soc_dai_ops avs_dai_fe_ops; + static int avs_dai_fe_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct snd_pcm_runtime *runtime = substream->runtime; @@ -416,7 +462,7 @@ static int avs_dai_fe_startup(struct snd_pcm_substream *substream, struct snd_so struct hdac_ext_stream *host_stream; int ret; - ret = avs_dai_startup(substream, dai, true); + ret = avs_dai_startup(substream, dai, true, &avs_dai_fe_ops); if (ret) return ret; @@ -443,8 +489,13 @@ static int avs_dai_fe_startup(struct snd_pcm_substream *substream, struct snd_so static void avs_dai_fe_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { + struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream); + struct avs_dev *adev = to_avs_dev(dai->dev); struct avs_dma_data *data; + if (rtd->dai_link->ignore_suspend) + adev->num_lp_paths--; + data = snd_soc_dai_get_dma_data(dai, substream); snd_soc_dai_set_dma_data(dai, substream, NULL); @@ -499,7 +550,7 @@ create_err: return ret; } -static int avs_dai_fe_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) +static int __avs_dai_fe_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct avs_dma_data *data; struct hdac_ext_stream *host_stream; @@ -523,9 +574,15 @@ static int avs_dai_fe_hw_free(struct snd_pcm_substream *substream, struct snd_so snd_hdac_stream_cleanup(hdac_stream(host_stream)); hdac_stream(host_stream)->prepared = false; - ret = snd_pcm_lib_free_pages(substream); - if (ret < 0) - dev_dbg(dai->dev, "Failed to free pages!\n"); + return ret; +} + +static int avs_dai_fe_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) +{ + int ret; + + ret = __avs_dai_fe_hw_free(substream, dai); + snd_pcm_lib_free_pages(substream); return ret; } @@ -571,6 +628,7 @@ static int avs_dai_fe_prepare(struct snd_pcm_substream *substream, struct snd_so static int avs_dai_fe_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai) { + struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream); struct avs_dma_data *data; struct hdac_ext_stream *host_stream; struct hdac_bus *bus; @@ -582,17 +640,36 @@ static int avs_dai_fe_trigger(struct snd_pcm_substream *substream, int cmd, stru bus = hdac_stream(host_stream)->bus; switch (cmd) { + case SNDRV_PCM_TRIGGER_RESUME: + if (rtd->dai_link->ignore_suspend) + break; + fallthrough; case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: spin_lock_irqsave(&bus->reg_lock, flags); snd_hdac_stream_start(hdac_stream(host_stream), true); spin_unlock_irqrestore(&bus->reg_lock, flags); + /* Timeout on DRSM poll shall not stop the resume so ignore the result. */ + if (cmd == SNDRV_PCM_TRIGGER_RESUME) + snd_hdac_stream_wait_drsm(hdac_stream(host_stream)); + + ret = avs_path_pause(data->path); + if (ret < 0) { + dev_err(dai->dev, "pause FE path failed: %d\n", ret); + break; + } + ret = avs_path_run(data->path, AVS_TPLG_TRIGGER_AUTO); if (ret < 0) dev_err(dai->dev, "run FE path failed: %d\n", ret); + break; + case SNDRV_PCM_TRIGGER_SUSPEND: + if (rtd->dai_link->ignore_suspend) + break; + fallthrough; case SNDRV_PCM_TRIGGER_PAUSE_PUSH: case SNDRV_PCM_TRIGGER_STOP: ret = avs_path_pause(data->path); @@ -603,11 +680,9 @@ static int avs_dai_fe_trigger(struct snd_pcm_substream *substream, int cmd, stru snd_hdac_stream_stop(hdac_stream(host_stream)); spin_unlock_irqrestore(&bus->reg_lock, flags); - if (cmd == SNDRV_PCM_TRIGGER_STOP) { - ret = avs_path_reset(data->path); - if (ret < 0) - dev_err(dai->dev, "reset FE path failed: %d\n", ret); - } + ret = avs_path_reset(data->path); + if (ret < 0) + dev_err(dai->dev, "reset FE path failed: %d\n", ret); break; default: @@ -662,6 +737,7 @@ static int avs_component_load_libraries(struct avs_soc_component *acomp) if (ret < 0) return ret; + avs_hda_power_gating_enable(adev, false); avs_hda_clock_gating_enable(adev, false); avs_hda_l1sen_enable(adev, false); @@ -669,6 +745,7 @@ static int avs_component_load_libraries(struct avs_soc_component *acomp) avs_hda_l1sen_enable(adev, true); avs_hda_clock_gating_enable(adev, true); + avs_hda_power_gating_enable(adev, true); if (!ret) ret = avs_module_info_init(adev, false); @@ -752,33 +829,232 @@ static void avs_component_remove(struct snd_soc_component *component) } } +static int avs_dai_resume_hw_params(struct snd_soc_dai *dai, struct avs_dma_data *data) +{ + struct snd_pcm_substream *substream; + struct snd_soc_pcm_runtime *rtd; + int ret; + + substream = data->substream; + rtd = snd_pcm_substream_chip(substream); + + ret = dai->driver->ops->hw_params(substream, &rtd->dpcm[substream->stream].hw_params, dai); + if (ret) + dev_err(dai->dev, "hw_params on resume failed: %d\n", ret); + + return ret; +} + +static int avs_dai_resume_fe_prepare(struct snd_soc_dai *dai, struct avs_dma_data *data) +{ + struct hdac_ext_stream *host_stream; + struct hdac_stream *hstream; + struct hdac_bus *bus; + int ret; + + host_stream = data->host_stream; + hstream = hdac_stream(host_stream); + bus = hdac_stream(host_stream)->bus; + + /* Set DRSM before programming stream and position registers. */ + snd_hdac_stream_drsm_enable(bus, true, hstream->index); + + ret = dai->driver->ops->prepare(data->substream, dai); + if (ret) { + dev_err(dai->dev, "prepare FE on resume failed: %d\n", ret); + return ret; + } + + writel(host_stream->pphcllpl, host_stream->pphc_addr + AZX_REG_PPHCLLPL); + writel(host_stream->pphcllpu, host_stream->pphc_addr + AZX_REG_PPHCLLPU); + writel(host_stream->pphcldpl, host_stream->pphc_addr + AZX_REG_PPHCLDPL); + writel(host_stream->pphcldpu, host_stream->pphc_addr + AZX_REG_PPHCLDPU); + + /* As per HW spec recommendation, program LPIB and DPIB to the same value. */ + snd_hdac_stream_set_lpib(hstream, hstream->lpib); + snd_hdac_stream_set_dpibr(bus, hstream, hstream->lpib); + + return 0; +} + +static int avs_dai_resume_be_prepare(struct snd_soc_dai *dai, struct avs_dma_data *data) +{ + int ret; + + ret = dai->driver->ops->prepare(data->substream, dai); + if (ret) + dev_err(dai->dev, "prepare BE on resume failed: %d\n", ret); + + return ret; +} + +static int avs_dai_suspend_fe_hw_free(struct snd_soc_dai *dai, struct avs_dma_data *data) +{ + struct hdac_ext_stream *host_stream; + int ret; + + host_stream = data->host_stream; + + /* Store position addresses so we can resume from them later on. */ + hdac_stream(host_stream)->lpib = snd_hdac_stream_get_pos_lpib(hdac_stream(host_stream)); + host_stream->pphcllpl = readl(host_stream->pphc_addr + AZX_REG_PPHCLLPL); + host_stream->pphcllpu = readl(host_stream->pphc_addr + AZX_REG_PPHCLLPU); + host_stream->pphcldpl = readl(host_stream->pphc_addr + AZX_REG_PPHCLDPL); + host_stream->pphcldpu = readl(host_stream->pphc_addr + AZX_REG_PPHCLDPU); + + ret = __avs_dai_fe_hw_free(data->substream, dai); + if (ret < 0) + dev_err(dai->dev, "hw_free FE on suspend failed: %d\n", ret); + + return ret; +} + +static int avs_dai_suspend_be_hw_free(struct snd_soc_dai *dai, struct avs_dma_data *data) +{ + int ret; + + ret = dai->driver->ops->hw_free(data->substream, dai); + if (ret < 0) + dev_err(dai->dev, "hw_free BE on suspend failed: %d\n", ret); + + return ret; +} + +static int avs_component_pm_op(struct snd_soc_component *component, bool be, + int (*op)(struct snd_soc_dai *, struct avs_dma_data *)) +{ + struct snd_soc_pcm_runtime *rtd; + struct avs_dma_data *data; + struct snd_soc_dai *dai; + int ret; + + for_each_component_dais(component, dai) { + data = dai->playback_dma_data; + if (data) { + rtd = snd_pcm_substream_chip(data->substream); + if (rtd->dai_link->no_pcm == be && !rtd->dai_link->ignore_suspend) { + ret = op(dai, data); + if (ret < 0) { + __snd_pcm_set_state(data->substream->runtime, + SNDRV_PCM_STATE_DISCONNECTED); + return ret; + } + } + } + + data = dai->capture_dma_data; + if (data) { + rtd = snd_pcm_substream_chip(data->substream); + if (rtd->dai_link->no_pcm == be && !rtd->dai_link->ignore_suspend) { + ret = op(dai, data); + if (ret < 0) { + __snd_pcm_set_state(data->substream->runtime, + SNDRV_PCM_STATE_DISCONNECTED); + return ret; + } + } + } + } + + return 0; +} + +static int avs_component_resume_hw_params(struct snd_soc_component *component, bool be) +{ + return avs_component_pm_op(component, be, &avs_dai_resume_hw_params); +} + +static int avs_component_resume_prepare(struct snd_soc_component *component, bool be) +{ + int (*prepare_cb)(struct snd_soc_dai *dai, struct avs_dma_data *data); + + if (be) + prepare_cb = &avs_dai_resume_be_prepare; + else + prepare_cb = &avs_dai_resume_fe_prepare; + + return avs_component_pm_op(component, be, prepare_cb); +} + +static int avs_component_suspend_hw_free(struct snd_soc_component *component, bool be) +{ + int (*hw_free_cb)(struct snd_soc_dai *dai, struct avs_dma_data *data); + + if (be) + hw_free_cb = &avs_dai_suspend_be_hw_free; + else + hw_free_cb = &avs_dai_suspend_fe_hw_free; + + return avs_component_pm_op(component, be, hw_free_cb); +} + +static int avs_component_suspend(struct snd_soc_component *component) +{ + int ret; + + /* + * When freeing paths, FEs need to be first as they perform + * path unbinding. + */ + ret = avs_component_suspend_hw_free(component, false); + if (ret) + return ret; + + return avs_component_suspend_hw_free(component, true); +} + +static int avs_component_resume(struct snd_soc_component *component) +{ + int ret; + + /* + * When creating paths, FEs need to be last as they perform + * path binding. + */ + ret = avs_component_resume_hw_params(component, true); + if (ret) + return ret; + + ret = avs_component_resume_hw_params(component, false); + if (ret) + return ret; + + /* It is expected that the LINK stream is prepared first. */ + ret = avs_component_resume_prepare(component, true); + if (ret) + return ret; + + return avs_component_resume_prepare(component, false); +} + +static const struct snd_pcm_hardware avs_pcm_hardware = { + .info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_RESUME | + SNDRV_PCM_INFO_NO_PERIOD_WAKEUP, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .buffer_bytes_max = AZX_MAX_BUF_SIZE, + .period_bytes_min = 128, + .period_bytes_max = AZX_MAX_BUF_SIZE / 2, + .periods_min = 2, + .periods_max = AZX_MAX_FRAG, + .fifo_size = 0, +}; + static int avs_component_open(struct snd_soc_component *component, struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream); - struct snd_pcm_hardware hwparams; /* only FE DAI links are handled here */ if (rtd->dai_link->no_pcm) return 0; - hwparams.info = SNDRV_PCM_INFO_MMAP | - SNDRV_PCM_INFO_MMAP_VALID | - SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_PAUSE | - SNDRV_PCM_INFO_NO_PERIOD_WAKEUP; - - hwparams.formats = SNDRV_PCM_FMTBIT_S16_LE | - SNDRV_PCM_FMTBIT_S24_LE | - SNDRV_PCM_FMTBIT_S32_LE; - hwparams.period_bytes_min = 128; - hwparams.period_bytes_max = AZX_MAX_BUF_SIZE / 2; - hwparams.periods_min = 2; - hwparams.periods_max = AZX_MAX_FRAG; - hwparams.buffer_bytes_max = AZX_MAX_BUF_SIZE; - hwparams.fifo_size = 0; - - return snd_soc_set_runtime_hwparams(substream, &hwparams); + return snd_soc_set_runtime_hwparams(substream, &avs_pcm_hardware); } static unsigned int avs_hda_stream_dpib_read(struct hdac_ext_stream *stream) @@ -840,6 +1116,8 @@ static const struct snd_soc_component_driver avs_component_driver = { .name = "avs-pcm", .probe = avs_component_probe, .remove = avs_component_remove, + .suspend = avs_component_suspend, + .resume = avs_component_resume, .open = avs_component_open, .pointer = avs_component_pointer, .mmap = avs_component_mmap, @@ -1016,10 +1294,8 @@ static void avs_component_hda_unregister_dais(struct snd_soc_component *componen if (!strstr(dai->driver->name, name)) continue; - if (dai->playback_widget) - snd_soc_dapm_free_widget(dai->playback_widget); - if (dai->capture_widget) - snd_soc_dapm_free_widget(dai->capture_widget); + snd_soc_dapm_free_widget(dai->playback_widget); + snd_soc_dapm_free_widget(dai->capture_widget); snd_soc_unregister_dai(dai); } } @@ -1122,9 +1398,35 @@ static int avs_component_hda_open(struct snd_soc_component *component, struct hdac_ext_stream *link_stream; struct hda_codec *codec; - /* only BE DAI links are handled here */ - if (!rtd->dai_link->no_pcm) - return avs_component_open(component, substream); + if (!rtd->dai_link->no_pcm) { + struct snd_pcm_hardware hwparams = avs_pcm_hardware; + struct snd_soc_pcm_runtime *be; + struct snd_soc_dpcm *dpcm; + int dir = substream->stream; + + /* + * Support the DPCM reparenting while still fulfilling expectations of HDAudio + * common code - a valid stream pointer at substream->runtime->private_data - + * by having all FEs point to the same private data. + */ + for_each_dpcm_be(rtd, dir, dpcm) { + struct snd_pcm_substream *be_substream; + + be = dpcm->be; + if (be->dpcm[dir].users == 1) + break; + + be_substream = snd_soc_dpcm_get_substream(be, dir); + substream->runtime->private_data = be_substream->runtime->private_data; + break; + } + + /* RESUME unsupported for de-coupled HD-Audio capture. */ + if (dir == SNDRV_PCM_STREAM_CAPTURE) + hwparams.info &= ~SNDRV_PCM_INFO_RESUME; + + return snd_soc_set_runtime_hwparams(substream, &hwparams); + } codec = dev_to_hda_codec(asoc_rtd_to_codec(rtd, 0)->dev); link_stream = snd_hdac_ext_stream_assign(&codec->bus->core, substream, @@ -1157,6 +1459,8 @@ static const struct snd_soc_component_driver avs_hda_component_driver = { .name = "avs-hda-pcm", .probe = avs_component_hda_probe, .remove = avs_component_hda_remove, + .suspend = avs_component_suspend, + .resume = avs_component_resume, .open = avs_component_hda_open, .close = avs_component_hda_close, .pointer = avs_component_pointer, |