1f1b3b320SCezary Rojewski // SPDX-License-Identifier: GPL-2.0-only 2f1b3b320SCezary Rojewski // 3f1b3b320SCezary Rojewski // Copyright(c) 2021-2022 Intel Corporation. All rights reserved. 4f1b3b320SCezary Rojewski // 5f1b3b320SCezary Rojewski // Authors: Cezary Rojewski <cezary.rojewski@intel.com> 6f1b3b320SCezary Rojewski // Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com> 7f1b3b320SCezary Rojewski // 8f1b3b320SCezary Rojewski 9f1b3b320SCezary Rojewski #include <linux/debugfs.h> 10f1b3b320SCezary Rojewski #include <linux/device.h> 11f1b3b320SCezary Rojewski #include <sound/hda_register.h> 12f1b3b320SCezary Rojewski #include <sound/hdaudio_ext.h> 139114700bSCezary Rojewski #include <sound/pcm_params.h> 14f1b3b320SCezary Rojewski #include <sound/soc-acpi.h> 15f1b3b320SCezary Rojewski #include <sound/soc-acpi-intel-match.h> 16f1b3b320SCezary Rojewski #include <sound/soc-component.h> 17f1b3b320SCezary Rojewski #include "avs.h" 18f1b3b320SCezary Rojewski #include "path.h" 19f1b3b320SCezary Rojewski #include "topology.h" 20f1b3b320SCezary Rojewski 21f1b3b320SCezary Rojewski struct avs_dma_data { 22f1b3b320SCezary Rojewski struct avs_tplg_path_template *template; 23f1b3b320SCezary Rojewski struct avs_path *path; 24f1b3b320SCezary Rojewski /* 25f1b3b320SCezary Rojewski * link stream is stored within substream's runtime 26f1b3b320SCezary Rojewski * private_data to fulfill the needs of codec BE path 27f1b3b320SCezary Rojewski * 28f1b3b320SCezary Rojewski * host stream assigned 29f1b3b320SCezary Rojewski */ 30f1b3b320SCezary Rojewski struct hdac_ext_stream *host_stream; 312b9a50eaSCezary Rojewski 322b9a50eaSCezary Rojewski struct snd_pcm_substream *substream; 33f1b3b320SCezary Rojewski }; 34f1b3b320SCezary Rojewski 359114700bSCezary Rojewski static struct avs_tplg_path_template * 369114700bSCezary Rojewski avs_dai_find_path_template(struct snd_soc_dai *dai, bool is_fe, int direction) 379114700bSCezary Rojewski { 38*ec4b2099SKuninori Morimoto struct snd_soc_dapm_widget *dw = snd_soc_dai_get_widget(dai, direction); 399114700bSCezary Rojewski struct snd_soc_dapm_path *dp; 409114700bSCezary Rojewski enum snd_soc_dapm_direction dir; 419114700bSCezary Rojewski 429114700bSCezary Rojewski if (direction == SNDRV_PCM_STREAM_CAPTURE) { 439114700bSCezary Rojewski dir = is_fe ? SND_SOC_DAPM_DIR_OUT : SND_SOC_DAPM_DIR_IN; 449114700bSCezary Rojewski } else { 459114700bSCezary Rojewski dir = is_fe ? SND_SOC_DAPM_DIR_IN : SND_SOC_DAPM_DIR_OUT; 469114700bSCezary Rojewski } 479114700bSCezary Rojewski 489114700bSCezary Rojewski dp = list_first_entry_or_null(&dw->edges[dir], typeof(*dp), list_node[dir]); 499114700bSCezary Rojewski if (!dp) 509114700bSCezary Rojewski return NULL; 519114700bSCezary Rojewski 529114700bSCezary Rojewski /* Get the other widget, with actual path template data */ 539114700bSCezary Rojewski dw = (dp->source == dw) ? dp->sink : dp->source; 549114700bSCezary Rojewski 559114700bSCezary Rojewski return dw->priv; 569114700bSCezary Rojewski } 579114700bSCezary Rojewski 582b9a50eaSCezary Rojewski static int avs_dai_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai, bool is_fe, 592b9a50eaSCezary Rojewski const struct snd_soc_dai_ops *ops) 609114700bSCezary Rojewski { 618f28299fSCezary Rojewski struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 62730cb320SCezary Rojewski struct avs_dev *adev = to_avs_dev(dai->dev); 639114700bSCezary Rojewski struct avs_tplg_path_template *template; 649114700bSCezary Rojewski struct avs_dma_data *data; 659114700bSCezary Rojewski 669114700bSCezary Rojewski template = avs_dai_find_path_template(dai, is_fe, substream->stream); 679114700bSCezary Rojewski if (!template) { 689114700bSCezary Rojewski dev_err(dai->dev, "no %s path for dai %s, invalid tplg?\n", 699114700bSCezary Rojewski snd_pcm_stream_str(substream), dai->name); 709114700bSCezary Rojewski return -EINVAL; 719114700bSCezary Rojewski } 729114700bSCezary Rojewski 739114700bSCezary Rojewski data = kzalloc(sizeof(*data), GFP_KERNEL); 749114700bSCezary Rojewski if (!data) 759114700bSCezary Rojewski return -ENOMEM; 769114700bSCezary Rojewski 772b9a50eaSCezary Rojewski data->substream = substream; 789114700bSCezary Rojewski data->template = template; 799114700bSCezary Rojewski snd_soc_dai_set_dma_data(dai, substream, data); 809114700bSCezary Rojewski 81730cb320SCezary Rojewski if (rtd->dai_link->ignore_suspend) 82730cb320SCezary Rojewski adev->num_lp_paths++; 83730cb320SCezary Rojewski 849114700bSCezary Rojewski return 0; 859114700bSCezary Rojewski } 869114700bSCezary Rojewski 879114700bSCezary Rojewski static int avs_dai_hw_params(struct snd_pcm_substream *substream, 889114700bSCezary Rojewski struct snd_pcm_hw_params *fe_hw_params, 899114700bSCezary Rojewski struct snd_pcm_hw_params *be_hw_params, struct snd_soc_dai *dai, 909114700bSCezary Rojewski int dma_id) 919114700bSCezary Rojewski { 929114700bSCezary Rojewski struct avs_dma_data *data; 939114700bSCezary Rojewski struct avs_path *path; 949114700bSCezary Rojewski struct avs_dev *adev = to_avs_dev(dai->dev); 959114700bSCezary Rojewski int ret; 969114700bSCezary Rojewski 979114700bSCezary Rojewski data = snd_soc_dai_get_dma_data(dai, substream); 989114700bSCezary Rojewski 999114700bSCezary Rojewski dev_dbg(dai->dev, "%s FE hw_params str %p rtd %p", 1009114700bSCezary Rojewski __func__, substream, substream->runtime); 1019114700bSCezary Rojewski dev_dbg(dai->dev, "rate %d chn %d vbd %d bd %d\n", 1029114700bSCezary Rojewski params_rate(fe_hw_params), params_channels(fe_hw_params), 1039114700bSCezary Rojewski params_width(fe_hw_params), params_physical_width(fe_hw_params)); 1049114700bSCezary Rojewski 1059114700bSCezary Rojewski dev_dbg(dai->dev, "%s BE hw_params str %p rtd %p", 1069114700bSCezary Rojewski __func__, substream, substream->runtime); 1079114700bSCezary Rojewski dev_dbg(dai->dev, "rate %d chn %d vbd %d bd %d\n", 1089114700bSCezary Rojewski params_rate(be_hw_params), params_channels(be_hw_params), 1099114700bSCezary Rojewski params_width(be_hw_params), params_physical_width(be_hw_params)); 1109114700bSCezary Rojewski 1119114700bSCezary Rojewski path = avs_path_create(adev, dma_id, data->template, fe_hw_params, be_hw_params); 1129114700bSCezary Rojewski if (IS_ERR(path)) { 1139114700bSCezary Rojewski ret = PTR_ERR(path); 1149114700bSCezary Rojewski dev_err(dai->dev, "create path failed: %d\n", ret); 1159114700bSCezary Rojewski return ret; 1169114700bSCezary Rojewski } 1179114700bSCezary Rojewski 1189114700bSCezary Rojewski data->path = path; 1199114700bSCezary Rojewski return 0; 1209114700bSCezary Rojewski } 1219114700bSCezary Rojewski 122b9062f98SCezary Rojewski static int avs_dai_be_hw_params(struct snd_pcm_substream *substream, 123b9062f98SCezary Rojewski struct snd_pcm_hw_params *be_hw_params, struct snd_soc_dai *dai, 124b9062f98SCezary Rojewski int dma_id) 125b9062f98SCezary Rojewski { 126b9062f98SCezary Rojewski struct snd_pcm_hw_params *fe_hw_params = NULL; 127b9062f98SCezary Rojewski struct snd_soc_pcm_runtime *fe, *be; 128b9062f98SCezary Rojewski struct snd_soc_dpcm *dpcm; 129b9062f98SCezary Rojewski 130b9062f98SCezary Rojewski be = asoc_substream_to_rtd(substream); 131b9062f98SCezary Rojewski for_each_dpcm_fe(be, substream->stream, dpcm) { 132b9062f98SCezary Rojewski fe = dpcm->fe; 133b9062f98SCezary Rojewski fe_hw_params = &fe->dpcm[substream->stream].hw_params; 134b9062f98SCezary Rojewski } 135b9062f98SCezary Rojewski 136b9062f98SCezary Rojewski return avs_dai_hw_params(substream, fe_hw_params, be_hw_params, dai, dma_id); 137b9062f98SCezary Rojewski } 138b9062f98SCezary Rojewski 1399114700bSCezary Rojewski static int avs_dai_prepare(struct avs_dev *adev, struct snd_pcm_substream *substream, 1409114700bSCezary Rojewski struct snd_soc_dai *dai) 1419114700bSCezary Rojewski { 1429114700bSCezary Rojewski struct avs_dma_data *data; 1439114700bSCezary Rojewski int ret; 1449114700bSCezary Rojewski 1459114700bSCezary Rojewski data = snd_soc_dai_get_dma_data(dai, substream); 1469114700bSCezary Rojewski if (!data->path) 1479114700bSCezary Rojewski return 0; 1489114700bSCezary Rojewski 1499114700bSCezary Rojewski ret = avs_path_reset(data->path); 1509114700bSCezary Rojewski if (ret < 0) { 1519114700bSCezary Rojewski dev_err(dai->dev, "reset path failed: %d\n", ret); 1529114700bSCezary Rojewski return ret; 1539114700bSCezary Rojewski } 1549114700bSCezary Rojewski 1559114700bSCezary Rojewski ret = avs_path_pause(data->path); 1569114700bSCezary Rojewski if (ret < 0) 1579114700bSCezary Rojewski dev_err(dai->dev, "pause path failed: %d\n", ret); 1589114700bSCezary Rojewski return ret; 1599114700bSCezary Rojewski } 1609114700bSCezary Rojewski 1612b9a50eaSCezary Rojewski static const struct snd_soc_dai_ops avs_dai_nonhda_be_ops; 1622b9a50eaSCezary Rojewski 163b9062f98SCezary Rojewski static int avs_dai_nonhda_be_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) 164b9062f98SCezary Rojewski { 1652b9a50eaSCezary Rojewski return avs_dai_startup(substream, dai, false, &avs_dai_nonhda_be_ops); 166b9062f98SCezary Rojewski } 167b9062f98SCezary Rojewski 168b9062f98SCezary Rojewski static void avs_dai_nonhda_be_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) 169b9062f98SCezary Rojewski { 1708f28299fSCezary Rojewski struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 171730cb320SCezary Rojewski struct avs_dev *adev = to_avs_dev(dai->dev); 172b9062f98SCezary Rojewski struct avs_dma_data *data; 173b9062f98SCezary Rojewski 174730cb320SCezary Rojewski if (rtd->dai_link->ignore_suspend) 175730cb320SCezary Rojewski adev->num_lp_paths--; 176730cb320SCezary Rojewski 177b9062f98SCezary Rojewski data = snd_soc_dai_get_dma_data(dai, substream); 178b9062f98SCezary Rojewski 179b9062f98SCezary Rojewski snd_soc_dai_set_dma_data(dai, substream, NULL); 180b9062f98SCezary Rojewski kfree(data); 181b9062f98SCezary Rojewski } 182b9062f98SCezary Rojewski 183b9062f98SCezary Rojewski static int avs_dai_nonhda_be_hw_params(struct snd_pcm_substream *substream, 184b9062f98SCezary Rojewski struct snd_pcm_hw_params *hw_params, struct snd_soc_dai *dai) 185b9062f98SCezary Rojewski { 186b9062f98SCezary Rojewski struct avs_dma_data *data; 187b9062f98SCezary Rojewski 188b9062f98SCezary Rojewski data = snd_soc_dai_get_dma_data(dai, substream); 189b9062f98SCezary Rojewski if (data->path) 190b9062f98SCezary Rojewski return 0; 191b9062f98SCezary Rojewski 192b9062f98SCezary Rojewski /* Actual port-id comes from topology. */ 193b9062f98SCezary Rojewski return avs_dai_be_hw_params(substream, hw_params, dai, 0); 194b9062f98SCezary Rojewski } 195b9062f98SCezary Rojewski 196b9062f98SCezary Rojewski static int avs_dai_nonhda_be_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) 197b9062f98SCezary Rojewski { 198b9062f98SCezary Rojewski struct avs_dma_data *data; 199b9062f98SCezary Rojewski 200b9062f98SCezary Rojewski dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name); 201b9062f98SCezary Rojewski 202b9062f98SCezary Rojewski data = snd_soc_dai_get_dma_data(dai, substream); 203b9062f98SCezary Rojewski if (data->path) { 204b9062f98SCezary Rojewski avs_path_free(data->path); 205b9062f98SCezary Rojewski data->path = NULL; 206b9062f98SCezary Rojewski } 207b9062f98SCezary Rojewski 208b9062f98SCezary Rojewski return 0; 209b9062f98SCezary Rojewski } 210b9062f98SCezary Rojewski 211b9062f98SCezary Rojewski static int avs_dai_nonhda_be_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) 212b9062f98SCezary Rojewski { 213b9062f98SCezary Rojewski return avs_dai_prepare(to_avs_dev(dai->dev), substream, dai); 214b9062f98SCezary Rojewski } 215b9062f98SCezary Rojewski 216b9062f98SCezary Rojewski static int avs_dai_nonhda_be_trigger(struct snd_pcm_substream *substream, int cmd, 217b9062f98SCezary Rojewski struct snd_soc_dai *dai) 218b9062f98SCezary Rojewski { 2198f28299fSCezary Rojewski struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 220b9062f98SCezary Rojewski struct avs_dma_data *data; 221b9062f98SCezary Rojewski int ret = 0; 222b9062f98SCezary Rojewski 223b9062f98SCezary Rojewski data = snd_soc_dai_get_dma_data(dai, substream); 224b9062f98SCezary Rojewski 225b9062f98SCezary Rojewski switch (cmd) { 2268e097f9aSAmadeusz Sławiński case SNDRV_PCM_TRIGGER_RESUME: 2278e097f9aSAmadeusz Sławiński if (rtd->dai_link->ignore_suspend) 2288e097f9aSAmadeusz Sławiński break; 2298e097f9aSAmadeusz Sławiński fallthrough; 230b9062f98SCezary Rojewski case SNDRV_PCM_TRIGGER_START: 231b9062f98SCezary Rojewski case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 2328e097f9aSAmadeusz Sławiński ret = avs_path_pause(data->path); 2338e097f9aSAmadeusz Sławiński if (ret < 0) { 2348e097f9aSAmadeusz Sławiński dev_err(dai->dev, "pause BE path failed: %d\n", ret); 2358e097f9aSAmadeusz Sławiński break; 2368e097f9aSAmadeusz Sławiński } 2378e097f9aSAmadeusz Sławiński 238b9062f98SCezary Rojewski ret = avs_path_run(data->path, AVS_TPLG_TRIGGER_AUTO); 239b9062f98SCezary Rojewski if (ret < 0) 240b9062f98SCezary Rojewski dev_err(dai->dev, "run BE path failed: %d\n", ret); 241b9062f98SCezary Rojewski break; 242b9062f98SCezary Rojewski 2438e097f9aSAmadeusz Sławiński case SNDRV_PCM_TRIGGER_SUSPEND: 2448e097f9aSAmadeusz Sławiński if (rtd->dai_link->ignore_suspend) 2458e097f9aSAmadeusz Sławiński break; 2468e097f9aSAmadeusz Sławiński fallthrough; 247b9062f98SCezary Rojewski case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 248b9062f98SCezary Rojewski case SNDRV_PCM_TRIGGER_STOP: 249b9062f98SCezary Rojewski ret = avs_path_pause(data->path); 250b9062f98SCezary Rojewski if (ret < 0) 251b9062f98SCezary Rojewski dev_err(dai->dev, "pause BE path failed: %d\n", ret); 252b9062f98SCezary Rojewski 253b9062f98SCezary Rojewski ret = avs_path_reset(data->path); 254b9062f98SCezary Rojewski if (ret < 0) 255b9062f98SCezary Rojewski dev_err(dai->dev, "reset BE path failed: %d\n", ret); 256b9062f98SCezary Rojewski break; 257b9062f98SCezary Rojewski 258b9062f98SCezary Rojewski default: 259b9062f98SCezary Rojewski ret = -EINVAL; 260b9062f98SCezary Rojewski break; 261b9062f98SCezary Rojewski } 262b9062f98SCezary Rojewski 263b9062f98SCezary Rojewski return ret; 264b9062f98SCezary Rojewski } 265b9062f98SCezary Rojewski 266b9062f98SCezary Rojewski static const struct snd_soc_dai_ops avs_dai_nonhda_be_ops = { 267b9062f98SCezary Rojewski .startup = avs_dai_nonhda_be_startup, 268b9062f98SCezary Rojewski .shutdown = avs_dai_nonhda_be_shutdown, 269b9062f98SCezary Rojewski .hw_params = avs_dai_nonhda_be_hw_params, 270b9062f98SCezary Rojewski .hw_free = avs_dai_nonhda_be_hw_free, 271b9062f98SCezary Rojewski .prepare = avs_dai_nonhda_be_prepare, 272b9062f98SCezary Rojewski .trigger = avs_dai_nonhda_be_trigger, 273b9062f98SCezary Rojewski }; 274b9062f98SCezary Rojewski 2752b9a50eaSCezary Rojewski static const struct snd_soc_dai_ops avs_dai_hda_be_ops; 2762b9a50eaSCezary Rojewski 277d070002aSCezary Rojewski static int avs_dai_hda_be_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) 278d070002aSCezary Rojewski { 2792b9a50eaSCezary Rojewski return avs_dai_startup(substream, dai, false, &avs_dai_hda_be_ops); 280d070002aSCezary Rojewski } 281d070002aSCezary Rojewski 282d070002aSCezary Rojewski static void avs_dai_hda_be_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) 283d070002aSCezary Rojewski { 284d070002aSCezary Rojewski return avs_dai_nonhda_be_shutdown(substream, dai); 285d070002aSCezary Rojewski } 286d070002aSCezary Rojewski 287d070002aSCezary Rojewski static int avs_dai_hda_be_hw_params(struct snd_pcm_substream *substream, 288d070002aSCezary Rojewski struct snd_pcm_hw_params *hw_params, struct snd_soc_dai *dai) 289d070002aSCezary Rojewski { 290d070002aSCezary Rojewski struct avs_dma_data *data; 291d070002aSCezary Rojewski struct hdac_ext_stream *link_stream; 292d070002aSCezary Rojewski 293d070002aSCezary Rojewski data = snd_soc_dai_get_dma_data(dai, substream); 294d070002aSCezary Rojewski if (data->path) 295d070002aSCezary Rojewski return 0; 296d070002aSCezary Rojewski 297d070002aSCezary Rojewski link_stream = substream->runtime->private_data; 298d070002aSCezary Rojewski 299d070002aSCezary Rojewski return avs_dai_be_hw_params(substream, hw_params, dai, 300d070002aSCezary Rojewski hdac_stream(link_stream)->stream_tag - 1); 301d070002aSCezary Rojewski } 302d070002aSCezary Rojewski 303d070002aSCezary Rojewski static int avs_dai_hda_be_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) 304d070002aSCezary Rojewski { 305d070002aSCezary Rojewski struct avs_dma_data *data; 3068f28299fSCezary Rojewski struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 307d070002aSCezary Rojewski struct hdac_ext_stream *link_stream; 308d070002aSCezary Rojewski struct hdac_ext_link *link; 309d070002aSCezary Rojewski struct hda_codec *codec; 310d070002aSCezary Rojewski 311d070002aSCezary Rojewski dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name); 312d070002aSCezary Rojewski 313d070002aSCezary Rojewski data = snd_soc_dai_get_dma_data(dai, substream); 314d070002aSCezary Rojewski if (!data->path) 315d070002aSCezary Rojewski return 0; 316d070002aSCezary Rojewski 317d070002aSCezary Rojewski link_stream = substream->runtime->private_data; 318d070002aSCezary Rojewski link_stream->link_prepared = false; 319d070002aSCezary Rojewski avs_path_free(data->path); 320d070002aSCezary Rojewski data->path = NULL; 321d070002aSCezary Rojewski 322d070002aSCezary Rojewski /* clear link <-> stream mapping */ 323d070002aSCezary Rojewski codec = dev_to_hda_codec(asoc_rtd_to_codec(rtd, 0)->dev); 324b0cd60f3SPierre-Louis Bossart link = snd_hdac_ext_bus_get_hlink_by_addr(&codec->bus->core, codec->core.addr); 325d070002aSCezary Rojewski if (!link) 326d070002aSCezary Rojewski return -EINVAL; 327d070002aSCezary Rojewski 328d070002aSCezary Rojewski if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 3297fa403f2SPierre-Louis Bossart snd_hdac_ext_bus_link_clear_stream_id(link, hdac_stream(link_stream)->stream_tag); 330d070002aSCezary Rojewski 331d070002aSCezary Rojewski return 0; 332d070002aSCezary Rojewski } 333d070002aSCezary Rojewski 334d070002aSCezary Rojewski static int avs_dai_hda_be_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) 335d070002aSCezary Rojewski { 3368f28299fSCezary Rojewski struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 337d070002aSCezary Rojewski struct snd_pcm_runtime *runtime = substream->runtime; 338d070002aSCezary Rojewski struct hdac_ext_stream *link_stream = runtime->private_data; 339d070002aSCezary Rojewski struct hdac_ext_link *link; 340d070002aSCezary Rojewski struct hda_codec *codec; 341d070002aSCezary Rojewski struct hdac_bus *bus; 342d070002aSCezary Rojewski unsigned int format_val; 343d070002aSCezary Rojewski int ret; 344d070002aSCezary Rojewski 345d070002aSCezary Rojewski if (link_stream->link_prepared) 346d070002aSCezary Rojewski return 0; 347d070002aSCezary Rojewski 348d070002aSCezary Rojewski codec = dev_to_hda_codec(asoc_rtd_to_codec(rtd, 0)->dev); 349d070002aSCezary Rojewski bus = &codec->bus->core; 350d070002aSCezary Rojewski format_val = snd_hdac_calc_stream_format(runtime->rate, runtime->channels, runtime->format, 351d070002aSCezary Rojewski runtime->sample_bits, 0); 352d070002aSCezary Rojewski 353d070002aSCezary Rojewski snd_hdac_ext_stream_decouple(bus, link_stream, true); 35400b6cd95SPierre-Louis Bossart snd_hdac_ext_stream_reset(link_stream); 35500b6cd95SPierre-Louis Bossart snd_hdac_ext_stream_setup(link_stream, format_val); 356d070002aSCezary Rojewski 357b0cd60f3SPierre-Louis Bossart link = snd_hdac_ext_bus_get_hlink_by_addr(bus, codec->core.addr); 358d070002aSCezary Rojewski if (!link) 359d070002aSCezary Rojewski return -EINVAL; 360d070002aSCezary Rojewski 361d070002aSCezary Rojewski if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 3627fa403f2SPierre-Louis Bossart snd_hdac_ext_bus_link_set_stream_id(link, hdac_stream(link_stream)->stream_tag); 363d070002aSCezary Rojewski 364d070002aSCezary Rojewski ret = avs_dai_prepare(to_avs_dev(dai->dev), substream, dai); 365d070002aSCezary Rojewski if (ret) 366d070002aSCezary Rojewski return ret; 367d070002aSCezary Rojewski 368d070002aSCezary Rojewski link_stream->link_prepared = true; 369d070002aSCezary Rojewski return 0; 370d070002aSCezary Rojewski } 371d070002aSCezary Rojewski 372d070002aSCezary Rojewski static int avs_dai_hda_be_trigger(struct snd_pcm_substream *substream, int cmd, 373d070002aSCezary Rojewski struct snd_soc_dai *dai) 374d070002aSCezary Rojewski { 3758f28299fSCezary Rojewski struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 376d070002aSCezary Rojewski struct hdac_ext_stream *link_stream; 377d070002aSCezary Rojewski struct avs_dma_data *data; 378d070002aSCezary Rojewski int ret = 0; 379d070002aSCezary Rojewski 380d070002aSCezary Rojewski dev_dbg(dai->dev, "entry %s cmd=%d\n", __func__, cmd); 381d070002aSCezary Rojewski 382d070002aSCezary Rojewski data = snd_soc_dai_get_dma_data(dai, substream); 383d070002aSCezary Rojewski link_stream = substream->runtime->private_data; 384d070002aSCezary Rojewski 385d070002aSCezary Rojewski switch (cmd) { 3868e097f9aSAmadeusz Sławiński case SNDRV_PCM_TRIGGER_RESUME: 3878e097f9aSAmadeusz Sławiński if (rtd->dai_link->ignore_suspend) 3888e097f9aSAmadeusz Sławiński break; 3898e097f9aSAmadeusz Sławiński fallthrough; 390d070002aSCezary Rojewski case SNDRV_PCM_TRIGGER_START: 391d070002aSCezary Rojewski case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 39200b6cd95SPierre-Louis Bossart snd_hdac_ext_stream_start(link_stream); 393d070002aSCezary Rojewski 3948e097f9aSAmadeusz Sławiński ret = avs_path_pause(data->path); 3958e097f9aSAmadeusz Sławiński if (ret < 0) { 3968e097f9aSAmadeusz Sławiński dev_err(dai->dev, "pause BE path failed: %d\n", ret); 3978e097f9aSAmadeusz Sławiński break; 3988e097f9aSAmadeusz Sławiński } 3998e097f9aSAmadeusz Sławiński 400d070002aSCezary Rojewski ret = avs_path_run(data->path, AVS_TPLG_TRIGGER_AUTO); 401d070002aSCezary Rojewski if (ret < 0) 402d070002aSCezary Rojewski dev_err(dai->dev, "run BE path failed: %d\n", ret); 403d070002aSCezary Rojewski break; 404d070002aSCezary Rojewski 4058e097f9aSAmadeusz Sławiński case SNDRV_PCM_TRIGGER_SUSPEND: 4068e097f9aSAmadeusz Sławiński if (rtd->dai_link->ignore_suspend) 4078e097f9aSAmadeusz Sławiński break; 4088e097f9aSAmadeusz Sławiński fallthrough; 409d070002aSCezary Rojewski case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 410d070002aSCezary Rojewski case SNDRV_PCM_TRIGGER_STOP: 411d070002aSCezary Rojewski ret = avs_path_pause(data->path); 412d070002aSCezary Rojewski if (ret < 0) 413d070002aSCezary Rojewski dev_err(dai->dev, "pause BE path failed: %d\n", ret); 414d070002aSCezary Rojewski 41500b6cd95SPierre-Louis Bossart snd_hdac_ext_stream_clear(link_stream); 416d070002aSCezary Rojewski 417d070002aSCezary Rojewski ret = avs_path_reset(data->path); 418d070002aSCezary Rojewski if (ret < 0) 419d070002aSCezary Rojewski dev_err(dai->dev, "reset BE path failed: %d\n", ret); 420d070002aSCezary Rojewski break; 421d070002aSCezary Rojewski 422d070002aSCezary Rojewski default: 423d070002aSCezary Rojewski ret = -EINVAL; 424d070002aSCezary Rojewski break; 425d070002aSCezary Rojewski } 426d070002aSCezary Rojewski 427d070002aSCezary Rojewski return ret; 428d070002aSCezary Rojewski } 429d070002aSCezary Rojewski 430d070002aSCezary Rojewski static const struct snd_soc_dai_ops avs_dai_hda_be_ops = { 431d070002aSCezary Rojewski .startup = avs_dai_hda_be_startup, 432d070002aSCezary Rojewski .shutdown = avs_dai_hda_be_shutdown, 433d070002aSCezary Rojewski .hw_params = avs_dai_hda_be_hw_params, 434d070002aSCezary Rojewski .hw_free = avs_dai_hda_be_hw_free, 435d070002aSCezary Rojewski .prepare = avs_dai_hda_be_prepare, 436d070002aSCezary Rojewski .trigger = avs_dai_hda_be_trigger, 437d070002aSCezary Rojewski }; 438d070002aSCezary Rojewski 4399114700bSCezary Rojewski static const unsigned int rates[] = { 4409114700bSCezary Rojewski 8000, 11025, 12000, 16000, 4419114700bSCezary Rojewski 22050, 24000, 32000, 44100, 4429114700bSCezary Rojewski 48000, 64000, 88200, 96000, 4439114700bSCezary Rojewski 128000, 176400, 192000, 4449114700bSCezary Rojewski }; 4459114700bSCezary Rojewski 4469114700bSCezary Rojewski static const struct snd_pcm_hw_constraint_list hw_rates = { 4479114700bSCezary Rojewski .count = ARRAY_SIZE(rates), 4489114700bSCezary Rojewski .list = rates, 4499114700bSCezary Rojewski .mask = 0, 4509114700bSCezary Rojewski }; 4519114700bSCezary Rojewski 4522b9a50eaSCezary Rojewski const struct snd_soc_dai_ops avs_dai_fe_ops; 4532b9a50eaSCezary Rojewski 4549114700bSCezary Rojewski static int avs_dai_fe_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) 4559114700bSCezary Rojewski { 4569114700bSCezary Rojewski struct snd_pcm_runtime *runtime = substream->runtime; 4579114700bSCezary Rojewski struct avs_dma_data *data; 4589114700bSCezary Rojewski struct avs_dev *adev = to_avs_dev(dai->dev); 4599114700bSCezary Rojewski struct hdac_bus *bus = &adev->base.core; 4609114700bSCezary Rojewski struct hdac_ext_stream *host_stream; 4619114700bSCezary Rojewski int ret; 4629114700bSCezary Rojewski 4632b9a50eaSCezary Rojewski ret = avs_dai_startup(substream, dai, true, &avs_dai_fe_ops); 4649114700bSCezary Rojewski if (ret) 4659114700bSCezary Rojewski return ret; 4669114700bSCezary Rojewski 4679114700bSCezary Rojewski data = snd_soc_dai_get_dma_data(dai, substream); 4689114700bSCezary Rojewski 4699114700bSCezary Rojewski host_stream = snd_hdac_ext_stream_assign(bus, substream, HDAC_EXT_STREAM_TYPE_HOST); 4709114700bSCezary Rojewski if (!host_stream) { 4719114700bSCezary Rojewski kfree(data); 4729114700bSCezary Rojewski return -EBUSY; 4739114700bSCezary Rojewski } 4749114700bSCezary Rojewski 4759114700bSCezary Rojewski data->host_stream = host_stream; 4769114700bSCezary Rojewski snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); 4779114700bSCezary Rojewski /* avoid wrap-around with wall-clock */ 4789114700bSCezary Rojewski snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_TIME, 20, 178000000); 4799114700bSCezary Rojewski snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_rates); 4809114700bSCezary Rojewski snd_pcm_set_sync(substream); 4819114700bSCezary Rojewski 4829114700bSCezary Rojewski dev_dbg(dai->dev, "%s fe STARTUP tag %d str %p", 4839114700bSCezary Rojewski __func__, hdac_stream(host_stream)->stream_tag, substream); 4849114700bSCezary Rojewski 4859114700bSCezary Rojewski return 0; 4869114700bSCezary Rojewski } 4879114700bSCezary Rojewski 4889114700bSCezary Rojewski static void avs_dai_fe_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) 4899114700bSCezary Rojewski { 4908f28299fSCezary Rojewski struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 491730cb320SCezary Rojewski struct avs_dev *adev = to_avs_dev(dai->dev); 4929114700bSCezary Rojewski struct avs_dma_data *data; 4939114700bSCezary Rojewski 494730cb320SCezary Rojewski if (rtd->dai_link->ignore_suspend) 495730cb320SCezary Rojewski adev->num_lp_paths--; 496730cb320SCezary Rojewski 4979114700bSCezary Rojewski data = snd_soc_dai_get_dma_data(dai, substream); 4989114700bSCezary Rojewski 4999114700bSCezary Rojewski snd_soc_dai_set_dma_data(dai, substream, NULL); 5009114700bSCezary Rojewski snd_hdac_ext_stream_release(data->host_stream, HDAC_EXT_STREAM_TYPE_HOST); 5019114700bSCezary Rojewski kfree(data); 5029114700bSCezary Rojewski } 5039114700bSCezary Rojewski 5049114700bSCezary Rojewski static int avs_dai_fe_hw_params(struct snd_pcm_substream *substream, 5059114700bSCezary Rojewski struct snd_pcm_hw_params *hw_params, struct snd_soc_dai *dai) 5069114700bSCezary Rojewski { 5079114700bSCezary Rojewski struct snd_pcm_hw_params *be_hw_params = NULL; 5089114700bSCezary Rojewski struct snd_soc_pcm_runtime *fe, *be; 5099114700bSCezary Rojewski struct snd_soc_dpcm *dpcm; 5109114700bSCezary Rojewski struct avs_dma_data *data; 5119114700bSCezary Rojewski struct hdac_ext_stream *host_stream; 5129114700bSCezary Rojewski int ret; 5139114700bSCezary Rojewski 5149114700bSCezary Rojewski data = snd_soc_dai_get_dma_data(dai, substream); 5159114700bSCezary Rojewski if (data->path) 5169114700bSCezary Rojewski return 0; 5179114700bSCezary Rojewski 5189114700bSCezary Rojewski host_stream = data->host_stream; 5199114700bSCezary Rojewski 5209114700bSCezary Rojewski hdac_stream(host_stream)->bufsize = 0; 5219114700bSCezary Rojewski hdac_stream(host_stream)->period_bytes = 0; 5229114700bSCezary Rojewski hdac_stream(host_stream)->format_val = 0; 5239114700bSCezary Rojewski 5249114700bSCezary Rojewski fe = asoc_substream_to_rtd(substream); 5259114700bSCezary Rojewski for_each_dpcm_be(fe, substream->stream, dpcm) { 5269114700bSCezary Rojewski be = dpcm->be; 5279114700bSCezary Rojewski be_hw_params = &be->dpcm[substream->stream].hw_params; 5289114700bSCezary Rojewski } 5299114700bSCezary Rojewski 5309114700bSCezary Rojewski ret = avs_dai_hw_params(substream, hw_params, be_hw_params, dai, 5319114700bSCezary Rojewski hdac_stream(host_stream)->stream_tag - 1); 5329114700bSCezary Rojewski if (ret) 5339114700bSCezary Rojewski goto create_err; 5349114700bSCezary Rojewski 5359114700bSCezary Rojewski ret = avs_path_bind(data->path); 5369114700bSCezary Rojewski if (ret < 0) { 5379114700bSCezary Rojewski dev_err(dai->dev, "bind FE <-> BE failed: %d\n", ret); 5389114700bSCezary Rojewski goto bind_err; 5399114700bSCezary Rojewski } 5409114700bSCezary Rojewski 5419114700bSCezary Rojewski return 0; 5429114700bSCezary Rojewski 5439114700bSCezary Rojewski bind_err: 5449114700bSCezary Rojewski avs_path_free(data->path); 5459114700bSCezary Rojewski data->path = NULL; 5469114700bSCezary Rojewski create_err: 5479114700bSCezary Rojewski snd_pcm_lib_free_pages(substream); 5489114700bSCezary Rojewski return ret; 5499114700bSCezary Rojewski } 5509114700bSCezary Rojewski 5510abfc84bSCezary Rojewski static int __avs_dai_fe_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) 5529114700bSCezary Rojewski { 5539114700bSCezary Rojewski struct avs_dma_data *data; 5549114700bSCezary Rojewski struct hdac_ext_stream *host_stream; 5559114700bSCezary Rojewski int ret; 5569114700bSCezary Rojewski 5579114700bSCezary Rojewski dev_dbg(dai->dev, "%s fe HW_FREE str %p rtd %p", 5589114700bSCezary Rojewski __func__, substream, substream->runtime); 5599114700bSCezary Rojewski 5609114700bSCezary Rojewski data = snd_soc_dai_get_dma_data(dai, substream); 5619114700bSCezary Rojewski if (!data->path) 5629114700bSCezary Rojewski return 0; 5639114700bSCezary Rojewski 5649114700bSCezary Rojewski host_stream = data->host_stream; 5659114700bSCezary Rojewski 5669114700bSCezary Rojewski ret = avs_path_unbind(data->path); 5679114700bSCezary Rojewski if (ret < 0) 5689114700bSCezary Rojewski dev_err(dai->dev, "unbind FE <-> BE failed: %d\n", ret); 5699114700bSCezary Rojewski 5709114700bSCezary Rojewski avs_path_free(data->path); 5719114700bSCezary Rojewski data->path = NULL; 5729114700bSCezary Rojewski snd_hdac_stream_cleanup(hdac_stream(host_stream)); 5739114700bSCezary Rojewski hdac_stream(host_stream)->prepared = false; 5749114700bSCezary Rojewski 5750abfc84bSCezary Rojewski return ret; 5760abfc84bSCezary Rojewski } 5770abfc84bSCezary Rojewski 5780abfc84bSCezary Rojewski static int avs_dai_fe_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) 5790abfc84bSCezary Rojewski { 5800abfc84bSCezary Rojewski int ret; 5810abfc84bSCezary Rojewski 5820abfc84bSCezary Rojewski ret = __avs_dai_fe_hw_free(substream, dai); 5830abfc84bSCezary Rojewski snd_pcm_lib_free_pages(substream); 5849114700bSCezary Rojewski 5859114700bSCezary Rojewski return ret; 5869114700bSCezary Rojewski } 5879114700bSCezary Rojewski 5889114700bSCezary Rojewski static int avs_dai_fe_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) 5899114700bSCezary Rojewski { 5909114700bSCezary Rojewski struct snd_pcm_runtime *runtime = substream->runtime; 5919114700bSCezary Rojewski struct avs_dma_data *data; 5929114700bSCezary Rojewski struct avs_dev *adev = to_avs_dev(dai->dev); 5939114700bSCezary Rojewski struct hdac_ext_stream *host_stream; 5949114700bSCezary Rojewski struct hdac_bus *bus; 5959114700bSCezary Rojewski unsigned int format_val; 5969114700bSCezary Rojewski int ret; 5979114700bSCezary Rojewski 5989114700bSCezary Rojewski data = snd_soc_dai_get_dma_data(dai, substream); 5999114700bSCezary Rojewski host_stream = data->host_stream; 6009114700bSCezary Rojewski 6019114700bSCezary Rojewski if (hdac_stream(host_stream)->prepared) 6029114700bSCezary Rojewski return 0; 6039114700bSCezary Rojewski 6049114700bSCezary Rojewski bus = hdac_stream(host_stream)->bus; 6059114700bSCezary Rojewski snd_hdac_ext_stream_decouple(bus, data->host_stream, true); 6069114700bSCezary Rojewski snd_hdac_stream_reset(hdac_stream(host_stream)); 6079114700bSCezary Rojewski 6089114700bSCezary Rojewski format_val = snd_hdac_calc_stream_format(runtime->rate, runtime->channels, runtime->format, 6099114700bSCezary Rojewski runtime->sample_bits, 0); 6109114700bSCezary Rojewski 6119114700bSCezary Rojewski ret = snd_hdac_stream_set_params(hdac_stream(host_stream), format_val); 6129114700bSCezary Rojewski if (ret < 0) 6139114700bSCezary Rojewski return ret; 6149114700bSCezary Rojewski 6159114700bSCezary Rojewski ret = snd_hdac_stream_setup(hdac_stream(host_stream)); 6169114700bSCezary Rojewski if (ret < 0) 6179114700bSCezary Rojewski return ret; 6189114700bSCezary Rojewski 6199114700bSCezary Rojewski ret = avs_dai_prepare(adev, substream, dai); 6209114700bSCezary Rojewski if (ret) 6219114700bSCezary Rojewski return ret; 6229114700bSCezary Rojewski 6239114700bSCezary Rojewski hdac_stream(host_stream)->prepared = true; 6249114700bSCezary Rojewski return 0; 6259114700bSCezary Rojewski } 6269114700bSCezary Rojewski 6279114700bSCezary Rojewski static int avs_dai_fe_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai) 6289114700bSCezary Rojewski { 6298f28299fSCezary Rojewski struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 6309114700bSCezary Rojewski struct avs_dma_data *data; 6319114700bSCezary Rojewski struct hdac_ext_stream *host_stream; 6329114700bSCezary Rojewski struct hdac_bus *bus; 6339114700bSCezary Rojewski unsigned long flags; 6349114700bSCezary Rojewski int ret = 0; 6359114700bSCezary Rojewski 6369114700bSCezary Rojewski data = snd_soc_dai_get_dma_data(dai, substream); 6379114700bSCezary Rojewski host_stream = data->host_stream; 6389114700bSCezary Rojewski bus = hdac_stream(host_stream)->bus; 6399114700bSCezary Rojewski 6409114700bSCezary Rojewski switch (cmd) { 6418e097f9aSAmadeusz Sławiński case SNDRV_PCM_TRIGGER_RESUME: 6428e097f9aSAmadeusz Sławiński if (rtd->dai_link->ignore_suspend) 6438e097f9aSAmadeusz Sławiński break; 6448e097f9aSAmadeusz Sławiński fallthrough; 6459114700bSCezary Rojewski case SNDRV_PCM_TRIGGER_START: 6469114700bSCezary Rojewski case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 6479114700bSCezary Rojewski spin_lock_irqsave(&bus->reg_lock, flags); 6489114700bSCezary Rojewski snd_hdac_stream_start(hdac_stream(host_stream), true); 6499114700bSCezary Rojewski spin_unlock_irqrestore(&bus->reg_lock, flags); 6509114700bSCezary Rojewski 6518e097f9aSAmadeusz Sławiński /* Timeout on DRSM poll shall not stop the resume so ignore the result. */ 6528e097f9aSAmadeusz Sławiński if (cmd == SNDRV_PCM_TRIGGER_RESUME) 6538e097f9aSAmadeusz Sławiński snd_hdac_stream_wait_drsm(hdac_stream(host_stream)); 6548e097f9aSAmadeusz Sławiński 6558e097f9aSAmadeusz Sławiński ret = avs_path_pause(data->path); 6568e097f9aSAmadeusz Sławiński if (ret < 0) { 6578e097f9aSAmadeusz Sławiński dev_err(dai->dev, "pause FE path failed: %d\n", ret); 6588e097f9aSAmadeusz Sławiński break; 6598e097f9aSAmadeusz Sławiński } 6608e097f9aSAmadeusz Sławiński 6619114700bSCezary Rojewski ret = avs_path_run(data->path, AVS_TPLG_TRIGGER_AUTO); 6629114700bSCezary Rojewski if (ret < 0) 6639114700bSCezary Rojewski dev_err(dai->dev, "run FE path failed: %d\n", ret); 6648e097f9aSAmadeusz Sławiński 6659114700bSCezary Rojewski break; 6669114700bSCezary Rojewski 6678e097f9aSAmadeusz Sławiński case SNDRV_PCM_TRIGGER_SUSPEND: 6688e097f9aSAmadeusz Sławiński if (rtd->dai_link->ignore_suspend) 6698e097f9aSAmadeusz Sławiński break; 6708e097f9aSAmadeusz Sławiński fallthrough; 6719114700bSCezary Rojewski case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 6729114700bSCezary Rojewski case SNDRV_PCM_TRIGGER_STOP: 6739114700bSCezary Rojewski ret = avs_path_pause(data->path); 6749114700bSCezary Rojewski if (ret < 0) 6759114700bSCezary Rojewski dev_err(dai->dev, "pause FE path failed: %d\n", ret); 6769114700bSCezary Rojewski 6779114700bSCezary Rojewski spin_lock_irqsave(&bus->reg_lock, flags); 6789114700bSCezary Rojewski snd_hdac_stream_stop(hdac_stream(host_stream)); 6799114700bSCezary Rojewski spin_unlock_irqrestore(&bus->reg_lock, flags); 6809114700bSCezary Rojewski 6819114700bSCezary Rojewski ret = avs_path_reset(data->path); 6829114700bSCezary Rojewski if (ret < 0) 6839114700bSCezary Rojewski dev_err(dai->dev, "reset FE path failed: %d\n", ret); 6849114700bSCezary Rojewski break; 6859114700bSCezary Rojewski 6869114700bSCezary Rojewski default: 6879114700bSCezary Rojewski ret = -EINVAL; 6889114700bSCezary Rojewski break; 6899114700bSCezary Rojewski } 6909114700bSCezary Rojewski 6919114700bSCezary Rojewski return ret; 6929114700bSCezary Rojewski } 6939114700bSCezary Rojewski 6949114700bSCezary Rojewski const struct snd_soc_dai_ops avs_dai_fe_ops = { 6959114700bSCezary Rojewski .startup = avs_dai_fe_startup, 6969114700bSCezary Rojewski .shutdown = avs_dai_fe_shutdown, 6979114700bSCezary Rojewski .hw_params = avs_dai_fe_hw_params, 6989114700bSCezary Rojewski .hw_free = avs_dai_fe_hw_free, 6999114700bSCezary Rojewski .prepare = avs_dai_fe_prepare, 7009114700bSCezary Rojewski .trigger = avs_dai_fe_trigger, 7019114700bSCezary Rojewski }; 7029114700bSCezary Rojewski 703f1b3b320SCezary Rojewski static ssize_t topology_name_read(struct file *file, char __user *user_buf, size_t count, 704f1b3b320SCezary Rojewski loff_t *ppos) 705f1b3b320SCezary Rojewski { 706f1b3b320SCezary Rojewski struct snd_soc_component *component = file->private_data; 707f1b3b320SCezary Rojewski struct snd_soc_card *card = component->card; 708f1b3b320SCezary Rojewski struct snd_soc_acpi_mach *mach = dev_get_platdata(card->dev); 709f1b3b320SCezary Rojewski char buf[64]; 710f1b3b320SCezary Rojewski size_t len; 711f1b3b320SCezary Rojewski 712ca3b7b9dSTakashi Iwai len = scnprintf(buf, sizeof(buf), "%s/%s\n", component->driver->topology_name_prefix, 713f1b3b320SCezary Rojewski mach->tplg_filename); 714f1b3b320SCezary Rojewski 715f1b3b320SCezary Rojewski return simple_read_from_buffer(user_buf, count, ppos, buf, len); 716f1b3b320SCezary Rojewski } 717f1b3b320SCezary Rojewski 718f1b3b320SCezary Rojewski static const struct file_operations topology_name_fops = { 719f1b3b320SCezary Rojewski .open = simple_open, 720f1b3b320SCezary Rojewski .read = topology_name_read, 721f1b3b320SCezary Rojewski .llseek = default_llseek, 722f1b3b320SCezary Rojewski }; 723f1b3b320SCezary Rojewski 724f1b3b320SCezary Rojewski static int avs_component_load_libraries(struct avs_soc_component *acomp) 725f1b3b320SCezary Rojewski { 726f1b3b320SCezary Rojewski struct avs_tplg *tplg = acomp->tplg; 727f1b3b320SCezary Rojewski struct avs_dev *adev = to_avs_dev(acomp->base.dev); 728f1b3b320SCezary Rojewski int ret; 729f1b3b320SCezary Rojewski 730f1b3b320SCezary Rojewski if (!tplg->num_libs) 731f1b3b320SCezary Rojewski return 0; 732f1b3b320SCezary Rojewski 733f1b3b320SCezary Rojewski /* Parent device may be asleep and library loading involves IPCs. */ 734f1b3b320SCezary Rojewski ret = pm_runtime_resume_and_get(adev->dev); 735f1b3b320SCezary Rojewski if (ret < 0) 736f1b3b320SCezary Rojewski return ret; 737f1b3b320SCezary Rojewski 738758ba92fSCezary Rojewski avs_hda_power_gating_enable(adev, false); 739f1b3b320SCezary Rojewski avs_hda_clock_gating_enable(adev, false); 740f1b3b320SCezary Rojewski avs_hda_l1sen_enable(adev, false); 741f1b3b320SCezary Rojewski 742f1b3b320SCezary Rojewski ret = avs_dsp_load_libraries(adev, tplg->libs, tplg->num_libs); 743f1b3b320SCezary Rojewski 744f1b3b320SCezary Rojewski avs_hda_l1sen_enable(adev, true); 745f1b3b320SCezary Rojewski avs_hda_clock_gating_enable(adev, true); 746758ba92fSCezary Rojewski avs_hda_power_gating_enable(adev, true); 747f1b3b320SCezary Rojewski 748f1b3b320SCezary Rojewski if (!ret) 749f1b3b320SCezary Rojewski ret = avs_module_info_init(adev, false); 750f1b3b320SCezary Rojewski 751f1b3b320SCezary Rojewski pm_runtime_mark_last_busy(adev->dev); 752f1b3b320SCezary Rojewski pm_runtime_put_autosuspend(adev->dev); 753f1b3b320SCezary Rojewski 754f1b3b320SCezary Rojewski return ret; 755f1b3b320SCezary Rojewski } 756f1b3b320SCezary Rojewski 757f1b3b320SCezary Rojewski static int avs_component_probe(struct snd_soc_component *component) 758f1b3b320SCezary Rojewski { 759f1b3b320SCezary Rojewski struct snd_soc_card *card = component->card; 760f1b3b320SCezary Rojewski struct snd_soc_acpi_mach *mach; 761f1b3b320SCezary Rojewski struct avs_soc_component *acomp; 762f1b3b320SCezary Rojewski struct avs_dev *adev; 763f1b3b320SCezary Rojewski char *filename; 764f1b3b320SCezary Rojewski int ret; 765f1b3b320SCezary Rojewski 766f1b3b320SCezary Rojewski dev_dbg(card->dev, "probing %s card %s\n", component->name, card->name); 767f1b3b320SCezary Rojewski mach = dev_get_platdata(card->dev); 768f1b3b320SCezary Rojewski acomp = to_avs_soc_component(component); 769f1b3b320SCezary Rojewski adev = to_avs_dev(component->dev); 770f1b3b320SCezary Rojewski 771f1b3b320SCezary Rojewski acomp->tplg = avs_tplg_new(component); 772f1b3b320SCezary Rojewski if (!acomp->tplg) 773f1b3b320SCezary Rojewski return -ENOMEM; 774f1b3b320SCezary Rojewski 775f1b3b320SCezary Rojewski if (!mach->tplg_filename) 776f1b3b320SCezary Rojewski goto finalize; 777f1b3b320SCezary Rojewski 778f1b3b320SCezary Rojewski /* Load specified topology and create debugfs for it. */ 779f1b3b320SCezary Rojewski filename = kasprintf(GFP_KERNEL, "%s/%s", component->driver->topology_name_prefix, 780f1b3b320SCezary Rojewski mach->tplg_filename); 781f1b3b320SCezary Rojewski if (!filename) 782f1b3b320SCezary Rojewski return -ENOMEM; 783f1b3b320SCezary Rojewski 784f1b3b320SCezary Rojewski ret = avs_load_topology(component, filename); 785f1b3b320SCezary Rojewski kfree(filename); 786f1b3b320SCezary Rojewski if (ret < 0) 787f1b3b320SCezary Rojewski return ret; 788f1b3b320SCezary Rojewski 789f1b3b320SCezary Rojewski ret = avs_component_load_libraries(acomp); 790f1b3b320SCezary Rojewski if (ret < 0) { 791f1b3b320SCezary Rojewski dev_err(card->dev, "libraries loading failed: %d\n", ret); 792f1b3b320SCezary Rojewski goto err_load_libs; 793f1b3b320SCezary Rojewski } 794f1b3b320SCezary Rojewski 795f1b3b320SCezary Rojewski finalize: 796f1b3b320SCezary Rojewski debugfs_create_file("topology_name", 0444, component->debugfs_root, component, 797f1b3b320SCezary Rojewski &topology_name_fops); 798f1b3b320SCezary Rojewski 799f1b3b320SCezary Rojewski mutex_lock(&adev->comp_list_mutex); 800f1b3b320SCezary Rojewski list_add_tail(&acomp->node, &adev->comp_list); 801f1b3b320SCezary Rojewski mutex_unlock(&adev->comp_list_mutex); 802f1b3b320SCezary Rojewski 803f1b3b320SCezary Rojewski return 0; 804f1b3b320SCezary Rojewski 805f1b3b320SCezary Rojewski err_load_libs: 806f1b3b320SCezary Rojewski avs_remove_topology(component); 807f1b3b320SCezary Rojewski return ret; 808f1b3b320SCezary Rojewski } 809f1b3b320SCezary Rojewski 810f1b3b320SCezary Rojewski static void avs_component_remove(struct snd_soc_component *component) 811f1b3b320SCezary Rojewski { 812f1b3b320SCezary Rojewski struct avs_soc_component *acomp = to_avs_soc_component(component); 813f1b3b320SCezary Rojewski struct snd_soc_acpi_mach *mach; 814f1b3b320SCezary Rojewski struct avs_dev *adev = to_avs_dev(component->dev); 815f1b3b320SCezary Rojewski int ret; 816f1b3b320SCezary Rojewski 817f1b3b320SCezary Rojewski mach = dev_get_platdata(component->card->dev); 818f1b3b320SCezary Rojewski 819f1b3b320SCezary Rojewski mutex_lock(&adev->comp_list_mutex); 820f1b3b320SCezary Rojewski list_del(&acomp->node); 821f1b3b320SCezary Rojewski mutex_unlock(&adev->comp_list_mutex); 822f1b3b320SCezary Rojewski 823f1b3b320SCezary Rojewski if (mach->tplg_filename) { 824f1b3b320SCezary Rojewski ret = avs_remove_topology(component); 825f1b3b320SCezary Rojewski if (ret < 0) 826f1b3b320SCezary Rojewski dev_err(component->dev, "unload topology failed: %d\n", ret); 827f1b3b320SCezary Rojewski } 828f1b3b320SCezary Rojewski } 829f1b3b320SCezary Rojewski 8302b9a50eaSCezary Rojewski static int avs_dai_resume_hw_params(struct snd_soc_dai *dai, struct avs_dma_data *data) 8312b9a50eaSCezary Rojewski { 8322b9a50eaSCezary Rojewski struct snd_pcm_substream *substream; 8332b9a50eaSCezary Rojewski struct snd_soc_pcm_runtime *rtd; 8342b9a50eaSCezary Rojewski int ret; 8352b9a50eaSCezary Rojewski 8362b9a50eaSCezary Rojewski substream = data->substream; 8378f28299fSCezary Rojewski rtd = asoc_substream_to_rtd(substream); 8382b9a50eaSCezary Rojewski 8392b9a50eaSCezary Rojewski ret = dai->driver->ops->hw_params(substream, &rtd->dpcm[substream->stream].hw_params, dai); 8402b9a50eaSCezary Rojewski if (ret) 8412b9a50eaSCezary Rojewski dev_err(dai->dev, "hw_params on resume failed: %d\n", ret); 8422b9a50eaSCezary Rojewski 8432b9a50eaSCezary Rojewski return ret; 8442b9a50eaSCezary Rojewski } 8452b9a50eaSCezary Rojewski 8462b9a50eaSCezary Rojewski static int avs_dai_resume_fe_prepare(struct snd_soc_dai *dai, struct avs_dma_data *data) 8472b9a50eaSCezary Rojewski { 8482b9a50eaSCezary Rojewski struct hdac_ext_stream *host_stream; 8492b9a50eaSCezary Rojewski struct hdac_stream *hstream; 8502b9a50eaSCezary Rojewski struct hdac_bus *bus; 8512b9a50eaSCezary Rojewski int ret; 8522b9a50eaSCezary Rojewski 8532b9a50eaSCezary Rojewski host_stream = data->host_stream; 8542b9a50eaSCezary Rojewski hstream = hdac_stream(host_stream); 8552b9a50eaSCezary Rojewski bus = hdac_stream(host_stream)->bus; 8562b9a50eaSCezary Rojewski 8572b9a50eaSCezary Rojewski /* Set DRSM before programming stream and position registers. */ 8582b9a50eaSCezary Rojewski snd_hdac_stream_drsm_enable(bus, true, hstream->index); 8592b9a50eaSCezary Rojewski 8602b9a50eaSCezary Rojewski ret = dai->driver->ops->prepare(data->substream, dai); 8612b9a50eaSCezary Rojewski if (ret) { 8622b9a50eaSCezary Rojewski dev_err(dai->dev, "prepare FE on resume failed: %d\n", ret); 8632b9a50eaSCezary Rojewski return ret; 8642b9a50eaSCezary Rojewski } 8652b9a50eaSCezary Rojewski 8662b9a50eaSCezary Rojewski writel(host_stream->pphcllpl, host_stream->pphc_addr + AZX_REG_PPHCLLPL); 8672b9a50eaSCezary Rojewski writel(host_stream->pphcllpu, host_stream->pphc_addr + AZX_REG_PPHCLLPU); 8682b9a50eaSCezary Rojewski writel(host_stream->pphcldpl, host_stream->pphc_addr + AZX_REG_PPHCLDPL); 8692b9a50eaSCezary Rojewski writel(host_stream->pphcldpu, host_stream->pphc_addr + AZX_REG_PPHCLDPU); 8702b9a50eaSCezary Rojewski 8712b9a50eaSCezary Rojewski /* As per HW spec recommendation, program LPIB and DPIB to the same value. */ 8722b9a50eaSCezary Rojewski snd_hdac_stream_set_lpib(hstream, hstream->lpib); 8732b9a50eaSCezary Rojewski snd_hdac_stream_set_dpibr(bus, hstream, hstream->lpib); 8742b9a50eaSCezary Rojewski 8752b9a50eaSCezary Rojewski return 0; 8762b9a50eaSCezary Rojewski } 8772b9a50eaSCezary Rojewski 8782b9a50eaSCezary Rojewski static int avs_dai_resume_be_prepare(struct snd_soc_dai *dai, struct avs_dma_data *data) 8792b9a50eaSCezary Rojewski { 8802b9a50eaSCezary Rojewski int ret; 8812b9a50eaSCezary Rojewski 8822b9a50eaSCezary Rojewski ret = dai->driver->ops->prepare(data->substream, dai); 8832b9a50eaSCezary Rojewski if (ret) 8842b9a50eaSCezary Rojewski dev_err(dai->dev, "prepare BE on resume failed: %d\n", ret); 8852b9a50eaSCezary Rojewski 8862b9a50eaSCezary Rojewski return ret; 8872b9a50eaSCezary Rojewski } 8882b9a50eaSCezary Rojewski 8892b9a50eaSCezary Rojewski static int avs_dai_suspend_fe_hw_free(struct snd_soc_dai *dai, struct avs_dma_data *data) 8902b9a50eaSCezary Rojewski { 8912b9a50eaSCezary Rojewski struct hdac_ext_stream *host_stream; 8922b9a50eaSCezary Rojewski int ret; 8932b9a50eaSCezary Rojewski 8942b9a50eaSCezary Rojewski host_stream = data->host_stream; 8952b9a50eaSCezary Rojewski 8962b9a50eaSCezary Rojewski /* Store position addresses so we can resume from them later on. */ 8972b9a50eaSCezary Rojewski hdac_stream(host_stream)->lpib = snd_hdac_stream_get_pos_lpib(hdac_stream(host_stream)); 8982b9a50eaSCezary Rojewski host_stream->pphcllpl = readl(host_stream->pphc_addr + AZX_REG_PPHCLLPL); 8992b9a50eaSCezary Rojewski host_stream->pphcllpu = readl(host_stream->pphc_addr + AZX_REG_PPHCLLPU); 9002b9a50eaSCezary Rojewski host_stream->pphcldpl = readl(host_stream->pphc_addr + AZX_REG_PPHCLDPL); 9012b9a50eaSCezary Rojewski host_stream->pphcldpu = readl(host_stream->pphc_addr + AZX_REG_PPHCLDPU); 9022b9a50eaSCezary Rojewski 9032b9a50eaSCezary Rojewski ret = __avs_dai_fe_hw_free(data->substream, dai); 9042b9a50eaSCezary Rojewski if (ret < 0) 9052b9a50eaSCezary Rojewski dev_err(dai->dev, "hw_free FE on suspend failed: %d\n", ret); 9062b9a50eaSCezary Rojewski 9072b9a50eaSCezary Rojewski return ret; 9082b9a50eaSCezary Rojewski } 9092b9a50eaSCezary Rojewski 9102b9a50eaSCezary Rojewski static int avs_dai_suspend_be_hw_free(struct snd_soc_dai *dai, struct avs_dma_data *data) 9112b9a50eaSCezary Rojewski { 9122b9a50eaSCezary Rojewski int ret; 9132b9a50eaSCezary Rojewski 9142b9a50eaSCezary Rojewski ret = dai->driver->ops->hw_free(data->substream, dai); 9152b9a50eaSCezary Rojewski if (ret < 0) 9162b9a50eaSCezary Rojewski dev_err(dai->dev, "hw_free BE on suspend failed: %d\n", ret); 9172b9a50eaSCezary Rojewski 9182b9a50eaSCezary Rojewski return ret; 9192b9a50eaSCezary Rojewski } 9202b9a50eaSCezary Rojewski 9212b9a50eaSCezary Rojewski static int avs_component_pm_op(struct snd_soc_component *component, bool be, 9222b9a50eaSCezary Rojewski int (*op)(struct snd_soc_dai *, struct avs_dma_data *)) 9232b9a50eaSCezary Rojewski { 9242b9a50eaSCezary Rojewski struct snd_soc_pcm_runtime *rtd; 9252b9a50eaSCezary Rojewski struct avs_dma_data *data; 9262b9a50eaSCezary Rojewski struct snd_soc_dai *dai; 9272b9a50eaSCezary Rojewski int ret; 9282b9a50eaSCezary Rojewski 9292b9a50eaSCezary Rojewski for_each_component_dais(component, dai) { 930*ec4b2099SKuninori Morimoto data = snd_soc_dai_dma_data_get_playback(dai); 9312b9a50eaSCezary Rojewski if (data) { 9328f28299fSCezary Rojewski rtd = asoc_substream_to_rtd(data->substream); 9332b9a50eaSCezary Rojewski if (rtd->dai_link->no_pcm == be && !rtd->dai_link->ignore_suspend) { 9342b9a50eaSCezary Rojewski ret = op(dai, data); 935f3fbb553SCezary Rojewski if (ret < 0) { 936f3fbb553SCezary Rojewski __snd_pcm_set_state(data->substream->runtime, 937f3fbb553SCezary Rojewski SNDRV_PCM_STATE_DISCONNECTED); 9382b9a50eaSCezary Rojewski return ret; 9392b9a50eaSCezary Rojewski } 9402b9a50eaSCezary Rojewski } 941f3fbb553SCezary Rojewski } 9422b9a50eaSCezary Rojewski 943*ec4b2099SKuninori Morimoto data = snd_soc_dai_dma_data_get_capture(dai); 9442b9a50eaSCezary Rojewski if (data) { 9458f28299fSCezary Rojewski rtd = asoc_substream_to_rtd(data->substream); 9462b9a50eaSCezary Rojewski if (rtd->dai_link->no_pcm == be && !rtd->dai_link->ignore_suspend) { 9472b9a50eaSCezary Rojewski ret = op(dai, data); 948f3fbb553SCezary Rojewski if (ret < 0) { 949f3fbb553SCezary Rojewski __snd_pcm_set_state(data->substream->runtime, 950f3fbb553SCezary Rojewski SNDRV_PCM_STATE_DISCONNECTED); 9512b9a50eaSCezary Rojewski return ret; 9522b9a50eaSCezary Rojewski } 9532b9a50eaSCezary Rojewski } 9542b9a50eaSCezary Rojewski } 955f3fbb553SCezary Rojewski } 9562b9a50eaSCezary Rojewski 9572b9a50eaSCezary Rojewski return 0; 9582b9a50eaSCezary Rojewski } 9592b9a50eaSCezary Rojewski 9602b9a50eaSCezary Rojewski static int avs_component_resume_hw_params(struct snd_soc_component *component, bool be) 9612b9a50eaSCezary Rojewski { 9622b9a50eaSCezary Rojewski return avs_component_pm_op(component, be, &avs_dai_resume_hw_params); 9632b9a50eaSCezary Rojewski } 9642b9a50eaSCezary Rojewski 9652b9a50eaSCezary Rojewski static int avs_component_resume_prepare(struct snd_soc_component *component, bool be) 9662b9a50eaSCezary Rojewski { 9672b9a50eaSCezary Rojewski int (*prepare_cb)(struct snd_soc_dai *dai, struct avs_dma_data *data); 9682b9a50eaSCezary Rojewski 9692b9a50eaSCezary Rojewski if (be) 9702b9a50eaSCezary Rojewski prepare_cb = &avs_dai_resume_be_prepare; 9712b9a50eaSCezary Rojewski else 9722b9a50eaSCezary Rojewski prepare_cb = &avs_dai_resume_fe_prepare; 9732b9a50eaSCezary Rojewski 9742b9a50eaSCezary Rojewski return avs_component_pm_op(component, be, prepare_cb); 9752b9a50eaSCezary Rojewski } 9762b9a50eaSCezary Rojewski 9772b9a50eaSCezary Rojewski static int avs_component_suspend_hw_free(struct snd_soc_component *component, bool be) 9782b9a50eaSCezary Rojewski { 9792b9a50eaSCezary Rojewski int (*hw_free_cb)(struct snd_soc_dai *dai, struct avs_dma_data *data); 9802b9a50eaSCezary Rojewski 9812b9a50eaSCezary Rojewski if (be) 9822b9a50eaSCezary Rojewski hw_free_cb = &avs_dai_suspend_be_hw_free; 9832b9a50eaSCezary Rojewski else 9842b9a50eaSCezary Rojewski hw_free_cb = &avs_dai_suspend_fe_hw_free; 9852b9a50eaSCezary Rojewski 9862b9a50eaSCezary Rojewski return avs_component_pm_op(component, be, hw_free_cb); 9872b9a50eaSCezary Rojewski } 9882b9a50eaSCezary Rojewski 9892b9a50eaSCezary Rojewski static int avs_component_suspend(struct snd_soc_component *component) 9902b9a50eaSCezary Rojewski { 9912b9a50eaSCezary Rojewski int ret; 9922b9a50eaSCezary Rojewski 9932b9a50eaSCezary Rojewski /* 9942b9a50eaSCezary Rojewski * When freeing paths, FEs need to be first as they perform 9952b9a50eaSCezary Rojewski * path unbinding. 9962b9a50eaSCezary Rojewski */ 9972b9a50eaSCezary Rojewski ret = avs_component_suspend_hw_free(component, false); 9982b9a50eaSCezary Rojewski if (ret) 9992b9a50eaSCezary Rojewski return ret; 10002b9a50eaSCezary Rojewski 10012b9a50eaSCezary Rojewski return avs_component_suspend_hw_free(component, true); 10022b9a50eaSCezary Rojewski } 10032b9a50eaSCezary Rojewski 10042b9a50eaSCezary Rojewski static int avs_component_resume(struct snd_soc_component *component) 10052b9a50eaSCezary Rojewski { 10062b9a50eaSCezary Rojewski int ret; 10072b9a50eaSCezary Rojewski 10082b9a50eaSCezary Rojewski /* 10092b9a50eaSCezary Rojewski * When creating paths, FEs need to be last as they perform 10102b9a50eaSCezary Rojewski * path binding. 10112b9a50eaSCezary Rojewski */ 10122b9a50eaSCezary Rojewski ret = avs_component_resume_hw_params(component, true); 10132b9a50eaSCezary Rojewski if (ret) 10142b9a50eaSCezary Rojewski return ret; 10152b9a50eaSCezary Rojewski 10162b9a50eaSCezary Rojewski ret = avs_component_resume_hw_params(component, false); 10172b9a50eaSCezary Rojewski if (ret) 10182b9a50eaSCezary Rojewski return ret; 10192b9a50eaSCezary Rojewski 10202b9a50eaSCezary Rojewski /* It is expected that the LINK stream is prepared first. */ 10212b9a50eaSCezary Rojewski ret = avs_component_resume_prepare(component, true); 10222b9a50eaSCezary Rojewski if (ret) 10232b9a50eaSCezary Rojewski return ret; 10242b9a50eaSCezary Rojewski 10252b9a50eaSCezary Rojewski return avs_component_resume_prepare(component, false); 10262b9a50eaSCezary Rojewski } 10272b9a50eaSCezary Rojewski 1028eb0699c4SCezary Rojewski static const struct snd_pcm_hardware avs_pcm_hardware = { 1029eb0699c4SCezary Rojewski .info = SNDRV_PCM_INFO_MMAP | 1030eb0699c4SCezary Rojewski SNDRV_PCM_INFO_MMAP_VALID | 1031eb0699c4SCezary Rojewski SNDRV_PCM_INFO_INTERLEAVED | 1032eb0699c4SCezary Rojewski SNDRV_PCM_INFO_PAUSE | 1033eb0699c4SCezary Rojewski SNDRV_PCM_INFO_RESUME | 1034eb0699c4SCezary Rojewski SNDRV_PCM_INFO_NO_PERIOD_WAKEUP, 1035eb0699c4SCezary Rojewski .formats = SNDRV_PCM_FMTBIT_S16_LE | 1036eb0699c4SCezary Rojewski SNDRV_PCM_FMTBIT_S24_LE | 1037eb0699c4SCezary Rojewski SNDRV_PCM_FMTBIT_S32_LE, 1038eb0699c4SCezary Rojewski .buffer_bytes_max = AZX_MAX_BUF_SIZE, 1039eb0699c4SCezary Rojewski .period_bytes_min = 128, 1040eb0699c4SCezary Rojewski .period_bytes_max = AZX_MAX_BUF_SIZE / 2, 1041eb0699c4SCezary Rojewski .periods_min = 2, 1042eb0699c4SCezary Rojewski .periods_max = AZX_MAX_FRAG, 1043eb0699c4SCezary Rojewski .fifo_size = 0, 1044eb0699c4SCezary Rojewski }; 1045eb0699c4SCezary Rojewski 1046f1b3b320SCezary Rojewski static int avs_component_open(struct snd_soc_component *component, 1047f1b3b320SCezary Rojewski struct snd_pcm_substream *substream) 1048f1b3b320SCezary Rojewski { 10498f28299fSCezary Rojewski struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 1050f1b3b320SCezary Rojewski 1051f1b3b320SCezary Rojewski /* only FE DAI links are handled here */ 1052f1b3b320SCezary Rojewski if (rtd->dai_link->no_pcm) 1053f1b3b320SCezary Rojewski return 0; 1054f1b3b320SCezary Rojewski 1055eb0699c4SCezary Rojewski return snd_soc_set_runtime_hwparams(substream, &avs_pcm_hardware); 1056f1b3b320SCezary Rojewski } 1057f1b3b320SCezary Rojewski 1058f1b3b320SCezary Rojewski static unsigned int avs_hda_stream_dpib_read(struct hdac_ext_stream *stream) 1059f1b3b320SCezary Rojewski { 1060f1b3b320SCezary Rojewski return readl(hdac_stream(stream)->bus->remap_addr + AZX_REG_VS_SDXDPIB_XBASE + 1061f1b3b320SCezary Rojewski (AZX_REG_VS_SDXDPIB_XINTERVAL * hdac_stream(stream)->index)); 1062f1b3b320SCezary Rojewski } 1063f1b3b320SCezary Rojewski 1064f1b3b320SCezary Rojewski static snd_pcm_uframes_t 1065f1b3b320SCezary Rojewski avs_component_pointer(struct snd_soc_component *component, struct snd_pcm_substream *substream) 1066f1b3b320SCezary Rojewski { 10678f28299fSCezary Rojewski struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 1068f1b3b320SCezary Rojewski struct avs_dma_data *data; 1069f1b3b320SCezary Rojewski struct hdac_ext_stream *host_stream; 1070f1b3b320SCezary Rojewski unsigned int pos; 1071f1b3b320SCezary Rojewski 1072f1b3b320SCezary Rojewski data = snd_soc_dai_get_dma_data(asoc_rtd_to_cpu(rtd, 0), substream); 1073f1b3b320SCezary Rojewski if (!data->host_stream) 1074f1b3b320SCezary Rojewski return 0; 1075f1b3b320SCezary Rojewski 1076f1b3b320SCezary Rojewski host_stream = data->host_stream; 1077f1b3b320SCezary Rojewski pos = avs_hda_stream_dpib_read(host_stream); 1078f1b3b320SCezary Rojewski 1079f1b3b320SCezary Rojewski if (pos >= hdac_stream(host_stream)->bufsize) 1080f1b3b320SCezary Rojewski pos = 0; 1081f1b3b320SCezary Rojewski 1082f1b3b320SCezary Rojewski return bytes_to_frames(substream->runtime, pos); 1083f1b3b320SCezary Rojewski } 1084f1b3b320SCezary Rojewski 1085f1b3b320SCezary Rojewski static int avs_component_mmap(struct snd_soc_component *component, 1086f1b3b320SCezary Rojewski struct snd_pcm_substream *substream, 1087f1b3b320SCezary Rojewski struct vm_area_struct *vma) 1088f1b3b320SCezary Rojewski { 1089f1b3b320SCezary Rojewski return snd_pcm_lib_default_mmap(substream, vma); 1090f1b3b320SCezary Rojewski } 1091f1b3b320SCezary Rojewski 1092f1b3b320SCezary Rojewski #define MAX_PREALLOC_SIZE (32 * 1024 * 1024) 1093f1b3b320SCezary Rojewski 1094f1b3b320SCezary Rojewski static int avs_component_construct(struct snd_soc_component *component, 1095f1b3b320SCezary Rojewski struct snd_soc_pcm_runtime *rtd) 1096f1b3b320SCezary Rojewski { 1097f1b3b320SCezary Rojewski struct snd_soc_dai *dai = asoc_rtd_to_cpu(rtd, 0); 1098f1b3b320SCezary Rojewski struct snd_pcm *pcm = rtd->pcm; 1099f1b3b320SCezary Rojewski 1100f1b3b320SCezary Rojewski if (dai->driver->playback.channels_min) 1101f1b3b320SCezary Rojewski snd_pcm_set_managed_buffer(pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream, 1102f1b3b320SCezary Rojewski SNDRV_DMA_TYPE_DEV_SG, component->dev, 0, 1103f1b3b320SCezary Rojewski MAX_PREALLOC_SIZE); 1104f1b3b320SCezary Rojewski 1105f1b3b320SCezary Rojewski if (dai->driver->capture.channels_min) 1106f1b3b320SCezary Rojewski snd_pcm_set_managed_buffer(pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream, 1107f1b3b320SCezary Rojewski SNDRV_DMA_TYPE_DEV_SG, component->dev, 0, 1108f1b3b320SCezary Rojewski MAX_PREALLOC_SIZE); 1109f1b3b320SCezary Rojewski 1110f1b3b320SCezary Rojewski return 0; 1111f1b3b320SCezary Rojewski } 1112f1b3b320SCezary Rojewski 1113f1b3b320SCezary Rojewski static const struct snd_soc_component_driver avs_component_driver = { 1114f1b3b320SCezary Rojewski .name = "avs-pcm", 1115f1b3b320SCezary Rojewski .probe = avs_component_probe, 1116f1b3b320SCezary Rojewski .remove = avs_component_remove, 11172b9a50eaSCezary Rojewski .suspend = avs_component_suspend, 11182b9a50eaSCezary Rojewski .resume = avs_component_resume, 1119f1b3b320SCezary Rojewski .open = avs_component_open, 1120f1b3b320SCezary Rojewski .pointer = avs_component_pointer, 1121f1b3b320SCezary Rojewski .mmap = avs_component_mmap, 1122f1b3b320SCezary Rojewski .pcm_construct = avs_component_construct, 1123f1b3b320SCezary Rojewski .module_get_upon_open = 1, /* increment refcount when a pcm is opened */ 1124f1b3b320SCezary Rojewski .topology_name_prefix = "intel/avs", 1125f1b3b320SCezary Rojewski }; 1126f1b3b320SCezary Rojewski 1127ed914a2aSCezary Rojewski int avs_soc_component_register(struct device *dev, const char *name, 1128f1b3b320SCezary Rojewski const struct snd_soc_component_driver *drv, 1129f1b3b320SCezary Rojewski struct snd_soc_dai_driver *cpu_dais, int num_cpu_dais) 1130f1b3b320SCezary Rojewski { 1131f1b3b320SCezary Rojewski struct avs_soc_component *acomp; 1132f1b3b320SCezary Rojewski int ret; 1133f1b3b320SCezary Rojewski 1134f1b3b320SCezary Rojewski acomp = devm_kzalloc(dev, sizeof(*acomp), GFP_KERNEL); 1135f1b3b320SCezary Rojewski if (!acomp) 1136f1b3b320SCezary Rojewski return -ENOMEM; 1137f1b3b320SCezary Rojewski 1138f1b3b320SCezary Rojewski ret = snd_soc_component_initialize(&acomp->base, drv, dev); 1139f1b3b320SCezary Rojewski if (ret < 0) 1140f1b3b320SCezary Rojewski return ret; 1141f1b3b320SCezary Rojewski 1142f1b3b320SCezary Rojewski /* force name change after ASoC is done with its init */ 1143f1b3b320SCezary Rojewski acomp->base.name = name; 1144f1b3b320SCezary Rojewski INIT_LIST_HEAD(&acomp->node); 1145f1b3b320SCezary Rojewski 1146f1b3b320SCezary Rojewski return snd_soc_add_component(&acomp->base, cpu_dais, num_cpu_dais); 1147f1b3b320SCezary Rojewski } 1148b9062f98SCezary Rojewski 1149b9062f98SCezary Rojewski static struct snd_soc_dai_driver dmic_cpu_dais[] = { 1150b9062f98SCezary Rojewski { 1151b9062f98SCezary Rojewski .name = "DMIC Pin", 1152b9062f98SCezary Rojewski .ops = &avs_dai_nonhda_be_ops, 1153b9062f98SCezary Rojewski .capture = { 1154b9062f98SCezary Rojewski .stream_name = "DMIC Rx", 1155b9062f98SCezary Rojewski .channels_min = 1, 1156b9062f98SCezary Rojewski .channels_max = 4, 1157b9062f98SCezary Rojewski .rates = SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_48000, 1158b9062f98SCezary Rojewski .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE, 1159b9062f98SCezary Rojewski }, 1160b9062f98SCezary Rojewski }, 1161b9062f98SCezary Rojewski { 1162b9062f98SCezary Rojewski .name = "DMIC WoV Pin", 1163b9062f98SCezary Rojewski .ops = &avs_dai_nonhda_be_ops, 1164b9062f98SCezary Rojewski .capture = { 1165b9062f98SCezary Rojewski .stream_name = "DMIC WoV Rx", 1166b9062f98SCezary Rojewski .channels_min = 1, 1167b9062f98SCezary Rojewski .channels_max = 4, 1168b9062f98SCezary Rojewski .rates = SNDRV_PCM_RATE_16000, 1169b9062f98SCezary Rojewski .formats = SNDRV_PCM_FMTBIT_S16_LE, 1170b9062f98SCezary Rojewski }, 1171b9062f98SCezary Rojewski }, 1172b9062f98SCezary Rojewski }; 1173b9062f98SCezary Rojewski 1174b9062f98SCezary Rojewski int avs_dmic_platform_register(struct avs_dev *adev, const char *name) 1175b9062f98SCezary Rojewski { 1176b9062f98SCezary Rojewski return avs_soc_component_register(adev->dev, name, &avs_component_driver, dmic_cpu_dais, 1177b9062f98SCezary Rojewski ARRAY_SIZE(dmic_cpu_dais)); 1178b9062f98SCezary Rojewski } 1179b9062f98SCezary Rojewski 1180b9062f98SCezary Rojewski static const struct snd_soc_dai_driver i2s_dai_template = { 1181b9062f98SCezary Rojewski .ops = &avs_dai_nonhda_be_ops, 1182b9062f98SCezary Rojewski .playback = { 1183b9062f98SCezary Rojewski .channels_min = 1, 1184b9062f98SCezary Rojewski .channels_max = 8, 1185b9062f98SCezary Rojewski .rates = SNDRV_PCM_RATE_8000_192000 | 1186b9062f98SCezary Rojewski SNDRV_PCM_RATE_KNOT, 1187b9062f98SCezary Rojewski .formats = SNDRV_PCM_FMTBIT_S16_LE | 1188b9062f98SCezary Rojewski SNDRV_PCM_FMTBIT_S24_LE | 1189b9062f98SCezary Rojewski SNDRV_PCM_FMTBIT_S32_LE, 1190b9062f98SCezary Rojewski }, 1191b9062f98SCezary Rojewski .capture = { 1192b9062f98SCezary Rojewski .channels_min = 1, 1193b9062f98SCezary Rojewski .channels_max = 8, 1194b9062f98SCezary Rojewski .rates = SNDRV_PCM_RATE_8000_192000 | 1195b9062f98SCezary Rojewski SNDRV_PCM_RATE_KNOT, 1196b9062f98SCezary Rojewski .formats = SNDRV_PCM_FMTBIT_S16_LE | 1197b9062f98SCezary Rojewski SNDRV_PCM_FMTBIT_S24_LE | 1198b9062f98SCezary Rojewski SNDRV_PCM_FMTBIT_S32_LE, 1199b9062f98SCezary Rojewski }, 1200b9062f98SCezary Rojewski }; 1201b9062f98SCezary Rojewski 1202b9062f98SCezary Rojewski int avs_i2s_platform_register(struct avs_dev *adev, const char *name, unsigned long port_mask, 1203b9062f98SCezary Rojewski unsigned long *tdms) 1204b9062f98SCezary Rojewski { 1205b9062f98SCezary Rojewski struct snd_soc_dai_driver *cpus, *dai; 1206b9062f98SCezary Rojewski size_t ssp_count, cpu_count; 1207b9062f98SCezary Rojewski int i, j; 1208b9062f98SCezary Rojewski 1209b9062f98SCezary Rojewski ssp_count = adev->hw_cfg.i2s_caps.ctrl_count; 1210b9062f98SCezary Rojewski cpu_count = hweight_long(port_mask); 1211b9062f98SCezary Rojewski if (tdms) 1212b9062f98SCezary Rojewski for_each_set_bit(i, &port_mask, ssp_count) 1213b9062f98SCezary Rojewski cpu_count += hweight_long(tdms[i]); 1214b9062f98SCezary Rojewski 1215b9062f98SCezary Rojewski cpus = devm_kzalloc(adev->dev, sizeof(*cpus) * cpu_count, GFP_KERNEL); 1216b9062f98SCezary Rojewski if (!cpus) 1217b9062f98SCezary Rojewski return -ENOMEM; 1218b9062f98SCezary Rojewski 1219b9062f98SCezary Rojewski dai = cpus; 1220b9062f98SCezary Rojewski for_each_set_bit(i, &port_mask, ssp_count) { 1221b9062f98SCezary Rojewski memcpy(dai, &i2s_dai_template, sizeof(*dai)); 1222b9062f98SCezary Rojewski 1223b9062f98SCezary Rojewski dai->name = 1224b9062f98SCezary Rojewski devm_kasprintf(adev->dev, GFP_KERNEL, "SSP%d Pin", i); 1225b9062f98SCezary Rojewski dai->playback.stream_name = 1226b9062f98SCezary Rojewski devm_kasprintf(adev->dev, GFP_KERNEL, "ssp%d Tx", i); 1227b9062f98SCezary Rojewski dai->capture.stream_name = 1228b9062f98SCezary Rojewski devm_kasprintf(adev->dev, GFP_KERNEL, "ssp%d Rx", i); 1229b9062f98SCezary Rojewski 1230b9062f98SCezary Rojewski if (!dai->name || !dai->playback.stream_name || !dai->capture.stream_name) 1231b9062f98SCezary Rojewski return -ENOMEM; 1232b9062f98SCezary Rojewski dai++; 1233b9062f98SCezary Rojewski } 1234b9062f98SCezary Rojewski 1235b9062f98SCezary Rojewski if (!tdms) 1236b9062f98SCezary Rojewski goto plat_register; 1237b9062f98SCezary Rojewski 1238b9062f98SCezary Rojewski for_each_set_bit(i, &port_mask, ssp_count) { 1239b9062f98SCezary Rojewski for_each_set_bit(j, &tdms[i], ssp_count) { 1240b9062f98SCezary Rojewski memcpy(dai, &i2s_dai_template, sizeof(*dai)); 1241b9062f98SCezary Rojewski 1242b9062f98SCezary Rojewski dai->name = 1243b9062f98SCezary Rojewski devm_kasprintf(adev->dev, GFP_KERNEL, "SSP%d:%d Pin", i, j); 1244b9062f98SCezary Rojewski dai->playback.stream_name = 1245b9062f98SCezary Rojewski devm_kasprintf(adev->dev, GFP_KERNEL, "ssp%d:%d Tx", i, j); 1246b9062f98SCezary Rojewski dai->capture.stream_name = 1247b9062f98SCezary Rojewski devm_kasprintf(adev->dev, GFP_KERNEL, "ssp%d:%d Rx", i, j); 1248b9062f98SCezary Rojewski 1249b9062f98SCezary Rojewski if (!dai->name || !dai->playback.stream_name || !dai->capture.stream_name) 1250b9062f98SCezary Rojewski return -ENOMEM; 1251b9062f98SCezary Rojewski dai++; 1252b9062f98SCezary Rojewski } 1253b9062f98SCezary Rojewski } 1254b9062f98SCezary Rojewski 1255b9062f98SCezary Rojewski plat_register: 1256b9062f98SCezary Rojewski return avs_soc_component_register(adev->dev, name, &avs_component_driver, cpus, cpu_count); 1257b9062f98SCezary Rojewski } 1258d070002aSCezary Rojewski 1259d070002aSCezary Rojewski /* HD-Audio CPU DAI template */ 1260d070002aSCezary Rojewski static const struct snd_soc_dai_driver hda_cpu_dai = { 1261d070002aSCezary Rojewski .ops = &avs_dai_hda_be_ops, 1262d070002aSCezary Rojewski .playback = { 1263d070002aSCezary Rojewski .channels_min = 1, 1264d070002aSCezary Rojewski .channels_max = 8, 1265d070002aSCezary Rojewski .rates = SNDRV_PCM_RATE_8000_192000, 1266d070002aSCezary Rojewski .formats = SNDRV_PCM_FMTBIT_S16_LE | 1267d070002aSCezary Rojewski SNDRV_PCM_FMTBIT_S24_LE | 1268d070002aSCezary Rojewski SNDRV_PCM_FMTBIT_S32_LE, 1269d070002aSCezary Rojewski }, 1270d070002aSCezary Rojewski .capture = { 1271d070002aSCezary Rojewski .channels_min = 1, 1272d070002aSCezary Rojewski .channels_max = 8, 1273d070002aSCezary Rojewski .rates = SNDRV_PCM_RATE_8000_192000, 1274d070002aSCezary Rojewski .formats = SNDRV_PCM_FMTBIT_S16_LE | 1275d070002aSCezary Rojewski SNDRV_PCM_FMTBIT_S24_LE | 1276d070002aSCezary Rojewski SNDRV_PCM_FMTBIT_S32_LE, 1277d070002aSCezary Rojewski }, 1278d070002aSCezary Rojewski }; 1279d070002aSCezary Rojewski 1280d070002aSCezary Rojewski static void avs_component_hda_unregister_dais(struct snd_soc_component *component) 1281d070002aSCezary Rojewski { 1282d070002aSCezary Rojewski struct snd_soc_acpi_mach *mach; 1283d070002aSCezary Rojewski struct snd_soc_dai *dai, *save; 1284d070002aSCezary Rojewski struct hda_codec *codec; 1285d070002aSCezary Rojewski char name[32]; 1286d070002aSCezary Rojewski 1287d070002aSCezary Rojewski mach = dev_get_platdata(component->card->dev); 1288d070002aSCezary Rojewski codec = mach->pdata; 1289d070002aSCezary Rojewski sprintf(name, "%s-cpu", dev_name(&codec->core.dev)); 1290d070002aSCezary Rojewski 1291d070002aSCezary Rojewski for_each_component_dais_safe(component, dai, save) { 1292*ec4b2099SKuninori Morimoto int stream; 1293*ec4b2099SKuninori Morimoto 1294d070002aSCezary Rojewski if (!strstr(dai->driver->name, name)) 1295d070002aSCezary Rojewski continue; 1296d070002aSCezary Rojewski 1297*ec4b2099SKuninori Morimoto for_each_pcm_streams(stream) 1298*ec4b2099SKuninori Morimoto snd_soc_dapm_free_widget(snd_soc_dai_get_widget(dai, stream)); 1299*ec4b2099SKuninori Morimoto 1300d070002aSCezary Rojewski snd_soc_unregister_dai(dai); 1301d070002aSCezary Rojewski } 1302d070002aSCezary Rojewski } 1303d070002aSCezary Rojewski 1304d070002aSCezary Rojewski static int avs_component_hda_probe(struct snd_soc_component *component) 1305d070002aSCezary Rojewski { 1306d070002aSCezary Rojewski struct snd_soc_dapm_context *dapm; 1307d070002aSCezary Rojewski struct snd_soc_dai_driver *dais; 1308d070002aSCezary Rojewski struct snd_soc_acpi_mach *mach; 1309d070002aSCezary Rojewski struct hda_codec *codec; 1310d070002aSCezary Rojewski struct hda_pcm *pcm; 1311d070002aSCezary Rojewski const char *cname; 1312d070002aSCezary Rojewski int pcm_count = 0, ret, i; 1313d070002aSCezary Rojewski 1314d070002aSCezary Rojewski mach = dev_get_platdata(component->card->dev); 1315d070002aSCezary Rojewski if (!mach) 1316d070002aSCezary Rojewski return -EINVAL; 1317d070002aSCezary Rojewski 1318d070002aSCezary Rojewski codec = mach->pdata; 1319d070002aSCezary Rojewski if (list_empty(&codec->pcm_list_head)) 1320d070002aSCezary Rojewski return -EINVAL; 1321d070002aSCezary Rojewski list_for_each_entry(pcm, &codec->pcm_list_head, list) 1322d070002aSCezary Rojewski pcm_count++; 1323d070002aSCezary Rojewski 1324d070002aSCezary Rojewski dais = devm_kcalloc(component->dev, pcm_count, sizeof(*dais), 1325d070002aSCezary Rojewski GFP_KERNEL); 1326d070002aSCezary Rojewski if (!dais) 1327d070002aSCezary Rojewski return -ENOMEM; 1328d070002aSCezary Rojewski 1329d070002aSCezary Rojewski cname = dev_name(&codec->core.dev); 1330d070002aSCezary Rojewski dapm = snd_soc_component_get_dapm(component); 1331d070002aSCezary Rojewski pcm = list_first_entry(&codec->pcm_list_head, struct hda_pcm, list); 1332d070002aSCezary Rojewski 1333d070002aSCezary Rojewski for (i = 0; i < pcm_count; i++, pcm = list_next_entry(pcm, list)) { 1334d070002aSCezary Rojewski struct snd_soc_dai *dai; 1335d070002aSCezary Rojewski 1336d070002aSCezary Rojewski memcpy(&dais[i], &hda_cpu_dai, sizeof(*dais)); 1337d070002aSCezary Rojewski dais[i].id = i; 1338d070002aSCezary Rojewski dais[i].name = devm_kasprintf(component->dev, GFP_KERNEL, 1339d070002aSCezary Rojewski "%s-cpu%d", cname, i); 1340d070002aSCezary Rojewski if (!dais[i].name) { 1341d070002aSCezary Rojewski ret = -ENOMEM; 1342d070002aSCezary Rojewski goto exit; 1343d070002aSCezary Rojewski } 1344d070002aSCezary Rojewski 1345d070002aSCezary Rojewski if (pcm->stream[0].substreams) { 1346d070002aSCezary Rojewski dais[i].playback.stream_name = 1347d070002aSCezary Rojewski devm_kasprintf(component->dev, GFP_KERNEL, 1348d070002aSCezary Rojewski "%s-cpu%d Tx", cname, i); 1349d070002aSCezary Rojewski if (!dais[i].playback.stream_name) { 1350d070002aSCezary Rojewski ret = -ENOMEM; 1351d070002aSCezary Rojewski goto exit; 1352d070002aSCezary Rojewski } 1353d070002aSCezary Rojewski } 1354d070002aSCezary Rojewski 1355d070002aSCezary Rojewski if (pcm->stream[1].substreams) { 1356d070002aSCezary Rojewski dais[i].capture.stream_name = 1357d070002aSCezary Rojewski devm_kasprintf(component->dev, GFP_KERNEL, 1358d070002aSCezary Rojewski "%s-cpu%d Rx", cname, i); 1359d070002aSCezary Rojewski if (!dais[i].capture.stream_name) { 1360d070002aSCezary Rojewski ret = -ENOMEM; 1361d070002aSCezary Rojewski goto exit; 1362d070002aSCezary Rojewski } 1363d070002aSCezary Rojewski } 1364d070002aSCezary Rojewski 1365d070002aSCezary Rojewski dai = snd_soc_register_dai(component, &dais[i], false); 1366d070002aSCezary Rojewski if (!dai) { 1367d070002aSCezary Rojewski dev_err(component->dev, "register dai for %s failed\n", 1368d070002aSCezary Rojewski pcm->name); 1369d070002aSCezary Rojewski ret = -EINVAL; 1370d070002aSCezary Rojewski goto exit; 1371d070002aSCezary Rojewski } 1372d070002aSCezary Rojewski 1373d070002aSCezary Rojewski ret = snd_soc_dapm_new_dai_widgets(dapm, dai); 1374d070002aSCezary Rojewski if (ret < 0) { 1375d070002aSCezary Rojewski dev_err(component->dev, "create widgets failed: %d\n", 1376d070002aSCezary Rojewski ret); 1377d070002aSCezary Rojewski goto exit; 1378d070002aSCezary Rojewski } 1379d070002aSCezary Rojewski } 1380d070002aSCezary Rojewski 1381d070002aSCezary Rojewski ret = avs_component_probe(component); 1382d070002aSCezary Rojewski exit: 1383d070002aSCezary Rojewski if (ret) 1384d070002aSCezary Rojewski avs_component_hda_unregister_dais(component); 1385d070002aSCezary Rojewski 1386d070002aSCezary Rojewski return ret; 1387d070002aSCezary Rojewski } 1388d070002aSCezary Rojewski 1389d070002aSCezary Rojewski static void avs_component_hda_remove(struct snd_soc_component *component) 1390d070002aSCezary Rojewski { 1391d070002aSCezary Rojewski avs_component_hda_unregister_dais(component); 1392d070002aSCezary Rojewski avs_component_remove(component); 1393d070002aSCezary Rojewski } 1394d070002aSCezary Rojewski 1395d070002aSCezary Rojewski static int avs_component_hda_open(struct snd_soc_component *component, 1396d070002aSCezary Rojewski struct snd_pcm_substream *substream) 1397d070002aSCezary Rojewski { 13988f28299fSCezary Rojewski struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 1399d070002aSCezary Rojewski struct hdac_ext_stream *link_stream; 1400d070002aSCezary Rojewski struct hda_codec *codec; 1401d070002aSCezary Rojewski 1402eb0699c4SCezary Rojewski if (!rtd->dai_link->no_pcm) { 1403eb0699c4SCezary Rojewski struct snd_pcm_hardware hwparams = avs_pcm_hardware; 1404f38d4c72SCezary Rojewski struct snd_soc_pcm_runtime *be; 1405f38d4c72SCezary Rojewski struct snd_soc_dpcm *dpcm; 1406f38d4c72SCezary Rojewski int dir = substream->stream; 1407f38d4c72SCezary Rojewski 1408f38d4c72SCezary Rojewski /* 1409f38d4c72SCezary Rojewski * Support the DPCM reparenting while still fulfilling expectations of HDAudio 1410f38d4c72SCezary Rojewski * common code - a valid stream pointer at substream->runtime->private_data - 1411f38d4c72SCezary Rojewski * by having all FEs point to the same private data. 1412f38d4c72SCezary Rojewski */ 1413f38d4c72SCezary Rojewski for_each_dpcm_be(rtd, dir, dpcm) { 1414f38d4c72SCezary Rojewski struct snd_pcm_substream *be_substream; 1415f38d4c72SCezary Rojewski 1416f38d4c72SCezary Rojewski be = dpcm->be; 1417f38d4c72SCezary Rojewski if (be->dpcm[dir].users == 1) 1418f38d4c72SCezary Rojewski break; 1419f38d4c72SCezary Rojewski 1420f38d4c72SCezary Rojewski be_substream = snd_soc_dpcm_get_substream(be, dir); 1421f38d4c72SCezary Rojewski substream->runtime->private_data = be_substream->runtime->private_data; 1422f38d4c72SCezary Rojewski break; 1423f38d4c72SCezary Rojewski } 1424eb0699c4SCezary Rojewski 1425eb0699c4SCezary Rojewski /* RESUME unsupported for de-coupled HD-Audio capture. */ 1426f38d4c72SCezary Rojewski if (dir == SNDRV_PCM_STREAM_CAPTURE) 1427eb0699c4SCezary Rojewski hwparams.info &= ~SNDRV_PCM_INFO_RESUME; 1428eb0699c4SCezary Rojewski 1429eb0699c4SCezary Rojewski return snd_soc_set_runtime_hwparams(substream, &hwparams); 1430eb0699c4SCezary Rojewski } 1431d070002aSCezary Rojewski 1432d070002aSCezary Rojewski codec = dev_to_hda_codec(asoc_rtd_to_codec(rtd, 0)->dev); 1433d070002aSCezary Rojewski link_stream = snd_hdac_ext_stream_assign(&codec->bus->core, substream, 1434d070002aSCezary Rojewski HDAC_EXT_STREAM_TYPE_LINK); 1435d070002aSCezary Rojewski if (!link_stream) 1436d070002aSCezary Rojewski return -EBUSY; 1437d070002aSCezary Rojewski 1438d070002aSCezary Rojewski substream->runtime->private_data = link_stream; 1439d070002aSCezary Rojewski return 0; 1440d070002aSCezary Rojewski } 1441d070002aSCezary Rojewski 1442d070002aSCezary Rojewski static int avs_component_hda_close(struct snd_soc_component *component, 1443d070002aSCezary Rojewski struct snd_pcm_substream *substream) 1444d070002aSCezary Rojewski { 14458f28299fSCezary Rojewski struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 1446d070002aSCezary Rojewski struct hdac_ext_stream *link_stream; 1447d070002aSCezary Rojewski 1448d070002aSCezary Rojewski /* only BE DAI links are handled here */ 1449d070002aSCezary Rojewski if (!rtd->dai_link->no_pcm) 1450d070002aSCezary Rojewski return 0; 1451d070002aSCezary Rojewski 1452d070002aSCezary Rojewski link_stream = substream->runtime->private_data; 1453d070002aSCezary Rojewski snd_hdac_ext_stream_release(link_stream, HDAC_EXT_STREAM_TYPE_LINK); 1454d070002aSCezary Rojewski substream->runtime->private_data = NULL; 1455d070002aSCezary Rojewski 1456d070002aSCezary Rojewski return 0; 1457d070002aSCezary Rojewski } 1458d070002aSCezary Rojewski 1459d070002aSCezary Rojewski static const struct snd_soc_component_driver avs_hda_component_driver = { 1460d070002aSCezary Rojewski .name = "avs-hda-pcm", 1461d070002aSCezary Rojewski .probe = avs_component_hda_probe, 1462d070002aSCezary Rojewski .remove = avs_component_hda_remove, 14632b9a50eaSCezary Rojewski .suspend = avs_component_suspend, 14642b9a50eaSCezary Rojewski .resume = avs_component_resume, 1465d070002aSCezary Rojewski .open = avs_component_hda_open, 1466d070002aSCezary Rojewski .close = avs_component_hda_close, 1467d070002aSCezary Rojewski .pointer = avs_component_pointer, 1468d070002aSCezary Rojewski .mmap = avs_component_mmap, 1469d070002aSCezary Rojewski .pcm_construct = avs_component_construct, 1470d070002aSCezary Rojewski /* 1471d070002aSCezary Rojewski * hda platform component's probe() is dependent on 1472d070002aSCezary Rojewski * codec->pcm_list_head, it needs to be initialized after codec 1473d070002aSCezary Rojewski * component. remove_order is here for completeness sake 1474d070002aSCezary Rojewski */ 1475d070002aSCezary Rojewski .probe_order = SND_SOC_COMP_ORDER_LATE, 1476d070002aSCezary Rojewski .remove_order = SND_SOC_COMP_ORDER_EARLY, 1477d070002aSCezary Rojewski .module_get_upon_open = 1, 1478d070002aSCezary Rojewski .topology_name_prefix = "intel/avs", 1479d070002aSCezary Rojewski }; 1480d070002aSCezary Rojewski 1481d070002aSCezary Rojewski int avs_hda_platform_register(struct avs_dev *adev, const char *name) 1482d070002aSCezary Rojewski { 1483d070002aSCezary Rojewski return avs_soc_component_register(adev->dev, name, 1484d070002aSCezary Rojewski &avs_hda_component_driver, NULL, 0); 1485d070002aSCezary Rojewski } 1486