[RFC 27/37] ASoC: Intel: avs: non-HDA PCM BE operations
Cezary Rojewski
cezary.rojewski at intel.com
Wed Dec 8 12:12:51 CET 2021
DMIC and I2S interfaces differ in DMA operations from the HDAudio
interface. With that in mind, implement all DAI operations to handle
non-HDA BE interfaces.
To prevent code duplication, SSP platform registering is dynamic - makes
use of specified port_mask and TDMs array to populate as many DAIs as
required.
Signed-off-by: Amadeusz Sławiński <amadeuszx.slawinski at linux.intel.com>
Signed-off-by: Cezary Rojewski <cezary.rojewski at intel.com>
---
sound/soc/intel/avs/avs.h | 4 +
sound/soc/intel/avs/pcm.c | 260 +++++++++++++++++++++++++++++++++++++-
2 files changed, 263 insertions(+), 1 deletion(-)
diff --git a/sound/soc/intel/avs/avs.h b/sound/soc/intel/avs/avs.h
index 978d922c07be..90c3a4e83666 100644
--- a/sound/soc/intel/avs/avs.h
+++ b/sound/soc/intel/avs/avs.h
@@ -254,4 +254,8 @@ struct avs_soc_component {
extern const struct snd_soc_dai_ops avs_dai_fe_ops;
+int avs_dmic_platform_register(struct avs_dev *adev, const char *name);
+int avs_ssp_platform_register(struct avs_dev *adev, const char *name,
+ unsigned long port_mask, unsigned long *tdms);
+
#endif /* __SOUND_SOC_INTEL_AVS_H */
diff --git a/sound/soc/intel/avs/pcm.c b/sound/soc/intel/avs/pcm.c
index c868da4751e7..e8900155cd34 100644
--- a/sound/soc/intel/avs/pcm.c
+++ b/sound/soc/intel/avs/pcm.c
@@ -113,6 +113,25 @@ static int avs_pcm_fe_hw_params(struct avs_dev *adev,
dai, dma_id);
}
+static int avs_pcm_be_hw_params(struct avs_dev *adev,
+ struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *be_hw_params,
+ struct snd_soc_dai *dai, int dma_id)
+{
+ struct snd_pcm_hw_params *fe_hw_params = NULL;
+ struct snd_soc_pcm_runtime *fe, *be;
+ struct snd_soc_dpcm *dpcm;
+
+ be = asoc_substream_to_rtd(substream);
+ for_each_dpcm_fe(be, substream->stream, dpcm) {
+ fe = dpcm->fe;
+ fe_hw_params = &fe->dpcm[substream->stream].hw_params;
+ }
+
+ return avs_pcm_hw_params(adev, substream, fe_hw_params, be_hw_params,
+ dai, dma_id);
+}
+
static int avs_dai_prepare(struct avs_dev *adev,
struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
@@ -136,6 +155,132 @@ static int avs_dai_prepare(struct avs_dev *adev,
return ret;
}
+static int avs_dai_nonhda_be_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
+ struct snd_soc_component *component;
+ struct avs_tplg_path_template *template;
+ struct avs_pcm_dma_data *data;
+ int i;
+
+ for_each_rtd_components(rtd, i, component)
+ if (strstr(component->name, "-platform"))
+ break;
+
+ if (i == rtd->num_components)
+ return -EINVAL;
+
+ template = avs_pcm_find_path_template(dai, component, false, substream->stream);
+ if (!template) {
+ dev_err(dai->dev, "no %s path for dai %s, invalid tplg?\n",
+ snd_pcm_stream_str(substream), dai->name);
+ return -EINVAL;
+ }
+
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->template = template;
+ snd_soc_dai_set_dma_data(dai, substream, data);
+
+ return 0;
+}
+
+static void avs_dai_nonhda_be_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct avs_pcm_dma_data *data;
+
+ data = snd_soc_dai_get_dma_data(dai, substream);
+
+ snd_soc_dai_set_dma_data(dai, substream, NULL);
+ kfree(data);
+}
+
+static int avs_dai_nonhda_be_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params,
+ struct snd_soc_dai *dai)
+{
+ struct avs_pcm_dma_data *data;
+
+ data = snd_soc_dai_get_dma_data(dai, substream);
+ if (data->path)
+ return 0;
+
+ /* Actual port-id comes from topology. */
+ return avs_pcm_be_hw_params(to_avs_dev(dai->dev), substream, hw_params, dai, 0);
+}
+
+static int avs_dai_nonhda_be_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct avs_pcm_dma_data *data;
+
+ dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name);
+
+ data = snd_soc_dai_get_dma_data(dai, substream);
+ if (data->path) {
+ avs_path_free(data->path);
+ data->path = NULL;
+ }
+
+ return 0;
+}
+
+static int avs_dai_nonhda_be_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ return avs_dai_prepare(to_avs_dev(dai->dev), substream, dai);
+}
+
+static int avs_dai_nonhda_be_trigger(struct snd_pcm_substream *substream,
+ int cmd, struct snd_soc_dai *dai)
+{
+ struct avs_pcm_dma_data *data;
+ int ret = 0;
+
+ data = snd_soc_dai_get_dma_data(dai, substream);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ 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_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 FE path failed: %d\n", ret);
+ }
+ break;
+
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static const struct snd_soc_dai_ops avs_dai_nonhda_be_ops = {
+ .startup = avs_dai_nonhda_be_startup,
+ .shutdown = avs_dai_nonhda_be_shutdown,
+ .hw_params = avs_dai_nonhda_be_hw_params,
+ .hw_free = avs_dai_nonhda_be_hw_free,
+ .prepare = avs_dai_nonhda_be_prepare,
+ .trigger = avs_dai_nonhda_be_trigger,
+};
+
static const unsigned int rates[] = {
8000, 11025, 12000, 16000,
22050, 24000, 32000, 44100,
@@ -607,7 +752,6 @@ static const struct snd_soc_component_driver avs_component_driver = {
.non_legacy_dai_naming = true,
};
-__maybe_unused
static int avs_soc_component_register(struct device *dev, const char *name,
const struct snd_soc_component_driver *drv,
struct snd_soc_dai_driver *cpu_dais,
@@ -630,3 +774,117 @@ static int avs_soc_component_register(struct device *dev, const char *name,
return snd_soc_add_component(&acomp->base, cpu_dais, num_cpu_dais);
}
+
+static struct snd_soc_dai_driver dmic_cpu_dais[] = {
+{
+ .name = "DMIC Pin",
+ .ops = &avs_dai_nonhda_be_ops,
+ .capture = {
+ .stream_name = "DMIC Rx",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
+ },
+},
+{
+ .name = "DMIC WoV Pin",
+ .ops = &avs_dai_nonhda_be_ops,
+ .capture = {
+ .stream_name = "DMIC WoV Rx",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+},
+};
+
+int avs_dmic_platform_register(struct avs_dev *adev, const char *name)
+{
+ return avs_soc_component_register(adev->dev, name, &avs_component_driver,
+ dmic_cpu_dais,
+ ARRAY_SIZE(dmic_cpu_dais));
+}
+
+static const struct snd_soc_dai_driver ssp_dai_template = {
+ .ops = &avs_dai_nonhda_be_ops,
+ .playback = {
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = SNDRV_PCM_RATE_8000_192000 |
+ SNDRV_PCM_RATE_KNOT,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ },
+ .capture = {
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = SNDRV_PCM_RATE_8000_192000 |
+ SNDRV_PCM_RATE_KNOT,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ },
+};
+
+int avs_ssp_platform_register(struct avs_dev *adev, const char *name,
+ unsigned long port_mask, unsigned long *tdms)
+{
+ struct snd_soc_dai_driver *cpus, *dai;
+ size_t ssp_count, cpu_count;
+ int i, j;
+
+ ssp_count = adev->hw_cfg.i2s_caps.ctrl_count;
+ cpu_count = hweight_long(port_mask);
+ if (tdms)
+ for_each_set_bit(i, &port_mask, ssp_count)
+ cpu_count += hweight_long(tdms[i]);
+
+ cpus = devm_kzalloc(adev->dev, sizeof(*cpus) * cpu_count, GFP_KERNEL);
+ if (!cpus)
+ return -ENOMEM;
+
+ dai = cpus;
+ for_each_set_bit(i, &port_mask, ssp_count) {
+ memcpy(dai, &ssp_dai_template, sizeof(*dai));
+
+ dai->name =
+ devm_kasprintf(adev->dev, GFP_KERNEL, "SSP%d Pin", i);
+ dai->playback.stream_name =
+ devm_kasprintf(adev->dev, GFP_KERNEL, "ssp%d Tx", i);
+ dai->capture.stream_name =
+ devm_kasprintf(adev->dev, GFP_KERNEL, "ssp%d Rx", i);
+
+ if (!dai->name || !dai->playback.stream_name ||
+ !dai->capture.stream_name)
+ return -ENOMEM;
+ dai++;
+ }
+
+ if (!tdms)
+ goto plat_register;
+
+ for_each_set_bit(i, &port_mask, ssp_count) {
+ for_each_set_bit(j, &tdms[i], ssp_count) {
+ memcpy(dai, &ssp_dai_template, sizeof(*dai));
+
+ dai->name =
+ devm_kasprintf(adev->dev, GFP_KERNEL, "SSP%d:%d Pin", i, j);
+ dai->playback.stream_name =
+ devm_kasprintf(adev->dev, GFP_KERNEL, "ssp%d:%d Tx", i, j);
+ dai->capture.stream_name =
+ devm_kasprintf(adev->dev, GFP_KERNEL, "ssp%d:%d Rx", i, j);
+
+ if (!dai->name || !dai->playback.stream_name ||
+ !dai->capture.stream_name)
+ return -ENOMEM;
+ dai++;
+ }
+ }
+
+plat_register:
+ return avs_soc_component_register(adev->dev, name, &avs_component_driver,
+ cpus, cpu_count);
+}
--
2.25.1
More information about the Alsa-devel
mailing list