162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci// 362306a36Sopenharmony_ci// Copyright(c) 2021-2022 Intel Corporation. All rights reserved. 462306a36Sopenharmony_ci// 562306a36Sopenharmony_ci// Authors: Cezary Rojewski <cezary.rojewski@intel.com> 662306a36Sopenharmony_ci// Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com> 762306a36Sopenharmony_ci// 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/debugfs.h> 1062306a36Sopenharmony_ci#include <linux/device.h> 1162306a36Sopenharmony_ci#include <sound/hda_register.h> 1262306a36Sopenharmony_ci#include <sound/hdaudio_ext.h> 1362306a36Sopenharmony_ci#include <sound/pcm_params.h> 1462306a36Sopenharmony_ci#include <sound/soc-acpi.h> 1562306a36Sopenharmony_ci#include <sound/soc-acpi-intel-match.h> 1662306a36Sopenharmony_ci#include <sound/soc-component.h> 1762306a36Sopenharmony_ci#include "avs.h" 1862306a36Sopenharmony_ci#include "path.h" 1962306a36Sopenharmony_ci#include "topology.h" 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_cistruct avs_dma_data { 2262306a36Sopenharmony_ci struct avs_tplg_path_template *template; 2362306a36Sopenharmony_ci struct avs_path *path; 2462306a36Sopenharmony_ci /* 2562306a36Sopenharmony_ci * link stream is stored within substream's runtime 2662306a36Sopenharmony_ci * private_data to fulfill the needs of codec BE path 2762306a36Sopenharmony_ci * 2862306a36Sopenharmony_ci * host stream assigned 2962306a36Sopenharmony_ci */ 3062306a36Sopenharmony_ci struct hdac_ext_stream *host_stream; 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci struct snd_pcm_substream *substream; 3362306a36Sopenharmony_ci}; 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_cistatic struct avs_tplg_path_template * 3662306a36Sopenharmony_ciavs_dai_find_path_template(struct snd_soc_dai *dai, bool is_fe, int direction) 3762306a36Sopenharmony_ci{ 3862306a36Sopenharmony_ci struct snd_soc_dapm_widget *dw = snd_soc_dai_get_widget(dai, direction); 3962306a36Sopenharmony_ci struct snd_soc_dapm_path *dp; 4062306a36Sopenharmony_ci enum snd_soc_dapm_direction dir; 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci if (direction == SNDRV_PCM_STREAM_CAPTURE) { 4362306a36Sopenharmony_ci dir = is_fe ? SND_SOC_DAPM_DIR_OUT : SND_SOC_DAPM_DIR_IN; 4462306a36Sopenharmony_ci } else { 4562306a36Sopenharmony_ci dir = is_fe ? SND_SOC_DAPM_DIR_IN : SND_SOC_DAPM_DIR_OUT; 4662306a36Sopenharmony_ci } 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci dp = list_first_entry_or_null(&dw->edges[dir], typeof(*dp), list_node[dir]); 4962306a36Sopenharmony_ci if (!dp) 5062306a36Sopenharmony_ci return NULL; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci /* Get the other widget, with actual path template data */ 5362306a36Sopenharmony_ci dw = (dp->source == dw) ? dp->sink : dp->source; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci return dw->priv; 5662306a36Sopenharmony_ci} 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_cistatic int avs_dai_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai, bool is_fe, 5962306a36Sopenharmony_ci const struct snd_soc_dai_ops *ops) 6062306a36Sopenharmony_ci{ 6162306a36Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 6262306a36Sopenharmony_ci struct avs_dev *adev = to_avs_dev(dai->dev); 6362306a36Sopenharmony_ci struct avs_tplg_path_template *template; 6462306a36Sopenharmony_ci struct avs_dma_data *data; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci template = avs_dai_find_path_template(dai, is_fe, substream->stream); 6762306a36Sopenharmony_ci if (!template) { 6862306a36Sopenharmony_ci dev_err(dai->dev, "no %s path for dai %s, invalid tplg?\n", 6962306a36Sopenharmony_ci snd_pcm_stream_str(substream), dai->name); 7062306a36Sopenharmony_ci return -EINVAL; 7162306a36Sopenharmony_ci } 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci data = kzalloc(sizeof(*data), GFP_KERNEL); 7462306a36Sopenharmony_ci if (!data) 7562306a36Sopenharmony_ci return -ENOMEM; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci data->substream = substream; 7862306a36Sopenharmony_ci data->template = template; 7962306a36Sopenharmony_ci snd_soc_dai_set_dma_data(dai, substream, data); 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci if (rtd->dai_link->ignore_suspend) 8262306a36Sopenharmony_ci adev->num_lp_paths++; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci return 0; 8562306a36Sopenharmony_ci} 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_cistatic int avs_dai_hw_params(struct snd_pcm_substream *substream, 8862306a36Sopenharmony_ci struct snd_pcm_hw_params *fe_hw_params, 8962306a36Sopenharmony_ci struct snd_pcm_hw_params *be_hw_params, struct snd_soc_dai *dai, 9062306a36Sopenharmony_ci int dma_id) 9162306a36Sopenharmony_ci{ 9262306a36Sopenharmony_ci struct avs_dma_data *data; 9362306a36Sopenharmony_ci struct avs_path *path; 9462306a36Sopenharmony_ci struct avs_dev *adev = to_avs_dev(dai->dev); 9562306a36Sopenharmony_ci int ret; 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci data = snd_soc_dai_get_dma_data(dai, substream); 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci dev_dbg(dai->dev, "%s FE hw_params str %p rtd %p", 10062306a36Sopenharmony_ci __func__, substream, substream->runtime); 10162306a36Sopenharmony_ci dev_dbg(dai->dev, "rate %d chn %d vbd %d bd %d\n", 10262306a36Sopenharmony_ci params_rate(fe_hw_params), params_channels(fe_hw_params), 10362306a36Sopenharmony_ci params_width(fe_hw_params), params_physical_width(fe_hw_params)); 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci dev_dbg(dai->dev, "%s BE hw_params str %p rtd %p", 10662306a36Sopenharmony_ci __func__, substream, substream->runtime); 10762306a36Sopenharmony_ci dev_dbg(dai->dev, "rate %d chn %d vbd %d bd %d\n", 10862306a36Sopenharmony_ci params_rate(be_hw_params), params_channels(be_hw_params), 10962306a36Sopenharmony_ci params_width(be_hw_params), params_physical_width(be_hw_params)); 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci path = avs_path_create(adev, dma_id, data->template, fe_hw_params, be_hw_params); 11262306a36Sopenharmony_ci if (IS_ERR(path)) { 11362306a36Sopenharmony_ci ret = PTR_ERR(path); 11462306a36Sopenharmony_ci dev_err(dai->dev, "create path failed: %d\n", ret); 11562306a36Sopenharmony_ci return ret; 11662306a36Sopenharmony_ci } 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci data->path = path; 11962306a36Sopenharmony_ci return 0; 12062306a36Sopenharmony_ci} 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_cistatic int avs_dai_be_hw_params(struct snd_pcm_substream *substream, 12362306a36Sopenharmony_ci struct snd_pcm_hw_params *be_hw_params, struct snd_soc_dai *dai, 12462306a36Sopenharmony_ci int dma_id) 12562306a36Sopenharmony_ci{ 12662306a36Sopenharmony_ci struct snd_pcm_hw_params *fe_hw_params = NULL; 12762306a36Sopenharmony_ci struct snd_soc_pcm_runtime *fe, *be; 12862306a36Sopenharmony_ci struct snd_soc_dpcm *dpcm; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci be = asoc_substream_to_rtd(substream); 13162306a36Sopenharmony_ci for_each_dpcm_fe(be, substream->stream, dpcm) { 13262306a36Sopenharmony_ci fe = dpcm->fe; 13362306a36Sopenharmony_ci fe_hw_params = &fe->dpcm[substream->stream].hw_params; 13462306a36Sopenharmony_ci } 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci return avs_dai_hw_params(substream, fe_hw_params, be_hw_params, dai, dma_id); 13762306a36Sopenharmony_ci} 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_cistatic int avs_dai_prepare(struct avs_dev *adev, struct snd_pcm_substream *substream, 14062306a36Sopenharmony_ci struct snd_soc_dai *dai) 14162306a36Sopenharmony_ci{ 14262306a36Sopenharmony_ci struct avs_dma_data *data; 14362306a36Sopenharmony_ci int ret; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci data = snd_soc_dai_get_dma_data(dai, substream); 14662306a36Sopenharmony_ci if (!data->path) 14762306a36Sopenharmony_ci return 0; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci ret = avs_path_reset(data->path); 15062306a36Sopenharmony_ci if (ret < 0) { 15162306a36Sopenharmony_ci dev_err(dai->dev, "reset path failed: %d\n", ret); 15262306a36Sopenharmony_ci return ret; 15362306a36Sopenharmony_ci } 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci ret = avs_path_pause(data->path); 15662306a36Sopenharmony_ci if (ret < 0) 15762306a36Sopenharmony_ci dev_err(dai->dev, "pause path failed: %d\n", ret); 15862306a36Sopenharmony_ci return ret; 15962306a36Sopenharmony_ci} 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_cistatic const struct snd_soc_dai_ops avs_dai_nonhda_be_ops; 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_cistatic int avs_dai_nonhda_be_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) 16462306a36Sopenharmony_ci{ 16562306a36Sopenharmony_ci return avs_dai_startup(substream, dai, false, &avs_dai_nonhda_be_ops); 16662306a36Sopenharmony_ci} 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_cistatic void avs_dai_nonhda_be_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) 16962306a36Sopenharmony_ci{ 17062306a36Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 17162306a36Sopenharmony_ci struct avs_dev *adev = to_avs_dev(dai->dev); 17262306a36Sopenharmony_ci struct avs_dma_data *data; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci if (rtd->dai_link->ignore_suspend) 17562306a36Sopenharmony_ci adev->num_lp_paths--; 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci data = snd_soc_dai_get_dma_data(dai, substream); 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci snd_soc_dai_set_dma_data(dai, substream, NULL); 18062306a36Sopenharmony_ci kfree(data); 18162306a36Sopenharmony_ci} 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_cistatic int avs_dai_nonhda_be_hw_params(struct snd_pcm_substream *substream, 18462306a36Sopenharmony_ci struct snd_pcm_hw_params *hw_params, struct snd_soc_dai *dai) 18562306a36Sopenharmony_ci{ 18662306a36Sopenharmony_ci struct avs_dma_data *data; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci data = snd_soc_dai_get_dma_data(dai, substream); 18962306a36Sopenharmony_ci if (data->path) 19062306a36Sopenharmony_ci return 0; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci /* Actual port-id comes from topology. */ 19362306a36Sopenharmony_ci return avs_dai_be_hw_params(substream, hw_params, dai, 0); 19462306a36Sopenharmony_ci} 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_cistatic int avs_dai_nonhda_be_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) 19762306a36Sopenharmony_ci{ 19862306a36Sopenharmony_ci struct avs_dma_data *data; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name); 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci data = snd_soc_dai_get_dma_data(dai, substream); 20362306a36Sopenharmony_ci if (data->path) { 20462306a36Sopenharmony_ci avs_path_free(data->path); 20562306a36Sopenharmony_ci data->path = NULL; 20662306a36Sopenharmony_ci } 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci return 0; 20962306a36Sopenharmony_ci} 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_cistatic int avs_dai_nonhda_be_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) 21262306a36Sopenharmony_ci{ 21362306a36Sopenharmony_ci return avs_dai_prepare(to_avs_dev(dai->dev), substream, dai); 21462306a36Sopenharmony_ci} 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_cistatic int avs_dai_nonhda_be_trigger(struct snd_pcm_substream *substream, int cmd, 21762306a36Sopenharmony_ci struct snd_soc_dai *dai) 21862306a36Sopenharmony_ci{ 21962306a36Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 22062306a36Sopenharmony_ci struct avs_dma_data *data; 22162306a36Sopenharmony_ci int ret = 0; 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci data = snd_soc_dai_get_dma_data(dai, substream); 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci switch (cmd) { 22662306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_RESUME: 22762306a36Sopenharmony_ci if (rtd->dai_link->ignore_suspend) 22862306a36Sopenharmony_ci break; 22962306a36Sopenharmony_ci fallthrough; 23062306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 23162306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 23262306a36Sopenharmony_ci ret = avs_path_pause(data->path); 23362306a36Sopenharmony_ci if (ret < 0) { 23462306a36Sopenharmony_ci dev_err(dai->dev, "pause BE path failed: %d\n", ret); 23562306a36Sopenharmony_ci break; 23662306a36Sopenharmony_ci } 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci ret = avs_path_run(data->path, AVS_TPLG_TRIGGER_AUTO); 23962306a36Sopenharmony_ci if (ret < 0) 24062306a36Sopenharmony_ci dev_err(dai->dev, "run BE path failed: %d\n", ret); 24162306a36Sopenharmony_ci break; 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_SUSPEND: 24462306a36Sopenharmony_ci if (rtd->dai_link->ignore_suspend) 24562306a36Sopenharmony_ci break; 24662306a36Sopenharmony_ci fallthrough; 24762306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 24862306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 24962306a36Sopenharmony_ci ret = avs_path_pause(data->path); 25062306a36Sopenharmony_ci if (ret < 0) 25162306a36Sopenharmony_ci dev_err(dai->dev, "pause BE path failed: %d\n", ret); 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci ret = avs_path_reset(data->path); 25462306a36Sopenharmony_ci if (ret < 0) 25562306a36Sopenharmony_ci dev_err(dai->dev, "reset BE path failed: %d\n", ret); 25662306a36Sopenharmony_ci break; 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci default: 25962306a36Sopenharmony_ci ret = -EINVAL; 26062306a36Sopenharmony_ci break; 26162306a36Sopenharmony_ci } 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci return ret; 26462306a36Sopenharmony_ci} 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_cistatic const struct snd_soc_dai_ops avs_dai_nonhda_be_ops = { 26762306a36Sopenharmony_ci .startup = avs_dai_nonhda_be_startup, 26862306a36Sopenharmony_ci .shutdown = avs_dai_nonhda_be_shutdown, 26962306a36Sopenharmony_ci .hw_params = avs_dai_nonhda_be_hw_params, 27062306a36Sopenharmony_ci .hw_free = avs_dai_nonhda_be_hw_free, 27162306a36Sopenharmony_ci .prepare = avs_dai_nonhda_be_prepare, 27262306a36Sopenharmony_ci .trigger = avs_dai_nonhda_be_trigger, 27362306a36Sopenharmony_ci}; 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_cistatic const struct snd_soc_dai_ops avs_dai_hda_be_ops; 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_cistatic int avs_dai_hda_be_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) 27862306a36Sopenharmony_ci{ 27962306a36Sopenharmony_ci return avs_dai_startup(substream, dai, false, &avs_dai_hda_be_ops); 28062306a36Sopenharmony_ci} 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_cistatic void avs_dai_hda_be_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) 28362306a36Sopenharmony_ci{ 28462306a36Sopenharmony_ci return avs_dai_nonhda_be_shutdown(substream, dai); 28562306a36Sopenharmony_ci} 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_cistatic int avs_dai_hda_be_hw_params(struct snd_pcm_substream *substream, 28862306a36Sopenharmony_ci struct snd_pcm_hw_params *hw_params, struct snd_soc_dai *dai) 28962306a36Sopenharmony_ci{ 29062306a36Sopenharmony_ci struct avs_dma_data *data; 29162306a36Sopenharmony_ci struct hdac_ext_stream *link_stream; 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci data = snd_soc_dai_get_dma_data(dai, substream); 29462306a36Sopenharmony_ci if (data->path) 29562306a36Sopenharmony_ci return 0; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci link_stream = substream->runtime->private_data; 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci return avs_dai_be_hw_params(substream, hw_params, dai, 30062306a36Sopenharmony_ci hdac_stream(link_stream)->stream_tag - 1); 30162306a36Sopenharmony_ci} 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_cistatic int avs_dai_hda_be_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) 30462306a36Sopenharmony_ci{ 30562306a36Sopenharmony_ci struct avs_dma_data *data; 30662306a36Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 30762306a36Sopenharmony_ci struct hdac_ext_stream *link_stream; 30862306a36Sopenharmony_ci struct hdac_ext_link *link; 30962306a36Sopenharmony_ci struct hda_codec *codec; 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name); 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci data = snd_soc_dai_get_dma_data(dai, substream); 31462306a36Sopenharmony_ci if (!data->path) 31562306a36Sopenharmony_ci return 0; 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci link_stream = substream->runtime->private_data; 31862306a36Sopenharmony_ci link_stream->link_prepared = false; 31962306a36Sopenharmony_ci avs_path_free(data->path); 32062306a36Sopenharmony_ci data->path = NULL; 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci /* clear link <-> stream mapping */ 32362306a36Sopenharmony_ci codec = dev_to_hda_codec(asoc_rtd_to_codec(rtd, 0)->dev); 32462306a36Sopenharmony_ci link = snd_hdac_ext_bus_get_hlink_by_addr(&codec->bus->core, codec->core.addr); 32562306a36Sopenharmony_ci if (!link) 32662306a36Sopenharmony_ci return -EINVAL; 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 32962306a36Sopenharmony_ci snd_hdac_ext_bus_link_clear_stream_id(link, hdac_stream(link_stream)->stream_tag); 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci return 0; 33262306a36Sopenharmony_ci} 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_cistatic int avs_dai_hda_be_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) 33562306a36Sopenharmony_ci{ 33662306a36Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 33762306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 33862306a36Sopenharmony_ci struct hdac_ext_stream *link_stream = runtime->private_data; 33962306a36Sopenharmony_ci struct hdac_ext_link *link; 34062306a36Sopenharmony_ci struct hda_codec *codec; 34162306a36Sopenharmony_ci struct hdac_bus *bus; 34262306a36Sopenharmony_ci unsigned int format_val; 34362306a36Sopenharmony_ci int ret; 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci if (link_stream->link_prepared) 34662306a36Sopenharmony_ci return 0; 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci codec = dev_to_hda_codec(asoc_rtd_to_codec(rtd, 0)->dev); 34962306a36Sopenharmony_ci bus = &codec->bus->core; 35062306a36Sopenharmony_ci format_val = snd_hdac_calc_stream_format(runtime->rate, runtime->channels, runtime->format, 35162306a36Sopenharmony_ci runtime->sample_bits, 0); 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci snd_hdac_ext_stream_decouple(bus, link_stream, true); 35462306a36Sopenharmony_ci snd_hdac_ext_stream_reset(link_stream); 35562306a36Sopenharmony_ci snd_hdac_ext_stream_setup(link_stream, format_val); 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci link = snd_hdac_ext_bus_get_hlink_by_addr(bus, codec->core.addr); 35862306a36Sopenharmony_ci if (!link) 35962306a36Sopenharmony_ci return -EINVAL; 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 36262306a36Sopenharmony_ci snd_hdac_ext_bus_link_set_stream_id(link, hdac_stream(link_stream)->stream_tag); 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci ret = avs_dai_prepare(to_avs_dev(dai->dev), substream, dai); 36562306a36Sopenharmony_ci if (ret) 36662306a36Sopenharmony_ci return ret; 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci link_stream->link_prepared = true; 36962306a36Sopenharmony_ci return 0; 37062306a36Sopenharmony_ci} 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_cistatic int avs_dai_hda_be_trigger(struct snd_pcm_substream *substream, int cmd, 37362306a36Sopenharmony_ci struct snd_soc_dai *dai) 37462306a36Sopenharmony_ci{ 37562306a36Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 37662306a36Sopenharmony_ci struct hdac_ext_stream *link_stream; 37762306a36Sopenharmony_ci struct avs_dma_data *data; 37862306a36Sopenharmony_ci int ret = 0; 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci dev_dbg(dai->dev, "entry %s cmd=%d\n", __func__, cmd); 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci data = snd_soc_dai_get_dma_data(dai, substream); 38362306a36Sopenharmony_ci link_stream = substream->runtime->private_data; 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci switch (cmd) { 38662306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_RESUME: 38762306a36Sopenharmony_ci if (rtd->dai_link->ignore_suspend) 38862306a36Sopenharmony_ci break; 38962306a36Sopenharmony_ci fallthrough; 39062306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 39162306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 39262306a36Sopenharmony_ci snd_hdac_ext_stream_start(link_stream); 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci ret = avs_path_pause(data->path); 39562306a36Sopenharmony_ci if (ret < 0) { 39662306a36Sopenharmony_ci dev_err(dai->dev, "pause BE path failed: %d\n", ret); 39762306a36Sopenharmony_ci break; 39862306a36Sopenharmony_ci } 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci ret = avs_path_run(data->path, AVS_TPLG_TRIGGER_AUTO); 40162306a36Sopenharmony_ci if (ret < 0) 40262306a36Sopenharmony_ci dev_err(dai->dev, "run BE path failed: %d\n", ret); 40362306a36Sopenharmony_ci break; 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_SUSPEND: 40662306a36Sopenharmony_ci if (rtd->dai_link->ignore_suspend) 40762306a36Sopenharmony_ci break; 40862306a36Sopenharmony_ci fallthrough; 40962306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 41062306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 41162306a36Sopenharmony_ci ret = avs_path_pause(data->path); 41262306a36Sopenharmony_ci if (ret < 0) 41362306a36Sopenharmony_ci dev_err(dai->dev, "pause BE path failed: %d\n", ret); 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci snd_hdac_ext_stream_clear(link_stream); 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci ret = avs_path_reset(data->path); 41862306a36Sopenharmony_ci if (ret < 0) 41962306a36Sopenharmony_ci dev_err(dai->dev, "reset BE path failed: %d\n", ret); 42062306a36Sopenharmony_ci break; 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci default: 42362306a36Sopenharmony_ci ret = -EINVAL; 42462306a36Sopenharmony_ci break; 42562306a36Sopenharmony_ci } 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci return ret; 42862306a36Sopenharmony_ci} 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_cistatic const struct snd_soc_dai_ops avs_dai_hda_be_ops = { 43162306a36Sopenharmony_ci .startup = avs_dai_hda_be_startup, 43262306a36Sopenharmony_ci .shutdown = avs_dai_hda_be_shutdown, 43362306a36Sopenharmony_ci .hw_params = avs_dai_hda_be_hw_params, 43462306a36Sopenharmony_ci .hw_free = avs_dai_hda_be_hw_free, 43562306a36Sopenharmony_ci .prepare = avs_dai_hda_be_prepare, 43662306a36Sopenharmony_ci .trigger = avs_dai_hda_be_trigger, 43762306a36Sopenharmony_ci}; 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_cistatic const unsigned int rates[] = { 44062306a36Sopenharmony_ci 8000, 11025, 12000, 16000, 44162306a36Sopenharmony_ci 22050, 24000, 32000, 44100, 44262306a36Sopenharmony_ci 48000, 64000, 88200, 96000, 44362306a36Sopenharmony_ci 128000, 176400, 192000, 44462306a36Sopenharmony_ci}; 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_cistatic const struct snd_pcm_hw_constraint_list hw_rates = { 44762306a36Sopenharmony_ci .count = ARRAY_SIZE(rates), 44862306a36Sopenharmony_ci .list = rates, 44962306a36Sopenharmony_ci .mask = 0, 45062306a36Sopenharmony_ci}; 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ciconst struct snd_soc_dai_ops avs_dai_fe_ops; 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_cistatic int avs_dai_fe_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) 45562306a36Sopenharmony_ci{ 45662306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 45762306a36Sopenharmony_ci struct avs_dma_data *data; 45862306a36Sopenharmony_ci struct avs_dev *adev = to_avs_dev(dai->dev); 45962306a36Sopenharmony_ci struct hdac_bus *bus = &adev->base.core; 46062306a36Sopenharmony_ci struct hdac_ext_stream *host_stream; 46162306a36Sopenharmony_ci int ret; 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci ret = avs_dai_startup(substream, dai, true, &avs_dai_fe_ops); 46462306a36Sopenharmony_ci if (ret) 46562306a36Sopenharmony_ci return ret; 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci data = snd_soc_dai_get_dma_data(dai, substream); 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci host_stream = snd_hdac_ext_stream_assign(bus, substream, HDAC_EXT_STREAM_TYPE_HOST); 47062306a36Sopenharmony_ci if (!host_stream) { 47162306a36Sopenharmony_ci ret = -EBUSY; 47262306a36Sopenharmony_ci goto err; 47362306a36Sopenharmony_ci } 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci data->host_stream = host_stream; 47662306a36Sopenharmony_ci ret = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); 47762306a36Sopenharmony_ci if (ret < 0) 47862306a36Sopenharmony_ci goto err; 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci /* avoid wrap-around with wall-clock */ 48162306a36Sopenharmony_ci ret = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_TIME, 20, 178000000); 48262306a36Sopenharmony_ci if (ret < 0) 48362306a36Sopenharmony_ci goto err; 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci ret = snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_rates); 48662306a36Sopenharmony_ci if (ret < 0) 48762306a36Sopenharmony_ci goto err; 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci snd_pcm_set_sync(substream); 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci dev_dbg(dai->dev, "%s fe STARTUP tag %d str %p", 49262306a36Sopenharmony_ci __func__, hdac_stream(host_stream)->stream_tag, substream); 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci return 0; 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_cierr: 49762306a36Sopenharmony_ci kfree(data); 49862306a36Sopenharmony_ci return ret; 49962306a36Sopenharmony_ci} 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_cistatic void avs_dai_fe_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) 50262306a36Sopenharmony_ci{ 50362306a36Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 50462306a36Sopenharmony_ci struct avs_dev *adev = to_avs_dev(dai->dev); 50562306a36Sopenharmony_ci struct avs_dma_data *data; 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci if (rtd->dai_link->ignore_suspend) 50862306a36Sopenharmony_ci adev->num_lp_paths--; 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci data = snd_soc_dai_get_dma_data(dai, substream); 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci snd_soc_dai_set_dma_data(dai, substream, NULL); 51362306a36Sopenharmony_ci snd_hdac_ext_stream_release(data->host_stream, HDAC_EXT_STREAM_TYPE_HOST); 51462306a36Sopenharmony_ci kfree(data); 51562306a36Sopenharmony_ci} 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_cistatic int avs_dai_fe_hw_params(struct snd_pcm_substream *substream, 51862306a36Sopenharmony_ci struct snd_pcm_hw_params *hw_params, struct snd_soc_dai *dai) 51962306a36Sopenharmony_ci{ 52062306a36Sopenharmony_ci struct snd_pcm_hw_params *be_hw_params = NULL; 52162306a36Sopenharmony_ci struct snd_soc_pcm_runtime *fe, *be; 52262306a36Sopenharmony_ci struct snd_soc_dpcm *dpcm; 52362306a36Sopenharmony_ci struct avs_dma_data *data; 52462306a36Sopenharmony_ci struct hdac_ext_stream *host_stream; 52562306a36Sopenharmony_ci int ret; 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci data = snd_soc_dai_get_dma_data(dai, substream); 52862306a36Sopenharmony_ci if (data->path) 52962306a36Sopenharmony_ci return 0; 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci host_stream = data->host_stream; 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci hdac_stream(host_stream)->bufsize = 0; 53462306a36Sopenharmony_ci hdac_stream(host_stream)->period_bytes = 0; 53562306a36Sopenharmony_ci hdac_stream(host_stream)->format_val = 0; 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci fe = asoc_substream_to_rtd(substream); 53862306a36Sopenharmony_ci for_each_dpcm_be(fe, substream->stream, dpcm) { 53962306a36Sopenharmony_ci be = dpcm->be; 54062306a36Sopenharmony_ci be_hw_params = &be->dpcm[substream->stream].hw_params; 54162306a36Sopenharmony_ci } 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci ret = avs_dai_hw_params(substream, hw_params, be_hw_params, dai, 54462306a36Sopenharmony_ci hdac_stream(host_stream)->stream_tag - 1); 54562306a36Sopenharmony_ci if (ret) 54662306a36Sopenharmony_ci goto create_err; 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci ret = avs_path_bind(data->path); 54962306a36Sopenharmony_ci if (ret < 0) { 55062306a36Sopenharmony_ci dev_err(dai->dev, "bind FE <-> BE failed: %d\n", ret); 55162306a36Sopenharmony_ci goto bind_err; 55262306a36Sopenharmony_ci } 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci return 0; 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_cibind_err: 55762306a36Sopenharmony_ci avs_path_free(data->path); 55862306a36Sopenharmony_ci data->path = NULL; 55962306a36Sopenharmony_cicreate_err: 56062306a36Sopenharmony_ci snd_pcm_lib_free_pages(substream); 56162306a36Sopenharmony_ci return ret; 56262306a36Sopenharmony_ci} 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_cistatic int __avs_dai_fe_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) 56562306a36Sopenharmony_ci{ 56662306a36Sopenharmony_ci struct avs_dma_data *data; 56762306a36Sopenharmony_ci struct hdac_ext_stream *host_stream; 56862306a36Sopenharmony_ci int ret; 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci dev_dbg(dai->dev, "%s fe HW_FREE str %p rtd %p", 57162306a36Sopenharmony_ci __func__, substream, substream->runtime); 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci data = snd_soc_dai_get_dma_data(dai, substream); 57462306a36Sopenharmony_ci if (!data->path) 57562306a36Sopenharmony_ci return 0; 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ci host_stream = data->host_stream; 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci ret = avs_path_unbind(data->path); 58062306a36Sopenharmony_ci if (ret < 0) 58162306a36Sopenharmony_ci dev_err(dai->dev, "unbind FE <-> BE failed: %d\n", ret); 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci avs_path_free(data->path); 58462306a36Sopenharmony_ci data->path = NULL; 58562306a36Sopenharmony_ci snd_hdac_stream_cleanup(hdac_stream(host_stream)); 58662306a36Sopenharmony_ci hdac_stream(host_stream)->prepared = false; 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci return ret; 58962306a36Sopenharmony_ci} 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_cistatic int avs_dai_fe_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) 59262306a36Sopenharmony_ci{ 59362306a36Sopenharmony_ci int ret; 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci ret = __avs_dai_fe_hw_free(substream, dai); 59662306a36Sopenharmony_ci snd_pcm_lib_free_pages(substream); 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci return ret; 59962306a36Sopenharmony_ci} 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_cistatic int avs_dai_fe_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) 60262306a36Sopenharmony_ci{ 60362306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 60462306a36Sopenharmony_ci struct avs_dma_data *data; 60562306a36Sopenharmony_ci struct avs_dev *adev = to_avs_dev(dai->dev); 60662306a36Sopenharmony_ci struct hdac_ext_stream *host_stream; 60762306a36Sopenharmony_ci struct hdac_bus *bus; 60862306a36Sopenharmony_ci unsigned int format_val; 60962306a36Sopenharmony_ci int ret; 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci data = snd_soc_dai_get_dma_data(dai, substream); 61262306a36Sopenharmony_ci host_stream = data->host_stream; 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci if (hdac_stream(host_stream)->prepared) 61562306a36Sopenharmony_ci return 0; 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci bus = hdac_stream(host_stream)->bus; 61862306a36Sopenharmony_ci snd_hdac_ext_stream_decouple(bus, data->host_stream, true); 61962306a36Sopenharmony_ci snd_hdac_stream_reset(hdac_stream(host_stream)); 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci format_val = snd_hdac_calc_stream_format(runtime->rate, runtime->channels, runtime->format, 62262306a36Sopenharmony_ci runtime->sample_bits, 0); 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci ret = snd_hdac_stream_set_params(hdac_stream(host_stream), format_val); 62562306a36Sopenharmony_ci if (ret < 0) 62662306a36Sopenharmony_ci return ret; 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci ret = snd_hdac_stream_setup(hdac_stream(host_stream)); 62962306a36Sopenharmony_ci if (ret < 0) 63062306a36Sopenharmony_ci return ret; 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_ci ret = avs_dai_prepare(adev, substream, dai); 63362306a36Sopenharmony_ci if (ret) 63462306a36Sopenharmony_ci return ret; 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci hdac_stream(host_stream)->prepared = true; 63762306a36Sopenharmony_ci return 0; 63862306a36Sopenharmony_ci} 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_cistatic int avs_dai_fe_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai) 64162306a36Sopenharmony_ci{ 64262306a36Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 64362306a36Sopenharmony_ci struct avs_dma_data *data; 64462306a36Sopenharmony_ci struct hdac_ext_stream *host_stream; 64562306a36Sopenharmony_ci struct hdac_bus *bus; 64662306a36Sopenharmony_ci unsigned long flags; 64762306a36Sopenharmony_ci int ret = 0; 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ci data = snd_soc_dai_get_dma_data(dai, substream); 65062306a36Sopenharmony_ci host_stream = data->host_stream; 65162306a36Sopenharmony_ci bus = hdac_stream(host_stream)->bus; 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci switch (cmd) { 65462306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_RESUME: 65562306a36Sopenharmony_ci if (rtd->dai_link->ignore_suspend) 65662306a36Sopenharmony_ci break; 65762306a36Sopenharmony_ci fallthrough; 65862306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 65962306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 66062306a36Sopenharmony_ci spin_lock_irqsave(&bus->reg_lock, flags); 66162306a36Sopenharmony_ci snd_hdac_stream_start(hdac_stream(host_stream)); 66262306a36Sopenharmony_ci spin_unlock_irqrestore(&bus->reg_lock, flags); 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_ci /* Timeout on DRSM poll shall not stop the resume so ignore the result. */ 66562306a36Sopenharmony_ci if (cmd == SNDRV_PCM_TRIGGER_RESUME) 66662306a36Sopenharmony_ci snd_hdac_stream_wait_drsm(hdac_stream(host_stream)); 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_ci ret = avs_path_pause(data->path); 66962306a36Sopenharmony_ci if (ret < 0) { 67062306a36Sopenharmony_ci dev_err(dai->dev, "pause FE path failed: %d\n", ret); 67162306a36Sopenharmony_ci break; 67262306a36Sopenharmony_ci } 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ci ret = avs_path_run(data->path, AVS_TPLG_TRIGGER_AUTO); 67562306a36Sopenharmony_ci if (ret < 0) 67662306a36Sopenharmony_ci dev_err(dai->dev, "run FE path failed: %d\n", ret); 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci break; 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_SUSPEND: 68162306a36Sopenharmony_ci if (rtd->dai_link->ignore_suspend) 68262306a36Sopenharmony_ci break; 68362306a36Sopenharmony_ci fallthrough; 68462306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 68562306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 68662306a36Sopenharmony_ci ret = avs_path_pause(data->path); 68762306a36Sopenharmony_ci if (ret < 0) 68862306a36Sopenharmony_ci dev_err(dai->dev, "pause FE path failed: %d\n", ret); 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_ci spin_lock_irqsave(&bus->reg_lock, flags); 69162306a36Sopenharmony_ci snd_hdac_stream_stop(hdac_stream(host_stream)); 69262306a36Sopenharmony_ci spin_unlock_irqrestore(&bus->reg_lock, flags); 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_ci ret = avs_path_reset(data->path); 69562306a36Sopenharmony_ci if (ret < 0) 69662306a36Sopenharmony_ci dev_err(dai->dev, "reset FE path failed: %d\n", ret); 69762306a36Sopenharmony_ci break; 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci default: 70062306a36Sopenharmony_ci ret = -EINVAL; 70162306a36Sopenharmony_ci break; 70262306a36Sopenharmony_ci } 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_ci return ret; 70562306a36Sopenharmony_ci} 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_ciconst struct snd_soc_dai_ops avs_dai_fe_ops = { 70862306a36Sopenharmony_ci .startup = avs_dai_fe_startup, 70962306a36Sopenharmony_ci .shutdown = avs_dai_fe_shutdown, 71062306a36Sopenharmony_ci .hw_params = avs_dai_fe_hw_params, 71162306a36Sopenharmony_ci .hw_free = avs_dai_fe_hw_free, 71262306a36Sopenharmony_ci .prepare = avs_dai_fe_prepare, 71362306a36Sopenharmony_ci .trigger = avs_dai_fe_trigger, 71462306a36Sopenharmony_ci}; 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_cistatic ssize_t topology_name_read(struct file *file, char __user *user_buf, size_t count, 71762306a36Sopenharmony_ci loff_t *ppos) 71862306a36Sopenharmony_ci{ 71962306a36Sopenharmony_ci struct snd_soc_component *component = file->private_data; 72062306a36Sopenharmony_ci struct snd_soc_card *card = component->card; 72162306a36Sopenharmony_ci struct snd_soc_acpi_mach *mach = dev_get_platdata(card->dev); 72262306a36Sopenharmony_ci char buf[64]; 72362306a36Sopenharmony_ci size_t len; 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_ci len = scnprintf(buf, sizeof(buf), "%s/%s\n", component->driver->topology_name_prefix, 72662306a36Sopenharmony_ci mach->tplg_filename); 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_ci return simple_read_from_buffer(user_buf, count, ppos, buf, len); 72962306a36Sopenharmony_ci} 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_cistatic const struct file_operations topology_name_fops = { 73262306a36Sopenharmony_ci .open = simple_open, 73362306a36Sopenharmony_ci .read = topology_name_read, 73462306a36Sopenharmony_ci .llseek = default_llseek, 73562306a36Sopenharmony_ci}; 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_cistatic int avs_component_load_libraries(struct avs_soc_component *acomp) 73862306a36Sopenharmony_ci{ 73962306a36Sopenharmony_ci struct avs_tplg *tplg = acomp->tplg; 74062306a36Sopenharmony_ci struct avs_dev *adev = to_avs_dev(acomp->base.dev); 74162306a36Sopenharmony_ci int ret; 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_ci if (!tplg->num_libs) 74462306a36Sopenharmony_ci return 0; 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_ci /* Parent device may be asleep and library loading involves IPCs. */ 74762306a36Sopenharmony_ci ret = pm_runtime_resume_and_get(adev->dev); 74862306a36Sopenharmony_ci if (ret < 0) 74962306a36Sopenharmony_ci return ret; 75062306a36Sopenharmony_ci 75162306a36Sopenharmony_ci avs_hda_power_gating_enable(adev, false); 75262306a36Sopenharmony_ci avs_hda_clock_gating_enable(adev, false); 75362306a36Sopenharmony_ci avs_hda_l1sen_enable(adev, false); 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_ci ret = avs_dsp_load_libraries(adev, tplg->libs, tplg->num_libs); 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_ci avs_hda_l1sen_enable(adev, true); 75862306a36Sopenharmony_ci avs_hda_clock_gating_enable(adev, true); 75962306a36Sopenharmony_ci avs_hda_power_gating_enable(adev, true); 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_ci if (!ret) 76262306a36Sopenharmony_ci ret = avs_module_info_init(adev, false); 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_ci pm_runtime_mark_last_busy(adev->dev); 76562306a36Sopenharmony_ci pm_runtime_put_autosuspend(adev->dev); 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_ci return ret; 76862306a36Sopenharmony_ci} 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_cistatic int avs_component_probe(struct snd_soc_component *component) 77162306a36Sopenharmony_ci{ 77262306a36Sopenharmony_ci struct snd_soc_card *card = component->card; 77362306a36Sopenharmony_ci struct snd_soc_acpi_mach *mach; 77462306a36Sopenharmony_ci struct avs_soc_component *acomp; 77562306a36Sopenharmony_ci struct avs_dev *adev; 77662306a36Sopenharmony_ci char *filename; 77762306a36Sopenharmony_ci int ret; 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_ci dev_dbg(card->dev, "probing %s card %s\n", component->name, card->name); 78062306a36Sopenharmony_ci mach = dev_get_platdata(card->dev); 78162306a36Sopenharmony_ci acomp = to_avs_soc_component(component); 78262306a36Sopenharmony_ci adev = to_avs_dev(component->dev); 78362306a36Sopenharmony_ci 78462306a36Sopenharmony_ci acomp->tplg = avs_tplg_new(component); 78562306a36Sopenharmony_ci if (!acomp->tplg) 78662306a36Sopenharmony_ci return -ENOMEM; 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci if (!mach->tplg_filename) 78962306a36Sopenharmony_ci goto finalize; 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ci /* Load specified topology and create debugfs for it. */ 79262306a36Sopenharmony_ci filename = kasprintf(GFP_KERNEL, "%s/%s", component->driver->topology_name_prefix, 79362306a36Sopenharmony_ci mach->tplg_filename); 79462306a36Sopenharmony_ci if (!filename) 79562306a36Sopenharmony_ci return -ENOMEM; 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_ci ret = avs_load_topology(component, filename); 79862306a36Sopenharmony_ci kfree(filename); 79962306a36Sopenharmony_ci if (ret == -ENOENT && !strncmp(mach->tplg_filename, "hda-", 4)) { 80062306a36Sopenharmony_ci unsigned int vendor_id; 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_ci if (sscanf(mach->tplg_filename, "hda-%08x-tplg.bin", &vendor_id) != 1) 80362306a36Sopenharmony_ci return ret; 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_ci if (((vendor_id >> 16) & 0xFFFF) == 0x8086) 80662306a36Sopenharmony_ci mach->tplg_filename = devm_kasprintf(adev->dev, GFP_KERNEL, 80762306a36Sopenharmony_ci "hda-8086-generic-tplg.bin"); 80862306a36Sopenharmony_ci else 80962306a36Sopenharmony_ci mach->tplg_filename = devm_kasprintf(adev->dev, GFP_KERNEL, 81062306a36Sopenharmony_ci "hda-generic-tplg.bin"); 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_ci filename = kasprintf(GFP_KERNEL, "%s/%s", component->driver->topology_name_prefix, 81362306a36Sopenharmony_ci mach->tplg_filename); 81462306a36Sopenharmony_ci if (!filename) 81562306a36Sopenharmony_ci return -ENOMEM; 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_ci dev_info(card->dev, "trying to load fallback topology %s\n", mach->tplg_filename); 81862306a36Sopenharmony_ci ret = avs_load_topology(component, filename); 81962306a36Sopenharmony_ci kfree(filename); 82062306a36Sopenharmony_ci } 82162306a36Sopenharmony_ci if (ret < 0) 82262306a36Sopenharmony_ci return ret; 82362306a36Sopenharmony_ci 82462306a36Sopenharmony_ci ret = avs_component_load_libraries(acomp); 82562306a36Sopenharmony_ci if (ret < 0) { 82662306a36Sopenharmony_ci dev_err(card->dev, "libraries loading failed: %d\n", ret); 82762306a36Sopenharmony_ci goto err_load_libs; 82862306a36Sopenharmony_ci } 82962306a36Sopenharmony_ci 83062306a36Sopenharmony_cifinalize: 83162306a36Sopenharmony_ci debugfs_create_file("topology_name", 0444, component->debugfs_root, component, 83262306a36Sopenharmony_ci &topology_name_fops); 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_ci mutex_lock(&adev->comp_list_mutex); 83562306a36Sopenharmony_ci list_add_tail(&acomp->node, &adev->comp_list); 83662306a36Sopenharmony_ci mutex_unlock(&adev->comp_list_mutex); 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_ci return 0; 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_cierr_load_libs: 84162306a36Sopenharmony_ci avs_remove_topology(component); 84262306a36Sopenharmony_ci return ret; 84362306a36Sopenharmony_ci} 84462306a36Sopenharmony_ci 84562306a36Sopenharmony_cistatic void avs_component_remove(struct snd_soc_component *component) 84662306a36Sopenharmony_ci{ 84762306a36Sopenharmony_ci struct avs_soc_component *acomp = to_avs_soc_component(component); 84862306a36Sopenharmony_ci struct snd_soc_acpi_mach *mach; 84962306a36Sopenharmony_ci struct avs_dev *adev = to_avs_dev(component->dev); 85062306a36Sopenharmony_ci int ret; 85162306a36Sopenharmony_ci 85262306a36Sopenharmony_ci mach = dev_get_platdata(component->card->dev); 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_ci mutex_lock(&adev->comp_list_mutex); 85562306a36Sopenharmony_ci list_del(&acomp->node); 85662306a36Sopenharmony_ci mutex_unlock(&adev->comp_list_mutex); 85762306a36Sopenharmony_ci 85862306a36Sopenharmony_ci if (mach->tplg_filename) { 85962306a36Sopenharmony_ci ret = avs_remove_topology(component); 86062306a36Sopenharmony_ci if (ret < 0) 86162306a36Sopenharmony_ci dev_err(component->dev, "unload topology failed: %d\n", ret); 86262306a36Sopenharmony_ci } 86362306a36Sopenharmony_ci} 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_cistatic int avs_dai_resume_hw_params(struct snd_soc_dai *dai, struct avs_dma_data *data) 86662306a36Sopenharmony_ci{ 86762306a36Sopenharmony_ci struct snd_pcm_substream *substream; 86862306a36Sopenharmony_ci struct snd_soc_pcm_runtime *rtd; 86962306a36Sopenharmony_ci int ret; 87062306a36Sopenharmony_ci 87162306a36Sopenharmony_ci substream = data->substream; 87262306a36Sopenharmony_ci rtd = asoc_substream_to_rtd(substream); 87362306a36Sopenharmony_ci 87462306a36Sopenharmony_ci ret = dai->driver->ops->hw_params(substream, &rtd->dpcm[substream->stream].hw_params, dai); 87562306a36Sopenharmony_ci if (ret) 87662306a36Sopenharmony_ci dev_err(dai->dev, "hw_params on resume failed: %d\n", ret); 87762306a36Sopenharmony_ci 87862306a36Sopenharmony_ci return ret; 87962306a36Sopenharmony_ci} 88062306a36Sopenharmony_ci 88162306a36Sopenharmony_cistatic int avs_dai_resume_fe_prepare(struct snd_soc_dai *dai, struct avs_dma_data *data) 88262306a36Sopenharmony_ci{ 88362306a36Sopenharmony_ci struct hdac_ext_stream *host_stream; 88462306a36Sopenharmony_ci struct hdac_stream *hstream; 88562306a36Sopenharmony_ci struct hdac_bus *bus; 88662306a36Sopenharmony_ci int ret; 88762306a36Sopenharmony_ci 88862306a36Sopenharmony_ci host_stream = data->host_stream; 88962306a36Sopenharmony_ci hstream = hdac_stream(host_stream); 89062306a36Sopenharmony_ci bus = hdac_stream(host_stream)->bus; 89162306a36Sopenharmony_ci 89262306a36Sopenharmony_ci /* Set DRSM before programming stream and position registers. */ 89362306a36Sopenharmony_ci snd_hdac_stream_drsm_enable(bus, true, hstream->index); 89462306a36Sopenharmony_ci 89562306a36Sopenharmony_ci ret = dai->driver->ops->prepare(data->substream, dai); 89662306a36Sopenharmony_ci if (ret) { 89762306a36Sopenharmony_ci dev_err(dai->dev, "prepare FE on resume failed: %d\n", ret); 89862306a36Sopenharmony_ci return ret; 89962306a36Sopenharmony_ci } 90062306a36Sopenharmony_ci 90162306a36Sopenharmony_ci writel(host_stream->pphcllpl, host_stream->pphc_addr + AZX_REG_PPHCLLPL); 90262306a36Sopenharmony_ci writel(host_stream->pphcllpu, host_stream->pphc_addr + AZX_REG_PPHCLLPU); 90362306a36Sopenharmony_ci writel(host_stream->pphcldpl, host_stream->pphc_addr + AZX_REG_PPHCLDPL); 90462306a36Sopenharmony_ci writel(host_stream->pphcldpu, host_stream->pphc_addr + AZX_REG_PPHCLDPU); 90562306a36Sopenharmony_ci 90662306a36Sopenharmony_ci /* As per HW spec recommendation, program LPIB and DPIB to the same value. */ 90762306a36Sopenharmony_ci snd_hdac_stream_set_lpib(hstream, hstream->lpib); 90862306a36Sopenharmony_ci snd_hdac_stream_set_dpibr(bus, hstream, hstream->lpib); 90962306a36Sopenharmony_ci 91062306a36Sopenharmony_ci return 0; 91162306a36Sopenharmony_ci} 91262306a36Sopenharmony_ci 91362306a36Sopenharmony_cistatic int avs_dai_resume_be_prepare(struct snd_soc_dai *dai, struct avs_dma_data *data) 91462306a36Sopenharmony_ci{ 91562306a36Sopenharmony_ci int ret; 91662306a36Sopenharmony_ci 91762306a36Sopenharmony_ci ret = dai->driver->ops->prepare(data->substream, dai); 91862306a36Sopenharmony_ci if (ret) 91962306a36Sopenharmony_ci dev_err(dai->dev, "prepare BE on resume failed: %d\n", ret); 92062306a36Sopenharmony_ci 92162306a36Sopenharmony_ci return ret; 92262306a36Sopenharmony_ci} 92362306a36Sopenharmony_ci 92462306a36Sopenharmony_cistatic int avs_dai_suspend_fe_hw_free(struct snd_soc_dai *dai, struct avs_dma_data *data) 92562306a36Sopenharmony_ci{ 92662306a36Sopenharmony_ci struct hdac_ext_stream *host_stream; 92762306a36Sopenharmony_ci int ret; 92862306a36Sopenharmony_ci 92962306a36Sopenharmony_ci host_stream = data->host_stream; 93062306a36Sopenharmony_ci 93162306a36Sopenharmony_ci /* Store position addresses so we can resume from them later on. */ 93262306a36Sopenharmony_ci hdac_stream(host_stream)->lpib = snd_hdac_stream_get_pos_lpib(hdac_stream(host_stream)); 93362306a36Sopenharmony_ci host_stream->pphcllpl = readl(host_stream->pphc_addr + AZX_REG_PPHCLLPL); 93462306a36Sopenharmony_ci host_stream->pphcllpu = readl(host_stream->pphc_addr + AZX_REG_PPHCLLPU); 93562306a36Sopenharmony_ci host_stream->pphcldpl = readl(host_stream->pphc_addr + AZX_REG_PPHCLDPL); 93662306a36Sopenharmony_ci host_stream->pphcldpu = readl(host_stream->pphc_addr + AZX_REG_PPHCLDPU); 93762306a36Sopenharmony_ci 93862306a36Sopenharmony_ci ret = __avs_dai_fe_hw_free(data->substream, dai); 93962306a36Sopenharmony_ci if (ret < 0) 94062306a36Sopenharmony_ci dev_err(dai->dev, "hw_free FE on suspend failed: %d\n", ret); 94162306a36Sopenharmony_ci 94262306a36Sopenharmony_ci return ret; 94362306a36Sopenharmony_ci} 94462306a36Sopenharmony_ci 94562306a36Sopenharmony_cistatic int avs_dai_suspend_be_hw_free(struct snd_soc_dai *dai, struct avs_dma_data *data) 94662306a36Sopenharmony_ci{ 94762306a36Sopenharmony_ci int ret; 94862306a36Sopenharmony_ci 94962306a36Sopenharmony_ci ret = dai->driver->ops->hw_free(data->substream, dai); 95062306a36Sopenharmony_ci if (ret < 0) 95162306a36Sopenharmony_ci dev_err(dai->dev, "hw_free BE on suspend failed: %d\n", ret); 95262306a36Sopenharmony_ci 95362306a36Sopenharmony_ci return ret; 95462306a36Sopenharmony_ci} 95562306a36Sopenharmony_ci 95662306a36Sopenharmony_cistatic int avs_component_pm_op(struct snd_soc_component *component, bool be, 95762306a36Sopenharmony_ci int (*op)(struct snd_soc_dai *, struct avs_dma_data *)) 95862306a36Sopenharmony_ci{ 95962306a36Sopenharmony_ci struct snd_soc_pcm_runtime *rtd; 96062306a36Sopenharmony_ci struct avs_dma_data *data; 96162306a36Sopenharmony_ci struct snd_soc_dai *dai; 96262306a36Sopenharmony_ci int ret; 96362306a36Sopenharmony_ci 96462306a36Sopenharmony_ci for_each_component_dais(component, dai) { 96562306a36Sopenharmony_ci data = snd_soc_dai_dma_data_get_playback(dai); 96662306a36Sopenharmony_ci if (data) { 96762306a36Sopenharmony_ci rtd = asoc_substream_to_rtd(data->substream); 96862306a36Sopenharmony_ci if (rtd->dai_link->no_pcm == be && !rtd->dai_link->ignore_suspend) { 96962306a36Sopenharmony_ci ret = op(dai, data); 97062306a36Sopenharmony_ci if (ret < 0) { 97162306a36Sopenharmony_ci __snd_pcm_set_state(data->substream->runtime, 97262306a36Sopenharmony_ci SNDRV_PCM_STATE_DISCONNECTED); 97362306a36Sopenharmony_ci return ret; 97462306a36Sopenharmony_ci } 97562306a36Sopenharmony_ci } 97662306a36Sopenharmony_ci } 97762306a36Sopenharmony_ci 97862306a36Sopenharmony_ci data = snd_soc_dai_dma_data_get_capture(dai); 97962306a36Sopenharmony_ci if (data) { 98062306a36Sopenharmony_ci rtd = asoc_substream_to_rtd(data->substream); 98162306a36Sopenharmony_ci if (rtd->dai_link->no_pcm == be && !rtd->dai_link->ignore_suspend) { 98262306a36Sopenharmony_ci ret = op(dai, data); 98362306a36Sopenharmony_ci if (ret < 0) { 98462306a36Sopenharmony_ci __snd_pcm_set_state(data->substream->runtime, 98562306a36Sopenharmony_ci SNDRV_PCM_STATE_DISCONNECTED); 98662306a36Sopenharmony_ci return ret; 98762306a36Sopenharmony_ci } 98862306a36Sopenharmony_ci } 98962306a36Sopenharmony_ci } 99062306a36Sopenharmony_ci } 99162306a36Sopenharmony_ci 99262306a36Sopenharmony_ci return 0; 99362306a36Sopenharmony_ci} 99462306a36Sopenharmony_ci 99562306a36Sopenharmony_cistatic int avs_component_resume_hw_params(struct snd_soc_component *component, bool be) 99662306a36Sopenharmony_ci{ 99762306a36Sopenharmony_ci return avs_component_pm_op(component, be, &avs_dai_resume_hw_params); 99862306a36Sopenharmony_ci} 99962306a36Sopenharmony_ci 100062306a36Sopenharmony_cistatic int avs_component_resume_prepare(struct snd_soc_component *component, bool be) 100162306a36Sopenharmony_ci{ 100262306a36Sopenharmony_ci int (*prepare_cb)(struct snd_soc_dai *dai, struct avs_dma_data *data); 100362306a36Sopenharmony_ci 100462306a36Sopenharmony_ci if (be) 100562306a36Sopenharmony_ci prepare_cb = &avs_dai_resume_be_prepare; 100662306a36Sopenharmony_ci else 100762306a36Sopenharmony_ci prepare_cb = &avs_dai_resume_fe_prepare; 100862306a36Sopenharmony_ci 100962306a36Sopenharmony_ci return avs_component_pm_op(component, be, prepare_cb); 101062306a36Sopenharmony_ci} 101162306a36Sopenharmony_ci 101262306a36Sopenharmony_cistatic int avs_component_suspend_hw_free(struct snd_soc_component *component, bool be) 101362306a36Sopenharmony_ci{ 101462306a36Sopenharmony_ci int (*hw_free_cb)(struct snd_soc_dai *dai, struct avs_dma_data *data); 101562306a36Sopenharmony_ci 101662306a36Sopenharmony_ci if (be) 101762306a36Sopenharmony_ci hw_free_cb = &avs_dai_suspend_be_hw_free; 101862306a36Sopenharmony_ci else 101962306a36Sopenharmony_ci hw_free_cb = &avs_dai_suspend_fe_hw_free; 102062306a36Sopenharmony_ci 102162306a36Sopenharmony_ci return avs_component_pm_op(component, be, hw_free_cb); 102262306a36Sopenharmony_ci} 102362306a36Sopenharmony_ci 102462306a36Sopenharmony_cistatic int avs_component_suspend(struct snd_soc_component *component) 102562306a36Sopenharmony_ci{ 102662306a36Sopenharmony_ci int ret; 102762306a36Sopenharmony_ci 102862306a36Sopenharmony_ci /* 102962306a36Sopenharmony_ci * When freeing paths, FEs need to be first as they perform 103062306a36Sopenharmony_ci * path unbinding. 103162306a36Sopenharmony_ci */ 103262306a36Sopenharmony_ci ret = avs_component_suspend_hw_free(component, false); 103362306a36Sopenharmony_ci if (ret) 103462306a36Sopenharmony_ci return ret; 103562306a36Sopenharmony_ci 103662306a36Sopenharmony_ci return avs_component_suspend_hw_free(component, true); 103762306a36Sopenharmony_ci} 103862306a36Sopenharmony_ci 103962306a36Sopenharmony_cistatic int avs_component_resume(struct snd_soc_component *component) 104062306a36Sopenharmony_ci{ 104162306a36Sopenharmony_ci int ret; 104262306a36Sopenharmony_ci 104362306a36Sopenharmony_ci /* 104462306a36Sopenharmony_ci * When creating paths, FEs need to be last as they perform 104562306a36Sopenharmony_ci * path binding. 104662306a36Sopenharmony_ci */ 104762306a36Sopenharmony_ci ret = avs_component_resume_hw_params(component, true); 104862306a36Sopenharmony_ci if (ret) 104962306a36Sopenharmony_ci return ret; 105062306a36Sopenharmony_ci 105162306a36Sopenharmony_ci ret = avs_component_resume_hw_params(component, false); 105262306a36Sopenharmony_ci if (ret) 105362306a36Sopenharmony_ci return ret; 105462306a36Sopenharmony_ci 105562306a36Sopenharmony_ci /* It is expected that the LINK stream is prepared first. */ 105662306a36Sopenharmony_ci ret = avs_component_resume_prepare(component, true); 105762306a36Sopenharmony_ci if (ret) 105862306a36Sopenharmony_ci return ret; 105962306a36Sopenharmony_ci 106062306a36Sopenharmony_ci return avs_component_resume_prepare(component, false); 106162306a36Sopenharmony_ci} 106262306a36Sopenharmony_ci 106362306a36Sopenharmony_cistatic const struct snd_pcm_hardware avs_pcm_hardware = { 106462306a36Sopenharmony_ci .info = SNDRV_PCM_INFO_MMAP | 106562306a36Sopenharmony_ci SNDRV_PCM_INFO_MMAP_VALID | 106662306a36Sopenharmony_ci SNDRV_PCM_INFO_INTERLEAVED | 106762306a36Sopenharmony_ci SNDRV_PCM_INFO_PAUSE | 106862306a36Sopenharmony_ci SNDRV_PCM_INFO_RESUME | 106962306a36Sopenharmony_ci SNDRV_PCM_INFO_NO_PERIOD_WAKEUP, 107062306a36Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_S16_LE | 107162306a36Sopenharmony_ci SNDRV_PCM_FMTBIT_S24_LE | 107262306a36Sopenharmony_ci SNDRV_PCM_FMTBIT_S32_LE, 107362306a36Sopenharmony_ci .buffer_bytes_max = AZX_MAX_BUF_SIZE, 107462306a36Sopenharmony_ci .period_bytes_min = 128, 107562306a36Sopenharmony_ci .period_bytes_max = AZX_MAX_BUF_SIZE / 2, 107662306a36Sopenharmony_ci .periods_min = 2, 107762306a36Sopenharmony_ci .periods_max = AZX_MAX_FRAG, 107862306a36Sopenharmony_ci .fifo_size = 0, 107962306a36Sopenharmony_ci}; 108062306a36Sopenharmony_ci 108162306a36Sopenharmony_cistatic int avs_component_open(struct snd_soc_component *component, 108262306a36Sopenharmony_ci struct snd_pcm_substream *substream) 108362306a36Sopenharmony_ci{ 108462306a36Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 108562306a36Sopenharmony_ci 108662306a36Sopenharmony_ci /* only FE DAI links are handled here */ 108762306a36Sopenharmony_ci if (rtd->dai_link->no_pcm) 108862306a36Sopenharmony_ci return 0; 108962306a36Sopenharmony_ci 109062306a36Sopenharmony_ci return snd_soc_set_runtime_hwparams(substream, &avs_pcm_hardware); 109162306a36Sopenharmony_ci} 109262306a36Sopenharmony_ci 109362306a36Sopenharmony_cistatic unsigned int avs_hda_stream_dpib_read(struct hdac_ext_stream *stream) 109462306a36Sopenharmony_ci{ 109562306a36Sopenharmony_ci return readl(hdac_stream(stream)->bus->remap_addr + AZX_REG_VS_SDXDPIB_XBASE + 109662306a36Sopenharmony_ci (AZX_REG_VS_SDXDPIB_XINTERVAL * hdac_stream(stream)->index)); 109762306a36Sopenharmony_ci} 109862306a36Sopenharmony_ci 109962306a36Sopenharmony_cistatic snd_pcm_uframes_t 110062306a36Sopenharmony_ciavs_component_pointer(struct snd_soc_component *component, struct snd_pcm_substream *substream) 110162306a36Sopenharmony_ci{ 110262306a36Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 110362306a36Sopenharmony_ci struct avs_dma_data *data; 110462306a36Sopenharmony_ci struct hdac_ext_stream *host_stream; 110562306a36Sopenharmony_ci unsigned int pos; 110662306a36Sopenharmony_ci 110762306a36Sopenharmony_ci data = snd_soc_dai_get_dma_data(asoc_rtd_to_cpu(rtd, 0), substream); 110862306a36Sopenharmony_ci if (!data->host_stream) 110962306a36Sopenharmony_ci return 0; 111062306a36Sopenharmony_ci 111162306a36Sopenharmony_ci host_stream = data->host_stream; 111262306a36Sopenharmony_ci pos = avs_hda_stream_dpib_read(host_stream); 111362306a36Sopenharmony_ci 111462306a36Sopenharmony_ci if (pos >= hdac_stream(host_stream)->bufsize) 111562306a36Sopenharmony_ci pos = 0; 111662306a36Sopenharmony_ci 111762306a36Sopenharmony_ci return bytes_to_frames(substream->runtime, pos); 111862306a36Sopenharmony_ci} 111962306a36Sopenharmony_ci 112062306a36Sopenharmony_cistatic int avs_component_mmap(struct snd_soc_component *component, 112162306a36Sopenharmony_ci struct snd_pcm_substream *substream, 112262306a36Sopenharmony_ci struct vm_area_struct *vma) 112362306a36Sopenharmony_ci{ 112462306a36Sopenharmony_ci return snd_pcm_lib_default_mmap(substream, vma); 112562306a36Sopenharmony_ci} 112662306a36Sopenharmony_ci 112762306a36Sopenharmony_ci#define MAX_PREALLOC_SIZE (32 * 1024 * 1024) 112862306a36Sopenharmony_ci 112962306a36Sopenharmony_cistatic int avs_component_construct(struct snd_soc_component *component, 113062306a36Sopenharmony_ci struct snd_soc_pcm_runtime *rtd) 113162306a36Sopenharmony_ci{ 113262306a36Sopenharmony_ci struct snd_soc_dai *dai = asoc_rtd_to_cpu(rtd, 0); 113362306a36Sopenharmony_ci struct snd_pcm *pcm = rtd->pcm; 113462306a36Sopenharmony_ci 113562306a36Sopenharmony_ci if (dai->driver->playback.channels_min) 113662306a36Sopenharmony_ci snd_pcm_set_managed_buffer(pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream, 113762306a36Sopenharmony_ci SNDRV_DMA_TYPE_DEV_SG, component->dev, 0, 113862306a36Sopenharmony_ci MAX_PREALLOC_SIZE); 113962306a36Sopenharmony_ci 114062306a36Sopenharmony_ci if (dai->driver->capture.channels_min) 114162306a36Sopenharmony_ci snd_pcm_set_managed_buffer(pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream, 114262306a36Sopenharmony_ci SNDRV_DMA_TYPE_DEV_SG, component->dev, 0, 114362306a36Sopenharmony_ci MAX_PREALLOC_SIZE); 114462306a36Sopenharmony_ci 114562306a36Sopenharmony_ci return 0; 114662306a36Sopenharmony_ci} 114762306a36Sopenharmony_ci 114862306a36Sopenharmony_cistatic const struct snd_soc_component_driver avs_component_driver = { 114962306a36Sopenharmony_ci .name = "avs-pcm", 115062306a36Sopenharmony_ci .probe = avs_component_probe, 115162306a36Sopenharmony_ci .remove = avs_component_remove, 115262306a36Sopenharmony_ci .suspend = avs_component_suspend, 115362306a36Sopenharmony_ci .resume = avs_component_resume, 115462306a36Sopenharmony_ci .open = avs_component_open, 115562306a36Sopenharmony_ci .pointer = avs_component_pointer, 115662306a36Sopenharmony_ci .mmap = avs_component_mmap, 115762306a36Sopenharmony_ci .pcm_construct = avs_component_construct, 115862306a36Sopenharmony_ci .module_get_upon_open = 1, /* increment refcount when a pcm is opened */ 115962306a36Sopenharmony_ci .topology_name_prefix = "intel/avs", 116062306a36Sopenharmony_ci}; 116162306a36Sopenharmony_ci 116262306a36Sopenharmony_ciint avs_soc_component_register(struct device *dev, const char *name, 116362306a36Sopenharmony_ci const struct snd_soc_component_driver *drv, 116462306a36Sopenharmony_ci struct snd_soc_dai_driver *cpu_dais, int num_cpu_dais) 116562306a36Sopenharmony_ci{ 116662306a36Sopenharmony_ci struct avs_soc_component *acomp; 116762306a36Sopenharmony_ci int ret; 116862306a36Sopenharmony_ci 116962306a36Sopenharmony_ci acomp = devm_kzalloc(dev, sizeof(*acomp), GFP_KERNEL); 117062306a36Sopenharmony_ci if (!acomp) 117162306a36Sopenharmony_ci return -ENOMEM; 117262306a36Sopenharmony_ci 117362306a36Sopenharmony_ci ret = snd_soc_component_initialize(&acomp->base, drv, dev); 117462306a36Sopenharmony_ci if (ret < 0) 117562306a36Sopenharmony_ci return ret; 117662306a36Sopenharmony_ci 117762306a36Sopenharmony_ci /* force name change after ASoC is done with its init */ 117862306a36Sopenharmony_ci acomp->base.name = name; 117962306a36Sopenharmony_ci INIT_LIST_HEAD(&acomp->node); 118062306a36Sopenharmony_ci 118162306a36Sopenharmony_ci return snd_soc_add_component(&acomp->base, cpu_dais, num_cpu_dais); 118262306a36Sopenharmony_ci} 118362306a36Sopenharmony_ci 118462306a36Sopenharmony_cistatic struct snd_soc_dai_driver dmic_cpu_dais[] = { 118562306a36Sopenharmony_ci{ 118662306a36Sopenharmony_ci .name = "DMIC Pin", 118762306a36Sopenharmony_ci .ops = &avs_dai_nonhda_be_ops, 118862306a36Sopenharmony_ci .capture = { 118962306a36Sopenharmony_ci .stream_name = "DMIC Rx", 119062306a36Sopenharmony_ci .channels_min = 1, 119162306a36Sopenharmony_ci .channels_max = 4, 119262306a36Sopenharmony_ci .rates = SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_48000, 119362306a36Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE, 119462306a36Sopenharmony_ci }, 119562306a36Sopenharmony_ci}, 119662306a36Sopenharmony_ci{ 119762306a36Sopenharmony_ci .name = "DMIC WoV Pin", 119862306a36Sopenharmony_ci .ops = &avs_dai_nonhda_be_ops, 119962306a36Sopenharmony_ci .capture = { 120062306a36Sopenharmony_ci .stream_name = "DMIC WoV Rx", 120162306a36Sopenharmony_ci .channels_min = 1, 120262306a36Sopenharmony_ci .channels_max = 4, 120362306a36Sopenharmony_ci .rates = SNDRV_PCM_RATE_16000, 120462306a36Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_S16_LE, 120562306a36Sopenharmony_ci }, 120662306a36Sopenharmony_ci}, 120762306a36Sopenharmony_ci}; 120862306a36Sopenharmony_ci 120962306a36Sopenharmony_ciint avs_dmic_platform_register(struct avs_dev *adev, const char *name) 121062306a36Sopenharmony_ci{ 121162306a36Sopenharmony_ci return avs_soc_component_register(adev->dev, name, &avs_component_driver, dmic_cpu_dais, 121262306a36Sopenharmony_ci ARRAY_SIZE(dmic_cpu_dais)); 121362306a36Sopenharmony_ci} 121462306a36Sopenharmony_ci 121562306a36Sopenharmony_cistatic const struct snd_soc_dai_driver i2s_dai_template = { 121662306a36Sopenharmony_ci .ops = &avs_dai_nonhda_be_ops, 121762306a36Sopenharmony_ci .playback = { 121862306a36Sopenharmony_ci .channels_min = 1, 121962306a36Sopenharmony_ci .channels_max = 8, 122062306a36Sopenharmony_ci .rates = SNDRV_PCM_RATE_8000_192000 | 122162306a36Sopenharmony_ci SNDRV_PCM_RATE_KNOT, 122262306a36Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_S16_LE | 122362306a36Sopenharmony_ci SNDRV_PCM_FMTBIT_S24_LE | 122462306a36Sopenharmony_ci SNDRV_PCM_FMTBIT_S32_LE, 122562306a36Sopenharmony_ci }, 122662306a36Sopenharmony_ci .capture = { 122762306a36Sopenharmony_ci .channels_min = 1, 122862306a36Sopenharmony_ci .channels_max = 8, 122962306a36Sopenharmony_ci .rates = SNDRV_PCM_RATE_8000_192000 | 123062306a36Sopenharmony_ci SNDRV_PCM_RATE_KNOT, 123162306a36Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_S16_LE | 123262306a36Sopenharmony_ci SNDRV_PCM_FMTBIT_S24_LE | 123362306a36Sopenharmony_ci SNDRV_PCM_FMTBIT_S32_LE, 123462306a36Sopenharmony_ci }, 123562306a36Sopenharmony_ci}; 123662306a36Sopenharmony_ci 123762306a36Sopenharmony_ciint avs_i2s_platform_register(struct avs_dev *adev, const char *name, unsigned long port_mask, 123862306a36Sopenharmony_ci unsigned long *tdms) 123962306a36Sopenharmony_ci{ 124062306a36Sopenharmony_ci struct snd_soc_dai_driver *cpus, *dai; 124162306a36Sopenharmony_ci size_t ssp_count, cpu_count; 124262306a36Sopenharmony_ci int i, j; 124362306a36Sopenharmony_ci 124462306a36Sopenharmony_ci ssp_count = adev->hw_cfg.i2s_caps.ctrl_count; 124562306a36Sopenharmony_ci cpu_count = hweight_long(port_mask); 124662306a36Sopenharmony_ci if (tdms) 124762306a36Sopenharmony_ci for_each_set_bit(i, &port_mask, ssp_count) 124862306a36Sopenharmony_ci cpu_count += hweight_long(tdms[i]); 124962306a36Sopenharmony_ci 125062306a36Sopenharmony_ci cpus = devm_kzalloc(adev->dev, sizeof(*cpus) * cpu_count, GFP_KERNEL); 125162306a36Sopenharmony_ci if (!cpus) 125262306a36Sopenharmony_ci return -ENOMEM; 125362306a36Sopenharmony_ci 125462306a36Sopenharmony_ci dai = cpus; 125562306a36Sopenharmony_ci for_each_set_bit(i, &port_mask, ssp_count) { 125662306a36Sopenharmony_ci memcpy(dai, &i2s_dai_template, sizeof(*dai)); 125762306a36Sopenharmony_ci 125862306a36Sopenharmony_ci dai->name = 125962306a36Sopenharmony_ci devm_kasprintf(adev->dev, GFP_KERNEL, "SSP%d Pin", i); 126062306a36Sopenharmony_ci dai->playback.stream_name = 126162306a36Sopenharmony_ci devm_kasprintf(adev->dev, GFP_KERNEL, "ssp%d Tx", i); 126262306a36Sopenharmony_ci dai->capture.stream_name = 126362306a36Sopenharmony_ci devm_kasprintf(adev->dev, GFP_KERNEL, "ssp%d Rx", i); 126462306a36Sopenharmony_ci 126562306a36Sopenharmony_ci if (!dai->name || !dai->playback.stream_name || !dai->capture.stream_name) 126662306a36Sopenharmony_ci return -ENOMEM; 126762306a36Sopenharmony_ci dai++; 126862306a36Sopenharmony_ci } 126962306a36Sopenharmony_ci 127062306a36Sopenharmony_ci if (!tdms) 127162306a36Sopenharmony_ci goto plat_register; 127262306a36Sopenharmony_ci 127362306a36Sopenharmony_ci for_each_set_bit(i, &port_mask, ssp_count) { 127462306a36Sopenharmony_ci for_each_set_bit(j, &tdms[i], ssp_count) { 127562306a36Sopenharmony_ci memcpy(dai, &i2s_dai_template, sizeof(*dai)); 127662306a36Sopenharmony_ci 127762306a36Sopenharmony_ci dai->name = 127862306a36Sopenharmony_ci devm_kasprintf(adev->dev, GFP_KERNEL, "SSP%d:%d Pin", i, j); 127962306a36Sopenharmony_ci dai->playback.stream_name = 128062306a36Sopenharmony_ci devm_kasprintf(adev->dev, GFP_KERNEL, "ssp%d:%d Tx", i, j); 128162306a36Sopenharmony_ci dai->capture.stream_name = 128262306a36Sopenharmony_ci devm_kasprintf(adev->dev, GFP_KERNEL, "ssp%d:%d Rx", i, j); 128362306a36Sopenharmony_ci 128462306a36Sopenharmony_ci if (!dai->name || !dai->playback.stream_name || !dai->capture.stream_name) 128562306a36Sopenharmony_ci return -ENOMEM; 128662306a36Sopenharmony_ci dai++; 128762306a36Sopenharmony_ci } 128862306a36Sopenharmony_ci } 128962306a36Sopenharmony_ci 129062306a36Sopenharmony_ciplat_register: 129162306a36Sopenharmony_ci return avs_soc_component_register(adev->dev, name, &avs_component_driver, cpus, cpu_count); 129262306a36Sopenharmony_ci} 129362306a36Sopenharmony_ci 129462306a36Sopenharmony_ci/* HD-Audio CPU DAI template */ 129562306a36Sopenharmony_cistatic const struct snd_soc_dai_driver hda_cpu_dai = { 129662306a36Sopenharmony_ci .ops = &avs_dai_hda_be_ops, 129762306a36Sopenharmony_ci .playback = { 129862306a36Sopenharmony_ci .channels_min = 1, 129962306a36Sopenharmony_ci .channels_max = 8, 130062306a36Sopenharmony_ci .rates = SNDRV_PCM_RATE_8000_192000, 130162306a36Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_S16_LE | 130262306a36Sopenharmony_ci SNDRV_PCM_FMTBIT_S24_LE | 130362306a36Sopenharmony_ci SNDRV_PCM_FMTBIT_S32_LE, 130462306a36Sopenharmony_ci }, 130562306a36Sopenharmony_ci .capture = { 130662306a36Sopenharmony_ci .channels_min = 1, 130762306a36Sopenharmony_ci .channels_max = 8, 130862306a36Sopenharmony_ci .rates = SNDRV_PCM_RATE_8000_192000, 130962306a36Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_S16_LE | 131062306a36Sopenharmony_ci SNDRV_PCM_FMTBIT_S24_LE | 131162306a36Sopenharmony_ci SNDRV_PCM_FMTBIT_S32_LE, 131262306a36Sopenharmony_ci }, 131362306a36Sopenharmony_ci}; 131462306a36Sopenharmony_ci 131562306a36Sopenharmony_cistatic void avs_component_hda_unregister_dais(struct snd_soc_component *component) 131662306a36Sopenharmony_ci{ 131762306a36Sopenharmony_ci struct snd_soc_acpi_mach *mach; 131862306a36Sopenharmony_ci struct snd_soc_dai *dai, *save; 131962306a36Sopenharmony_ci struct hda_codec *codec; 132062306a36Sopenharmony_ci char name[32]; 132162306a36Sopenharmony_ci 132262306a36Sopenharmony_ci mach = dev_get_platdata(component->card->dev); 132362306a36Sopenharmony_ci codec = mach->pdata; 132462306a36Sopenharmony_ci sprintf(name, "%s-cpu", dev_name(&codec->core.dev)); 132562306a36Sopenharmony_ci 132662306a36Sopenharmony_ci for_each_component_dais_safe(component, dai, save) { 132762306a36Sopenharmony_ci int stream; 132862306a36Sopenharmony_ci 132962306a36Sopenharmony_ci if (!strstr(dai->driver->name, name)) 133062306a36Sopenharmony_ci continue; 133162306a36Sopenharmony_ci 133262306a36Sopenharmony_ci for_each_pcm_streams(stream) 133362306a36Sopenharmony_ci snd_soc_dapm_free_widget(snd_soc_dai_get_widget(dai, stream)); 133462306a36Sopenharmony_ci 133562306a36Sopenharmony_ci snd_soc_unregister_dai(dai); 133662306a36Sopenharmony_ci } 133762306a36Sopenharmony_ci} 133862306a36Sopenharmony_ci 133962306a36Sopenharmony_cistatic int avs_component_hda_probe(struct snd_soc_component *component) 134062306a36Sopenharmony_ci{ 134162306a36Sopenharmony_ci struct snd_soc_dapm_context *dapm; 134262306a36Sopenharmony_ci struct snd_soc_dai_driver *dais; 134362306a36Sopenharmony_ci struct snd_soc_acpi_mach *mach; 134462306a36Sopenharmony_ci struct hda_codec *codec; 134562306a36Sopenharmony_ci struct hda_pcm *pcm; 134662306a36Sopenharmony_ci const char *cname; 134762306a36Sopenharmony_ci int pcm_count = 0, ret, i; 134862306a36Sopenharmony_ci 134962306a36Sopenharmony_ci mach = dev_get_platdata(component->card->dev); 135062306a36Sopenharmony_ci if (!mach) 135162306a36Sopenharmony_ci return -EINVAL; 135262306a36Sopenharmony_ci 135362306a36Sopenharmony_ci codec = mach->pdata; 135462306a36Sopenharmony_ci if (list_empty(&codec->pcm_list_head)) 135562306a36Sopenharmony_ci return -EINVAL; 135662306a36Sopenharmony_ci list_for_each_entry(pcm, &codec->pcm_list_head, list) 135762306a36Sopenharmony_ci pcm_count++; 135862306a36Sopenharmony_ci 135962306a36Sopenharmony_ci dais = devm_kcalloc(component->dev, pcm_count, sizeof(*dais), 136062306a36Sopenharmony_ci GFP_KERNEL); 136162306a36Sopenharmony_ci if (!dais) 136262306a36Sopenharmony_ci return -ENOMEM; 136362306a36Sopenharmony_ci 136462306a36Sopenharmony_ci cname = dev_name(&codec->core.dev); 136562306a36Sopenharmony_ci dapm = snd_soc_component_get_dapm(component); 136662306a36Sopenharmony_ci pcm = list_first_entry(&codec->pcm_list_head, struct hda_pcm, list); 136762306a36Sopenharmony_ci 136862306a36Sopenharmony_ci for (i = 0; i < pcm_count; i++, pcm = list_next_entry(pcm, list)) { 136962306a36Sopenharmony_ci struct snd_soc_dai *dai; 137062306a36Sopenharmony_ci 137162306a36Sopenharmony_ci memcpy(&dais[i], &hda_cpu_dai, sizeof(*dais)); 137262306a36Sopenharmony_ci dais[i].id = i; 137362306a36Sopenharmony_ci dais[i].name = devm_kasprintf(component->dev, GFP_KERNEL, 137462306a36Sopenharmony_ci "%s-cpu%d", cname, i); 137562306a36Sopenharmony_ci if (!dais[i].name) { 137662306a36Sopenharmony_ci ret = -ENOMEM; 137762306a36Sopenharmony_ci goto exit; 137862306a36Sopenharmony_ci } 137962306a36Sopenharmony_ci 138062306a36Sopenharmony_ci if (pcm->stream[0].substreams) { 138162306a36Sopenharmony_ci dais[i].playback.stream_name = 138262306a36Sopenharmony_ci devm_kasprintf(component->dev, GFP_KERNEL, 138362306a36Sopenharmony_ci "%s-cpu%d Tx", cname, i); 138462306a36Sopenharmony_ci if (!dais[i].playback.stream_name) { 138562306a36Sopenharmony_ci ret = -ENOMEM; 138662306a36Sopenharmony_ci goto exit; 138762306a36Sopenharmony_ci } 138862306a36Sopenharmony_ci } 138962306a36Sopenharmony_ci 139062306a36Sopenharmony_ci if (pcm->stream[1].substreams) { 139162306a36Sopenharmony_ci dais[i].capture.stream_name = 139262306a36Sopenharmony_ci devm_kasprintf(component->dev, GFP_KERNEL, 139362306a36Sopenharmony_ci "%s-cpu%d Rx", cname, i); 139462306a36Sopenharmony_ci if (!dais[i].capture.stream_name) { 139562306a36Sopenharmony_ci ret = -ENOMEM; 139662306a36Sopenharmony_ci goto exit; 139762306a36Sopenharmony_ci } 139862306a36Sopenharmony_ci } 139962306a36Sopenharmony_ci 140062306a36Sopenharmony_ci dai = snd_soc_register_dai(component, &dais[i], false); 140162306a36Sopenharmony_ci if (!dai) { 140262306a36Sopenharmony_ci dev_err(component->dev, "register dai for %s failed\n", 140362306a36Sopenharmony_ci pcm->name); 140462306a36Sopenharmony_ci ret = -EINVAL; 140562306a36Sopenharmony_ci goto exit; 140662306a36Sopenharmony_ci } 140762306a36Sopenharmony_ci 140862306a36Sopenharmony_ci ret = snd_soc_dapm_new_dai_widgets(dapm, dai); 140962306a36Sopenharmony_ci if (ret < 0) { 141062306a36Sopenharmony_ci dev_err(component->dev, "create widgets failed: %d\n", 141162306a36Sopenharmony_ci ret); 141262306a36Sopenharmony_ci goto exit; 141362306a36Sopenharmony_ci } 141462306a36Sopenharmony_ci } 141562306a36Sopenharmony_ci 141662306a36Sopenharmony_ci ret = avs_component_probe(component); 141762306a36Sopenharmony_ciexit: 141862306a36Sopenharmony_ci if (ret) 141962306a36Sopenharmony_ci avs_component_hda_unregister_dais(component); 142062306a36Sopenharmony_ci 142162306a36Sopenharmony_ci return ret; 142262306a36Sopenharmony_ci} 142362306a36Sopenharmony_ci 142462306a36Sopenharmony_cistatic void avs_component_hda_remove(struct snd_soc_component *component) 142562306a36Sopenharmony_ci{ 142662306a36Sopenharmony_ci avs_component_hda_unregister_dais(component); 142762306a36Sopenharmony_ci avs_component_remove(component); 142862306a36Sopenharmony_ci} 142962306a36Sopenharmony_ci 143062306a36Sopenharmony_cistatic int avs_component_hda_open(struct snd_soc_component *component, 143162306a36Sopenharmony_ci struct snd_pcm_substream *substream) 143262306a36Sopenharmony_ci{ 143362306a36Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 143462306a36Sopenharmony_ci struct hdac_ext_stream *link_stream; 143562306a36Sopenharmony_ci struct hda_codec *codec; 143662306a36Sopenharmony_ci 143762306a36Sopenharmony_ci if (!rtd->dai_link->no_pcm) { 143862306a36Sopenharmony_ci struct snd_pcm_hardware hwparams = avs_pcm_hardware; 143962306a36Sopenharmony_ci struct snd_soc_pcm_runtime *be; 144062306a36Sopenharmony_ci struct snd_soc_dpcm *dpcm; 144162306a36Sopenharmony_ci int dir = substream->stream; 144262306a36Sopenharmony_ci 144362306a36Sopenharmony_ci /* 144462306a36Sopenharmony_ci * Support the DPCM reparenting while still fulfilling expectations of HDAudio 144562306a36Sopenharmony_ci * common code - a valid stream pointer at substream->runtime->private_data - 144662306a36Sopenharmony_ci * by having all FEs point to the same private data. 144762306a36Sopenharmony_ci */ 144862306a36Sopenharmony_ci for_each_dpcm_be(rtd, dir, dpcm) { 144962306a36Sopenharmony_ci struct snd_pcm_substream *be_substream; 145062306a36Sopenharmony_ci 145162306a36Sopenharmony_ci be = dpcm->be; 145262306a36Sopenharmony_ci if (be->dpcm[dir].users == 1) 145362306a36Sopenharmony_ci break; 145462306a36Sopenharmony_ci 145562306a36Sopenharmony_ci be_substream = snd_soc_dpcm_get_substream(be, dir); 145662306a36Sopenharmony_ci substream->runtime->private_data = be_substream->runtime->private_data; 145762306a36Sopenharmony_ci break; 145862306a36Sopenharmony_ci } 145962306a36Sopenharmony_ci 146062306a36Sopenharmony_ci /* RESUME unsupported for de-coupled HD-Audio capture. */ 146162306a36Sopenharmony_ci if (dir == SNDRV_PCM_STREAM_CAPTURE) 146262306a36Sopenharmony_ci hwparams.info &= ~SNDRV_PCM_INFO_RESUME; 146362306a36Sopenharmony_ci 146462306a36Sopenharmony_ci return snd_soc_set_runtime_hwparams(substream, &hwparams); 146562306a36Sopenharmony_ci } 146662306a36Sopenharmony_ci 146762306a36Sopenharmony_ci codec = dev_to_hda_codec(asoc_rtd_to_codec(rtd, 0)->dev); 146862306a36Sopenharmony_ci link_stream = snd_hdac_ext_stream_assign(&codec->bus->core, substream, 146962306a36Sopenharmony_ci HDAC_EXT_STREAM_TYPE_LINK); 147062306a36Sopenharmony_ci if (!link_stream) 147162306a36Sopenharmony_ci return -EBUSY; 147262306a36Sopenharmony_ci 147362306a36Sopenharmony_ci substream->runtime->private_data = link_stream; 147462306a36Sopenharmony_ci return 0; 147562306a36Sopenharmony_ci} 147662306a36Sopenharmony_ci 147762306a36Sopenharmony_cistatic int avs_component_hda_close(struct snd_soc_component *component, 147862306a36Sopenharmony_ci struct snd_pcm_substream *substream) 147962306a36Sopenharmony_ci{ 148062306a36Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 148162306a36Sopenharmony_ci struct hdac_ext_stream *link_stream; 148262306a36Sopenharmony_ci 148362306a36Sopenharmony_ci /* only BE DAI links are handled here */ 148462306a36Sopenharmony_ci if (!rtd->dai_link->no_pcm) 148562306a36Sopenharmony_ci return 0; 148662306a36Sopenharmony_ci 148762306a36Sopenharmony_ci link_stream = substream->runtime->private_data; 148862306a36Sopenharmony_ci snd_hdac_ext_stream_release(link_stream, HDAC_EXT_STREAM_TYPE_LINK); 148962306a36Sopenharmony_ci substream->runtime->private_data = NULL; 149062306a36Sopenharmony_ci 149162306a36Sopenharmony_ci return 0; 149262306a36Sopenharmony_ci} 149362306a36Sopenharmony_ci 149462306a36Sopenharmony_cistatic const struct snd_soc_component_driver avs_hda_component_driver = { 149562306a36Sopenharmony_ci .name = "avs-hda-pcm", 149662306a36Sopenharmony_ci .probe = avs_component_hda_probe, 149762306a36Sopenharmony_ci .remove = avs_component_hda_remove, 149862306a36Sopenharmony_ci .suspend = avs_component_suspend, 149962306a36Sopenharmony_ci .resume = avs_component_resume, 150062306a36Sopenharmony_ci .open = avs_component_hda_open, 150162306a36Sopenharmony_ci .close = avs_component_hda_close, 150262306a36Sopenharmony_ci .pointer = avs_component_pointer, 150362306a36Sopenharmony_ci .mmap = avs_component_mmap, 150462306a36Sopenharmony_ci .pcm_construct = avs_component_construct, 150562306a36Sopenharmony_ci /* 150662306a36Sopenharmony_ci * hda platform component's probe() is dependent on 150762306a36Sopenharmony_ci * codec->pcm_list_head, it needs to be initialized after codec 150862306a36Sopenharmony_ci * component. remove_order is here for completeness sake 150962306a36Sopenharmony_ci */ 151062306a36Sopenharmony_ci .probe_order = SND_SOC_COMP_ORDER_LATE, 151162306a36Sopenharmony_ci .remove_order = SND_SOC_COMP_ORDER_EARLY, 151262306a36Sopenharmony_ci .module_get_upon_open = 1, 151362306a36Sopenharmony_ci .topology_name_prefix = "intel/avs", 151462306a36Sopenharmony_ci}; 151562306a36Sopenharmony_ci 151662306a36Sopenharmony_ciint avs_hda_platform_register(struct avs_dev *adev, const char *name) 151762306a36Sopenharmony_ci{ 151862306a36Sopenharmony_ci return avs_soc_component_register(adev->dev, name, 151962306a36Sopenharmony_ci &avs_hda_component_driver, NULL, 0); 152062306a36Sopenharmony_ci} 1521