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