Skip to content

Commit d070002

Browse files
crojewsk-intelbroonie
authored andcommitted
ASoC: Intel: avs: HDA PCM BE operations
HDA streaming in DSP world means enlisting HDAudio links as BE interfaces. Another difference when compared to its DMIC and I2S friends is lack of NHLT blob usage - no additional hardware configuration is needed. Similarly to I2S component, HDA populates its DAIs dynamically, here by the means of codec->pcm_list_head. Allows for cutting the number of soc components required to support the interface. Signed-off-by: Amadeusz Sławiński <amadeuszx.slawinski@linux.intel.com> Signed-off-by: Cezary Rojewski <cezary.rojewski@intel.com> Link: https://lore.kernel.org/r/20220516101116.190192-6-cezary.rojewski@intel.com Signed-off-by: Mark Brown <broonie@kernel.org>
1 parent b9062f9 commit d070002

File tree

2 files changed

+350
-0
lines changed

2 files changed

+350
-0
lines changed

sound/soc/intel/avs/avs.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,5 +273,6 @@ extern const struct snd_soc_dai_ops avs_dai_fe_ops;
273273
int avs_dmic_platform_register(struct avs_dev *adev, const char *name);
274274
int avs_i2s_platform_register(struct avs_dev *adev, const char *name, unsigned long port_mask,
275275
unsigned long *tdms);
276+
int avs_hda_platform_register(struct avs_dev *adev, const char *name);
276277

277278
#endif /* __SOUND_SOC_INTEL_AVS_H */

sound/soc/intel/avs/pcm.c

Lines changed: 349 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,155 @@ static const struct snd_soc_dai_ops avs_dai_nonhda_be_ops = {
245245
.trigger = avs_dai_nonhda_be_trigger,
246246
};
247247

248+
static int avs_dai_hda_be_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
249+
{
250+
return avs_dai_startup(substream, dai, false);
251+
}
252+
253+
static void avs_dai_hda_be_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
254+
{
255+
return avs_dai_nonhda_be_shutdown(substream, dai);
256+
}
257+
258+
static int avs_dai_hda_be_hw_params(struct snd_pcm_substream *substream,
259+
struct snd_pcm_hw_params *hw_params, struct snd_soc_dai *dai)
260+
{
261+
struct avs_dma_data *data;
262+
struct hdac_ext_stream *link_stream;
263+
264+
data = snd_soc_dai_get_dma_data(dai, substream);
265+
if (data->path)
266+
return 0;
267+
268+
link_stream = substream->runtime->private_data;
269+
270+
return avs_dai_be_hw_params(substream, hw_params, dai,
271+
hdac_stream(link_stream)->stream_tag - 1);
272+
}
273+
274+
static int avs_dai_hda_be_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
275+
{
276+
struct avs_dma_data *data;
277+
struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
278+
struct hdac_ext_stream *link_stream;
279+
struct hdac_ext_link *link;
280+
struct hda_codec *codec;
281+
282+
dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name);
283+
284+
data = snd_soc_dai_get_dma_data(dai, substream);
285+
if (!data->path)
286+
return 0;
287+
288+
link_stream = substream->runtime->private_data;
289+
link_stream->link_prepared = false;
290+
avs_path_free(data->path);
291+
data->path = NULL;
292+
293+
/* clear link <-> stream mapping */
294+
codec = dev_to_hda_codec(asoc_rtd_to_codec(rtd, 0)->dev);
295+
link = snd_hdac_ext_bus_link_at(&codec->bus->core, codec->core.addr);
296+
if (!link)
297+
return -EINVAL;
298+
299+
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
300+
snd_hdac_ext_link_clear_stream_id(link, hdac_stream(link_stream)->stream_tag);
301+
302+
return 0;
303+
}
304+
305+
static int avs_dai_hda_be_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
306+
{
307+
struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
308+
struct snd_pcm_runtime *runtime = substream->runtime;
309+
struct hdac_ext_stream *link_stream = runtime->private_data;
310+
struct hdac_ext_link *link;
311+
struct hda_codec *codec;
312+
struct hdac_bus *bus;
313+
unsigned int format_val;
314+
int ret;
315+
316+
if (link_stream->link_prepared)
317+
return 0;
318+
319+
codec = dev_to_hda_codec(asoc_rtd_to_codec(rtd, 0)->dev);
320+
bus = &codec->bus->core;
321+
format_val = snd_hdac_calc_stream_format(runtime->rate, runtime->channels, runtime->format,
322+
runtime->sample_bits, 0);
323+
324+
snd_hdac_ext_stream_decouple(bus, link_stream, true);
325+
snd_hdac_ext_link_stream_reset(link_stream);
326+
snd_hdac_ext_link_stream_setup(link_stream, format_val);
327+
328+
link = snd_hdac_ext_bus_link_at(bus, codec->core.addr);
329+
if (!link)
330+
return -EINVAL;
331+
332+
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
333+
snd_hdac_ext_link_set_stream_id(link, hdac_stream(link_stream)->stream_tag);
334+
335+
ret = avs_dai_prepare(to_avs_dev(dai->dev), substream, dai);
336+
if (ret)
337+
return ret;
338+
339+
link_stream->link_prepared = true;
340+
return 0;
341+
}
342+
343+
static int avs_dai_hda_be_trigger(struct snd_pcm_substream *substream, int cmd,
344+
struct snd_soc_dai *dai)
345+
{
346+
struct hdac_ext_stream *link_stream;
347+
struct avs_dma_data *data;
348+
int ret = 0;
349+
350+
dev_dbg(dai->dev, "entry %s cmd=%d\n", __func__, cmd);
351+
352+
data = snd_soc_dai_get_dma_data(dai, substream);
353+
link_stream = substream->runtime->private_data;
354+
355+
switch (cmd) {
356+
case SNDRV_PCM_TRIGGER_START:
357+
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
358+
snd_hdac_ext_link_stream_start(link_stream);
359+
360+
ret = avs_path_run(data->path, AVS_TPLG_TRIGGER_AUTO);
361+
if (ret < 0)
362+
dev_err(dai->dev, "run BE path failed: %d\n", ret);
363+
break;
364+
365+
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
366+
case SNDRV_PCM_TRIGGER_STOP:
367+
ret = avs_path_pause(data->path);
368+
if (ret < 0)
369+
dev_err(dai->dev, "pause BE path failed: %d\n", ret);
370+
371+
snd_hdac_ext_link_stream_clear(link_stream);
372+
373+
if (cmd == SNDRV_PCM_TRIGGER_STOP) {
374+
ret = avs_path_reset(data->path);
375+
if (ret < 0)
376+
dev_err(dai->dev, "reset BE path failed: %d\n", ret);
377+
}
378+
break;
379+
380+
default:
381+
ret = -EINVAL;
382+
break;
383+
}
384+
385+
return ret;
386+
}
387+
388+
static const struct snd_soc_dai_ops avs_dai_hda_be_ops = {
389+
.startup = avs_dai_hda_be_startup,
390+
.shutdown = avs_dai_hda_be_shutdown,
391+
.hw_params = avs_dai_hda_be_hw_params,
392+
.hw_free = avs_dai_hda_be_hw_free,
393+
.prepare = avs_dai_hda_be_prepare,
394+
.trigger = avs_dai_hda_be_trigger,
395+
};
396+
248397
static const unsigned int rates[] = {
249398
8000, 11025, 12000, 16000,
250399
22050, 24000, 32000, 44100,
@@ -831,3 +980,203 @@ int avs_i2s_platform_register(struct avs_dev *adev, const char *name, unsigned l
831980
plat_register:
832981
return avs_soc_component_register(adev->dev, name, &avs_component_driver, cpus, cpu_count);
833982
}
983+
984+
/* HD-Audio CPU DAI template */
985+
static const struct snd_soc_dai_driver hda_cpu_dai = {
986+
.ops = &avs_dai_hda_be_ops,
987+
.playback = {
988+
.channels_min = 1,
989+
.channels_max = 8,
990+
.rates = SNDRV_PCM_RATE_8000_192000,
991+
.formats = SNDRV_PCM_FMTBIT_S16_LE |
992+
SNDRV_PCM_FMTBIT_S24_LE |
993+
SNDRV_PCM_FMTBIT_S32_LE,
994+
},
995+
.capture = {
996+
.channels_min = 1,
997+
.channels_max = 8,
998+
.rates = SNDRV_PCM_RATE_8000_192000,
999+
.formats = SNDRV_PCM_FMTBIT_S16_LE |
1000+
SNDRV_PCM_FMTBIT_S24_LE |
1001+
SNDRV_PCM_FMTBIT_S32_LE,
1002+
},
1003+
};
1004+
1005+
static void avs_component_hda_unregister_dais(struct snd_soc_component *component)
1006+
{
1007+
struct snd_soc_acpi_mach *mach;
1008+
struct snd_soc_dai *dai, *save;
1009+
struct hda_codec *codec;
1010+
char name[32];
1011+
1012+
mach = dev_get_platdata(component->card->dev);
1013+
codec = mach->pdata;
1014+
sprintf(name, "%s-cpu", dev_name(&codec->core.dev));
1015+
1016+
for_each_component_dais_safe(component, dai, save) {
1017+
if (!strstr(dai->driver->name, name))
1018+
continue;
1019+
1020+
if (dai->playback_widget)
1021+
snd_soc_dapm_free_widget(dai->playback_widget);
1022+
if (dai->capture_widget)
1023+
snd_soc_dapm_free_widget(dai->capture_widget);
1024+
snd_soc_unregister_dai(dai);
1025+
}
1026+
}
1027+
1028+
static int avs_component_hda_probe(struct snd_soc_component *component)
1029+
{
1030+
struct snd_soc_dapm_context *dapm;
1031+
struct snd_soc_dai_driver *dais;
1032+
struct snd_soc_acpi_mach *mach;
1033+
struct hda_codec *codec;
1034+
struct hda_pcm *pcm;
1035+
const char *cname;
1036+
int pcm_count = 0, ret, i;
1037+
1038+
mach = dev_get_platdata(component->card->dev);
1039+
if (!mach)
1040+
return -EINVAL;
1041+
1042+
codec = mach->pdata;
1043+
if (list_empty(&codec->pcm_list_head))
1044+
return -EINVAL;
1045+
list_for_each_entry(pcm, &codec->pcm_list_head, list)
1046+
pcm_count++;
1047+
1048+
dais = devm_kcalloc(component->dev, pcm_count, sizeof(*dais),
1049+
GFP_KERNEL);
1050+
if (!dais)
1051+
return -ENOMEM;
1052+
1053+
cname = dev_name(&codec->core.dev);
1054+
dapm = snd_soc_component_get_dapm(component);
1055+
pcm = list_first_entry(&codec->pcm_list_head, struct hda_pcm, list);
1056+
1057+
for (i = 0; i < pcm_count; i++, pcm = list_next_entry(pcm, list)) {
1058+
struct snd_soc_dai *dai;
1059+
1060+
memcpy(&dais[i], &hda_cpu_dai, sizeof(*dais));
1061+
dais[i].id = i;
1062+
dais[i].name = devm_kasprintf(component->dev, GFP_KERNEL,
1063+
"%s-cpu%d", cname, i);
1064+
if (!dais[i].name) {
1065+
ret = -ENOMEM;
1066+
goto exit;
1067+
}
1068+
1069+
if (pcm->stream[0].substreams) {
1070+
dais[i].playback.stream_name =
1071+
devm_kasprintf(component->dev, GFP_KERNEL,
1072+
"%s-cpu%d Tx", cname, i);
1073+
if (!dais[i].playback.stream_name) {
1074+
ret = -ENOMEM;
1075+
goto exit;
1076+
}
1077+
}
1078+
1079+
if (pcm->stream[1].substreams) {
1080+
dais[i].capture.stream_name =
1081+
devm_kasprintf(component->dev, GFP_KERNEL,
1082+
"%s-cpu%d Rx", cname, i);
1083+
if (!dais[i].capture.stream_name) {
1084+
ret = -ENOMEM;
1085+
goto exit;
1086+
}
1087+
}
1088+
1089+
dai = snd_soc_register_dai(component, &dais[i], false);
1090+
if (!dai) {
1091+
dev_err(component->dev, "register dai for %s failed\n",
1092+
pcm->name);
1093+
ret = -EINVAL;
1094+
goto exit;
1095+
}
1096+
1097+
ret = snd_soc_dapm_new_dai_widgets(dapm, dai);
1098+
if (ret < 0) {
1099+
dev_err(component->dev, "create widgets failed: %d\n",
1100+
ret);
1101+
goto exit;
1102+
}
1103+
}
1104+
1105+
ret = avs_component_probe(component);
1106+
exit:
1107+
if (ret)
1108+
avs_component_hda_unregister_dais(component);
1109+
1110+
return ret;
1111+
}
1112+
1113+
static void avs_component_hda_remove(struct snd_soc_component *component)
1114+
{
1115+
avs_component_hda_unregister_dais(component);
1116+
avs_component_remove(component);
1117+
}
1118+
1119+
static int avs_component_hda_open(struct snd_soc_component *component,
1120+
struct snd_pcm_substream *substream)
1121+
{
1122+
struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
1123+
struct hdac_ext_stream *link_stream;
1124+
struct hda_codec *codec;
1125+
1126+
/* only BE DAI links are handled here */
1127+
if (!rtd->dai_link->no_pcm)
1128+
return avs_component_open(component, substream);
1129+
1130+
codec = dev_to_hda_codec(asoc_rtd_to_codec(rtd, 0)->dev);
1131+
link_stream = snd_hdac_ext_stream_assign(&codec->bus->core, substream,
1132+
HDAC_EXT_STREAM_TYPE_LINK);
1133+
if (!link_stream)
1134+
return -EBUSY;
1135+
1136+
substream->runtime->private_data = link_stream;
1137+
return 0;
1138+
}
1139+
1140+
static int avs_component_hda_close(struct snd_soc_component *component,
1141+
struct snd_pcm_substream *substream)
1142+
{
1143+
struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
1144+
struct hdac_ext_stream *link_stream;
1145+
1146+
/* only BE DAI links are handled here */
1147+
if (!rtd->dai_link->no_pcm)
1148+
return 0;
1149+
1150+
link_stream = substream->runtime->private_data;
1151+
snd_hdac_ext_stream_release(link_stream, HDAC_EXT_STREAM_TYPE_LINK);
1152+
substream->runtime->private_data = NULL;
1153+
1154+
return 0;
1155+
}
1156+
1157+
static const struct snd_soc_component_driver avs_hda_component_driver = {
1158+
.name = "avs-hda-pcm",
1159+
.probe = avs_component_hda_probe,
1160+
.remove = avs_component_hda_remove,
1161+
.open = avs_component_hda_open,
1162+
.close = avs_component_hda_close,
1163+
.pointer = avs_component_pointer,
1164+
.mmap = avs_component_mmap,
1165+
.pcm_construct = avs_component_construct,
1166+
/*
1167+
* hda platform component's probe() is dependent on
1168+
* codec->pcm_list_head, it needs to be initialized after codec
1169+
* component. remove_order is here for completeness sake
1170+
*/
1171+
.probe_order = SND_SOC_COMP_ORDER_LATE,
1172+
.remove_order = SND_SOC_COMP_ORDER_EARLY,
1173+
.module_get_upon_open = 1,
1174+
.topology_name_prefix = "intel/avs",
1175+
.non_legacy_dai_naming = true,
1176+
};
1177+
1178+
int avs_hda_platform_register(struct avs_dev *adev, const char *name)
1179+
{
1180+
return avs_soc_component_register(adev->dev, name,
1181+
&avs_hda_component_driver, NULL, 0);
1182+
}

0 commit comments

Comments
 (0)