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 { 38ec4b2099SKuninori 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 427*aec3eeccSAmadeusz Sławiński return 0; 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) { 47125148f57SAmadeusz Sławiński ret = -EBUSY; 47225148f57SAmadeusz Sławiński goto err; 4739114700bSCezary Rojewski } 4749114700bSCezary Rojewski 4759114700bSCezary Rojewski data->host_stream = host_stream; 47625148f57SAmadeusz Sławiński ret = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); 47725148f57SAmadeusz Sławiński if (ret < 0) 47825148f57SAmadeusz Sławiński goto err; 47925148f57SAmadeusz Sławiński 4809114700bSCezary Rojewski /* avoid wrap-around with wall-clock */ 48125148f57SAmadeusz Sławiński ret = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_TIME, 20, 178000000); 48225148f57SAmadeusz Sławiński if (ret < 0) 48325148f57SAmadeusz Sławiński goto err; 48425148f57SAmadeusz Sławiński 48525148f57SAmadeusz Sławiński ret = snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_rates); 48625148f57SAmadeusz Sławiński if (ret < 0) 48725148f57SAmadeusz Sławiński goto err; 48825148f57SAmadeusz Sławiński 4899114700bSCezary Rojewski snd_pcm_set_sync(substream); 4909114700bSCezary Rojewski 4919114700bSCezary Rojewski dev_dbg(dai->dev, "%s fe STARTUP tag %d str %p", 4929114700bSCezary Rojewski __func__, hdac_stream(host_stream)->stream_tag, substream); 4939114700bSCezary Rojewski 4949114700bSCezary Rojewski return 0; 49525148f57SAmadeusz Sławiński 49625148f57SAmadeusz Sławiński err: 49725148f57SAmadeusz Sławiński kfree(data); 49825148f57SAmadeusz Sławiński return ret; 4999114700bSCezary Rojewski } 5009114700bSCezary Rojewski 5019114700bSCezary Rojewski static void avs_dai_fe_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) 5029114700bSCezary Rojewski { 5038f28299fSCezary Rojewski struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 504730cb320SCezary Rojewski struct avs_dev *adev = to_avs_dev(dai->dev); 5059114700bSCezary Rojewski struct avs_dma_data *data; 5069114700bSCezary Rojewski 507730cb320SCezary Rojewski if (rtd->dai_link->ignore_suspend) 508730cb320SCezary Rojewski adev->num_lp_paths--; 509730cb320SCezary Rojewski 5109114700bSCezary Rojewski data = snd_soc_dai_get_dma_data(dai, substream); 5119114700bSCezary Rojewski 5129114700bSCezary Rojewski snd_soc_dai_set_dma_data(dai, substream, NULL); 5139114700bSCezary Rojewski snd_hdac_ext_stream_release(data->host_stream, HDAC_EXT_STREAM_TYPE_HOST); 5149114700bSCezary Rojewski kfree(data); 5159114700bSCezary Rojewski } 5169114700bSCezary Rojewski 5179114700bSCezary Rojewski static int avs_dai_fe_hw_params(struct snd_pcm_substream *substream, 5189114700bSCezary Rojewski struct snd_pcm_hw_params *hw_params, struct snd_soc_dai *dai) 5199114700bSCezary Rojewski { 5209114700bSCezary Rojewski struct snd_pcm_hw_params *be_hw_params = NULL; 5219114700bSCezary Rojewski struct snd_soc_pcm_runtime *fe, *be; 5229114700bSCezary Rojewski struct snd_soc_dpcm *dpcm; 5239114700bSCezary Rojewski struct avs_dma_data *data; 5249114700bSCezary Rojewski struct hdac_ext_stream *host_stream; 5259114700bSCezary Rojewski int ret; 5269114700bSCezary Rojewski 5279114700bSCezary Rojewski data = snd_soc_dai_get_dma_data(dai, substream); 5289114700bSCezary Rojewski if (data->path) 5299114700bSCezary Rojewski return 0; 5309114700bSCezary Rojewski 5319114700bSCezary Rojewski host_stream = data->host_stream; 5329114700bSCezary Rojewski 5339114700bSCezary Rojewski hdac_stream(host_stream)->bufsize = 0; 5349114700bSCezary Rojewski hdac_stream(host_stream)->period_bytes = 0; 5359114700bSCezary Rojewski hdac_stream(host_stream)->format_val = 0; 5369114700bSCezary Rojewski 5379114700bSCezary Rojewski fe = asoc_substream_to_rtd(substream); 5389114700bSCezary Rojewski for_each_dpcm_be(fe, substream->stream, dpcm) { 5399114700bSCezary Rojewski be = dpcm->be; 5409114700bSCezary Rojewski be_hw_params = &be->dpcm[substream->stream].hw_params; 5419114700bSCezary Rojewski } 5429114700bSCezary Rojewski 5439114700bSCezary Rojewski ret = avs_dai_hw_params(substream, hw_params, be_hw_params, dai, 5449114700bSCezary Rojewski hdac_stream(host_stream)->stream_tag - 1); 5459114700bSCezary Rojewski if (ret) 5469114700bSCezary Rojewski goto create_err; 5479114700bSCezary Rojewski 5489114700bSCezary Rojewski ret = avs_path_bind(data->path); 5499114700bSCezary Rojewski if (ret < 0) { 5509114700bSCezary Rojewski dev_err(dai->dev, "bind FE <-> BE failed: %d\n", ret); 5519114700bSCezary Rojewski goto bind_err; 5529114700bSCezary Rojewski } 5539114700bSCezary Rojewski 5549114700bSCezary Rojewski return 0; 5559114700bSCezary Rojewski 5569114700bSCezary Rojewski bind_err: 5579114700bSCezary Rojewski avs_path_free(data->path); 5589114700bSCezary Rojewski data->path = NULL; 5599114700bSCezary Rojewski create_err: 5609114700bSCezary Rojewski snd_pcm_lib_free_pages(substream); 5619114700bSCezary Rojewski return ret; 5629114700bSCezary Rojewski } 5639114700bSCezary Rojewski 5640abfc84bSCezary Rojewski static int __avs_dai_fe_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) 5659114700bSCezary Rojewski { 5669114700bSCezary Rojewski struct avs_dma_data *data; 5679114700bSCezary Rojewski struct hdac_ext_stream *host_stream; 5689114700bSCezary Rojewski int ret; 5699114700bSCezary Rojewski 5709114700bSCezary Rojewski dev_dbg(dai->dev, "%s fe HW_FREE str %p rtd %p", 5719114700bSCezary Rojewski __func__, substream, substream->runtime); 5729114700bSCezary Rojewski 5739114700bSCezary Rojewski data = snd_soc_dai_get_dma_data(dai, substream); 5749114700bSCezary Rojewski if (!data->path) 5759114700bSCezary Rojewski return 0; 5769114700bSCezary Rojewski 5779114700bSCezary Rojewski host_stream = data->host_stream; 5789114700bSCezary Rojewski 5799114700bSCezary Rojewski ret = avs_path_unbind(data->path); 5809114700bSCezary Rojewski if (ret < 0) 5819114700bSCezary Rojewski dev_err(dai->dev, "unbind FE <-> BE failed: %d\n", ret); 5829114700bSCezary Rojewski 5839114700bSCezary Rojewski avs_path_free(data->path); 5849114700bSCezary Rojewski data->path = NULL; 5859114700bSCezary Rojewski snd_hdac_stream_cleanup(hdac_stream(host_stream)); 5869114700bSCezary Rojewski hdac_stream(host_stream)->prepared = false; 5879114700bSCezary Rojewski 5880abfc84bSCezary Rojewski return ret; 5890abfc84bSCezary Rojewski } 5900abfc84bSCezary Rojewski 5910abfc84bSCezary Rojewski static int avs_dai_fe_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) 5920abfc84bSCezary Rojewski { 5930abfc84bSCezary Rojewski int ret; 5940abfc84bSCezary Rojewski 5950abfc84bSCezary Rojewski ret = __avs_dai_fe_hw_free(substream, dai); 5960abfc84bSCezary Rojewski snd_pcm_lib_free_pages(substream); 5979114700bSCezary Rojewski 5989114700bSCezary Rojewski return ret; 5999114700bSCezary Rojewski } 6009114700bSCezary Rojewski 6019114700bSCezary Rojewski static int avs_dai_fe_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) 6029114700bSCezary Rojewski { 6039114700bSCezary Rojewski struct snd_pcm_runtime *runtime = substream->runtime; 6049114700bSCezary Rojewski struct avs_dma_data *data; 6059114700bSCezary Rojewski struct avs_dev *adev = to_avs_dev(dai->dev); 6069114700bSCezary Rojewski struct hdac_ext_stream *host_stream; 6079114700bSCezary Rojewski struct hdac_bus *bus; 6089114700bSCezary Rojewski unsigned int format_val; 6099114700bSCezary Rojewski int ret; 6109114700bSCezary Rojewski 6119114700bSCezary Rojewski data = snd_soc_dai_get_dma_data(dai, substream); 6129114700bSCezary Rojewski host_stream = data->host_stream; 6139114700bSCezary Rojewski 6149114700bSCezary Rojewski if (hdac_stream(host_stream)->prepared) 6159114700bSCezary Rojewski return 0; 6169114700bSCezary Rojewski 6179114700bSCezary Rojewski bus = hdac_stream(host_stream)->bus; 6189114700bSCezary Rojewski snd_hdac_ext_stream_decouple(bus, data->host_stream, true); 6199114700bSCezary Rojewski snd_hdac_stream_reset(hdac_stream(host_stream)); 6209114700bSCezary Rojewski 6219114700bSCezary Rojewski format_val = snd_hdac_calc_stream_format(runtime->rate, runtime->channels, runtime->format, 6229114700bSCezary Rojewski runtime->sample_bits, 0); 6239114700bSCezary Rojewski 6249114700bSCezary Rojewski ret = snd_hdac_stream_set_params(hdac_stream(host_stream), format_val); 6259114700bSCezary Rojewski if (ret < 0) 6269114700bSCezary Rojewski return ret; 6279114700bSCezary Rojewski 6289114700bSCezary Rojewski ret = snd_hdac_stream_setup(hdac_stream(host_stream)); 6299114700bSCezary Rojewski if (ret < 0) 6309114700bSCezary Rojewski return ret; 6319114700bSCezary Rojewski 6329114700bSCezary Rojewski ret = avs_dai_prepare(adev, substream, dai); 6339114700bSCezary Rojewski if (ret) 6349114700bSCezary Rojewski return ret; 6359114700bSCezary Rojewski 6369114700bSCezary Rojewski hdac_stream(host_stream)->prepared = true; 6379114700bSCezary Rojewski return 0; 6389114700bSCezary Rojewski } 6399114700bSCezary Rojewski 6409114700bSCezary Rojewski static int avs_dai_fe_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai) 6419114700bSCezary Rojewski { 6428f28299fSCezary Rojewski struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 6439114700bSCezary Rojewski struct avs_dma_data *data; 6449114700bSCezary Rojewski struct hdac_ext_stream *host_stream; 6459114700bSCezary Rojewski struct hdac_bus *bus; 6469114700bSCezary Rojewski unsigned long flags; 6479114700bSCezary Rojewski int ret = 0; 6489114700bSCezary Rojewski 6499114700bSCezary Rojewski data = snd_soc_dai_get_dma_data(dai, substream); 6509114700bSCezary Rojewski host_stream = data->host_stream; 6519114700bSCezary Rojewski bus = hdac_stream(host_stream)->bus; 6529114700bSCezary Rojewski 6539114700bSCezary Rojewski switch (cmd) { 6548e097f9aSAmadeusz Sławiński case SNDRV_PCM_TRIGGER_RESUME: 6558e097f9aSAmadeusz Sławiński if (rtd->dai_link->ignore_suspend) 6568e097f9aSAmadeusz Sławiński break; 6578e097f9aSAmadeusz Sławiński fallthrough; 6589114700bSCezary Rojewski case SNDRV_PCM_TRIGGER_START: 6599114700bSCezary Rojewski case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 6609114700bSCezary Rojewski spin_lock_irqsave(&bus->reg_lock, flags); 6614fe20d62SZhang Yiqun snd_hdac_stream_start(hdac_stream(host_stream)); 6629114700bSCezary Rojewski spin_unlock_irqrestore(&bus->reg_lock, flags); 6639114700bSCezary Rojewski 6648e097f9aSAmadeusz Sławiński /* Timeout on DRSM poll shall not stop the resume so ignore the result. */ 6658e097f9aSAmadeusz Sławiński if (cmd == SNDRV_PCM_TRIGGER_RESUME) 6668e097f9aSAmadeusz Sławiński snd_hdac_stream_wait_drsm(hdac_stream(host_stream)); 6678e097f9aSAmadeusz Sławiński 6688e097f9aSAmadeusz Sławiński ret = avs_path_pause(data->path); 6698e097f9aSAmadeusz Sławiński if (ret < 0) { 6708e097f9aSAmadeusz Sławiński dev_err(dai->dev, "pause FE path failed: %d\n", ret); 6718e097f9aSAmadeusz Sławiński break; 6728e097f9aSAmadeusz Sławiński } 6738e097f9aSAmadeusz Sławiński 6749114700bSCezary Rojewski ret = avs_path_run(data->path, AVS_TPLG_TRIGGER_AUTO); 6759114700bSCezary Rojewski if (ret < 0) 6769114700bSCezary Rojewski dev_err(dai->dev, "run FE path failed: %d\n", ret); 6778e097f9aSAmadeusz Sławiński 6789114700bSCezary Rojewski break; 6799114700bSCezary Rojewski 6808e097f9aSAmadeusz Sławiński case SNDRV_PCM_TRIGGER_SUSPEND: 6818e097f9aSAmadeusz Sławiński if (rtd->dai_link->ignore_suspend) 6828e097f9aSAmadeusz Sławiński break; 6838e097f9aSAmadeusz Sławiński fallthrough; 6849114700bSCezary Rojewski case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 6859114700bSCezary Rojewski case SNDRV_PCM_TRIGGER_STOP: 6869114700bSCezary Rojewski ret = avs_path_pause(data->path); 6879114700bSCezary Rojewski if (ret < 0) 6889114700bSCezary Rojewski dev_err(dai->dev, "pause FE path failed: %d\n", ret); 6899114700bSCezary Rojewski 6909114700bSCezary Rojewski spin_lock_irqsave(&bus->reg_lock, flags); 6919114700bSCezary Rojewski snd_hdac_stream_stop(hdac_stream(host_stream)); 6929114700bSCezary Rojewski spin_unlock_irqrestore(&bus->reg_lock, flags); 6939114700bSCezary Rojewski 6949114700bSCezary Rojewski ret = avs_path_reset(data->path); 6959114700bSCezary Rojewski if (ret < 0) 6969114700bSCezary Rojewski dev_err(dai->dev, "reset FE path failed: %d\n", ret); 6979114700bSCezary Rojewski break; 6989114700bSCezary Rojewski 6999114700bSCezary Rojewski default: 7009114700bSCezary Rojewski ret = -EINVAL; 7019114700bSCezary Rojewski break; 7029114700bSCezary Rojewski } 7039114700bSCezary Rojewski 7049114700bSCezary Rojewski return ret; 7059114700bSCezary Rojewski } 7069114700bSCezary Rojewski 7079114700bSCezary Rojewski const struct snd_soc_dai_ops avs_dai_fe_ops = { 7089114700bSCezary Rojewski .startup = avs_dai_fe_startup, 7099114700bSCezary Rojewski .shutdown = avs_dai_fe_shutdown, 7109114700bSCezary Rojewski .hw_params = avs_dai_fe_hw_params, 7119114700bSCezary Rojewski .hw_free = avs_dai_fe_hw_free, 7129114700bSCezary Rojewski .prepare = avs_dai_fe_prepare, 7139114700bSCezary Rojewski .trigger = avs_dai_fe_trigger, 7149114700bSCezary Rojewski }; 7159114700bSCezary Rojewski 716f1b3b320SCezary Rojewski static ssize_t topology_name_read(struct file *file, char __user *user_buf, size_t count, 717f1b3b320SCezary Rojewski loff_t *ppos) 718f1b3b320SCezary Rojewski { 719f1b3b320SCezary Rojewski struct snd_soc_component *component = file->private_data; 720f1b3b320SCezary Rojewski struct snd_soc_card *card = component->card; 721f1b3b320SCezary Rojewski struct snd_soc_acpi_mach *mach = dev_get_platdata(card->dev); 722f1b3b320SCezary Rojewski char buf[64]; 723f1b3b320SCezary Rojewski size_t len; 724f1b3b320SCezary Rojewski 725ca3b7b9dSTakashi Iwai len = scnprintf(buf, sizeof(buf), "%s/%s\n", component->driver->topology_name_prefix, 726f1b3b320SCezary Rojewski mach->tplg_filename); 727f1b3b320SCezary Rojewski 728f1b3b320SCezary Rojewski return simple_read_from_buffer(user_buf, count, ppos, buf, len); 729f1b3b320SCezary Rojewski } 730f1b3b320SCezary Rojewski 731f1b3b320SCezary Rojewski static const struct file_operations topology_name_fops = { 732f1b3b320SCezary Rojewski .open = simple_open, 733f1b3b320SCezary Rojewski .read = topology_name_read, 734f1b3b320SCezary Rojewski .llseek = default_llseek, 735f1b3b320SCezary Rojewski }; 736f1b3b320SCezary Rojewski 737f1b3b320SCezary Rojewski static int avs_component_load_libraries(struct avs_soc_component *acomp) 738f1b3b320SCezary Rojewski { 739f1b3b320SCezary Rojewski struct avs_tplg *tplg = acomp->tplg; 740f1b3b320SCezary Rojewski struct avs_dev *adev = to_avs_dev(acomp->base.dev); 741f1b3b320SCezary Rojewski int ret; 742f1b3b320SCezary Rojewski 743f1b3b320SCezary Rojewski if (!tplg->num_libs) 744f1b3b320SCezary Rojewski return 0; 745f1b3b320SCezary Rojewski 746f1b3b320SCezary Rojewski /* Parent device may be asleep and library loading involves IPCs. */ 747f1b3b320SCezary Rojewski ret = pm_runtime_resume_and_get(adev->dev); 748f1b3b320SCezary Rojewski if (ret < 0) 749f1b3b320SCezary Rojewski return ret; 750f1b3b320SCezary Rojewski 751758ba92fSCezary Rojewski avs_hda_power_gating_enable(adev, false); 752f1b3b320SCezary Rojewski avs_hda_clock_gating_enable(adev, false); 753f1b3b320SCezary Rojewski avs_hda_l1sen_enable(adev, false); 754f1b3b320SCezary Rojewski 755f1b3b320SCezary Rojewski ret = avs_dsp_load_libraries(adev, tplg->libs, tplg->num_libs); 756f1b3b320SCezary Rojewski 757f1b3b320SCezary Rojewski avs_hda_l1sen_enable(adev, true); 758f1b3b320SCezary Rojewski avs_hda_clock_gating_enable(adev, true); 759758ba92fSCezary Rojewski avs_hda_power_gating_enable(adev, true); 760f1b3b320SCezary Rojewski 761f1b3b320SCezary Rojewski if (!ret) 762f1b3b320SCezary Rojewski ret = avs_module_info_init(adev, false); 763f1b3b320SCezary Rojewski 764f1b3b320SCezary Rojewski pm_runtime_mark_last_busy(adev->dev); 765f1b3b320SCezary Rojewski pm_runtime_put_autosuspend(adev->dev); 766f1b3b320SCezary Rojewski 767f1b3b320SCezary Rojewski return ret; 768f1b3b320SCezary Rojewski } 769f1b3b320SCezary Rojewski 770f1b3b320SCezary Rojewski static int avs_component_probe(struct snd_soc_component *component) 771f1b3b320SCezary Rojewski { 772f1b3b320SCezary Rojewski struct snd_soc_card *card = component->card; 773f1b3b320SCezary Rojewski struct snd_soc_acpi_mach *mach; 774f1b3b320SCezary Rojewski struct avs_soc_component *acomp; 775f1b3b320SCezary Rojewski struct avs_dev *adev; 776f1b3b320SCezary Rojewski char *filename; 777f1b3b320SCezary Rojewski int ret; 778f1b3b320SCezary Rojewski 779f1b3b320SCezary Rojewski dev_dbg(card->dev, "probing %s card %s\n", component->name, card->name); 780f1b3b320SCezary Rojewski mach = dev_get_platdata(card->dev); 781f1b3b320SCezary Rojewski acomp = to_avs_soc_component(component); 782f1b3b320SCezary Rojewski adev = to_avs_dev(component->dev); 783f1b3b320SCezary Rojewski 784f1b3b320SCezary Rojewski acomp->tplg = avs_tplg_new(component); 785f1b3b320SCezary Rojewski if (!acomp->tplg) 786f1b3b320SCezary Rojewski return -ENOMEM; 787f1b3b320SCezary Rojewski 788f1b3b320SCezary Rojewski if (!mach->tplg_filename) 789f1b3b320SCezary Rojewski goto finalize; 790f1b3b320SCezary Rojewski 791f1b3b320SCezary Rojewski /* Load specified topology and create debugfs for it. */ 792f1b3b320SCezary Rojewski filename = kasprintf(GFP_KERNEL, "%s/%s", component->driver->topology_name_prefix, 793f1b3b320SCezary Rojewski mach->tplg_filename); 794f1b3b320SCezary Rojewski if (!filename) 795f1b3b320SCezary Rojewski return -ENOMEM; 796f1b3b320SCezary Rojewski 797f1b3b320SCezary Rojewski ret = avs_load_topology(component, filename); 798f1b3b320SCezary Rojewski kfree(filename); 799739c0311SAmadeusz Sławiński if (ret == -ENOENT && !strncmp(mach->tplg_filename, "hda-", 4)) { 800739c0311SAmadeusz Sławiński unsigned int vendor_id; 801739c0311SAmadeusz Sławiński 802739c0311SAmadeusz Sławiński if (sscanf(mach->tplg_filename, "hda-%08x-tplg.bin", &vendor_id) != 1) 803739c0311SAmadeusz Sławiński return ret; 804739c0311SAmadeusz Sławiński 805739c0311SAmadeusz Sławiński if (((vendor_id >> 16) & 0xFFFF) == 0x8086) 806739c0311SAmadeusz Sławiński mach->tplg_filename = devm_kasprintf(adev->dev, GFP_KERNEL, 807739c0311SAmadeusz Sławiński "hda-8086-generic-tplg.bin"); 808739c0311SAmadeusz Sławiński else 809739c0311SAmadeusz Sławiński mach->tplg_filename = devm_kasprintf(adev->dev, GFP_KERNEL, 810739c0311SAmadeusz Sławiński "hda-generic-tplg.bin"); 811739c0311SAmadeusz Sławiński 812739c0311SAmadeusz Sławiński filename = kasprintf(GFP_KERNEL, "%s/%s", component->driver->topology_name_prefix, 813739c0311SAmadeusz Sławiński mach->tplg_filename); 814739c0311SAmadeusz Sławiński if (!filename) 815739c0311SAmadeusz Sławiński return -ENOMEM; 816739c0311SAmadeusz Sławiński 817739c0311SAmadeusz Sławiński dev_info(card->dev, "trying to load fallback topology %s\n", mach->tplg_filename); 818739c0311SAmadeusz Sławiński ret = avs_load_topology(component, filename); 819739c0311SAmadeusz Sławiński kfree(filename); 820739c0311SAmadeusz Sławiński } 821f1b3b320SCezary Rojewski if (ret < 0) 822f1b3b320SCezary Rojewski return ret; 823f1b3b320SCezary Rojewski 824f1b3b320SCezary Rojewski ret = avs_component_load_libraries(acomp); 825f1b3b320SCezary Rojewski if (ret < 0) { 826f1b3b320SCezary Rojewski dev_err(card->dev, "libraries loading failed: %d\n", ret); 827f1b3b320SCezary Rojewski goto err_load_libs; 828f1b3b320SCezary Rojewski } 829f1b3b320SCezary Rojewski 830f1b3b320SCezary Rojewski finalize: 831f1b3b320SCezary Rojewski debugfs_create_file("topology_name", 0444, component->debugfs_root, component, 832f1b3b320SCezary Rojewski &topology_name_fops); 833f1b3b320SCezary Rojewski 834f1b3b320SCezary Rojewski mutex_lock(&adev->comp_list_mutex); 835f1b3b320SCezary Rojewski list_add_tail(&acomp->node, &adev->comp_list); 836f1b3b320SCezary Rojewski mutex_unlock(&adev->comp_list_mutex); 837f1b3b320SCezary Rojewski 838f1b3b320SCezary Rojewski return 0; 839f1b3b320SCezary Rojewski 840f1b3b320SCezary Rojewski err_load_libs: 841f1b3b320SCezary Rojewski avs_remove_topology(component); 842f1b3b320SCezary Rojewski return ret; 843f1b3b320SCezary Rojewski } 844f1b3b320SCezary Rojewski 845f1b3b320SCezary Rojewski static void avs_component_remove(struct snd_soc_component *component) 846f1b3b320SCezary Rojewski { 847f1b3b320SCezary Rojewski struct avs_soc_component *acomp = to_avs_soc_component(component); 848f1b3b320SCezary Rojewski struct snd_soc_acpi_mach *mach; 849f1b3b320SCezary Rojewski struct avs_dev *adev = to_avs_dev(component->dev); 850f1b3b320SCezary Rojewski int ret; 851f1b3b320SCezary Rojewski 852f1b3b320SCezary Rojewski mach = dev_get_platdata(component->card->dev); 853f1b3b320SCezary Rojewski 854f1b3b320SCezary Rojewski mutex_lock(&adev->comp_list_mutex); 855f1b3b320SCezary Rojewski list_del(&acomp->node); 856f1b3b320SCezary Rojewski mutex_unlock(&adev->comp_list_mutex); 857f1b3b320SCezary Rojewski 858f1b3b320SCezary Rojewski if (mach->tplg_filename) { 859f1b3b320SCezary Rojewski ret = avs_remove_topology(component); 860f1b3b320SCezary Rojewski if (ret < 0) 861f1b3b320SCezary Rojewski dev_err(component->dev, "unload topology failed: %d\n", ret); 862f1b3b320SCezary Rojewski } 863f1b3b320SCezary Rojewski } 864f1b3b320SCezary Rojewski 8652b9a50eaSCezary Rojewski static int avs_dai_resume_hw_params(struct snd_soc_dai *dai, struct avs_dma_data *data) 8662b9a50eaSCezary Rojewski { 8672b9a50eaSCezary Rojewski struct snd_pcm_substream *substream; 8682b9a50eaSCezary Rojewski struct snd_soc_pcm_runtime *rtd; 8692b9a50eaSCezary Rojewski int ret; 8702b9a50eaSCezary Rojewski 8712b9a50eaSCezary Rojewski substream = data->substream; 8728f28299fSCezary Rojewski rtd = asoc_substream_to_rtd(substream); 8732b9a50eaSCezary Rojewski 8742b9a50eaSCezary Rojewski ret = dai->driver->ops->hw_params(substream, &rtd->dpcm[substream->stream].hw_params, dai); 8752b9a50eaSCezary Rojewski if (ret) 8762b9a50eaSCezary Rojewski dev_err(dai->dev, "hw_params on resume failed: %d\n", ret); 8772b9a50eaSCezary Rojewski 8782b9a50eaSCezary Rojewski return ret; 8792b9a50eaSCezary Rojewski } 8802b9a50eaSCezary Rojewski 8812b9a50eaSCezary Rojewski static int avs_dai_resume_fe_prepare(struct snd_soc_dai *dai, struct avs_dma_data *data) 8822b9a50eaSCezary Rojewski { 8832b9a50eaSCezary Rojewski struct hdac_ext_stream *host_stream; 8842b9a50eaSCezary Rojewski struct hdac_stream *hstream; 8852b9a50eaSCezary Rojewski struct hdac_bus *bus; 8862b9a50eaSCezary Rojewski int ret; 8872b9a50eaSCezary Rojewski 8882b9a50eaSCezary Rojewski host_stream = data->host_stream; 8892b9a50eaSCezary Rojewski hstream = hdac_stream(host_stream); 8902b9a50eaSCezary Rojewski bus = hdac_stream(host_stream)->bus; 8912b9a50eaSCezary Rojewski 8922b9a50eaSCezary Rojewski /* Set DRSM before programming stream and position registers. */ 8932b9a50eaSCezary Rojewski snd_hdac_stream_drsm_enable(bus, true, hstream->index); 8942b9a50eaSCezary Rojewski 8952b9a50eaSCezary Rojewski ret = dai->driver->ops->prepare(data->substream, dai); 8962b9a50eaSCezary Rojewski if (ret) { 8972b9a50eaSCezary Rojewski dev_err(dai->dev, "prepare FE on resume failed: %d\n", ret); 8982b9a50eaSCezary Rojewski return ret; 8992b9a50eaSCezary Rojewski } 9002b9a50eaSCezary Rojewski 9012b9a50eaSCezary Rojewski writel(host_stream->pphcllpl, host_stream->pphc_addr + AZX_REG_PPHCLLPL); 9022b9a50eaSCezary Rojewski writel(host_stream->pphcllpu, host_stream->pphc_addr + AZX_REG_PPHCLLPU); 9032b9a50eaSCezary Rojewski writel(host_stream->pphcldpl, host_stream->pphc_addr + AZX_REG_PPHCLDPL); 9042b9a50eaSCezary Rojewski writel(host_stream->pphcldpu, host_stream->pphc_addr + AZX_REG_PPHCLDPU); 9052b9a50eaSCezary Rojewski 9062b9a50eaSCezary Rojewski /* As per HW spec recommendation, program LPIB and DPIB to the same value. */ 9072b9a50eaSCezary Rojewski snd_hdac_stream_set_lpib(hstream, hstream->lpib); 9082b9a50eaSCezary Rojewski snd_hdac_stream_set_dpibr(bus, hstream, hstream->lpib); 9092b9a50eaSCezary Rojewski 9102b9a50eaSCezary Rojewski return 0; 9112b9a50eaSCezary Rojewski } 9122b9a50eaSCezary Rojewski 9132b9a50eaSCezary Rojewski static int avs_dai_resume_be_prepare(struct snd_soc_dai *dai, struct avs_dma_data *data) 9142b9a50eaSCezary Rojewski { 9152b9a50eaSCezary Rojewski int ret; 9162b9a50eaSCezary Rojewski 9172b9a50eaSCezary Rojewski ret = dai->driver->ops->prepare(data->substream, dai); 9182b9a50eaSCezary Rojewski if (ret) 9192b9a50eaSCezary Rojewski dev_err(dai->dev, "prepare BE on resume failed: %d\n", ret); 9202b9a50eaSCezary Rojewski 9212b9a50eaSCezary Rojewski return ret; 9222b9a50eaSCezary Rojewski } 9232b9a50eaSCezary Rojewski 9242b9a50eaSCezary Rojewski static int avs_dai_suspend_fe_hw_free(struct snd_soc_dai *dai, struct avs_dma_data *data) 9252b9a50eaSCezary Rojewski { 9262b9a50eaSCezary Rojewski struct hdac_ext_stream *host_stream; 9272b9a50eaSCezary Rojewski int ret; 9282b9a50eaSCezary Rojewski 9292b9a50eaSCezary Rojewski host_stream = data->host_stream; 9302b9a50eaSCezary Rojewski 9312b9a50eaSCezary Rojewski /* Store position addresses so we can resume from them later on. */ 9322b9a50eaSCezary Rojewski hdac_stream(host_stream)->lpib = snd_hdac_stream_get_pos_lpib(hdac_stream(host_stream)); 9332b9a50eaSCezary Rojewski host_stream->pphcllpl = readl(host_stream->pphc_addr + AZX_REG_PPHCLLPL); 9342b9a50eaSCezary Rojewski host_stream->pphcllpu = readl(host_stream->pphc_addr + AZX_REG_PPHCLLPU); 9352b9a50eaSCezary Rojewski host_stream->pphcldpl = readl(host_stream->pphc_addr + AZX_REG_PPHCLDPL); 9362b9a50eaSCezary Rojewski host_stream->pphcldpu = readl(host_stream->pphc_addr + AZX_REG_PPHCLDPU); 9372b9a50eaSCezary Rojewski 9382b9a50eaSCezary Rojewski ret = __avs_dai_fe_hw_free(data->substream, dai); 9392b9a50eaSCezary Rojewski if (ret < 0) 9402b9a50eaSCezary Rojewski dev_err(dai->dev, "hw_free FE on suspend failed: %d\n", ret); 9412b9a50eaSCezary Rojewski 9422b9a50eaSCezary Rojewski return ret; 9432b9a50eaSCezary Rojewski } 9442b9a50eaSCezary Rojewski 9452b9a50eaSCezary Rojewski static int avs_dai_suspend_be_hw_free(struct snd_soc_dai *dai, struct avs_dma_data *data) 9462b9a50eaSCezary Rojewski { 9472b9a50eaSCezary Rojewski int ret; 9482b9a50eaSCezary Rojewski 9492b9a50eaSCezary Rojewski ret = dai->driver->ops->hw_free(data->substream, dai); 9502b9a50eaSCezary Rojewski if (ret < 0) 9512b9a50eaSCezary Rojewski dev_err(dai->dev, "hw_free BE on suspend failed: %d\n", ret); 9522b9a50eaSCezary Rojewski 9532b9a50eaSCezary Rojewski return ret; 9542b9a50eaSCezary Rojewski } 9552b9a50eaSCezary Rojewski 9562b9a50eaSCezary Rojewski static int avs_component_pm_op(struct snd_soc_component *component, bool be, 9572b9a50eaSCezary Rojewski int (*op)(struct snd_soc_dai *, struct avs_dma_data *)) 9582b9a50eaSCezary Rojewski { 9592b9a50eaSCezary Rojewski struct snd_soc_pcm_runtime *rtd; 9602b9a50eaSCezary Rojewski struct avs_dma_data *data; 9612b9a50eaSCezary Rojewski struct snd_soc_dai *dai; 9622b9a50eaSCezary Rojewski int ret; 9632b9a50eaSCezary Rojewski 9642b9a50eaSCezary Rojewski for_each_component_dais(component, dai) { 965ec4b2099SKuninori Morimoto data = snd_soc_dai_dma_data_get_playback(dai); 9662b9a50eaSCezary Rojewski if (data) { 9678f28299fSCezary Rojewski rtd = asoc_substream_to_rtd(data->substream); 9682b9a50eaSCezary Rojewski if (rtd->dai_link->no_pcm == be && !rtd->dai_link->ignore_suspend) { 9692b9a50eaSCezary Rojewski ret = op(dai, data); 970f3fbb553SCezary Rojewski if (ret < 0) { 971f3fbb553SCezary Rojewski __snd_pcm_set_state(data->substream->runtime, 972f3fbb553SCezary Rojewski SNDRV_PCM_STATE_DISCONNECTED); 9732b9a50eaSCezary Rojewski return ret; 9742b9a50eaSCezary Rojewski } 9752b9a50eaSCezary Rojewski } 976f3fbb553SCezary Rojewski } 9772b9a50eaSCezary Rojewski 978ec4b2099SKuninori Morimoto data = snd_soc_dai_dma_data_get_capture(dai); 9792b9a50eaSCezary Rojewski if (data) { 9808f28299fSCezary Rojewski rtd = asoc_substream_to_rtd(data->substream); 9812b9a50eaSCezary Rojewski if (rtd->dai_link->no_pcm == be && !rtd->dai_link->ignore_suspend) { 9822b9a50eaSCezary Rojewski ret = op(dai, data); 983f3fbb553SCezary Rojewski if (ret < 0) { 984f3fbb553SCezary Rojewski __snd_pcm_set_state(data->substream->runtime, 985f3fbb553SCezary Rojewski SNDRV_PCM_STATE_DISCONNECTED); 9862b9a50eaSCezary Rojewski return ret; 9872b9a50eaSCezary Rojewski } 9882b9a50eaSCezary Rojewski } 9892b9a50eaSCezary Rojewski } 990f3fbb553SCezary Rojewski } 9912b9a50eaSCezary Rojewski 9922b9a50eaSCezary Rojewski return 0; 9932b9a50eaSCezary Rojewski } 9942b9a50eaSCezary Rojewski 9952b9a50eaSCezary Rojewski static int avs_component_resume_hw_params(struct snd_soc_component *component, bool be) 9962b9a50eaSCezary Rojewski { 9972b9a50eaSCezary Rojewski return avs_component_pm_op(component, be, &avs_dai_resume_hw_params); 9982b9a50eaSCezary Rojewski } 9992b9a50eaSCezary Rojewski 10002b9a50eaSCezary Rojewski static int avs_component_resume_prepare(struct snd_soc_component *component, bool be) 10012b9a50eaSCezary Rojewski { 10022b9a50eaSCezary Rojewski int (*prepare_cb)(struct snd_soc_dai *dai, struct avs_dma_data *data); 10032b9a50eaSCezary Rojewski 10042b9a50eaSCezary Rojewski if (be) 10052b9a50eaSCezary Rojewski prepare_cb = &avs_dai_resume_be_prepare; 10062b9a50eaSCezary Rojewski else 10072b9a50eaSCezary Rojewski prepare_cb = &avs_dai_resume_fe_prepare; 10082b9a50eaSCezary Rojewski 10092b9a50eaSCezary Rojewski return avs_component_pm_op(component, be, prepare_cb); 10102b9a50eaSCezary Rojewski } 10112b9a50eaSCezary Rojewski 10122b9a50eaSCezary Rojewski static int avs_component_suspend_hw_free(struct snd_soc_component *component, bool be) 10132b9a50eaSCezary Rojewski { 10142b9a50eaSCezary Rojewski int (*hw_free_cb)(struct snd_soc_dai *dai, struct avs_dma_data *data); 10152b9a50eaSCezary Rojewski 10162b9a50eaSCezary Rojewski if (be) 10172b9a50eaSCezary Rojewski hw_free_cb = &avs_dai_suspend_be_hw_free; 10182b9a50eaSCezary Rojewski else 10192b9a50eaSCezary Rojewski hw_free_cb = &avs_dai_suspend_fe_hw_free; 10202b9a50eaSCezary Rojewski 10212b9a50eaSCezary Rojewski return avs_component_pm_op(component, be, hw_free_cb); 10222b9a50eaSCezary Rojewski } 10232b9a50eaSCezary Rojewski 10242b9a50eaSCezary Rojewski static int avs_component_suspend(struct snd_soc_component *component) 10252b9a50eaSCezary Rojewski { 10262b9a50eaSCezary Rojewski int ret; 10272b9a50eaSCezary Rojewski 10282b9a50eaSCezary Rojewski /* 10292b9a50eaSCezary Rojewski * When freeing paths, FEs need to be first as they perform 10302b9a50eaSCezary Rojewski * path unbinding. 10312b9a50eaSCezary Rojewski */ 10322b9a50eaSCezary Rojewski ret = avs_component_suspend_hw_free(component, false); 10332b9a50eaSCezary Rojewski if (ret) 10342b9a50eaSCezary Rojewski return ret; 10352b9a50eaSCezary Rojewski 10362b9a50eaSCezary Rojewski return avs_component_suspend_hw_free(component, true); 10372b9a50eaSCezary Rojewski } 10382b9a50eaSCezary Rojewski 10392b9a50eaSCezary Rojewski static int avs_component_resume(struct snd_soc_component *component) 10402b9a50eaSCezary Rojewski { 10412b9a50eaSCezary Rojewski int ret; 10422b9a50eaSCezary Rojewski 10432b9a50eaSCezary Rojewski /* 10442b9a50eaSCezary Rojewski * When creating paths, FEs need to be last as they perform 10452b9a50eaSCezary Rojewski * path binding. 10462b9a50eaSCezary Rojewski */ 10472b9a50eaSCezary Rojewski ret = avs_component_resume_hw_params(component, true); 10482b9a50eaSCezary Rojewski if (ret) 10492b9a50eaSCezary Rojewski return ret; 10502b9a50eaSCezary Rojewski 10512b9a50eaSCezary Rojewski ret = avs_component_resume_hw_params(component, false); 10522b9a50eaSCezary Rojewski if (ret) 10532b9a50eaSCezary Rojewski return ret; 10542b9a50eaSCezary Rojewski 10552b9a50eaSCezary Rojewski /* It is expected that the LINK stream is prepared first. */ 10562b9a50eaSCezary Rojewski ret = avs_component_resume_prepare(component, true); 10572b9a50eaSCezary Rojewski if (ret) 10582b9a50eaSCezary Rojewski return ret; 10592b9a50eaSCezary Rojewski 10602b9a50eaSCezary Rojewski return avs_component_resume_prepare(component, false); 10612b9a50eaSCezary Rojewski } 10622b9a50eaSCezary Rojewski 1063eb0699c4SCezary Rojewski static const struct snd_pcm_hardware avs_pcm_hardware = { 1064eb0699c4SCezary Rojewski .info = SNDRV_PCM_INFO_MMAP | 1065eb0699c4SCezary Rojewski SNDRV_PCM_INFO_MMAP_VALID | 1066eb0699c4SCezary Rojewski SNDRV_PCM_INFO_INTERLEAVED | 1067eb0699c4SCezary Rojewski SNDRV_PCM_INFO_PAUSE | 1068eb0699c4SCezary Rojewski SNDRV_PCM_INFO_RESUME | 1069eb0699c4SCezary Rojewski SNDRV_PCM_INFO_NO_PERIOD_WAKEUP, 1070eb0699c4SCezary Rojewski .formats = SNDRV_PCM_FMTBIT_S16_LE | 1071eb0699c4SCezary Rojewski SNDRV_PCM_FMTBIT_S24_LE | 1072eb0699c4SCezary Rojewski SNDRV_PCM_FMTBIT_S32_LE, 1073eb0699c4SCezary Rojewski .buffer_bytes_max = AZX_MAX_BUF_SIZE, 1074eb0699c4SCezary Rojewski .period_bytes_min = 128, 1075eb0699c4SCezary Rojewski .period_bytes_max = AZX_MAX_BUF_SIZE / 2, 1076eb0699c4SCezary Rojewski .periods_min = 2, 1077eb0699c4SCezary Rojewski .periods_max = AZX_MAX_FRAG, 1078eb0699c4SCezary Rojewski .fifo_size = 0, 1079eb0699c4SCezary Rojewski }; 1080eb0699c4SCezary Rojewski 1081f1b3b320SCezary Rojewski static int avs_component_open(struct snd_soc_component *component, 1082f1b3b320SCezary Rojewski struct snd_pcm_substream *substream) 1083f1b3b320SCezary Rojewski { 10848f28299fSCezary Rojewski struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 1085f1b3b320SCezary Rojewski 1086f1b3b320SCezary Rojewski /* only FE DAI links are handled here */ 1087f1b3b320SCezary Rojewski if (rtd->dai_link->no_pcm) 1088f1b3b320SCezary Rojewski return 0; 1089f1b3b320SCezary Rojewski 1090eb0699c4SCezary Rojewski return snd_soc_set_runtime_hwparams(substream, &avs_pcm_hardware); 1091f1b3b320SCezary Rojewski } 1092f1b3b320SCezary Rojewski 1093f1b3b320SCezary Rojewski static unsigned int avs_hda_stream_dpib_read(struct hdac_ext_stream *stream) 1094f1b3b320SCezary Rojewski { 1095f1b3b320SCezary Rojewski return readl(hdac_stream(stream)->bus->remap_addr + AZX_REG_VS_SDXDPIB_XBASE + 1096f1b3b320SCezary Rojewski (AZX_REG_VS_SDXDPIB_XINTERVAL * hdac_stream(stream)->index)); 1097f1b3b320SCezary Rojewski } 1098f1b3b320SCezary Rojewski 1099f1b3b320SCezary Rojewski static snd_pcm_uframes_t 1100f1b3b320SCezary Rojewski avs_component_pointer(struct snd_soc_component *component, struct snd_pcm_substream *substream) 1101f1b3b320SCezary Rojewski { 11028f28299fSCezary Rojewski struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 1103f1b3b320SCezary Rojewski struct avs_dma_data *data; 1104f1b3b320SCezary Rojewski struct hdac_ext_stream *host_stream; 1105f1b3b320SCezary Rojewski unsigned int pos; 1106f1b3b320SCezary Rojewski 1107f1b3b320SCezary Rojewski data = snd_soc_dai_get_dma_data(asoc_rtd_to_cpu(rtd, 0), substream); 1108f1b3b320SCezary Rojewski if (!data->host_stream) 1109f1b3b320SCezary Rojewski return 0; 1110f1b3b320SCezary Rojewski 1111f1b3b320SCezary Rojewski host_stream = data->host_stream; 1112f1b3b320SCezary Rojewski pos = avs_hda_stream_dpib_read(host_stream); 1113f1b3b320SCezary Rojewski 1114f1b3b320SCezary Rojewski if (pos >= hdac_stream(host_stream)->bufsize) 1115f1b3b320SCezary Rojewski pos = 0; 1116f1b3b320SCezary Rojewski 1117f1b3b320SCezary Rojewski return bytes_to_frames(substream->runtime, pos); 1118f1b3b320SCezary Rojewski } 1119f1b3b320SCezary Rojewski 1120f1b3b320SCezary Rojewski static int avs_component_mmap(struct snd_soc_component *component, 1121f1b3b320SCezary Rojewski struct snd_pcm_substream *substream, 1122f1b3b320SCezary Rojewski struct vm_area_struct *vma) 1123f1b3b320SCezary Rojewski { 1124f1b3b320SCezary Rojewski return snd_pcm_lib_default_mmap(substream, vma); 1125f1b3b320SCezary Rojewski } 1126f1b3b320SCezary Rojewski 1127f1b3b320SCezary Rojewski #define MAX_PREALLOC_SIZE (32 * 1024 * 1024) 1128f1b3b320SCezary Rojewski 1129f1b3b320SCezary Rojewski static int avs_component_construct(struct snd_soc_component *component, 1130f1b3b320SCezary Rojewski struct snd_soc_pcm_runtime *rtd) 1131f1b3b320SCezary Rojewski { 1132f1b3b320SCezary Rojewski struct snd_soc_dai *dai = asoc_rtd_to_cpu(rtd, 0); 1133f1b3b320SCezary Rojewski struct snd_pcm *pcm = rtd->pcm; 1134f1b3b320SCezary Rojewski 1135f1b3b320SCezary Rojewski if (dai->driver->playback.channels_min) 1136f1b3b320SCezary Rojewski snd_pcm_set_managed_buffer(pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream, 1137f1b3b320SCezary Rojewski SNDRV_DMA_TYPE_DEV_SG, component->dev, 0, 1138f1b3b320SCezary Rojewski MAX_PREALLOC_SIZE); 1139f1b3b320SCezary Rojewski 1140f1b3b320SCezary Rojewski if (dai->driver->capture.channels_min) 1141f1b3b320SCezary Rojewski snd_pcm_set_managed_buffer(pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream, 1142f1b3b320SCezary Rojewski SNDRV_DMA_TYPE_DEV_SG, component->dev, 0, 1143f1b3b320SCezary Rojewski MAX_PREALLOC_SIZE); 1144f1b3b320SCezary Rojewski 1145f1b3b320SCezary Rojewski return 0; 1146f1b3b320SCezary Rojewski } 1147f1b3b320SCezary Rojewski 1148f1b3b320SCezary Rojewski static const struct snd_soc_component_driver avs_component_driver = { 1149f1b3b320SCezary Rojewski .name = "avs-pcm", 1150f1b3b320SCezary Rojewski .probe = avs_component_probe, 1151f1b3b320SCezary Rojewski .remove = avs_component_remove, 11522b9a50eaSCezary Rojewski .suspend = avs_component_suspend, 11532b9a50eaSCezary Rojewski .resume = avs_component_resume, 1154f1b3b320SCezary Rojewski .open = avs_component_open, 1155f1b3b320SCezary Rojewski .pointer = avs_component_pointer, 1156f1b3b320SCezary Rojewski .mmap = avs_component_mmap, 1157f1b3b320SCezary Rojewski .pcm_construct = avs_component_construct, 1158f1b3b320SCezary Rojewski .module_get_upon_open = 1, /* increment refcount when a pcm is opened */ 1159f1b3b320SCezary Rojewski .topology_name_prefix = "intel/avs", 1160f1b3b320SCezary Rojewski }; 1161f1b3b320SCezary Rojewski 1162ed914a2aSCezary Rojewski int avs_soc_component_register(struct device *dev, const char *name, 1163f1b3b320SCezary Rojewski const struct snd_soc_component_driver *drv, 1164f1b3b320SCezary Rojewski struct snd_soc_dai_driver *cpu_dais, int num_cpu_dais) 1165f1b3b320SCezary Rojewski { 1166f1b3b320SCezary Rojewski struct avs_soc_component *acomp; 1167f1b3b320SCezary Rojewski int ret; 1168f1b3b320SCezary Rojewski 1169f1b3b320SCezary Rojewski acomp = devm_kzalloc(dev, sizeof(*acomp), GFP_KERNEL); 1170f1b3b320SCezary Rojewski if (!acomp) 1171f1b3b320SCezary Rojewski return -ENOMEM; 1172f1b3b320SCezary Rojewski 1173f1b3b320SCezary Rojewski ret = snd_soc_component_initialize(&acomp->base, drv, dev); 1174f1b3b320SCezary Rojewski if (ret < 0) 1175f1b3b320SCezary Rojewski return ret; 1176f1b3b320SCezary Rojewski 1177f1b3b320SCezary Rojewski /* force name change after ASoC is done with its init */ 1178f1b3b320SCezary Rojewski acomp->base.name = name; 1179f1b3b320SCezary Rojewski INIT_LIST_HEAD(&acomp->node); 1180f1b3b320SCezary Rojewski 1181f1b3b320SCezary Rojewski return snd_soc_add_component(&acomp->base, cpu_dais, num_cpu_dais); 1182f1b3b320SCezary Rojewski } 1183b9062f98SCezary Rojewski 1184b9062f98SCezary Rojewski static struct snd_soc_dai_driver dmic_cpu_dais[] = { 1185b9062f98SCezary Rojewski { 1186b9062f98SCezary Rojewski .name = "DMIC Pin", 1187b9062f98SCezary Rojewski .ops = &avs_dai_nonhda_be_ops, 1188b9062f98SCezary Rojewski .capture = { 1189b9062f98SCezary Rojewski .stream_name = "DMIC Rx", 1190b9062f98SCezary Rojewski .channels_min = 1, 1191b9062f98SCezary Rojewski .channels_max = 4, 1192b9062f98SCezary Rojewski .rates = SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_48000, 1193b9062f98SCezary Rojewski .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE, 1194b9062f98SCezary Rojewski }, 1195b9062f98SCezary Rojewski }, 1196b9062f98SCezary Rojewski { 1197b9062f98SCezary Rojewski .name = "DMIC WoV Pin", 1198b9062f98SCezary Rojewski .ops = &avs_dai_nonhda_be_ops, 1199b9062f98SCezary Rojewski .capture = { 1200b9062f98SCezary Rojewski .stream_name = "DMIC WoV Rx", 1201b9062f98SCezary Rojewski .channels_min = 1, 1202b9062f98SCezary Rojewski .channels_max = 4, 1203b9062f98SCezary Rojewski .rates = SNDRV_PCM_RATE_16000, 1204b9062f98SCezary Rojewski .formats = SNDRV_PCM_FMTBIT_S16_LE, 1205b9062f98SCezary Rojewski }, 1206b9062f98SCezary Rojewski }, 1207b9062f98SCezary Rojewski }; 1208b9062f98SCezary Rojewski 1209b9062f98SCezary Rojewski int avs_dmic_platform_register(struct avs_dev *adev, const char *name) 1210b9062f98SCezary Rojewski { 1211b9062f98SCezary Rojewski return avs_soc_component_register(adev->dev, name, &avs_component_driver, dmic_cpu_dais, 1212b9062f98SCezary Rojewski ARRAY_SIZE(dmic_cpu_dais)); 1213b9062f98SCezary Rojewski } 1214b9062f98SCezary Rojewski 1215b9062f98SCezary Rojewski static const struct snd_soc_dai_driver i2s_dai_template = { 1216b9062f98SCezary Rojewski .ops = &avs_dai_nonhda_be_ops, 1217b9062f98SCezary Rojewski .playback = { 1218b9062f98SCezary Rojewski .channels_min = 1, 1219b9062f98SCezary Rojewski .channels_max = 8, 1220b9062f98SCezary Rojewski .rates = SNDRV_PCM_RATE_8000_192000 | 1221b9062f98SCezary Rojewski SNDRV_PCM_RATE_KNOT, 1222b9062f98SCezary Rojewski .formats = SNDRV_PCM_FMTBIT_S16_LE | 1223b9062f98SCezary Rojewski SNDRV_PCM_FMTBIT_S24_LE | 1224b9062f98SCezary Rojewski SNDRV_PCM_FMTBIT_S32_LE, 1225b9062f98SCezary Rojewski }, 1226b9062f98SCezary Rojewski .capture = { 1227b9062f98SCezary Rojewski .channels_min = 1, 1228b9062f98SCezary Rojewski .channels_max = 8, 1229b9062f98SCezary Rojewski .rates = SNDRV_PCM_RATE_8000_192000 | 1230b9062f98SCezary Rojewski SNDRV_PCM_RATE_KNOT, 1231b9062f98SCezary Rojewski .formats = SNDRV_PCM_FMTBIT_S16_LE | 1232b9062f98SCezary Rojewski SNDRV_PCM_FMTBIT_S24_LE | 1233b9062f98SCezary Rojewski SNDRV_PCM_FMTBIT_S32_LE, 1234b9062f98SCezary Rojewski }, 1235b9062f98SCezary Rojewski }; 1236b9062f98SCezary Rojewski 1237b9062f98SCezary Rojewski int avs_i2s_platform_register(struct avs_dev *adev, const char *name, unsigned long port_mask, 1238b9062f98SCezary Rojewski unsigned long *tdms) 1239b9062f98SCezary Rojewski { 1240b9062f98SCezary Rojewski struct snd_soc_dai_driver *cpus, *dai; 1241b9062f98SCezary Rojewski size_t ssp_count, cpu_count; 1242b9062f98SCezary Rojewski int i, j; 1243b9062f98SCezary Rojewski 1244b9062f98SCezary Rojewski ssp_count = adev->hw_cfg.i2s_caps.ctrl_count; 1245b9062f98SCezary Rojewski cpu_count = hweight_long(port_mask); 1246b9062f98SCezary Rojewski if (tdms) 1247b9062f98SCezary Rojewski for_each_set_bit(i, &port_mask, ssp_count) 1248b9062f98SCezary Rojewski cpu_count += hweight_long(tdms[i]); 1249b9062f98SCezary Rojewski 1250b9062f98SCezary Rojewski cpus = devm_kzalloc(adev->dev, sizeof(*cpus) * cpu_count, GFP_KERNEL); 1251b9062f98SCezary Rojewski if (!cpus) 1252b9062f98SCezary Rojewski return -ENOMEM; 1253b9062f98SCezary Rojewski 1254b9062f98SCezary Rojewski dai = cpus; 1255b9062f98SCezary Rojewski for_each_set_bit(i, &port_mask, ssp_count) { 1256b9062f98SCezary Rojewski memcpy(dai, &i2s_dai_template, sizeof(*dai)); 1257b9062f98SCezary Rojewski 1258b9062f98SCezary Rojewski dai->name = 1259b9062f98SCezary Rojewski devm_kasprintf(adev->dev, GFP_KERNEL, "SSP%d Pin", i); 1260b9062f98SCezary Rojewski dai->playback.stream_name = 1261b9062f98SCezary Rojewski devm_kasprintf(adev->dev, GFP_KERNEL, "ssp%d Tx", i); 1262b9062f98SCezary Rojewski dai->capture.stream_name = 1263b9062f98SCezary Rojewski devm_kasprintf(adev->dev, GFP_KERNEL, "ssp%d Rx", i); 1264b9062f98SCezary Rojewski 1265b9062f98SCezary Rojewski if (!dai->name || !dai->playback.stream_name || !dai->capture.stream_name) 1266b9062f98SCezary Rojewski return -ENOMEM; 1267b9062f98SCezary Rojewski dai++; 1268b9062f98SCezary Rojewski } 1269b9062f98SCezary Rojewski 1270b9062f98SCezary Rojewski if (!tdms) 1271b9062f98SCezary Rojewski goto plat_register; 1272b9062f98SCezary Rojewski 1273b9062f98SCezary Rojewski for_each_set_bit(i, &port_mask, ssp_count) { 1274b9062f98SCezary Rojewski for_each_set_bit(j, &tdms[i], ssp_count) { 1275b9062f98SCezary Rojewski memcpy(dai, &i2s_dai_template, sizeof(*dai)); 1276b9062f98SCezary Rojewski 1277b9062f98SCezary Rojewski dai->name = 1278b9062f98SCezary Rojewski devm_kasprintf(adev->dev, GFP_KERNEL, "SSP%d:%d Pin", i, j); 1279b9062f98SCezary Rojewski dai->playback.stream_name = 1280b9062f98SCezary Rojewski devm_kasprintf(adev->dev, GFP_KERNEL, "ssp%d:%d Tx", i, j); 1281b9062f98SCezary Rojewski dai->capture.stream_name = 1282b9062f98SCezary Rojewski devm_kasprintf(adev->dev, GFP_KERNEL, "ssp%d:%d Rx", i, j); 1283b9062f98SCezary Rojewski 1284b9062f98SCezary Rojewski if (!dai->name || !dai->playback.stream_name || !dai->capture.stream_name) 1285b9062f98SCezary Rojewski return -ENOMEM; 1286b9062f98SCezary Rojewski dai++; 1287b9062f98SCezary Rojewski } 1288b9062f98SCezary Rojewski } 1289b9062f98SCezary Rojewski 1290b9062f98SCezary Rojewski plat_register: 1291b9062f98SCezary Rojewski return avs_soc_component_register(adev->dev, name, &avs_component_driver, cpus, cpu_count); 1292b9062f98SCezary Rojewski } 1293d070002aSCezary Rojewski 1294d070002aSCezary Rojewski /* HD-Audio CPU DAI template */ 1295d070002aSCezary Rojewski static const struct snd_soc_dai_driver hda_cpu_dai = { 1296d070002aSCezary Rojewski .ops = &avs_dai_hda_be_ops, 1297d070002aSCezary Rojewski .playback = { 1298d070002aSCezary Rojewski .channels_min = 1, 1299d070002aSCezary Rojewski .channels_max = 8, 1300d070002aSCezary Rojewski .rates = SNDRV_PCM_RATE_8000_192000, 1301d070002aSCezary Rojewski .formats = SNDRV_PCM_FMTBIT_S16_LE | 1302d070002aSCezary Rojewski SNDRV_PCM_FMTBIT_S24_LE | 1303d070002aSCezary Rojewski SNDRV_PCM_FMTBIT_S32_LE, 1304d070002aSCezary Rojewski }, 1305d070002aSCezary Rojewski .capture = { 1306d070002aSCezary Rojewski .channels_min = 1, 1307d070002aSCezary Rojewski .channels_max = 8, 1308d070002aSCezary Rojewski .rates = SNDRV_PCM_RATE_8000_192000, 1309d070002aSCezary Rojewski .formats = SNDRV_PCM_FMTBIT_S16_LE | 1310d070002aSCezary Rojewski SNDRV_PCM_FMTBIT_S24_LE | 1311d070002aSCezary Rojewski SNDRV_PCM_FMTBIT_S32_LE, 1312d070002aSCezary Rojewski }, 1313d070002aSCezary Rojewski }; 1314d070002aSCezary Rojewski 1315d070002aSCezary Rojewski static void avs_component_hda_unregister_dais(struct snd_soc_component *component) 1316d070002aSCezary Rojewski { 1317d070002aSCezary Rojewski struct snd_soc_acpi_mach *mach; 1318d070002aSCezary Rojewski struct snd_soc_dai *dai, *save; 1319d070002aSCezary Rojewski struct hda_codec *codec; 1320d070002aSCezary Rojewski char name[32]; 1321d070002aSCezary Rojewski 1322d070002aSCezary Rojewski mach = dev_get_platdata(component->card->dev); 1323d070002aSCezary Rojewski codec = mach->pdata; 1324d070002aSCezary Rojewski sprintf(name, "%s-cpu", dev_name(&codec->core.dev)); 1325d070002aSCezary Rojewski 1326d070002aSCezary Rojewski for_each_component_dais_safe(component, dai, save) { 1327ec4b2099SKuninori Morimoto int stream; 1328ec4b2099SKuninori Morimoto 1329d070002aSCezary Rojewski if (!strstr(dai->driver->name, name)) 1330d070002aSCezary Rojewski continue; 1331d070002aSCezary Rojewski 1332ec4b2099SKuninori Morimoto for_each_pcm_streams(stream) 1333ec4b2099SKuninori Morimoto snd_soc_dapm_free_widget(snd_soc_dai_get_widget(dai, stream)); 1334ec4b2099SKuninori Morimoto 1335d070002aSCezary Rojewski snd_soc_unregister_dai(dai); 1336d070002aSCezary Rojewski } 1337d070002aSCezary Rojewski } 1338d070002aSCezary Rojewski 1339d070002aSCezary Rojewski static int avs_component_hda_probe(struct snd_soc_component *component) 1340d070002aSCezary Rojewski { 1341d070002aSCezary Rojewski struct snd_soc_dapm_context *dapm; 1342d070002aSCezary Rojewski struct snd_soc_dai_driver *dais; 1343d070002aSCezary Rojewski struct snd_soc_acpi_mach *mach; 1344d070002aSCezary Rojewski struct hda_codec *codec; 1345d070002aSCezary Rojewski struct hda_pcm *pcm; 1346d070002aSCezary Rojewski const char *cname; 1347d070002aSCezary Rojewski int pcm_count = 0, ret, i; 1348d070002aSCezary Rojewski 1349d070002aSCezary Rojewski mach = dev_get_platdata(component->card->dev); 1350d070002aSCezary Rojewski if (!mach) 1351d070002aSCezary Rojewski return -EINVAL; 1352d070002aSCezary Rojewski 1353d070002aSCezary Rojewski codec = mach->pdata; 1354d070002aSCezary Rojewski if (list_empty(&codec->pcm_list_head)) 1355d070002aSCezary Rojewski return -EINVAL; 1356d070002aSCezary Rojewski list_for_each_entry(pcm, &codec->pcm_list_head, list) 1357d070002aSCezary Rojewski pcm_count++; 1358d070002aSCezary Rojewski 1359d070002aSCezary Rojewski dais = devm_kcalloc(component->dev, pcm_count, sizeof(*dais), 1360d070002aSCezary Rojewski GFP_KERNEL); 1361d070002aSCezary Rojewski if (!dais) 1362d070002aSCezary Rojewski return -ENOMEM; 1363d070002aSCezary Rojewski 1364d070002aSCezary Rojewski cname = dev_name(&codec->core.dev); 1365d070002aSCezary Rojewski dapm = snd_soc_component_get_dapm(component); 1366d070002aSCezary Rojewski pcm = list_first_entry(&codec->pcm_list_head, struct hda_pcm, list); 1367d070002aSCezary Rojewski 1368d070002aSCezary Rojewski for (i = 0; i < pcm_count; i++, pcm = list_next_entry(pcm, list)) { 1369d070002aSCezary Rojewski struct snd_soc_dai *dai; 1370d070002aSCezary Rojewski 1371d070002aSCezary Rojewski memcpy(&dais[i], &hda_cpu_dai, sizeof(*dais)); 1372d070002aSCezary Rojewski dais[i].id = i; 1373d070002aSCezary Rojewski dais[i].name = devm_kasprintf(component->dev, GFP_KERNEL, 1374d070002aSCezary Rojewski "%s-cpu%d", cname, i); 1375d070002aSCezary Rojewski if (!dais[i].name) { 1376d070002aSCezary Rojewski ret = -ENOMEM; 1377d070002aSCezary Rojewski goto exit; 1378d070002aSCezary Rojewski } 1379d070002aSCezary Rojewski 1380d070002aSCezary Rojewski if (pcm->stream[0].substreams) { 1381d070002aSCezary Rojewski dais[i].playback.stream_name = 1382d070002aSCezary Rojewski devm_kasprintf(component->dev, GFP_KERNEL, 1383d070002aSCezary Rojewski "%s-cpu%d Tx", cname, i); 1384d070002aSCezary Rojewski if (!dais[i].playback.stream_name) { 1385d070002aSCezary Rojewski ret = -ENOMEM; 1386d070002aSCezary Rojewski goto exit; 1387d070002aSCezary Rojewski } 1388d070002aSCezary Rojewski } 1389d070002aSCezary Rojewski 1390d070002aSCezary Rojewski if (pcm->stream[1].substreams) { 1391d070002aSCezary Rojewski dais[i].capture.stream_name = 1392d070002aSCezary Rojewski devm_kasprintf(component->dev, GFP_KERNEL, 1393d070002aSCezary Rojewski "%s-cpu%d Rx", cname, i); 1394d070002aSCezary Rojewski if (!dais[i].capture.stream_name) { 1395d070002aSCezary Rojewski ret = -ENOMEM; 1396d070002aSCezary Rojewski goto exit; 1397d070002aSCezary Rojewski } 1398d070002aSCezary Rojewski } 1399d070002aSCezary Rojewski 1400d070002aSCezary Rojewski dai = snd_soc_register_dai(component, &dais[i], false); 1401d070002aSCezary Rojewski if (!dai) { 1402d070002aSCezary Rojewski dev_err(component->dev, "register dai for %s failed\n", 1403d070002aSCezary Rojewski pcm->name); 1404d070002aSCezary Rojewski ret = -EINVAL; 1405d070002aSCezary Rojewski goto exit; 1406d070002aSCezary Rojewski } 1407d070002aSCezary Rojewski 1408d070002aSCezary Rojewski ret = snd_soc_dapm_new_dai_widgets(dapm, dai); 1409d070002aSCezary Rojewski if (ret < 0) { 1410d070002aSCezary Rojewski dev_err(component->dev, "create widgets failed: %d\n", 1411d070002aSCezary Rojewski ret); 1412d070002aSCezary Rojewski goto exit; 1413d070002aSCezary Rojewski } 1414d070002aSCezary Rojewski } 1415d070002aSCezary Rojewski 1416d070002aSCezary Rojewski ret = avs_component_probe(component); 1417d070002aSCezary Rojewski exit: 1418d070002aSCezary Rojewski if (ret) 1419d070002aSCezary Rojewski avs_component_hda_unregister_dais(component); 1420d070002aSCezary Rojewski 1421d070002aSCezary Rojewski return ret; 1422d070002aSCezary Rojewski } 1423d070002aSCezary Rojewski 1424d070002aSCezary Rojewski static void avs_component_hda_remove(struct snd_soc_component *component) 1425d070002aSCezary Rojewski { 1426d070002aSCezary Rojewski avs_component_hda_unregister_dais(component); 1427d070002aSCezary Rojewski avs_component_remove(component); 1428d070002aSCezary Rojewski } 1429d070002aSCezary Rojewski 1430d070002aSCezary Rojewski static int avs_component_hda_open(struct snd_soc_component *component, 1431d070002aSCezary Rojewski struct snd_pcm_substream *substream) 1432d070002aSCezary Rojewski { 14338f28299fSCezary Rojewski struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 1434d070002aSCezary Rojewski struct hdac_ext_stream *link_stream; 1435d070002aSCezary Rojewski struct hda_codec *codec; 1436d070002aSCezary Rojewski 1437eb0699c4SCezary Rojewski if (!rtd->dai_link->no_pcm) { 1438eb0699c4SCezary Rojewski struct snd_pcm_hardware hwparams = avs_pcm_hardware; 1439f38d4c72SCezary Rojewski struct snd_soc_pcm_runtime *be; 1440f38d4c72SCezary Rojewski struct snd_soc_dpcm *dpcm; 1441f38d4c72SCezary Rojewski int dir = substream->stream; 1442f38d4c72SCezary Rojewski 1443f38d4c72SCezary Rojewski /* 1444f38d4c72SCezary Rojewski * Support the DPCM reparenting while still fulfilling expectations of HDAudio 1445f38d4c72SCezary Rojewski * common code - a valid stream pointer at substream->runtime->private_data - 1446f38d4c72SCezary Rojewski * by having all FEs point to the same private data. 1447f38d4c72SCezary Rojewski */ 1448f38d4c72SCezary Rojewski for_each_dpcm_be(rtd, dir, dpcm) { 1449f38d4c72SCezary Rojewski struct snd_pcm_substream *be_substream; 1450f38d4c72SCezary Rojewski 1451f38d4c72SCezary Rojewski be = dpcm->be; 1452f38d4c72SCezary Rojewski if (be->dpcm[dir].users == 1) 1453f38d4c72SCezary Rojewski break; 1454f38d4c72SCezary Rojewski 1455f38d4c72SCezary Rojewski be_substream = snd_soc_dpcm_get_substream(be, dir); 1456f38d4c72SCezary Rojewski substream->runtime->private_data = be_substream->runtime->private_data; 1457f38d4c72SCezary Rojewski break; 1458f38d4c72SCezary Rojewski } 1459eb0699c4SCezary Rojewski 1460eb0699c4SCezary Rojewski /* RESUME unsupported for de-coupled HD-Audio capture. */ 1461f38d4c72SCezary Rojewski if (dir == SNDRV_PCM_STREAM_CAPTURE) 1462eb0699c4SCezary Rojewski hwparams.info &= ~SNDRV_PCM_INFO_RESUME; 1463eb0699c4SCezary Rojewski 1464eb0699c4SCezary Rojewski return snd_soc_set_runtime_hwparams(substream, &hwparams); 1465eb0699c4SCezary Rojewski } 1466d070002aSCezary Rojewski 1467d070002aSCezary Rojewski codec = dev_to_hda_codec(asoc_rtd_to_codec(rtd, 0)->dev); 1468d070002aSCezary Rojewski link_stream = snd_hdac_ext_stream_assign(&codec->bus->core, substream, 1469d070002aSCezary Rojewski HDAC_EXT_STREAM_TYPE_LINK); 1470d070002aSCezary Rojewski if (!link_stream) 1471d070002aSCezary Rojewski return -EBUSY; 1472d070002aSCezary Rojewski 1473d070002aSCezary Rojewski substream->runtime->private_data = link_stream; 1474d070002aSCezary Rojewski return 0; 1475d070002aSCezary Rojewski } 1476d070002aSCezary Rojewski 1477d070002aSCezary Rojewski static int avs_component_hda_close(struct snd_soc_component *component, 1478d070002aSCezary Rojewski struct snd_pcm_substream *substream) 1479d070002aSCezary Rojewski { 14808f28299fSCezary Rojewski struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 1481d070002aSCezary Rojewski struct hdac_ext_stream *link_stream; 1482d070002aSCezary Rojewski 1483d070002aSCezary Rojewski /* only BE DAI links are handled here */ 1484d070002aSCezary Rojewski if (!rtd->dai_link->no_pcm) 1485d070002aSCezary Rojewski return 0; 1486d070002aSCezary Rojewski 1487d070002aSCezary Rojewski link_stream = substream->runtime->private_data; 1488d070002aSCezary Rojewski snd_hdac_ext_stream_release(link_stream, HDAC_EXT_STREAM_TYPE_LINK); 1489d070002aSCezary Rojewski substream->runtime->private_data = NULL; 1490d070002aSCezary Rojewski 1491d070002aSCezary Rojewski return 0; 1492d070002aSCezary Rojewski } 1493d070002aSCezary Rojewski 1494d070002aSCezary Rojewski static const struct snd_soc_component_driver avs_hda_component_driver = { 1495d070002aSCezary Rojewski .name = "avs-hda-pcm", 1496d070002aSCezary Rojewski .probe = avs_component_hda_probe, 1497d070002aSCezary Rojewski .remove = avs_component_hda_remove, 14982b9a50eaSCezary Rojewski .suspend = avs_component_suspend, 14992b9a50eaSCezary Rojewski .resume = avs_component_resume, 1500d070002aSCezary Rojewski .open = avs_component_hda_open, 1501d070002aSCezary Rojewski .close = avs_component_hda_close, 1502d070002aSCezary Rojewski .pointer = avs_component_pointer, 1503d070002aSCezary Rojewski .mmap = avs_component_mmap, 1504d070002aSCezary Rojewski .pcm_construct = avs_component_construct, 1505d070002aSCezary Rojewski /* 1506d070002aSCezary Rojewski * hda platform component's probe() is dependent on 1507d070002aSCezary Rojewski * codec->pcm_list_head, it needs to be initialized after codec 1508d070002aSCezary Rojewski * component. remove_order is here for completeness sake 1509d070002aSCezary Rojewski */ 1510d070002aSCezary Rojewski .probe_order = SND_SOC_COMP_ORDER_LATE, 1511d070002aSCezary Rojewski .remove_order = SND_SOC_COMP_ORDER_EARLY, 1512d070002aSCezary Rojewski .module_get_upon_open = 1, 1513d070002aSCezary Rojewski .topology_name_prefix = "intel/avs", 1514d070002aSCezary Rojewski }; 1515d070002aSCezary Rojewski 1516d070002aSCezary Rojewski int avs_hda_platform_register(struct avs_dev *adev, const char *name) 1517d070002aSCezary Rojewski { 1518d070002aSCezary Rojewski return avs_soc_component_register(adev->dev, name, 1519d070002aSCezary Rojewski &avs_hda_component_driver, NULL, 0); 1520d070002aSCezary Rojewski } 1521