1ddee627cSLiam Girdwood /* 2ddee627cSLiam Girdwood * soc-pcm.c -- ALSA SoC PCM 3ddee627cSLiam Girdwood * 4ddee627cSLiam Girdwood * Copyright 2005 Wolfson Microelectronics PLC. 5ddee627cSLiam Girdwood * Copyright 2005 Openedhand Ltd. 6ddee627cSLiam Girdwood * Copyright (C) 2010 Slimlogic Ltd. 7ddee627cSLiam Girdwood * Copyright (C) 2010 Texas Instruments Inc. 8ddee627cSLiam Girdwood * 9ddee627cSLiam Girdwood * Authors: Liam Girdwood <lrg@ti.com> 10ddee627cSLiam Girdwood * Mark Brown <broonie@opensource.wolfsonmicro.com> 11ddee627cSLiam Girdwood * 12ddee627cSLiam Girdwood * This program is free software; you can redistribute it and/or modify it 13ddee627cSLiam Girdwood * under the terms of the GNU General Public License as published by the 14ddee627cSLiam Girdwood * Free Software Foundation; either version 2 of the License, or (at your 15ddee627cSLiam Girdwood * option) any later version. 16ddee627cSLiam Girdwood * 17ddee627cSLiam Girdwood */ 18ddee627cSLiam Girdwood 19ddee627cSLiam Girdwood #include <linux/kernel.h> 20ddee627cSLiam Girdwood #include <linux/init.h> 21ddee627cSLiam Girdwood #include <linux/delay.h> 22988e8cc4SNicolin Chen #include <linux/pinctrl/consumer.h> 23d6652ef8SMark Brown #include <linux/pm_runtime.h> 24ddee627cSLiam Girdwood #include <linux/slab.h> 25ddee627cSLiam Girdwood #include <linux/workqueue.h> 2601d7584cSLiam Girdwood #include <linux/export.h> 27f86dcef8SLiam Girdwood #include <linux/debugfs.h> 28ddee627cSLiam Girdwood #include <sound/core.h> 29ddee627cSLiam Girdwood #include <sound/pcm.h> 30ddee627cSLiam Girdwood #include <sound/pcm_params.h> 31ddee627cSLiam Girdwood #include <sound/soc.h> 3201d7584cSLiam Girdwood #include <sound/soc-dpcm.h> 33ddee627cSLiam Girdwood #include <sound/initval.h> 34ddee627cSLiam Girdwood 3501d7584cSLiam Girdwood #define DPCM_MAX_BE_USERS 8 3601d7584cSLiam Girdwood 3790996f43SLars-Peter Clausen /** 38*208a1589SLars-Peter Clausen * snd_soc_runtime_ignore_pmdown_time() - Check whether to ignore the power down delay 39*208a1589SLars-Peter Clausen * @rtd: The ASoC PCM runtime that should be checked. 40*208a1589SLars-Peter Clausen * 41*208a1589SLars-Peter Clausen * This function checks whether the power down delay should be ignored for a 42*208a1589SLars-Peter Clausen * specific PCM runtime. Returns true if the delay is 0, if it the DAI link has 43*208a1589SLars-Peter Clausen * been configured to ignore the delay, or if none of the components benefits 44*208a1589SLars-Peter Clausen * from having the delay. 45*208a1589SLars-Peter Clausen */ 46*208a1589SLars-Peter Clausen bool snd_soc_runtime_ignore_pmdown_time(struct snd_soc_pcm_runtime *rtd) 47*208a1589SLars-Peter Clausen { 48*208a1589SLars-Peter Clausen bool ignore = true; 49*208a1589SLars-Peter Clausen 50*208a1589SLars-Peter Clausen if (!rtd->pmdown_time || rtd->dai_link->ignore_pmdown_time) 51*208a1589SLars-Peter Clausen return true; 52*208a1589SLars-Peter Clausen 53*208a1589SLars-Peter Clausen if (rtd->cpu_dai->codec) 54*208a1589SLars-Peter Clausen ignore &= rtd->cpu_dai->codec->ignore_pmdown_time; 55*208a1589SLars-Peter Clausen 56*208a1589SLars-Peter Clausen ignore &= rtd->codec_dai->codec->ignore_pmdown_time; 57*208a1589SLars-Peter Clausen 58*208a1589SLars-Peter Clausen return ignore; 59*208a1589SLars-Peter Clausen } 60*208a1589SLars-Peter Clausen 61*208a1589SLars-Peter Clausen /** 6290996f43SLars-Peter Clausen * snd_soc_set_runtime_hwparams - set the runtime hardware parameters 6390996f43SLars-Peter Clausen * @substream: the pcm substream 6490996f43SLars-Peter Clausen * @hw: the hardware parameters 6590996f43SLars-Peter Clausen * 6690996f43SLars-Peter Clausen * Sets the substream runtime hardware parameters. 6790996f43SLars-Peter Clausen */ 6890996f43SLars-Peter Clausen int snd_soc_set_runtime_hwparams(struct snd_pcm_substream *substream, 6990996f43SLars-Peter Clausen const struct snd_pcm_hardware *hw) 7090996f43SLars-Peter Clausen { 7190996f43SLars-Peter Clausen struct snd_pcm_runtime *runtime = substream->runtime; 7290996f43SLars-Peter Clausen runtime->hw.info = hw->info; 7390996f43SLars-Peter Clausen runtime->hw.formats = hw->formats; 7490996f43SLars-Peter Clausen runtime->hw.period_bytes_min = hw->period_bytes_min; 7590996f43SLars-Peter Clausen runtime->hw.period_bytes_max = hw->period_bytes_max; 7690996f43SLars-Peter Clausen runtime->hw.periods_min = hw->periods_min; 7790996f43SLars-Peter Clausen runtime->hw.periods_max = hw->periods_max; 7890996f43SLars-Peter Clausen runtime->hw.buffer_bytes_max = hw->buffer_bytes_max; 7990996f43SLars-Peter Clausen runtime->hw.fifo_size = hw->fifo_size; 8090996f43SLars-Peter Clausen return 0; 8190996f43SLars-Peter Clausen } 8290996f43SLars-Peter Clausen EXPORT_SYMBOL_GPL(snd_soc_set_runtime_hwparams); 8390996f43SLars-Peter Clausen 8401d7584cSLiam Girdwood /* DPCM stream event, send event to FE and all active BEs. */ 8523607025SLiam Girdwood int dpcm_dapm_stream_event(struct snd_soc_pcm_runtime *fe, int dir, 8601d7584cSLiam Girdwood int event) 8701d7584cSLiam Girdwood { 8801d7584cSLiam Girdwood struct snd_soc_dpcm *dpcm; 8901d7584cSLiam Girdwood 9001d7584cSLiam Girdwood list_for_each_entry(dpcm, &fe->dpcm[dir].be_clients, list_be) { 9101d7584cSLiam Girdwood 9201d7584cSLiam Girdwood struct snd_soc_pcm_runtime *be = dpcm->be; 9301d7584cSLiam Girdwood 94103d84a3SLiam Girdwood dev_dbg(be->dev, "ASoC: BE %s event %d dir %d\n", 9501d7584cSLiam Girdwood be->dai_link->name, event, dir); 9601d7584cSLiam Girdwood 9701d7584cSLiam Girdwood snd_soc_dapm_stream_event(be, dir, event); 9801d7584cSLiam Girdwood } 9901d7584cSLiam Girdwood 10001d7584cSLiam Girdwood snd_soc_dapm_stream_event(fe, dir, event); 10101d7584cSLiam Girdwood 10201d7584cSLiam Girdwood return 0; 10301d7584cSLiam Girdwood } 10401d7584cSLiam Girdwood 10517841020SDong Aisheng static int soc_pcm_apply_symmetry(struct snd_pcm_substream *substream, 10617841020SDong Aisheng struct snd_soc_dai *soc_dai) 107ddee627cSLiam Girdwood { 108ddee627cSLiam Girdwood struct snd_soc_pcm_runtime *rtd = substream->private_data; 109ddee627cSLiam Girdwood int ret; 110ddee627cSLiam Girdwood 1113635bf09SNicolin Chen if (soc_dai->rate && (soc_dai->driver->symmetric_rates || 1123635bf09SNicolin Chen rtd->dai_link->symmetric_rates)) { 1133635bf09SNicolin Chen dev_dbg(soc_dai->dev, "ASoC: Symmetry forces %dHz rate\n", 1143635bf09SNicolin Chen soc_dai->rate); 115ddee627cSLiam Girdwood 116ddee627cSLiam Girdwood ret = snd_pcm_hw_constraint_minmax(substream->runtime, 117ddee627cSLiam Girdwood SNDRV_PCM_HW_PARAM_RATE, 11817841020SDong Aisheng soc_dai->rate, soc_dai->rate); 119ddee627cSLiam Girdwood if (ret < 0) { 12017841020SDong Aisheng dev_err(soc_dai->dev, 1213635bf09SNicolin Chen "ASoC: Unable to apply rate constraint: %d\n", 122103d84a3SLiam Girdwood ret); 123ddee627cSLiam Girdwood return ret; 124ddee627cSLiam Girdwood } 1253635bf09SNicolin Chen } 1263635bf09SNicolin Chen 1273635bf09SNicolin Chen if (soc_dai->channels && (soc_dai->driver->symmetric_channels || 1283635bf09SNicolin Chen rtd->dai_link->symmetric_channels)) { 1293635bf09SNicolin Chen dev_dbg(soc_dai->dev, "ASoC: Symmetry forces %d channel(s)\n", 1303635bf09SNicolin Chen soc_dai->channels); 1313635bf09SNicolin Chen 1323635bf09SNicolin Chen ret = snd_pcm_hw_constraint_minmax(substream->runtime, 1333635bf09SNicolin Chen SNDRV_PCM_HW_PARAM_CHANNELS, 1343635bf09SNicolin Chen soc_dai->channels, 1353635bf09SNicolin Chen soc_dai->channels); 1363635bf09SNicolin Chen if (ret < 0) { 1373635bf09SNicolin Chen dev_err(soc_dai->dev, 1383635bf09SNicolin Chen "ASoC: Unable to apply channel symmetry constraint: %d\n", 1393635bf09SNicolin Chen ret); 1403635bf09SNicolin Chen return ret; 1413635bf09SNicolin Chen } 1423635bf09SNicolin Chen } 1433635bf09SNicolin Chen 1443635bf09SNicolin Chen if (soc_dai->sample_bits && (soc_dai->driver->symmetric_samplebits || 1453635bf09SNicolin Chen rtd->dai_link->symmetric_samplebits)) { 1463635bf09SNicolin Chen dev_dbg(soc_dai->dev, "ASoC: Symmetry forces %d sample bits\n", 1473635bf09SNicolin Chen soc_dai->sample_bits); 1483635bf09SNicolin Chen 1493635bf09SNicolin Chen ret = snd_pcm_hw_constraint_minmax(substream->runtime, 1503635bf09SNicolin Chen SNDRV_PCM_HW_PARAM_SAMPLE_BITS, 1513635bf09SNicolin Chen soc_dai->sample_bits, 1523635bf09SNicolin Chen soc_dai->sample_bits); 1533635bf09SNicolin Chen if (ret < 0) { 1543635bf09SNicolin Chen dev_err(soc_dai->dev, 1553635bf09SNicolin Chen "ASoC: Unable to apply sample bits symmetry constraint: %d\n", 1563635bf09SNicolin Chen ret); 1573635bf09SNicolin Chen return ret; 1583635bf09SNicolin Chen } 1593635bf09SNicolin Chen } 160ddee627cSLiam Girdwood 161ddee627cSLiam Girdwood return 0; 162ddee627cSLiam Girdwood } 163ddee627cSLiam Girdwood 1643635bf09SNicolin Chen static int soc_pcm_params_symmetry(struct snd_pcm_substream *substream, 1653635bf09SNicolin Chen struct snd_pcm_hw_params *params) 1663635bf09SNicolin Chen { 1673635bf09SNicolin Chen struct snd_soc_pcm_runtime *rtd = substream->private_data; 1683635bf09SNicolin Chen struct snd_soc_dai *cpu_dai = rtd->cpu_dai; 1693635bf09SNicolin Chen struct snd_soc_dai *codec_dai = rtd->codec_dai; 1703635bf09SNicolin Chen unsigned int rate, channels, sample_bits, symmetry; 1713635bf09SNicolin Chen 1723635bf09SNicolin Chen rate = params_rate(params); 1733635bf09SNicolin Chen channels = params_channels(params); 1743635bf09SNicolin Chen sample_bits = snd_pcm_format_physical_width(params_format(params)); 1753635bf09SNicolin Chen 1763635bf09SNicolin Chen /* reject unmatched parameters when applying symmetry */ 1773635bf09SNicolin Chen symmetry = cpu_dai->driver->symmetric_rates || 1783635bf09SNicolin Chen codec_dai->driver->symmetric_rates || 1793635bf09SNicolin Chen rtd->dai_link->symmetric_rates; 1803635bf09SNicolin Chen if (symmetry && cpu_dai->rate && cpu_dai->rate != rate) { 1813635bf09SNicolin Chen dev_err(rtd->dev, "ASoC: unmatched rate symmetry: %d - %d\n", 1823635bf09SNicolin Chen cpu_dai->rate, rate); 1833635bf09SNicolin Chen return -EINVAL; 1843635bf09SNicolin Chen } 1853635bf09SNicolin Chen 1863635bf09SNicolin Chen symmetry = cpu_dai->driver->symmetric_channels || 1873635bf09SNicolin Chen codec_dai->driver->symmetric_channels || 1883635bf09SNicolin Chen rtd->dai_link->symmetric_channels; 1893635bf09SNicolin Chen if (symmetry && cpu_dai->channels && cpu_dai->channels != channels) { 1903635bf09SNicolin Chen dev_err(rtd->dev, "ASoC: unmatched channel symmetry: %d - %d\n", 1913635bf09SNicolin Chen cpu_dai->channels, channels); 1923635bf09SNicolin Chen return -EINVAL; 1933635bf09SNicolin Chen } 1943635bf09SNicolin Chen 1953635bf09SNicolin Chen symmetry = cpu_dai->driver->symmetric_samplebits || 1963635bf09SNicolin Chen codec_dai->driver->symmetric_samplebits || 1973635bf09SNicolin Chen rtd->dai_link->symmetric_samplebits; 1983635bf09SNicolin Chen if (symmetry && cpu_dai->sample_bits && cpu_dai->sample_bits != sample_bits) { 1993635bf09SNicolin Chen dev_err(rtd->dev, "ASoC: unmatched sample bits symmetry: %d - %d\n", 2003635bf09SNicolin Chen cpu_dai->sample_bits, sample_bits); 2013635bf09SNicolin Chen return -EINVAL; 2023635bf09SNicolin Chen } 203ddee627cSLiam Girdwood 204ddee627cSLiam Girdwood return 0; 205ddee627cSLiam Girdwood } 206ddee627cSLiam Girdwood 20762e5f676SLars-Peter Clausen static bool soc_pcm_has_symmetry(struct snd_pcm_substream *substream) 20862e5f676SLars-Peter Clausen { 20962e5f676SLars-Peter Clausen struct snd_soc_pcm_runtime *rtd = substream->private_data; 21062e5f676SLars-Peter Clausen struct snd_soc_dai_driver *cpu_driver = rtd->cpu_dai->driver; 21162e5f676SLars-Peter Clausen struct snd_soc_dai_driver *codec_driver = rtd->codec_dai->driver; 21262e5f676SLars-Peter Clausen struct snd_soc_dai_link *link = rtd->dai_link; 21362e5f676SLars-Peter Clausen 21462e5f676SLars-Peter Clausen return cpu_driver->symmetric_rates || codec_driver->symmetric_rates || 21562e5f676SLars-Peter Clausen link->symmetric_rates || cpu_driver->symmetric_channels || 21662e5f676SLars-Peter Clausen codec_driver->symmetric_channels || link->symmetric_channels || 21762e5f676SLars-Peter Clausen cpu_driver->symmetric_samplebits || 21862e5f676SLars-Peter Clausen codec_driver->symmetric_samplebits || 21962e5f676SLars-Peter Clausen link->symmetric_samplebits; 22062e5f676SLars-Peter Clausen } 22162e5f676SLars-Peter Clausen 222ddee627cSLiam Girdwood /* 22358ba9b25SMark Brown * List of sample sizes that might go over the bus for parameter 22458ba9b25SMark Brown * application. There ought to be a wildcard sample size for things 22558ba9b25SMark Brown * like the DAC/ADC resolution to use but there isn't right now. 22658ba9b25SMark Brown */ 22758ba9b25SMark Brown static int sample_sizes[] = { 22888e33954SPeter Ujfalusi 24, 32, 22958ba9b25SMark Brown }; 23058ba9b25SMark Brown 23158ba9b25SMark Brown static void soc_pcm_apply_msb(struct snd_pcm_substream *substream, 23258ba9b25SMark Brown struct snd_soc_dai *dai) 23358ba9b25SMark Brown { 23458ba9b25SMark Brown int ret, i, bits; 23558ba9b25SMark Brown 23658ba9b25SMark Brown if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 23758ba9b25SMark Brown bits = dai->driver->playback.sig_bits; 23858ba9b25SMark Brown else 23958ba9b25SMark Brown bits = dai->driver->capture.sig_bits; 24058ba9b25SMark Brown 24158ba9b25SMark Brown if (!bits) 24258ba9b25SMark Brown return; 24358ba9b25SMark Brown 24458ba9b25SMark Brown for (i = 0; i < ARRAY_SIZE(sample_sizes); i++) { 245278047fdSMark Brown if (bits >= sample_sizes[i]) 246278047fdSMark Brown continue; 247278047fdSMark Brown 248278047fdSMark Brown ret = snd_pcm_hw_constraint_msbits(substream->runtime, 0, 249278047fdSMark Brown sample_sizes[i], bits); 25058ba9b25SMark Brown if (ret != 0) 25158ba9b25SMark Brown dev_warn(dai->dev, 252103d84a3SLiam Girdwood "ASoC: Failed to set MSB %d/%d: %d\n", 25358ba9b25SMark Brown bits, sample_sizes[i], ret); 25458ba9b25SMark Brown } 25558ba9b25SMark Brown } 25658ba9b25SMark Brown 25778e45c99SLars-Peter Clausen static void soc_pcm_init_runtime_hw(struct snd_pcm_runtime *runtime, 258bd477c31SLars-Peter Clausen struct snd_soc_pcm_stream *codec_stream, 259bd477c31SLars-Peter Clausen struct snd_soc_pcm_stream *cpu_stream) 260bd477c31SLars-Peter Clausen { 26178e45c99SLars-Peter Clausen struct snd_pcm_hardware *hw = &runtime->hw; 26278e45c99SLars-Peter Clausen 263bd477c31SLars-Peter Clausen hw->channels_min = max(codec_stream->channels_min, 264bd477c31SLars-Peter Clausen cpu_stream->channels_min); 265bd477c31SLars-Peter Clausen hw->channels_max = min(codec_stream->channels_max, 266bd477c31SLars-Peter Clausen cpu_stream->channels_max); 26716d7ea91SLars-Peter Clausen if (hw->formats) 26816d7ea91SLars-Peter Clausen hw->formats &= codec_stream->formats & cpu_stream->formats; 26916d7ea91SLars-Peter Clausen else 270bd477c31SLars-Peter Clausen hw->formats = codec_stream->formats & cpu_stream->formats; 27155dcdb50SLars-Peter Clausen hw->rates = snd_pcm_rate_mask_intersect(codec_stream->rates, 27255dcdb50SLars-Peter Clausen cpu_stream->rates); 27378e45c99SLars-Peter Clausen 274817873f4SLars-Peter Clausen hw->rate_min = 0; 275817873f4SLars-Peter Clausen hw->rate_max = UINT_MAX; 27678e45c99SLars-Peter Clausen 27778e45c99SLars-Peter Clausen snd_pcm_limit_hw_rates(runtime); 27878e45c99SLars-Peter Clausen 27978e45c99SLars-Peter Clausen hw->rate_min = max(hw->rate_min, cpu_stream->rate_min); 28078e45c99SLars-Peter Clausen hw->rate_min = max(hw->rate_min, codec_stream->rate_min); 28178e45c99SLars-Peter Clausen hw->rate_max = min_not_zero(hw->rate_max, cpu_stream->rate_max); 28278e45c99SLars-Peter Clausen hw->rate_max = min_not_zero(hw->rate_max, codec_stream->rate_max); 283bd477c31SLars-Peter Clausen } 284bd477c31SLars-Peter Clausen 28558ba9b25SMark Brown /* 286ddee627cSLiam Girdwood * Called by ALSA when a PCM substream is opened, the runtime->hw record is 287ddee627cSLiam Girdwood * then initialized and any private data can be allocated. This also calls 288ddee627cSLiam Girdwood * startup for the cpu DAI, platform, machine and codec DAI. 289ddee627cSLiam Girdwood */ 290ddee627cSLiam Girdwood static int soc_pcm_open(struct snd_pcm_substream *substream) 291ddee627cSLiam Girdwood { 292ddee627cSLiam Girdwood struct snd_soc_pcm_runtime *rtd = substream->private_data; 293ddee627cSLiam Girdwood struct snd_pcm_runtime *runtime = substream->runtime; 294ddee627cSLiam Girdwood struct snd_soc_platform *platform = rtd->platform; 295ddee627cSLiam Girdwood struct snd_soc_dai *cpu_dai = rtd->cpu_dai; 296ddee627cSLiam Girdwood struct snd_soc_dai *codec_dai = rtd->codec_dai; 297ddee627cSLiam Girdwood struct snd_soc_dai_driver *cpu_dai_drv = cpu_dai->driver; 298ddee627cSLiam Girdwood struct snd_soc_dai_driver *codec_dai_drv = codec_dai->driver; 299ddee627cSLiam Girdwood int ret = 0; 300ddee627cSLiam Girdwood 301988e8cc4SNicolin Chen pinctrl_pm_select_default_state(cpu_dai->dev); 302988e8cc4SNicolin Chen pinctrl_pm_select_default_state(codec_dai->dev); 303d6652ef8SMark Brown pm_runtime_get_sync(cpu_dai->dev); 304d6652ef8SMark Brown pm_runtime_get_sync(codec_dai->dev); 305d6652ef8SMark Brown pm_runtime_get_sync(platform->dev); 306d6652ef8SMark Brown 307b8c0dab9SLiam Girdwood mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); 308ddee627cSLiam Girdwood 309ddee627cSLiam Girdwood /* startup the audio subsystem */ 310c5914b0aSMark Brown if (cpu_dai->driver->ops && cpu_dai->driver->ops->startup) { 311ddee627cSLiam Girdwood ret = cpu_dai->driver->ops->startup(substream, cpu_dai); 312ddee627cSLiam Girdwood if (ret < 0) { 313103d84a3SLiam Girdwood dev_err(cpu_dai->dev, "ASoC: can't open interface" 314103d84a3SLiam Girdwood " %s: %d\n", cpu_dai->name, ret); 315ddee627cSLiam Girdwood goto out; 316ddee627cSLiam Girdwood } 317ddee627cSLiam Girdwood } 318ddee627cSLiam Girdwood 319ddee627cSLiam Girdwood if (platform->driver->ops && platform->driver->ops->open) { 320ddee627cSLiam Girdwood ret = platform->driver->ops->open(substream); 321ddee627cSLiam Girdwood if (ret < 0) { 322103d84a3SLiam Girdwood dev_err(platform->dev, "ASoC: can't open platform" 323103d84a3SLiam Girdwood " %s: %d\n", platform->name, ret); 324ddee627cSLiam Girdwood goto platform_err; 325ddee627cSLiam Girdwood } 326ddee627cSLiam Girdwood } 327ddee627cSLiam Girdwood 328c5914b0aSMark Brown if (codec_dai->driver->ops && codec_dai->driver->ops->startup) { 329ddee627cSLiam Girdwood ret = codec_dai->driver->ops->startup(substream, codec_dai); 330ddee627cSLiam Girdwood if (ret < 0) { 331103d84a3SLiam Girdwood dev_err(codec_dai->dev, "ASoC: can't open codec" 332103d84a3SLiam Girdwood " %s: %d\n", codec_dai->name, ret); 333ddee627cSLiam Girdwood goto codec_dai_err; 334ddee627cSLiam Girdwood } 335ddee627cSLiam Girdwood } 336ddee627cSLiam Girdwood 337ddee627cSLiam Girdwood if (rtd->dai_link->ops && rtd->dai_link->ops->startup) { 338ddee627cSLiam Girdwood ret = rtd->dai_link->ops->startup(substream); 339ddee627cSLiam Girdwood if (ret < 0) { 340103d84a3SLiam Girdwood pr_err("ASoC: %s startup failed: %d\n", 34125bfe662SMark Brown rtd->dai_link->name, ret); 342ddee627cSLiam Girdwood goto machine_err; 343ddee627cSLiam Girdwood } 344ddee627cSLiam Girdwood } 345ddee627cSLiam Girdwood 34601d7584cSLiam Girdwood /* Dynamic PCM DAI links compat checks use dynamic capabilities */ 34701d7584cSLiam Girdwood if (rtd->dai_link->dynamic || rtd->dai_link->no_pcm) 34801d7584cSLiam Girdwood goto dynamic; 34901d7584cSLiam Girdwood 350ddee627cSLiam Girdwood /* Check that the codec and cpu DAIs are compatible */ 351ddee627cSLiam Girdwood if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 35278e45c99SLars-Peter Clausen soc_pcm_init_runtime_hw(runtime, &codec_dai_drv->playback, 353bd477c31SLars-Peter Clausen &cpu_dai_drv->playback); 354ddee627cSLiam Girdwood } else { 35578e45c99SLars-Peter Clausen soc_pcm_init_runtime_hw(runtime, &codec_dai_drv->capture, 356bd477c31SLars-Peter Clausen &cpu_dai_drv->capture); 357ddee627cSLiam Girdwood } 358ddee627cSLiam Girdwood 35962e5f676SLars-Peter Clausen if (soc_pcm_has_symmetry(substream)) 36062e5f676SLars-Peter Clausen runtime->hw.info |= SNDRV_PCM_INFO_JOINT_DUPLEX; 36162e5f676SLars-Peter Clausen 362ddee627cSLiam Girdwood ret = -EINVAL; 363ddee627cSLiam Girdwood if (!runtime->hw.rates) { 364103d84a3SLiam Girdwood printk(KERN_ERR "ASoC: %s <-> %s No matching rates\n", 365ddee627cSLiam Girdwood codec_dai->name, cpu_dai->name); 366ddee627cSLiam Girdwood goto config_err; 367ddee627cSLiam Girdwood } 368ddee627cSLiam Girdwood if (!runtime->hw.formats) { 369103d84a3SLiam Girdwood printk(KERN_ERR "ASoC: %s <-> %s No matching formats\n", 370ddee627cSLiam Girdwood codec_dai->name, cpu_dai->name); 371ddee627cSLiam Girdwood goto config_err; 372ddee627cSLiam Girdwood } 373ddee627cSLiam Girdwood if (!runtime->hw.channels_min || !runtime->hw.channels_max || 374ddee627cSLiam Girdwood runtime->hw.channels_min > runtime->hw.channels_max) { 375103d84a3SLiam Girdwood printk(KERN_ERR "ASoC: %s <-> %s No matching channels\n", 376ddee627cSLiam Girdwood codec_dai->name, cpu_dai->name); 377ddee627cSLiam Girdwood goto config_err; 378ddee627cSLiam Girdwood } 379ddee627cSLiam Girdwood 38058ba9b25SMark Brown soc_pcm_apply_msb(substream, codec_dai); 38158ba9b25SMark Brown soc_pcm_apply_msb(substream, cpu_dai); 38258ba9b25SMark Brown 383ddee627cSLiam Girdwood /* Symmetry only applies if we've already got an active stream. */ 38417841020SDong Aisheng if (cpu_dai->active) { 38517841020SDong Aisheng ret = soc_pcm_apply_symmetry(substream, cpu_dai); 38617841020SDong Aisheng if (ret != 0) 38717841020SDong Aisheng goto config_err; 38817841020SDong Aisheng } 38917841020SDong Aisheng 39017841020SDong Aisheng if (codec_dai->active) { 39117841020SDong Aisheng ret = soc_pcm_apply_symmetry(substream, codec_dai); 392ddee627cSLiam Girdwood if (ret != 0) 393ddee627cSLiam Girdwood goto config_err; 394ddee627cSLiam Girdwood } 395ddee627cSLiam Girdwood 396103d84a3SLiam Girdwood pr_debug("ASoC: %s <-> %s info:\n", 397ddee627cSLiam Girdwood codec_dai->name, cpu_dai->name); 398103d84a3SLiam Girdwood pr_debug("ASoC: rate mask 0x%x\n", runtime->hw.rates); 399103d84a3SLiam Girdwood pr_debug("ASoC: min ch %d max ch %d\n", runtime->hw.channels_min, 400ddee627cSLiam Girdwood runtime->hw.channels_max); 401103d84a3SLiam Girdwood pr_debug("ASoC: min rate %d max rate %d\n", runtime->hw.rate_min, 402ddee627cSLiam Girdwood runtime->hw.rate_max); 403ddee627cSLiam Girdwood 40401d7584cSLiam Girdwood dynamic: 405ddee627cSLiam Girdwood if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 406ddee627cSLiam Girdwood cpu_dai->playback_active++; 407ddee627cSLiam Girdwood codec_dai->playback_active++; 408ddee627cSLiam Girdwood } else { 409ddee627cSLiam Girdwood cpu_dai->capture_active++; 410ddee627cSLiam Girdwood codec_dai->capture_active++; 411ddee627cSLiam Girdwood } 412ddee627cSLiam Girdwood cpu_dai->active++; 413ddee627cSLiam Girdwood codec_dai->active++; 414ddee627cSLiam Girdwood rtd->codec->active++; 415b8c0dab9SLiam Girdwood mutex_unlock(&rtd->pcm_mutex); 416ddee627cSLiam Girdwood return 0; 417ddee627cSLiam Girdwood 418ddee627cSLiam Girdwood config_err: 419ddee627cSLiam Girdwood if (rtd->dai_link->ops && rtd->dai_link->ops->shutdown) 420ddee627cSLiam Girdwood rtd->dai_link->ops->shutdown(substream); 421ddee627cSLiam Girdwood 422ddee627cSLiam Girdwood machine_err: 423ddee627cSLiam Girdwood if (codec_dai->driver->ops->shutdown) 424ddee627cSLiam Girdwood codec_dai->driver->ops->shutdown(substream, codec_dai); 425ddee627cSLiam Girdwood 426ddee627cSLiam Girdwood codec_dai_err: 427ddee627cSLiam Girdwood if (platform->driver->ops && platform->driver->ops->close) 428ddee627cSLiam Girdwood platform->driver->ops->close(substream); 429ddee627cSLiam Girdwood 430ddee627cSLiam Girdwood platform_err: 431ddee627cSLiam Girdwood if (cpu_dai->driver->ops->shutdown) 432ddee627cSLiam Girdwood cpu_dai->driver->ops->shutdown(substream, cpu_dai); 433ddee627cSLiam Girdwood out: 434b8c0dab9SLiam Girdwood mutex_unlock(&rtd->pcm_mutex); 435d6652ef8SMark Brown 436d6652ef8SMark Brown pm_runtime_put(platform->dev); 437d6652ef8SMark Brown pm_runtime_put(codec_dai->dev); 438d6652ef8SMark Brown pm_runtime_put(cpu_dai->dev); 439988e8cc4SNicolin Chen if (!codec_dai->active) 440988e8cc4SNicolin Chen pinctrl_pm_select_sleep_state(codec_dai->dev); 441988e8cc4SNicolin Chen if (!cpu_dai->active) 442988e8cc4SNicolin Chen pinctrl_pm_select_sleep_state(cpu_dai->dev); 443d6652ef8SMark Brown 444ddee627cSLiam Girdwood return ret; 445ddee627cSLiam Girdwood } 446ddee627cSLiam Girdwood 447ddee627cSLiam Girdwood /* 448ddee627cSLiam Girdwood * Power down the audio subsystem pmdown_time msecs after close is called. 449ddee627cSLiam Girdwood * This is to ensure there are no pops or clicks in between any music tracks 450ddee627cSLiam Girdwood * due to DAPM power cycling. 451ddee627cSLiam Girdwood */ 452ddee627cSLiam Girdwood static void close_delayed_work(struct work_struct *work) 453ddee627cSLiam Girdwood { 454ddee627cSLiam Girdwood struct snd_soc_pcm_runtime *rtd = 455ddee627cSLiam Girdwood container_of(work, struct snd_soc_pcm_runtime, delayed_work.work); 456ddee627cSLiam Girdwood struct snd_soc_dai *codec_dai = rtd->codec_dai; 457ddee627cSLiam Girdwood 458b8c0dab9SLiam Girdwood mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); 459ddee627cSLiam Girdwood 460103d84a3SLiam Girdwood dev_dbg(rtd->dev, "ASoC: pop wq checking: %s status: %s waiting: %s\n", 461ddee627cSLiam Girdwood codec_dai->driver->playback.stream_name, 462ddee627cSLiam Girdwood codec_dai->playback_active ? "active" : "inactive", 4639bffb1fbSMisael Lopez Cruz rtd->pop_wait ? "yes" : "no"); 464ddee627cSLiam Girdwood 465ddee627cSLiam Girdwood /* are we waiting on this codec DAI stream */ 4669bffb1fbSMisael Lopez Cruz if (rtd->pop_wait == 1) { 4679bffb1fbSMisael Lopez Cruz rtd->pop_wait = 0; 4687bd3a6f3SMark Brown snd_soc_dapm_stream_event(rtd, SNDRV_PCM_STREAM_PLAYBACK, 469d9b0951bSLiam Girdwood SND_SOC_DAPM_STREAM_STOP); 470ddee627cSLiam Girdwood } 471ddee627cSLiam Girdwood 472b8c0dab9SLiam Girdwood mutex_unlock(&rtd->pcm_mutex); 473ddee627cSLiam Girdwood } 474ddee627cSLiam Girdwood 475ddee627cSLiam Girdwood /* 476ddee627cSLiam Girdwood * Called by ALSA when a PCM substream is closed. Private data can be 477ddee627cSLiam Girdwood * freed here. The cpu DAI, codec DAI, machine and platform are also 478ddee627cSLiam Girdwood * shutdown. 479ddee627cSLiam Girdwood */ 48091d5e6b4SLiam Girdwood static int soc_pcm_close(struct snd_pcm_substream *substream) 481ddee627cSLiam Girdwood { 482ddee627cSLiam Girdwood struct snd_soc_pcm_runtime *rtd = substream->private_data; 483ddee627cSLiam Girdwood struct snd_soc_platform *platform = rtd->platform; 484ddee627cSLiam Girdwood struct snd_soc_dai *cpu_dai = rtd->cpu_dai; 485ddee627cSLiam Girdwood struct snd_soc_dai *codec_dai = rtd->codec_dai; 486ddee627cSLiam Girdwood struct snd_soc_codec *codec = rtd->codec; 487ddee627cSLiam Girdwood 488b8c0dab9SLiam Girdwood mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); 489ddee627cSLiam Girdwood 490ddee627cSLiam Girdwood if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 491ddee627cSLiam Girdwood cpu_dai->playback_active--; 492ddee627cSLiam Girdwood codec_dai->playback_active--; 493ddee627cSLiam Girdwood } else { 494ddee627cSLiam Girdwood cpu_dai->capture_active--; 495ddee627cSLiam Girdwood codec_dai->capture_active--; 496ddee627cSLiam Girdwood } 497ddee627cSLiam Girdwood 498ddee627cSLiam Girdwood cpu_dai->active--; 499ddee627cSLiam Girdwood codec_dai->active--; 500ddee627cSLiam Girdwood codec->active--; 501ddee627cSLiam Girdwood 50217841020SDong Aisheng /* clear the corresponding DAIs rate when inactive */ 50317841020SDong Aisheng if (!cpu_dai->active) 50417841020SDong Aisheng cpu_dai->rate = 0; 50517841020SDong Aisheng 50617841020SDong Aisheng if (!codec_dai->active) 50717841020SDong Aisheng codec_dai->rate = 0; 50825b76791SSascha Hauer 509ddee627cSLiam Girdwood if (cpu_dai->driver->ops->shutdown) 510ddee627cSLiam Girdwood cpu_dai->driver->ops->shutdown(substream, cpu_dai); 511ddee627cSLiam Girdwood 512ddee627cSLiam Girdwood if (codec_dai->driver->ops->shutdown) 513ddee627cSLiam Girdwood codec_dai->driver->ops->shutdown(substream, codec_dai); 514ddee627cSLiam Girdwood 515ddee627cSLiam Girdwood if (rtd->dai_link->ops && rtd->dai_link->ops->shutdown) 516ddee627cSLiam Girdwood rtd->dai_link->ops->shutdown(substream); 517ddee627cSLiam Girdwood 518ddee627cSLiam Girdwood if (platform->driver->ops && platform->driver->ops->close) 519ddee627cSLiam Girdwood platform->driver->ops->close(substream); 520ddee627cSLiam Girdwood cpu_dai->runtime = NULL; 521ddee627cSLiam Girdwood 522ddee627cSLiam Girdwood if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 523*208a1589SLars-Peter Clausen if (snd_soc_runtime_ignore_pmdown_time(rtd)) { 5241d69c5c5SPeter Ujfalusi /* powered down playback stream now */ 5251d69c5c5SPeter Ujfalusi snd_soc_dapm_stream_event(rtd, 5267bd3a6f3SMark Brown SNDRV_PCM_STREAM_PLAYBACK, 5271d69c5c5SPeter Ujfalusi SND_SOC_DAPM_STREAM_STOP); 5281d69c5c5SPeter Ujfalusi } else { 529ddee627cSLiam Girdwood /* start delayed pop wq here for playback streams */ 5309bffb1fbSMisael Lopez Cruz rtd->pop_wait = 1; 531d4e1a73aSMark Brown queue_delayed_work(system_power_efficient_wq, 532d4e1a73aSMark Brown &rtd->delayed_work, 533ddee627cSLiam Girdwood msecs_to_jiffies(rtd->pmdown_time)); 5341d69c5c5SPeter Ujfalusi } 535ddee627cSLiam Girdwood } else { 536ddee627cSLiam Girdwood /* capture streams can be powered down now */ 5377bd3a6f3SMark Brown snd_soc_dapm_stream_event(rtd, SNDRV_PCM_STREAM_CAPTURE, 538d9b0951bSLiam Girdwood SND_SOC_DAPM_STREAM_STOP); 539ddee627cSLiam Girdwood } 540ddee627cSLiam Girdwood 541b8c0dab9SLiam Girdwood mutex_unlock(&rtd->pcm_mutex); 542d6652ef8SMark Brown 543d6652ef8SMark Brown pm_runtime_put(platform->dev); 544d6652ef8SMark Brown pm_runtime_put(codec_dai->dev); 545d6652ef8SMark Brown pm_runtime_put(cpu_dai->dev); 546988e8cc4SNicolin Chen if (!codec_dai->active) 547988e8cc4SNicolin Chen pinctrl_pm_select_sleep_state(codec_dai->dev); 548988e8cc4SNicolin Chen if (!cpu_dai->active) 549988e8cc4SNicolin Chen pinctrl_pm_select_sleep_state(cpu_dai->dev); 550d6652ef8SMark Brown 551ddee627cSLiam Girdwood return 0; 552ddee627cSLiam Girdwood } 553ddee627cSLiam Girdwood 554ddee627cSLiam Girdwood /* 555ddee627cSLiam Girdwood * Called by ALSA when the PCM substream is prepared, can set format, sample 556ddee627cSLiam Girdwood * rate, etc. This function is non atomic and can be called multiple times, 557ddee627cSLiam Girdwood * it can refer to the runtime info. 558ddee627cSLiam Girdwood */ 559ddee627cSLiam Girdwood static int soc_pcm_prepare(struct snd_pcm_substream *substream) 560ddee627cSLiam Girdwood { 561ddee627cSLiam Girdwood struct snd_soc_pcm_runtime *rtd = substream->private_data; 562ddee627cSLiam Girdwood struct snd_soc_platform *platform = rtd->platform; 563ddee627cSLiam Girdwood struct snd_soc_dai *cpu_dai = rtd->cpu_dai; 564ddee627cSLiam Girdwood struct snd_soc_dai *codec_dai = rtd->codec_dai; 565ddee627cSLiam Girdwood int ret = 0; 566ddee627cSLiam Girdwood 567b8c0dab9SLiam Girdwood mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); 568ddee627cSLiam Girdwood 569ddee627cSLiam Girdwood if (rtd->dai_link->ops && rtd->dai_link->ops->prepare) { 570ddee627cSLiam Girdwood ret = rtd->dai_link->ops->prepare(substream); 571ddee627cSLiam Girdwood if (ret < 0) { 572103d84a3SLiam Girdwood dev_err(rtd->card->dev, "ASoC: machine prepare error:" 573103d84a3SLiam Girdwood " %d\n", ret); 574ddee627cSLiam Girdwood goto out; 575ddee627cSLiam Girdwood } 576ddee627cSLiam Girdwood } 577ddee627cSLiam Girdwood 578ddee627cSLiam Girdwood if (platform->driver->ops && platform->driver->ops->prepare) { 579ddee627cSLiam Girdwood ret = platform->driver->ops->prepare(substream); 580ddee627cSLiam Girdwood if (ret < 0) { 581103d84a3SLiam Girdwood dev_err(platform->dev, "ASoC: platform prepare error:" 582103d84a3SLiam Girdwood " %d\n", ret); 583ddee627cSLiam Girdwood goto out; 584ddee627cSLiam Girdwood } 585ddee627cSLiam Girdwood } 586ddee627cSLiam Girdwood 587c5914b0aSMark Brown if (codec_dai->driver->ops && codec_dai->driver->ops->prepare) { 588ddee627cSLiam Girdwood ret = codec_dai->driver->ops->prepare(substream, codec_dai); 589ddee627cSLiam Girdwood if (ret < 0) { 590103d84a3SLiam Girdwood dev_err(codec_dai->dev, "ASoC: DAI prepare error: %d\n", 59125bfe662SMark Brown ret); 592ddee627cSLiam Girdwood goto out; 593ddee627cSLiam Girdwood } 594ddee627cSLiam Girdwood } 595ddee627cSLiam Girdwood 596c5914b0aSMark Brown if (cpu_dai->driver->ops && cpu_dai->driver->ops->prepare) { 597ddee627cSLiam Girdwood ret = cpu_dai->driver->ops->prepare(substream, cpu_dai); 598ddee627cSLiam Girdwood if (ret < 0) { 599103d84a3SLiam Girdwood dev_err(cpu_dai->dev, "ASoC: DAI prepare error: %d\n", 60025bfe662SMark Brown ret); 601ddee627cSLiam Girdwood goto out; 602ddee627cSLiam Girdwood } 603ddee627cSLiam Girdwood } 604ddee627cSLiam Girdwood 605ddee627cSLiam Girdwood /* cancel any delayed stream shutdown that is pending */ 606ddee627cSLiam Girdwood if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && 6079bffb1fbSMisael Lopez Cruz rtd->pop_wait) { 6089bffb1fbSMisael Lopez Cruz rtd->pop_wait = 0; 609ddee627cSLiam Girdwood cancel_delayed_work(&rtd->delayed_work); 610ddee627cSLiam Girdwood } 611ddee627cSLiam Girdwood 612d9b0951bSLiam Girdwood snd_soc_dapm_stream_event(rtd, substream->stream, 613ddee627cSLiam Girdwood SND_SOC_DAPM_STREAM_START); 614ddee627cSLiam Girdwood 615da18396fSMark Brown snd_soc_dai_digital_mute(codec_dai, 0, substream->stream); 616ddee627cSLiam Girdwood 617ddee627cSLiam Girdwood out: 618b8c0dab9SLiam Girdwood mutex_unlock(&rtd->pcm_mutex); 619ddee627cSLiam Girdwood return ret; 620ddee627cSLiam Girdwood } 621ddee627cSLiam Girdwood 622ddee627cSLiam Girdwood /* 623ddee627cSLiam Girdwood * Called by ALSA when the hardware params are set by application. This 624ddee627cSLiam Girdwood * function can also be called multiple times and can allocate buffers 625ddee627cSLiam Girdwood * (using snd_pcm_lib_* ). It's non-atomic. 626ddee627cSLiam Girdwood */ 627ddee627cSLiam Girdwood static int soc_pcm_hw_params(struct snd_pcm_substream *substream, 628ddee627cSLiam Girdwood struct snd_pcm_hw_params *params) 629ddee627cSLiam Girdwood { 630ddee627cSLiam Girdwood struct snd_soc_pcm_runtime *rtd = substream->private_data; 631ddee627cSLiam Girdwood struct snd_soc_platform *platform = rtd->platform; 632ddee627cSLiam Girdwood struct snd_soc_dai *cpu_dai = rtd->cpu_dai; 633ddee627cSLiam Girdwood struct snd_soc_dai *codec_dai = rtd->codec_dai; 634ddee627cSLiam Girdwood int ret = 0; 635ddee627cSLiam Girdwood 636b8c0dab9SLiam Girdwood mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); 637ddee627cSLiam Girdwood 6383635bf09SNicolin Chen ret = soc_pcm_params_symmetry(substream, params); 6393635bf09SNicolin Chen if (ret) 6403635bf09SNicolin Chen goto out; 6413635bf09SNicolin Chen 642ddee627cSLiam Girdwood if (rtd->dai_link->ops && rtd->dai_link->ops->hw_params) { 643ddee627cSLiam Girdwood ret = rtd->dai_link->ops->hw_params(substream, params); 644ddee627cSLiam Girdwood if (ret < 0) { 645103d84a3SLiam Girdwood dev_err(rtd->card->dev, "ASoC: machine hw_params" 646103d84a3SLiam Girdwood " failed: %d\n", ret); 647ddee627cSLiam Girdwood goto out; 648ddee627cSLiam Girdwood } 649ddee627cSLiam Girdwood } 650ddee627cSLiam Girdwood 651c5914b0aSMark Brown if (codec_dai->driver->ops && codec_dai->driver->ops->hw_params) { 652ddee627cSLiam Girdwood ret = codec_dai->driver->ops->hw_params(substream, params, codec_dai); 653ddee627cSLiam Girdwood if (ret < 0) { 654103d84a3SLiam Girdwood dev_err(codec_dai->dev, "ASoC: can't set %s hw params:" 655103d84a3SLiam Girdwood " %d\n", codec_dai->name, ret); 656ddee627cSLiam Girdwood goto codec_err; 657ddee627cSLiam Girdwood } 658ddee627cSLiam Girdwood } 659ddee627cSLiam Girdwood 660c5914b0aSMark Brown if (cpu_dai->driver->ops && cpu_dai->driver->ops->hw_params) { 661ddee627cSLiam Girdwood ret = cpu_dai->driver->ops->hw_params(substream, params, cpu_dai); 662ddee627cSLiam Girdwood if (ret < 0) { 663103d84a3SLiam Girdwood dev_err(cpu_dai->dev, "ASoC: %s hw params failed: %d\n", 66425bfe662SMark Brown cpu_dai->name, ret); 665ddee627cSLiam Girdwood goto interface_err; 666ddee627cSLiam Girdwood } 667ddee627cSLiam Girdwood } 668ddee627cSLiam Girdwood 669ddee627cSLiam Girdwood if (platform->driver->ops && platform->driver->ops->hw_params) { 670ddee627cSLiam Girdwood ret = platform->driver->ops->hw_params(substream, params); 671ddee627cSLiam Girdwood if (ret < 0) { 672103d84a3SLiam Girdwood dev_err(platform->dev, "ASoC: %s hw params failed: %d\n", 67325bfe662SMark Brown platform->name, ret); 674ddee627cSLiam Girdwood goto platform_err; 675ddee627cSLiam Girdwood } 676ddee627cSLiam Girdwood } 677ddee627cSLiam Girdwood 6783635bf09SNicolin Chen /* store the parameters for each DAIs */ 67917841020SDong Aisheng cpu_dai->rate = params_rate(params); 6803635bf09SNicolin Chen cpu_dai->channels = params_channels(params); 6813635bf09SNicolin Chen cpu_dai->sample_bits = 6823635bf09SNicolin Chen snd_pcm_format_physical_width(params_format(params)); 6833635bf09SNicolin Chen 68417841020SDong Aisheng codec_dai->rate = params_rate(params); 6853635bf09SNicolin Chen codec_dai->channels = params_channels(params); 6863635bf09SNicolin Chen codec_dai->sample_bits = 6873635bf09SNicolin Chen snd_pcm_format_physical_width(params_format(params)); 688ddee627cSLiam Girdwood 689ddee627cSLiam Girdwood out: 690b8c0dab9SLiam Girdwood mutex_unlock(&rtd->pcm_mutex); 691ddee627cSLiam Girdwood return ret; 692ddee627cSLiam Girdwood 693ddee627cSLiam Girdwood platform_err: 694c5914b0aSMark Brown if (cpu_dai->driver->ops && cpu_dai->driver->ops->hw_free) 695ddee627cSLiam Girdwood cpu_dai->driver->ops->hw_free(substream, cpu_dai); 696ddee627cSLiam Girdwood 697ddee627cSLiam Girdwood interface_err: 698c5914b0aSMark Brown if (codec_dai->driver->ops && codec_dai->driver->ops->hw_free) 699ddee627cSLiam Girdwood codec_dai->driver->ops->hw_free(substream, codec_dai); 700ddee627cSLiam Girdwood 701ddee627cSLiam Girdwood codec_err: 702ddee627cSLiam Girdwood if (rtd->dai_link->ops && rtd->dai_link->ops->hw_free) 703ddee627cSLiam Girdwood rtd->dai_link->ops->hw_free(substream); 704ddee627cSLiam Girdwood 705b8c0dab9SLiam Girdwood mutex_unlock(&rtd->pcm_mutex); 706ddee627cSLiam Girdwood return ret; 707ddee627cSLiam Girdwood } 708ddee627cSLiam Girdwood 709ddee627cSLiam Girdwood /* 710ddee627cSLiam Girdwood * Frees resources allocated by hw_params, can be called multiple times 711ddee627cSLiam Girdwood */ 712ddee627cSLiam Girdwood static int soc_pcm_hw_free(struct snd_pcm_substream *substream) 713ddee627cSLiam Girdwood { 714ddee627cSLiam Girdwood struct snd_soc_pcm_runtime *rtd = substream->private_data; 715ddee627cSLiam Girdwood struct snd_soc_platform *platform = rtd->platform; 716ddee627cSLiam Girdwood struct snd_soc_dai *cpu_dai = rtd->cpu_dai; 717ddee627cSLiam Girdwood struct snd_soc_dai *codec_dai = rtd->codec_dai; 7187f62b6eeSNicolin Chen bool playback = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; 719ddee627cSLiam Girdwood 720b8c0dab9SLiam Girdwood mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); 721ddee627cSLiam Girdwood 722d3383420SNicolin Chen /* clear the corresponding DAIs parameters when going to be inactive */ 723d3383420SNicolin Chen if (cpu_dai->active == 1) { 724d3383420SNicolin Chen cpu_dai->rate = 0; 725d3383420SNicolin Chen cpu_dai->channels = 0; 726d3383420SNicolin Chen cpu_dai->sample_bits = 0; 727d3383420SNicolin Chen } 728d3383420SNicolin Chen 729d3383420SNicolin Chen if (codec_dai->active == 1) { 730d3383420SNicolin Chen codec_dai->rate = 0; 731d3383420SNicolin Chen codec_dai->channels = 0; 732d3383420SNicolin Chen codec_dai->sample_bits = 0; 733d3383420SNicolin Chen } 734d3383420SNicolin Chen 735ddee627cSLiam Girdwood /* apply codec digital mute */ 7367f62b6eeSNicolin Chen if ((playback && codec_dai->playback_active == 1) || 7377f62b6eeSNicolin Chen (!playback && codec_dai->capture_active == 1)) 738da18396fSMark Brown snd_soc_dai_digital_mute(codec_dai, 1, substream->stream); 739ddee627cSLiam Girdwood 740ddee627cSLiam Girdwood /* free any machine hw params */ 741ddee627cSLiam Girdwood if (rtd->dai_link->ops && rtd->dai_link->ops->hw_free) 742ddee627cSLiam Girdwood rtd->dai_link->ops->hw_free(substream); 743ddee627cSLiam Girdwood 744ddee627cSLiam Girdwood /* free any DMA resources */ 745ddee627cSLiam Girdwood if (platform->driver->ops && platform->driver->ops->hw_free) 746ddee627cSLiam Girdwood platform->driver->ops->hw_free(substream); 747ddee627cSLiam Girdwood 748ddee627cSLiam Girdwood /* now free hw params for the DAIs */ 749c5914b0aSMark Brown if (codec_dai->driver->ops && codec_dai->driver->ops->hw_free) 750ddee627cSLiam Girdwood codec_dai->driver->ops->hw_free(substream, codec_dai); 751ddee627cSLiam Girdwood 752c5914b0aSMark Brown if (cpu_dai->driver->ops && cpu_dai->driver->ops->hw_free) 753ddee627cSLiam Girdwood cpu_dai->driver->ops->hw_free(substream, cpu_dai); 754ddee627cSLiam Girdwood 755b8c0dab9SLiam Girdwood mutex_unlock(&rtd->pcm_mutex); 756ddee627cSLiam Girdwood return 0; 757ddee627cSLiam Girdwood } 758ddee627cSLiam Girdwood 759ddee627cSLiam Girdwood static int soc_pcm_trigger(struct snd_pcm_substream *substream, int cmd) 760ddee627cSLiam Girdwood { 761ddee627cSLiam Girdwood struct snd_soc_pcm_runtime *rtd = substream->private_data; 762ddee627cSLiam Girdwood struct snd_soc_platform *platform = rtd->platform; 763ddee627cSLiam Girdwood struct snd_soc_dai *cpu_dai = rtd->cpu_dai; 764ddee627cSLiam Girdwood struct snd_soc_dai *codec_dai = rtd->codec_dai; 765ddee627cSLiam Girdwood int ret; 766ddee627cSLiam Girdwood 767c5914b0aSMark Brown if (codec_dai->driver->ops && codec_dai->driver->ops->trigger) { 768ddee627cSLiam Girdwood ret = codec_dai->driver->ops->trigger(substream, cmd, codec_dai); 769ddee627cSLiam Girdwood if (ret < 0) 770ddee627cSLiam Girdwood return ret; 771ddee627cSLiam Girdwood } 772ddee627cSLiam Girdwood 773ddee627cSLiam Girdwood if (platform->driver->ops && platform->driver->ops->trigger) { 774ddee627cSLiam Girdwood ret = platform->driver->ops->trigger(substream, cmd); 775ddee627cSLiam Girdwood if (ret < 0) 776ddee627cSLiam Girdwood return ret; 777ddee627cSLiam Girdwood } 778ddee627cSLiam Girdwood 779c5914b0aSMark Brown if (cpu_dai->driver->ops && cpu_dai->driver->ops->trigger) { 780ddee627cSLiam Girdwood ret = cpu_dai->driver->ops->trigger(substream, cmd, cpu_dai); 781ddee627cSLiam Girdwood if (ret < 0) 782ddee627cSLiam Girdwood return ret; 783ddee627cSLiam Girdwood } 784ddee627cSLiam Girdwood return 0; 785ddee627cSLiam Girdwood } 786ddee627cSLiam Girdwood 78745c0a188SMark Brown static int soc_pcm_bespoke_trigger(struct snd_pcm_substream *substream, 78845c0a188SMark Brown int cmd) 78907bf84aaSLiam Girdwood { 79007bf84aaSLiam Girdwood struct snd_soc_pcm_runtime *rtd = substream->private_data; 79107bf84aaSLiam Girdwood struct snd_soc_platform *platform = rtd->platform; 79207bf84aaSLiam Girdwood struct snd_soc_dai *cpu_dai = rtd->cpu_dai; 79307bf84aaSLiam Girdwood struct snd_soc_dai *codec_dai = rtd->codec_dai; 79407bf84aaSLiam Girdwood int ret; 79507bf84aaSLiam Girdwood 796c5914b0aSMark Brown if (codec_dai->driver->ops && 797c5914b0aSMark Brown codec_dai->driver->ops->bespoke_trigger) { 79807bf84aaSLiam Girdwood ret = codec_dai->driver->ops->bespoke_trigger(substream, cmd, codec_dai); 79907bf84aaSLiam Girdwood if (ret < 0) 80007bf84aaSLiam Girdwood return ret; 80107bf84aaSLiam Girdwood } 80207bf84aaSLiam Girdwood 803dcf0fa27SJean-Francois Moine if (platform->driver->bespoke_trigger) { 80407bf84aaSLiam Girdwood ret = platform->driver->bespoke_trigger(substream, cmd); 80507bf84aaSLiam Girdwood if (ret < 0) 80607bf84aaSLiam Girdwood return ret; 80707bf84aaSLiam Girdwood } 80807bf84aaSLiam Girdwood 809c5914b0aSMark Brown if (cpu_dai->driver->ops && cpu_dai->driver->ops->bespoke_trigger) { 81007bf84aaSLiam Girdwood ret = cpu_dai->driver->ops->bespoke_trigger(substream, cmd, cpu_dai); 81107bf84aaSLiam Girdwood if (ret < 0) 81207bf84aaSLiam Girdwood return ret; 81307bf84aaSLiam Girdwood } 81407bf84aaSLiam Girdwood return 0; 81507bf84aaSLiam Girdwood } 816ddee627cSLiam Girdwood /* 817ddee627cSLiam Girdwood * soc level wrapper for pointer callback 818ddee627cSLiam Girdwood * If cpu_dai, codec_dai, platform driver has the delay callback, than 819ddee627cSLiam Girdwood * the runtime->delay will be updated accordingly. 820ddee627cSLiam Girdwood */ 821ddee627cSLiam Girdwood static snd_pcm_uframes_t soc_pcm_pointer(struct snd_pcm_substream *substream) 822ddee627cSLiam Girdwood { 823ddee627cSLiam Girdwood struct snd_soc_pcm_runtime *rtd = substream->private_data; 824ddee627cSLiam Girdwood struct snd_soc_platform *platform = rtd->platform; 825ddee627cSLiam Girdwood struct snd_soc_dai *cpu_dai = rtd->cpu_dai; 826ddee627cSLiam Girdwood struct snd_soc_dai *codec_dai = rtd->codec_dai; 827ddee627cSLiam Girdwood struct snd_pcm_runtime *runtime = substream->runtime; 828ddee627cSLiam Girdwood snd_pcm_uframes_t offset = 0; 829ddee627cSLiam Girdwood snd_pcm_sframes_t delay = 0; 830ddee627cSLiam Girdwood 831ddee627cSLiam Girdwood if (platform->driver->ops && platform->driver->ops->pointer) 832ddee627cSLiam Girdwood offset = platform->driver->ops->pointer(substream); 833ddee627cSLiam Girdwood 834c5914b0aSMark Brown if (cpu_dai->driver->ops && cpu_dai->driver->ops->delay) 835ddee627cSLiam Girdwood delay += cpu_dai->driver->ops->delay(substream, cpu_dai); 836ddee627cSLiam Girdwood 837c5914b0aSMark Brown if (codec_dai->driver->ops && codec_dai->driver->ops->delay) 838ddee627cSLiam Girdwood delay += codec_dai->driver->ops->delay(substream, codec_dai); 839ddee627cSLiam Girdwood 840ddee627cSLiam Girdwood if (platform->driver->delay) 841ddee627cSLiam Girdwood delay += platform->driver->delay(substream, codec_dai); 842ddee627cSLiam Girdwood 843ddee627cSLiam Girdwood runtime->delay = delay; 844ddee627cSLiam Girdwood 845ddee627cSLiam Girdwood return offset; 846ddee627cSLiam Girdwood } 847ddee627cSLiam Girdwood 84801d7584cSLiam Girdwood /* connect a FE and BE */ 84901d7584cSLiam Girdwood static int dpcm_be_connect(struct snd_soc_pcm_runtime *fe, 85001d7584cSLiam Girdwood struct snd_soc_pcm_runtime *be, int stream) 85101d7584cSLiam Girdwood { 85201d7584cSLiam Girdwood struct snd_soc_dpcm *dpcm; 85301d7584cSLiam Girdwood 85401d7584cSLiam Girdwood /* only add new dpcms */ 85501d7584cSLiam Girdwood list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) { 85601d7584cSLiam Girdwood if (dpcm->be == be && dpcm->fe == fe) 85701d7584cSLiam Girdwood return 0; 85801d7584cSLiam Girdwood } 85901d7584cSLiam Girdwood 86001d7584cSLiam Girdwood dpcm = kzalloc(sizeof(struct snd_soc_dpcm), GFP_KERNEL); 86101d7584cSLiam Girdwood if (!dpcm) 86201d7584cSLiam Girdwood return -ENOMEM; 86301d7584cSLiam Girdwood 86401d7584cSLiam Girdwood dpcm->be = be; 86501d7584cSLiam Girdwood dpcm->fe = fe; 86601d7584cSLiam Girdwood be->dpcm[stream].runtime = fe->dpcm[stream].runtime; 86701d7584cSLiam Girdwood dpcm->state = SND_SOC_DPCM_LINK_STATE_NEW; 86801d7584cSLiam Girdwood list_add(&dpcm->list_be, &fe->dpcm[stream].be_clients); 86901d7584cSLiam Girdwood list_add(&dpcm->list_fe, &be->dpcm[stream].fe_clients); 87001d7584cSLiam Girdwood 87101d7584cSLiam Girdwood dev_dbg(fe->dev, "connected new DPCM %s path %s %s %s\n", 87201d7584cSLiam Girdwood stream ? "capture" : "playback", fe->dai_link->name, 87301d7584cSLiam Girdwood stream ? "<-" : "->", be->dai_link->name); 87401d7584cSLiam Girdwood 875f86dcef8SLiam Girdwood #ifdef CONFIG_DEBUG_FS 876f86dcef8SLiam Girdwood dpcm->debugfs_state = debugfs_create_u32(be->dai_link->name, 0644, 877f86dcef8SLiam Girdwood fe->debugfs_dpcm_root, &dpcm->state); 878f86dcef8SLiam Girdwood #endif 87901d7584cSLiam Girdwood return 1; 88001d7584cSLiam Girdwood } 88101d7584cSLiam Girdwood 88201d7584cSLiam Girdwood /* reparent a BE onto another FE */ 88301d7584cSLiam Girdwood static void dpcm_be_reparent(struct snd_soc_pcm_runtime *fe, 88401d7584cSLiam Girdwood struct snd_soc_pcm_runtime *be, int stream) 88501d7584cSLiam Girdwood { 88601d7584cSLiam Girdwood struct snd_soc_dpcm *dpcm; 88701d7584cSLiam Girdwood struct snd_pcm_substream *fe_substream, *be_substream; 88801d7584cSLiam Girdwood 88901d7584cSLiam Girdwood /* reparent if BE is connected to other FEs */ 89001d7584cSLiam Girdwood if (!be->dpcm[stream].users) 89101d7584cSLiam Girdwood return; 89201d7584cSLiam Girdwood 89301d7584cSLiam Girdwood be_substream = snd_soc_dpcm_get_substream(be, stream); 89401d7584cSLiam Girdwood 89501d7584cSLiam Girdwood list_for_each_entry(dpcm, &be->dpcm[stream].fe_clients, list_fe) { 89601d7584cSLiam Girdwood if (dpcm->fe == fe) 89701d7584cSLiam Girdwood continue; 89801d7584cSLiam Girdwood 89901d7584cSLiam Girdwood dev_dbg(fe->dev, "reparent %s path %s %s %s\n", 90001d7584cSLiam Girdwood stream ? "capture" : "playback", 90101d7584cSLiam Girdwood dpcm->fe->dai_link->name, 90201d7584cSLiam Girdwood stream ? "<-" : "->", dpcm->be->dai_link->name); 90301d7584cSLiam Girdwood 90401d7584cSLiam Girdwood fe_substream = snd_soc_dpcm_get_substream(dpcm->fe, stream); 90501d7584cSLiam Girdwood be_substream->runtime = fe_substream->runtime; 90601d7584cSLiam Girdwood break; 90701d7584cSLiam Girdwood } 90801d7584cSLiam Girdwood } 90901d7584cSLiam Girdwood 91001d7584cSLiam Girdwood /* disconnect a BE and FE */ 91123607025SLiam Girdwood void dpcm_be_disconnect(struct snd_soc_pcm_runtime *fe, int stream) 91201d7584cSLiam Girdwood { 91301d7584cSLiam Girdwood struct snd_soc_dpcm *dpcm, *d; 91401d7584cSLiam Girdwood 91501d7584cSLiam Girdwood list_for_each_entry_safe(dpcm, d, &fe->dpcm[stream].be_clients, list_be) { 916103d84a3SLiam Girdwood dev_dbg(fe->dev, "ASoC: BE %s disconnect check for %s\n", 91701d7584cSLiam Girdwood stream ? "capture" : "playback", 91801d7584cSLiam Girdwood dpcm->be->dai_link->name); 91901d7584cSLiam Girdwood 92001d7584cSLiam Girdwood if (dpcm->state != SND_SOC_DPCM_LINK_STATE_FREE) 92101d7584cSLiam Girdwood continue; 92201d7584cSLiam Girdwood 92301d7584cSLiam Girdwood dev_dbg(fe->dev, "freed DSP %s path %s %s %s\n", 92401d7584cSLiam Girdwood stream ? "capture" : "playback", fe->dai_link->name, 92501d7584cSLiam Girdwood stream ? "<-" : "->", dpcm->be->dai_link->name); 92601d7584cSLiam Girdwood 92701d7584cSLiam Girdwood /* BEs still alive need new FE */ 92801d7584cSLiam Girdwood dpcm_be_reparent(fe, dpcm->be, stream); 92901d7584cSLiam Girdwood 930f86dcef8SLiam Girdwood #ifdef CONFIG_DEBUG_FS 931f86dcef8SLiam Girdwood debugfs_remove(dpcm->debugfs_state); 932f86dcef8SLiam Girdwood #endif 93301d7584cSLiam Girdwood list_del(&dpcm->list_be); 93401d7584cSLiam Girdwood list_del(&dpcm->list_fe); 93501d7584cSLiam Girdwood kfree(dpcm); 93601d7584cSLiam Girdwood } 93701d7584cSLiam Girdwood } 93801d7584cSLiam Girdwood 93901d7584cSLiam Girdwood /* get BE for DAI widget and stream */ 94001d7584cSLiam Girdwood static struct snd_soc_pcm_runtime *dpcm_get_be(struct snd_soc_card *card, 94101d7584cSLiam Girdwood struct snd_soc_dapm_widget *widget, int stream) 94201d7584cSLiam Girdwood { 94301d7584cSLiam Girdwood struct snd_soc_pcm_runtime *be; 94401d7584cSLiam Girdwood int i; 94501d7584cSLiam Girdwood 94601d7584cSLiam Girdwood if (stream == SNDRV_PCM_STREAM_PLAYBACK) { 94701d7584cSLiam Girdwood for (i = 0; i < card->num_links; i++) { 94801d7584cSLiam Girdwood be = &card->rtd[i]; 94901d7584cSLiam Girdwood 95035ea0655SLiam Girdwood if (!be->dai_link->no_pcm) 95135ea0655SLiam Girdwood continue; 95235ea0655SLiam Girdwood 95301d7584cSLiam Girdwood if (be->cpu_dai->playback_widget == widget || 95401d7584cSLiam Girdwood be->codec_dai->playback_widget == widget) 95501d7584cSLiam Girdwood return be; 95601d7584cSLiam Girdwood } 95701d7584cSLiam Girdwood } else { 95801d7584cSLiam Girdwood 95901d7584cSLiam Girdwood for (i = 0; i < card->num_links; i++) { 96001d7584cSLiam Girdwood be = &card->rtd[i]; 96101d7584cSLiam Girdwood 96235ea0655SLiam Girdwood if (!be->dai_link->no_pcm) 96335ea0655SLiam Girdwood continue; 96435ea0655SLiam Girdwood 96501d7584cSLiam Girdwood if (be->cpu_dai->capture_widget == widget || 96601d7584cSLiam Girdwood be->codec_dai->capture_widget == widget) 96701d7584cSLiam Girdwood return be; 96801d7584cSLiam Girdwood } 96901d7584cSLiam Girdwood } 97001d7584cSLiam Girdwood 971103d84a3SLiam Girdwood dev_err(card->dev, "ASoC: can't get %s BE for %s\n", 97201d7584cSLiam Girdwood stream ? "capture" : "playback", widget->name); 97301d7584cSLiam Girdwood return NULL; 97401d7584cSLiam Girdwood } 97501d7584cSLiam Girdwood 97601d7584cSLiam Girdwood static inline struct snd_soc_dapm_widget * 97701d7584cSLiam Girdwood rtd_get_cpu_widget(struct snd_soc_pcm_runtime *rtd, int stream) 97801d7584cSLiam Girdwood { 97901d7584cSLiam Girdwood if (stream == SNDRV_PCM_STREAM_PLAYBACK) 98001d7584cSLiam Girdwood return rtd->cpu_dai->playback_widget; 98101d7584cSLiam Girdwood else 98201d7584cSLiam Girdwood return rtd->cpu_dai->capture_widget; 98301d7584cSLiam Girdwood } 98401d7584cSLiam Girdwood 98501d7584cSLiam Girdwood static inline struct snd_soc_dapm_widget * 98601d7584cSLiam Girdwood rtd_get_codec_widget(struct snd_soc_pcm_runtime *rtd, int stream) 98701d7584cSLiam Girdwood { 98801d7584cSLiam Girdwood if (stream == SNDRV_PCM_STREAM_PLAYBACK) 98901d7584cSLiam Girdwood return rtd->codec_dai->playback_widget; 99001d7584cSLiam Girdwood else 99101d7584cSLiam Girdwood return rtd->codec_dai->capture_widget; 99201d7584cSLiam Girdwood } 99301d7584cSLiam Girdwood 99401d7584cSLiam Girdwood static int widget_in_list(struct snd_soc_dapm_widget_list *list, 99501d7584cSLiam Girdwood struct snd_soc_dapm_widget *widget) 99601d7584cSLiam Girdwood { 99701d7584cSLiam Girdwood int i; 99801d7584cSLiam Girdwood 99901d7584cSLiam Girdwood for (i = 0; i < list->num_widgets; i++) { 100001d7584cSLiam Girdwood if (widget == list->widgets[i]) 100101d7584cSLiam Girdwood return 1; 100201d7584cSLiam Girdwood } 100301d7584cSLiam Girdwood 100401d7584cSLiam Girdwood return 0; 100501d7584cSLiam Girdwood } 100601d7584cSLiam Girdwood 100723607025SLiam Girdwood int dpcm_path_get(struct snd_soc_pcm_runtime *fe, 100801d7584cSLiam Girdwood int stream, struct snd_soc_dapm_widget_list **list_) 100901d7584cSLiam Girdwood { 101001d7584cSLiam Girdwood struct snd_soc_dai *cpu_dai = fe->cpu_dai; 101101d7584cSLiam Girdwood struct snd_soc_dapm_widget_list *list; 101201d7584cSLiam Girdwood int paths; 101301d7584cSLiam Girdwood 101401d7584cSLiam Girdwood list = kzalloc(sizeof(struct snd_soc_dapm_widget_list) + 101501d7584cSLiam Girdwood sizeof(struct snd_soc_dapm_widget *), GFP_KERNEL); 101601d7584cSLiam Girdwood if (list == NULL) 101701d7584cSLiam Girdwood return -ENOMEM; 101801d7584cSLiam Girdwood 101901d7584cSLiam Girdwood /* get number of valid DAI paths and their widgets */ 102001d7584cSLiam Girdwood paths = snd_soc_dapm_dai_get_connected_widgets(cpu_dai, stream, &list); 102101d7584cSLiam Girdwood 1022103d84a3SLiam Girdwood dev_dbg(fe->dev, "ASoC: found %d audio %s paths\n", paths, 102301d7584cSLiam Girdwood stream ? "capture" : "playback"); 102401d7584cSLiam Girdwood 102501d7584cSLiam Girdwood *list_ = list; 102601d7584cSLiam Girdwood return paths; 102701d7584cSLiam Girdwood } 102801d7584cSLiam Girdwood 102901d7584cSLiam Girdwood static int dpcm_prune_paths(struct snd_soc_pcm_runtime *fe, int stream, 103001d7584cSLiam Girdwood struct snd_soc_dapm_widget_list **list_) 103101d7584cSLiam Girdwood { 103201d7584cSLiam Girdwood struct snd_soc_dpcm *dpcm; 103301d7584cSLiam Girdwood struct snd_soc_dapm_widget_list *list = *list_; 103401d7584cSLiam Girdwood struct snd_soc_dapm_widget *widget; 103501d7584cSLiam Girdwood int prune = 0; 103601d7584cSLiam Girdwood 103701d7584cSLiam Girdwood /* Destroy any old FE <--> BE connections */ 103801d7584cSLiam Girdwood list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) { 103901d7584cSLiam Girdwood 104001d7584cSLiam Girdwood /* is there a valid CPU DAI widget for this BE */ 104101d7584cSLiam Girdwood widget = rtd_get_cpu_widget(dpcm->be, stream); 104201d7584cSLiam Girdwood 104301d7584cSLiam Girdwood /* prune the BE if it's no longer in our active list */ 104401d7584cSLiam Girdwood if (widget && widget_in_list(list, widget)) 104501d7584cSLiam Girdwood continue; 104601d7584cSLiam Girdwood 104701d7584cSLiam Girdwood /* is there a valid CODEC DAI widget for this BE */ 104801d7584cSLiam Girdwood widget = rtd_get_codec_widget(dpcm->be, stream); 104901d7584cSLiam Girdwood 105001d7584cSLiam Girdwood /* prune the BE if it's no longer in our active list */ 105101d7584cSLiam Girdwood if (widget && widget_in_list(list, widget)) 105201d7584cSLiam Girdwood continue; 105301d7584cSLiam Girdwood 1054103d84a3SLiam Girdwood dev_dbg(fe->dev, "ASoC: pruning %s BE %s for %s\n", 105501d7584cSLiam Girdwood stream ? "capture" : "playback", 105601d7584cSLiam Girdwood dpcm->be->dai_link->name, fe->dai_link->name); 105701d7584cSLiam Girdwood dpcm->state = SND_SOC_DPCM_LINK_STATE_FREE; 105801d7584cSLiam Girdwood dpcm->be->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_BE; 105901d7584cSLiam Girdwood prune++; 106001d7584cSLiam Girdwood } 106101d7584cSLiam Girdwood 1062103d84a3SLiam Girdwood dev_dbg(fe->dev, "ASoC: found %d old BE paths for pruning\n", prune); 106301d7584cSLiam Girdwood return prune; 106401d7584cSLiam Girdwood } 106501d7584cSLiam Girdwood 106601d7584cSLiam Girdwood static int dpcm_add_paths(struct snd_soc_pcm_runtime *fe, int stream, 106701d7584cSLiam Girdwood struct snd_soc_dapm_widget_list **list_) 106801d7584cSLiam Girdwood { 106901d7584cSLiam Girdwood struct snd_soc_card *card = fe->card; 107001d7584cSLiam Girdwood struct snd_soc_dapm_widget_list *list = *list_; 107101d7584cSLiam Girdwood struct snd_soc_pcm_runtime *be; 107201d7584cSLiam Girdwood int i, new = 0, err; 107301d7584cSLiam Girdwood 107401d7584cSLiam Girdwood /* Create any new FE <--> BE connections */ 107501d7584cSLiam Girdwood for (i = 0; i < list->num_widgets; i++) { 107601d7584cSLiam Girdwood 10774616274dSMark Brown switch (list->widgets[i]->id) { 10784616274dSMark Brown case snd_soc_dapm_dai_in: 10794616274dSMark Brown case snd_soc_dapm_dai_out: 10804616274dSMark Brown break; 10814616274dSMark Brown default: 108201d7584cSLiam Girdwood continue; 10834616274dSMark Brown } 108401d7584cSLiam Girdwood 108501d7584cSLiam Girdwood /* is there a valid BE rtd for this widget */ 108601d7584cSLiam Girdwood be = dpcm_get_be(card, list->widgets[i], stream); 108701d7584cSLiam Girdwood if (!be) { 1088103d84a3SLiam Girdwood dev_err(fe->dev, "ASoC: no BE found for %s\n", 108901d7584cSLiam Girdwood list->widgets[i]->name); 109001d7584cSLiam Girdwood continue; 109101d7584cSLiam Girdwood } 109201d7584cSLiam Girdwood 109301d7584cSLiam Girdwood /* make sure BE is a real BE */ 109401d7584cSLiam Girdwood if (!be->dai_link->no_pcm) 109501d7584cSLiam Girdwood continue; 109601d7584cSLiam Girdwood 109701d7584cSLiam Girdwood /* don't connect if FE is not running */ 109823607025SLiam Girdwood if (!fe->dpcm[stream].runtime && !fe->fe_compr) 109901d7584cSLiam Girdwood continue; 110001d7584cSLiam Girdwood 110101d7584cSLiam Girdwood /* newly connected FE and BE */ 110201d7584cSLiam Girdwood err = dpcm_be_connect(fe, be, stream); 110301d7584cSLiam Girdwood if (err < 0) { 1104103d84a3SLiam Girdwood dev_err(fe->dev, "ASoC: can't connect %s\n", 110501d7584cSLiam Girdwood list->widgets[i]->name); 110601d7584cSLiam Girdwood break; 110701d7584cSLiam Girdwood } else if (err == 0) /* already connected */ 110801d7584cSLiam Girdwood continue; 110901d7584cSLiam Girdwood 111001d7584cSLiam Girdwood /* new */ 111101d7584cSLiam Girdwood be->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_BE; 111201d7584cSLiam Girdwood new++; 111301d7584cSLiam Girdwood } 111401d7584cSLiam Girdwood 1115103d84a3SLiam Girdwood dev_dbg(fe->dev, "ASoC: found %d new BE paths\n", new); 111601d7584cSLiam Girdwood return new; 111701d7584cSLiam Girdwood } 111801d7584cSLiam Girdwood 111901d7584cSLiam Girdwood /* 112001d7584cSLiam Girdwood * Find the corresponding BE DAIs that source or sink audio to this 112101d7584cSLiam Girdwood * FE substream. 112201d7584cSLiam Girdwood */ 112323607025SLiam Girdwood int dpcm_process_paths(struct snd_soc_pcm_runtime *fe, 112401d7584cSLiam Girdwood int stream, struct snd_soc_dapm_widget_list **list, int new) 112501d7584cSLiam Girdwood { 112601d7584cSLiam Girdwood if (new) 112701d7584cSLiam Girdwood return dpcm_add_paths(fe, stream, list); 112801d7584cSLiam Girdwood else 112901d7584cSLiam Girdwood return dpcm_prune_paths(fe, stream, list); 113001d7584cSLiam Girdwood } 113101d7584cSLiam Girdwood 113223607025SLiam Girdwood void dpcm_clear_pending_state(struct snd_soc_pcm_runtime *fe, int stream) 113301d7584cSLiam Girdwood { 113401d7584cSLiam Girdwood struct snd_soc_dpcm *dpcm; 113501d7584cSLiam Girdwood 113601d7584cSLiam Girdwood list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) 113701d7584cSLiam Girdwood dpcm->be->dpcm[stream].runtime_update = 113801d7584cSLiam Girdwood SND_SOC_DPCM_UPDATE_NO; 113901d7584cSLiam Girdwood } 114001d7584cSLiam Girdwood 114101d7584cSLiam Girdwood static void dpcm_be_dai_startup_unwind(struct snd_soc_pcm_runtime *fe, 114201d7584cSLiam Girdwood int stream) 114301d7584cSLiam Girdwood { 114401d7584cSLiam Girdwood struct snd_soc_dpcm *dpcm; 114501d7584cSLiam Girdwood 114601d7584cSLiam Girdwood /* disable any enabled and non active backends */ 114701d7584cSLiam Girdwood list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) { 114801d7584cSLiam Girdwood 114901d7584cSLiam Girdwood struct snd_soc_pcm_runtime *be = dpcm->be; 115001d7584cSLiam Girdwood struct snd_pcm_substream *be_substream = 115101d7584cSLiam Girdwood snd_soc_dpcm_get_substream(be, stream); 115201d7584cSLiam Girdwood 115301d7584cSLiam Girdwood if (be->dpcm[stream].users == 0) 1154103d84a3SLiam Girdwood dev_err(be->dev, "ASoC: no users %s at close - state %d\n", 115501d7584cSLiam Girdwood stream ? "capture" : "playback", 115601d7584cSLiam Girdwood be->dpcm[stream].state); 115701d7584cSLiam Girdwood 115801d7584cSLiam Girdwood if (--be->dpcm[stream].users != 0) 115901d7584cSLiam Girdwood continue; 116001d7584cSLiam Girdwood 116101d7584cSLiam Girdwood if (be->dpcm[stream].state != SND_SOC_DPCM_STATE_OPEN) 116201d7584cSLiam Girdwood continue; 116301d7584cSLiam Girdwood 116401d7584cSLiam Girdwood soc_pcm_close(be_substream); 116501d7584cSLiam Girdwood be_substream->runtime = NULL; 116601d7584cSLiam Girdwood be->dpcm[stream].state = SND_SOC_DPCM_STATE_CLOSE; 116701d7584cSLiam Girdwood } 116801d7584cSLiam Girdwood } 116901d7584cSLiam Girdwood 117023607025SLiam Girdwood int dpcm_be_dai_startup(struct snd_soc_pcm_runtime *fe, int stream) 117101d7584cSLiam Girdwood { 117201d7584cSLiam Girdwood struct snd_soc_dpcm *dpcm; 117301d7584cSLiam Girdwood int err, count = 0; 117401d7584cSLiam Girdwood 117501d7584cSLiam Girdwood /* only startup BE DAIs that are either sinks or sources to this FE DAI */ 117601d7584cSLiam Girdwood list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) { 117701d7584cSLiam Girdwood 117801d7584cSLiam Girdwood struct snd_soc_pcm_runtime *be = dpcm->be; 117901d7584cSLiam Girdwood struct snd_pcm_substream *be_substream = 118001d7584cSLiam Girdwood snd_soc_dpcm_get_substream(be, stream); 118101d7584cSLiam Girdwood 11822062b4c5SRussell King - ARM Linux if (!be_substream) { 11832062b4c5SRussell King - ARM Linux dev_err(be->dev, "ASoC: no backend %s stream\n", 11842062b4c5SRussell King - ARM Linux stream ? "capture" : "playback"); 11852062b4c5SRussell King - ARM Linux continue; 11862062b4c5SRussell King - ARM Linux } 11872062b4c5SRussell King - ARM Linux 118801d7584cSLiam Girdwood /* is this op for this BE ? */ 118901d7584cSLiam Girdwood if (!snd_soc_dpcm_be_can_update(fe, be, stream)) 119001d7584cSLiam Girdwood continue; 119101d7584cSLiam Girdwood 119201d7584cSLiam Girdwood /* first time the dpcm is open ? */ 119301d7584cSLiam Girdwood if (be->dpcm[stream].users == DPCM_MAX_BE_USERS) 1194103d84a3SLiam Girdwood dev_err(be->dev, "ASoC: too many users %s at open %d\n", 119501d7584cSLiam Girdwood stream ? "capture" : "playback", 119601d7584cSLiam Girdwood be->dpcm[stream].state); 119701d7584cSLiam Girdwood 119801d7584cSLiam Girdwood if (be->dpcm[stream].users++ != 0) 119901d7584cSLiam Girdwood continue; 120001d7584cSLiam Girdwood 120101d7584cSLiam Girdwood if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_NEW) && 120201d7584cSLiam Girdwood (be->dpcm[stream].state != SND_SOC_DPCM_STATE_CLOSE)) 120301d7584cSLiam Girdwood continue; 120401d7584cSLiam Girdwood 12052062b4c5SRussell King - ARM Linux dev_dbg(be->dev, "ASoC: open %s BE %s\n", 12062062b4c5SRussell King - ARM Linux stream ? "capture" : "playback", be->dai_link->name); 120701d7584cSLiam Girdwood 120801d7584cSLiam Girdwood be_substream->runtime = be->dpcm[stream].runtime; 120901d7584cSLiam Girdwood err = soc_pcm_open(be_substream); 121001d7584cSLiam Girdwood if (err < 0) { 1211103d84a3SLiam Girdwood dev_err(be->dev, "ASoC: BE open failed %d\n", err); 121201d7584cSLiam Girdwood be->dpcm[stream].users--; 121301d7584cSLiam Girdwood if (be->dpcm[stream].users < 0) 1214103d84a3SLiam Girdwood dev_err(be->dev, "ASoC: no users %s at unwind %d\n", 121501d7584cSLiam Girdwood stream ? "capture" : "playback", 121601d7584cSLiam Girdwood be->dpcm[stream].state); 121701d7584cSLiam Girdwood 121801d7584cSLiam Girdwood be->dpcm[stream].state = SND_SOC_DPCM_STATE_CLOSE; 121901d7584cSLiam Girdwood goto unwind; 122001d7584cSLiam Girdwood } 122101d7584cSLiam Girdwood 122201d7584cSLiam Girdwood be->dpcm[stream].state = SND_SOC_DPCM_STATE_OPEN; 122301d7584cSLiam Girdwood count++; 122401d7584cSLiam Girdwood } 122501d7584cSLiam Girdwood 122601d7584cSLiam Girdwood return count; 122701d7584cSLiam Girdwood 122801d7584cSLiam Girdwood unwind: 122901d7584cSLiam Girdwood /* disable any enabled and non active backends */ 123001d7584cSLiam Girdwood list_for_each_entry_continue_reverse(dpcm, &fe->dpcm[stream].be_clients, list_be) { 123101d7584cSLiam Girdwood struct snd_soc_pcm_runtime *be = dpcm->be; 123201d7584cSLiam Girdwood struct snd_pcm_substream *be_substream = 123301d7584cSLiam Girdwood snd_soc_dpcm_get_substream(be, stream); 123401d7584cSLiam Girdwood 123501d7584cSLiam Girdwood if (!snd_soc_dpcm_be_can_update(fe, be, stream)) 123601d7584cSLiam Girdwood continue; 123701d7584cSLiam Girdwood 123801d7584cSLiam Girdwood if (be->dpcm[stream].users == 0) 1239103d84a3SLiam Girdwood dev_err(be->dev, "ASoC: no users %s at close %d\n", 124001d7584cSLiam Girdwood stream ? "capture" : "playback", 124101d7584cSLiam Girdwood be->dpcm[stream].state); 124201d7584cSLiam Girdwood 124301d7584cSLiam Girdwood if (--be->dpcm[stream].users != 0) 124401d7584cSLiam Girdwood continue; 124501d7584cSLiam Girdwood 124601d7584cSLiam Girdwood if (be->dpcm[stream].state != SND_SOC_DPCM_STATE_OPEN) 124701d7584cSLiam Girdwood continue; 124801d7584cSLiam Girdwood 124901d7584cSLiam Girdwood soc_pcm_close(be_substream); 125001d7584cSLiam Girdwood be_substream->runtime = NULL; 125101d7584cSLiam Girdwood be->dpcm[stream].state = SND_SOC_DPCM_STATE_CLOSE; 125201d7584cSLiam Girdwood } 125301d7584cSLiam Girdwood 125401d7584cSLiam Girdwood return err; 125501d7584cSLiam Girdwood } 125601d7584cSLiam Girdwood 125708ae9b45SLars-Peter Clausen static void dpcm_init_runtime_hw(struct snd_pcm_runtime *runtime, 125808ae9b45SLars-Peter Clausen struct snd_soc_pcm_stream *stream) 125908ae9b45SLars-Peter Clausen { 126008ae9b45SLars-Peter Clausen runtime->hw.rate_min = stream->rate_min; 126108ae9b45SLars-Peter Clausen runtime->hw.rate_max = stream->rate_max; 126208ae9b45SLars-Peter Clausen runtime->hw.channels_min = stream->channels_min; 126308ae9b45SLars-Peter Clausen runtime->hw.channels_max = stream->channels_max; 1264002220a9SLars-Peter Clausen if (runtime->hw.formats) 126508ae9b45SLars-Peter Clausen runtime->hw.formats &= stream->formats; 1266002220a9SLars-Peter Clausen else 1267002220a9SLars-Peter Clausen runtime->hw.formats = stream->formats; 126808ae9b45SLars-Peter Clausen runtime->hw.rates = stream->rates; 126908ae9b45SLars-Peter Clausen } 127008ae9b45SLars-Peter Clausen 127145c0a188SMark Brown static void dpcm_set_fe_runtime(struct snd_pcm_substream *substream) 127201d7584cSLiam Girdwood { 127301d7584cSLiam Girdwood struct snd_pcm_runtime *runtime = substream->runtime; 127401d7584cSLiam Girdwood struct snd_soc_pcm_runtime *rtd = substream->private_data; 127501d7584cSLiam Girdwood struct snd_soc_dai *cpu_dai = rtd->cpu_dai; 127601d7584cSLiam Girdwood struct snd_soc_dai_driver *cpu_dai_drv = cpu_dai->driver; 127701d7584cSLiam Girdwood 127808ae9b45SLars-Peter Clausen if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 127908ae9b45SLars-Peter Clausen dpcm_init_runtime_hw(runtime, &cpu_dai_drv->playback); 128008ae9b45SLars-Peter Clausen else 128108ae9b45SLars-Peter Clausen dpcm_init_runtime_hw(runtime, &cpu_dai_drv->capture); 128201d7584cSLiam Girdwood } 128301d7584cSLiam Girdwood 128401d7584cSLiam Girdwood static int dpcm_fe_dai_startup(struct snd_pcm_substream *fe_substream) 128501d7584cSLiam Girdwood { 128601d7584cSLiam Girdwood struct snd_soc_pcm_runtime *fe = fe_substream->private_data; 128701d7584cSLiam Girdwood struct snd_pcm_runtime *runtime = fe_substream->runtime; 128801d7584cSLiam Girdwood int stream = fe_substream->stream, ret = 0; 128901d7584cSLiam Girdwood 129001d7584cSLiam Girdwood fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE; 129101d7584cSLiam Girdwood 129201d7584cSLiam Girdwood ret = dpcm_be_dai_startup(fe, fe_substream->stream); 129301d7584cSLiam Girdwood if (ret < 0) { 1294103d84a3SLiam Girdwood dev_err(fe->dev,"ASoC: failed to start some BEs %d\n", ret); 129501d7584cSLiam Girdwood goto be_err; 129601d7584cSLiam Girdwood } 129701d7584cSLiam Girdwood 1298103d84a3SLiam Girdwood dev_dbg(fe->dev, "ASoC: open FE %s\n", fe->dai_link->name); 129901d7584cSLiam Girdwood 130001d7584cSLiam Girdwood /* start the DAI frontend */ 130101d7584cSLiam Girdwood ret = soc_pcm_open(fe_substream); 130201d7584cSLiam Girdwood if (ret < 0) { 1303103d84a3SLiam Girdwood dev_err(fe->dev,"ASoC: failed to start FE %d\n", ret); 130401d7584cSLiam Girdwood goto unwind; 130501d7584cSLiam Girdwood } 130601d7584cSLiam Girdwood 130701d7584cSLiam Girdwood fe->dpcm[stream].state = SND_SOC_DPCM_STATE_OPEN; 130801d7584cSLiam Girdwood 130901d7584cSLiam Girdwood dpcm_set_fe_runtime(fe_substream); 131001d7584cSLiam Girdwood snd_pcm_limit_hw_rates(runtime); 131101d7584cSLiam Girdwood 131201d7584cSLiam Girdwood fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO; 131301d7584cSLiam Girdwood return 0; 131401d7584cSLiam Girdwood 131501d7584cSLiam Girdwood unwind: 131601d7584cSLiam Girdwood dpcm_be_dai_startup_unwind(fe, fe_substream->stream); 131701d7584cSLiam Girdwood be_err: 131801d7584cSLiam Girdwood fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO; 131901d7584cSLiam Girdwood return ret; 132001d7584cSLiam Girdwood } 132101d7584cSLiam Girdwood 132223607025SLiam Girdwood int dpcm_be_dai_shutdown(struct snd_soc_pcm_runtime *fe, int stream) 132301d7584cSLiam Girdwood { 132401d7584cSLiam Girdwood struct snd_soc_dpcm *dpcm; 132501d7584cSLiam Girdwood 132601d7584cSLiam Girdwood /* only shutdown BEs that are either sinks or sources to this FE DAI */ 132701d7584cSLiam Girdwood list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) { 132801d7584cSLiam Girdwood 132901d7584cSLiam Girdwood struct snd_soc_pcm_runtime *be = dpcm->be; 133001d7584cSLiam Girdwood struct snd_pcm_substream *be_substream = 133101d7584cSLiam Girdwood snd_soc_dpcm_get_substream(be, stream); 133201d7584cSLiam Girdwood 133301d7584cSLiam Girdwood /* is this op for this BE ? */ 133401d7584cSLiam Girdwood if (!snd_soc_dpcm_be_can_update(fe, be, stream)) 133501d7584cSLiam Girdwood continue; 133601d7584cSLiam Girdwood 133701d7584cSLiam Girdwood if (be->dpcm[stream].users == 0) 1338103d84a3SLiam Girdwood dev_err(be->dev, "ASoC: no users %s at close - state %d\n", 133901d7584cSLiam Girdwood stream ? "capture" : "playback", 134001d7584cSLiam Girdwood be->dpcm[stream].state); 134101d7584cSLiam Girdwood 134201d7584cSLiam Girdwood if (--be->dpcm[stream].users != 0) 134301d7584cSLiam Girdwood continue; 134401d7584cSLiam Girdwood 134501d7584cSLiam Girdwood if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_FREE) && 134601d7584cSLiam Girdwood (be->dpcm[stream].state != SND_SOC_DPCM_STATE_OPEN)) 134701d7584cSLiam Girdwood continue; 134801d7584cSLiam Girdwood 1349103d84a3SLiam Girdwood dev_dbg(be->dev, "ASoC: close BE %s\n", 135001d7584cSLiam Girdwood dpcm->fe->dai_link->name); 135101d7584cSLiam Girdwood 135201d7584cSLiam Girdwood soc_pcm_close(be_substream); 135301d7584cSLiam Girdwood be_substream->runtime = NULL; 135401d7584cSLiam Girdwood 135501d7584cSLiam Girdwood be->dpcm[stream].state = SND_SOC_DPCM_STATE_CLOSE; 135601d7584cSLiam Girdwood } 135701d7584cSLiam Girdwood return 0; 135801d7584cSLiam Girdwood } 135901d7584cSLiam Girdwood 136001d7584cSLiam Girdwood static int dpcm_fe_dai_shutdown(struct snd_pcm_substream *substream) 136101d7584cSLiam Girdwood { 136201d7584cSLiam Girdwood struct snd_soc_pcm_runtime *fe = substream->private_data; 136301d7584cSLiam Girdwood int stream = substream->stream; 136401d7584cSLiam Girdwood 136501d7584cSLiam Girdwood fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE; 136601d7584cSLiam Girdwood 136701d7584cSLiam Girdwood /* shutdown the BEs */ 136801d7584cSLiam Girdwood dpcm_be_dai_shutdown(fe, substream->stream); 136901d7584cSLiam Girdwood 1370103d84a3SLiam Girdwood dev_dbg(fe->dev, "ASoC: close FE %s\n", fe->dai_link->name); 137101d7584cSLiam Girdwood 137201d7584cSLiam Girdwood /* now shutdown the frontend */ 137301d7584cSLiam Girdwood soc_pcm_close(substream); 137401d7584cSLiam Girdwood 137501d7584cSLiam Girdwood /* run the stream event for each BE */ 137601d7584cSLiam Girdwood dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_STOP); 137701d7584cSLiam Girdwood 137801d7584cSLiam Girdwood fe->dpcm[stream].state = SND_SOC_DPCM_STATE_CLOSE; 137901d7584cSLiam Girdwood fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO; 138001d7584cSLiam Girdwood return 0; 138101d7584cSLiam Girdwood } 138201d7584cSLiam Girdwood 138323607025SLiam Girdwood int dpcm_be_dai_hw_free(struct snd_soc_pcm_runtime *fe, int stream) 138401d7584cSLiam Girdwood { 138501d7584cSLiam Girdwood struct snd_soc_dpcm *dpcm; 138601d7584cSLiam Girdwood 138701d7584cSLiam Girdwood /* only hw_params backends that are either sinks or sources 138801d7584cSLiam Girdwood * to this frontend DAI */ 138901d7584cSLiam Girdwood list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) { 139001d7584cSLiam Girdwood 139101d7584cSLiam Girdwood struct snd_soc_pcm_runtime *be = dpcm->be; 139201d7584cSLiam Girdwood struct snd_pcm_substream *be_substream = 139301d7584cSLiam Girdwood snd_soc_dpcm_get_substream(be, stream); 139401d7584cSLiam Girdwood 139501d7584cSLiam Girdwood /* is this op for this BE ? */ 139601d7584cSLiam Girdwood if (!snd_soc_dpcm_be_can_update(fe, be, stream)) 139701d7584cSLiam Girdwood continue; 139801d7584cSLiam Girdwood 139901d7584cSLiam Girdwood /* only free hw when no longer used - check all FEs */ 140001d7584cSLiam Girdwood if (!snd_soc_dpcm_can_be_free_stop(fe, be, stream)) 140101d7584cSLiam Girdwood continue; 140201d7584cSLiam Girdwood 140301d7584cSLiam Girdwood if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_PARAMS) && 140401d7584cSLiam Girdwood (be->dpcm[stream].state != SND_SOC_DPCM_STATE_PREPARE) && 140501d7584cSLiam Girdwood (be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_FREE) && 140608b27848SPatrick Lai (be->dpcm[stream].state != SND_SOC_DPCM_STATE_PAUSED) && 140701d7584cSLiam Girdwood (be->dpcm[stream].state != SND_SOC_DPCM_STATE_STOP)) 140801d7584cSLiam Girdwood continue; 140901d7584cSLiam Girdwood 1410103d84a3SLiam Girdwood dev_dbg(be->dev, "ASoC: hw_free BE %s\n", 141101d7584cSLiam Girdwood dpcm->fe->dai_link->name); 141201d7584cSLiam Girdwood 141301d7584cSLiam Girdwood soc_pcm_hw_free(be_substream); 141401d7584cSLiam Girdwood 141501d7584cSLiam Girdwood be->dpcm[stream].state = SND_SOC_DPCM_STATE_HW_FREE; 141601d7584cSLiam Girdwood } 141701d7584cSLiam Girdwood 141801d7584cSLiam Girdwood return 0; 141901d7584cSLiam Girdwood } 142001d7584cSLiam Girdwood 142145c0a188SMark Brown static int dpcm_fe_dai_hw_free(struct snd_pcm_substream *substream) 142201d7584cSLiam Girdwood { 142301d7584cSLiam Girdwood struct snd_soc_pcm_runtime *fe = substream->private_data; 142401d7584cSLiam Girdwood int err, stream = substream->stream; 142501d7584cSLiam Girdwood 142601d7584cSLiam Girdwood mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME); 142701d7584cSLiam Girdwood fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE; 142801d7584cSLiam Girdwood 1429103d84a3SLiam Girdwood dev_dbg(fe->dev, "ASoC: hw_free FE %s\n", fe->dai_link->name); 143001d7584cSLiam Girdwood 143101d7584cSLiam Girdwood /* call hw_free on the frontend */ 143201d7584cSLiam Girdwood err = soc_pcm_hw_free(substream); 143301d7584cSLiam Girdwood if (err < 0) 1434103d84a3SLiam Girdwood dev_err(fe->dev,"ASoC: hw_free FE %s failed\n", 143501d7584cSLiam Girdwood fe->dai_link->name); 143601d7584cSLiam Girdwood 143701d7584cSLiam Girdwood /* only hw_params backends that are either sinks or sources 143801d7584cSLiam Girdwood * to this frontend DAI */ 143901d7584cSLiam Girdwood err = dpcm_be_dai_hw_free(fe, stream); 144001d7584cSLiam Girdwood 144101d7584cSLiam Girdwood fe->dpcm[stream].state = SND_SOC_DPCM_STATE_HW_FREE; 144201d7584cSLiam Girdwood fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO; 144301d7584cSLiam Girdwood 144401d7584cSLiam Girdwood mutex_unlock(&fe->card->mutex); 144501d7584cSLiam Girdwood return 0; 144601d7584cSLiam Girdwood } 144701d7584cSLiam Girdwood 144823607025SLiam Girdwood int dpcm_be_dai_hw_params(struct snd_soc_pcm_runtime *fe, int stream) 144901d7584cSLiam Girdwood { 145001d7584cSLiam Girdwood struct snd_soc_dpcm *dpcm; 145101d7584cSLiam Girdwood int ret; 145201d7584cSLiam Girdwood 145301d7584cSLiam Girdwood list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) { 145401d7584cSLiam Girdwood 145501d7584cSLiam Girdwood struct snd_soc_pcm_runtime *be = dpcm->be; 145601d7584cSLiam Girdwood struct snd_pcm_substream *be_substream = 145701d7584cSLiam Girdwood snd_soc_dpcm_get_substream(be, stream); 145801d7584cSLiam Girdwood 145901d7584cSLiam Girdwood /* is this op for this BE ? */ 146001d7584cSLiam Girdwood if (!snd_soc_dpcm_be_can_update(fe, be, stream)) 146101d7584cSLiam Girdwood continue; 146201d7584cSLiam Girdwood 146301d7584cSLiam Girdwood /* only allow hw_params() if no connected FEs are running */ 146401d7584cSLiam Girdwood if (!snd_soc_dpcm_can_be_params(fe, be, stream)) 146501d7584cSLiam Girdwood continue; 146601d7584cSLiam Girdwood 146701d7584cSLiam Girdwood if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_OPEN) && 146801d7584cSLiam Girdwood (be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_PARAMS) && 146901d7584cSLiam Girdwood (be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_FREE)) 147001d7584cSLiam Girdwood continue; 147101d7584cSLiam Girdwood 1472103d84a3SLiam Girdwood dev_dbg(be->dev, "ASoC: hw_params BE %s\n", 147301d7584cSLiam Girdwood dpcm->fe->dai_link->name); 147401d7584cSLiam Girdwood 147501d7584cSLiam Girdwood /* copy params for each dpcm */ 147601d7584cSLiam Girdwood memcpy(&dpcm->hw_params, &fe->dpcm[stream].hw_params, 147701d7584cSLiam Girdwood sizeof(struct snd_pcm_hw_params)); 147801d7584cSLiam Girdwood 147901d7584cSLiam Girdwood /* perform any hw_params fixups */ 148001d7584cSLiam Girdwood if (be->dai_link->be_hw_params_fixup) { 148101d7584cSLiam Girdwood ret = be->dai_link->be_hw_params_fixup(be, 148201d7584cSLiam Girdwood &dpcm->hw_params); 148301d7584cSLiam Girdwood if (ret < 0) { 148401d7584cSLiam Girdwood dev_err(be->dev, 1485103d84a3SLiam Girdwood "ASoC: hw_params BE fixup failed %d\n", 148601d7584cSLiam Girdwood ret); 148701d7584cSLiam Girdwood goto unwind; 148801d7584cSLiam Girdwood } 148901d7584cSLiam Girdwood } 149001d7584cSLiam Girdwood 149101d7584cSLiam Girdwood ret = soc_pcm_hw_params(be_substream, &dpcm->hw_params); 149201d7584cSLiam Girdwood if (ret < 0) { 149301d7584cSLiam Girdwood dev_err(dpcm->be->dev, 1494103d84a3SLiam Girdwood "ASoC: hw_params BE failed %d\n", ret); 149501d7584cSLiam Girdwood goto unwind; 149601d7584cSLiam Girdwood } 149701d7584cSLiam Girdwood 149801d7584cSLiam Girdwood be->dpcm[stream].state = SND_SOC_DPCM_STATE_HW_PARAMS; 149901d7584cSLiam Girdwood } 150001d7584cSLiam Girdwood return 0; 150101d7584cSLiam Girdwood 150201d7584cSLiam Girdwood unwind: 150301d7584cSLiam Girdwood /* disable any enabled and non active backends */ 150401d7584cSLiam Girdwood list_for_each_entry_continue_reverse(dpcm, &fe->dpcm[stream].be_clients, list_be) { 150501d7584cSLiam Girdwood struct snd_soc_pcm_runtime *be = dpcm->be; 150601d7584cSLiam Girdwood struct snd_pcm_substream *be_substream = 150701d7584cSLiam Girdwood snd_soc_dpcm_get_substream(be, stream); 150801d7584cSLiam Girdwood 150901d7584cSLiam Girdwood if (!snd_soc_dpcm_be_can_update(fe, be, stream)) 151001d7584cSLiam Girdwood continue; 151101d7584cSLiam Girdwood 151201d7584cSLiam Girdwood /* only allow hw_free() if no connected FEs are running */ 151301d7584cSLiam Girdwood if (!snd_soc_dpcm_can_be_free_stop(fe, be, stream)) 151401d7584cSLiam Girdwood continue; 151501d7584cSLiam Girdwood 151601d7584cSLiam Girdwood if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_OPEN) && 151701d7584cSLiam Girdwood (be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_PARAMS) && 151801d7584cSLiam Girdwood (be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_FREE) && 151901d7584cSLiam Girdwood (be->dpcm[stream].state != SND_SOC_DPCM_STATE_STOP)) 152001d7584cSLiam Girdwood continue; 152101d7584cSLiam Girdwood 152201d7584cSLiam Girdwood soc_pcm_hw_free(be_substream); 152301d7584cSLiam Girdwood } 152401d7584cSLiam Girdwood 152501d7584cSLiam Girdwood return ret; 152601d7584cSLiam Girdwood } 152701d7584cSLiam Girdwood 152845c0a188SMark Brown static int dpcm_fe_dai_hw_params(struct snd_pcm_substream *substream, 152901d7584cSLiam Girdwood struct snd_pcm_hw_params *params) 153001d7584cSLiam Girdwood { 153101d7584cSLiam Girdwood struct snd_soc_pcm_runtime *fe = substream->private_data; 153201d7584cSLiam Girdwood int ret, stream = substream->stream; 153301d7584cSLiam Girdwood 153401d7584cSLiam Girdwood mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME); 153501d7584cSLiam Girdwood fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE; 153601d7584cSLiam Girdwood 153701d7584cSLiam Girdwood memcpy(&fe->dpcm[substream->stream].hw_params, params, 153801d7584cSLiam Girdwood sizeof(struct snd_pcm_hw_params)); 153901d7584cSLiam Girdwood ret = dpcm_be_dai_hw_params(fe, substream->stream); 154001d7584cSLiam Girdwood if (ret < 0) { 1541103d84a3SLiam Girdwood dev_err(fe->dev,"ASoC: hw_params BE failed %d\n", ret); 154201d7584cSLiam Girdwood goto out; 154301d7584cSLiam Girdwood } 154401d7584cSLiam Girdwood 1545103d84a3SLiam Girdwood dev_dbg(fe->dev, "ASoC: hw_params FE %s rate %d chan %x fmt %d\n", 154601d7584cSLiam Girdwood fe->dai_link->name, params_rate(params), 154701d7584cSLiam Girdwood params_channels(params), params_format(params)); 154801d7584cSLiam Girdwood 154901d7584cSLiam Girdwood /* call hw_params on the frontend */ 155001d7584cSLiam Girdwood ret = soc_pcm_hw_params(substream, params); 155101d7584cSLiam Girdwood if (ret < 0) { 1552103d84a3SLiam Girdwood dev_err(fe->dev,"ASoC: hw_params FE failed %d\n", ret); 155301d7584cSLiam Girdwood dpcm_be_dai_hw_free(fe, stream); 155401d7584cSLiam Girdwood } else 155501d7584cSLiam Girdwood fe->dpcm[stream].state = SND_SOC_DPCM_STATE_HW_PARAMS; 155601d7584cSLiam Girdwood 155701d7584cSLiam Girdwood out: 155801d7584cSLiam Girdwood fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO; 155901d7584cSLiam Girdwood mutex_unlock(&fe->card->mutex); 156001d7584cSLiam Girdwood return ret; 156101d7584cSLiam Girdwood } 156201d7584cSLiam Girdwood 156301d7584cSLiam Girdwood static int dpcm_do_trigger(struct snd_soc_dpcm *dpcm, 156401d7584cSLiam Girdwood struct snd_pcm_substream *substream, int cmd) 156501d7584cSLiam Girdwood { 156601d7584cSLiam Girdwood int ret; 156701d7584cSLiam Girdwood 1568103d84a3SLiam Girdwood dev_dbg(dpcm->be->dev, "ASoC: trigger BE %s cmd %d\n", 156901d7584cSLiam Girdwood dpcm->fe->dai_link->name, cmd); 157001d7584cSLiam Girdwood 157101d7584cSLiam Girdwood ret = soc_pcm_trigger(substream, cmd); 157201d7584cSLiam Girdwood if (ret < 0) 1573103d84a3SLiam Girdwood dev_err(dpcm->be->dev,"ASoC: trigger BE failed %d\n", ret); 157401d7584cSLiam Girdwood 157501d7584cSLiam Girdwood return ret; 157601d7584cSLiam Girdwood } 157701d7584cSLiam Girdwood 157823607025SLiam Girdwood int dpcm_be_dai_trigger(struct snd_soc_pcm_runtime *fe, int stream, 157945c0a188SMark Brown int cmd) 158001d7584cSLiam Girdwood { 158101d7584cSLiam Girdwood struct snd_soc_dpcm *dpcm; 158201d7584cSLiam Girdwood int ret = 0; 158301d7584cSLiam Girdwood 158401d7584cSLiam Girdwood list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) { 158501d7584cSLiam Girdwood 158601d7584cSLiam Girdwood struct snd_soc_pcm_runtime *be = dpcm->be; 158701d7584cSLiam Girdwood struct snd_pcm_substream *be_substream = 158801d7584cSLiam Girdwood snd_soc_dpcm_get_substream(be, stream); 158901d7584cSLiam Girdwood 159001d7584cSLiam Girdwood /* is this op for this BE ? */ 159101d7584cSLiam Girdwood if (!snd_soc_dpcm_be_can_update(fe, be, stream)) 159201d7584cSLiam Girdwood continue; 159301d7584cSLiam Girdwood 159401d7584cSLiam Girdwood switch (cmd) { 159501d7584cSLiam Girdwood case SNDRV_PCM_TRIGGER_START: 159601d7584cSLiam Girdwood if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_PREPARE) && 159701d7584cSLiam Girdwood (be->dpcm[stream].state != SND_SOC_DPCM_STATE_STOP)) 159801d7584cSLiam Girdwood continue; 159901d7584cSLiam Girdwood 160001d7584cSLiam Girdwood ret = dpcm_do_trigger(dpcm, be_substream, cmd); 160101d7584cSLiam Girdwood if (ret) 160201d7584cSLiam Girdwood return ret; 160301d7584cSLiam Girdwood 160401d7584cSLiam Girdwood be->dpcm[stream].state = SND_SOC_DPCM_STATE_START; 160501d7584cSLiam Girdwood break; 160601d7584cSLiam Girdwood case SNDRV_PCM_TRIGGER_RESUME: 160701d7584cSLiam Girdwood if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_SUSPEND)) 160801d7584cSLiam Girdwood continue; 160901d7584cSLiam Girdwood 161001d7584cSLiam Girdwood ret = dpcm_do_trigger(dpcm, be_substream, cmd); 161101d7584cSLiam Girdwood if (ret) 161201d7584cSLiam Girdwood return ret; 161301d7584cSLiam Girdwood 161401d7584cSLiam Girdwood be->dpcm[stream].state = SND_SOC_DPCM_STATE_START; 161501d7584cSLiam Girdwood break; 161601d7584cSLiam Girdwood case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 161701d7584cSLiam Girdwood if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_PAUSED)) 161801d7584cSLiam Girdwood continue; 161901d7584cSLiam Girdwood 162001d7584cSLiam Girdwood ret = dpcm_do_trigger(dpcm, be_substream, cmd); 162101d7584cSLiam Girdwood if (ret) 162201d7584cSLiam Girdwood return ret; 162301d7584cSLiam Girdwood 162401d7584cSLiam Girdwood be->dpcm[stream].state = SND_SOC_DPCM_STATE_START; 162501d7584cSLiam Girdwood break; 162601d7584cSLiam Girdwood case SNDRV_PCM_TRIGGER_STOP: 162701d7584cSLiam Girdwood if (be->dpcm[stream].state != SND_SOC_DPCM_STATE_START) 162801d7584cSLiam Girdwood continue; 162901d7584cSLiam Girdwood 163001d7584cSLiam Girdwood if (!snd_soc_dpcm_can_be_free_stop(fe, be, stream)) 163101d7584cSLiam Girdwood continue; 163201d7584cSLiam Girdwood 163301d7584cSLiam Girdwood ret = dpcm_do_trigger(dpcm, be_substream, cmd); 163401d7584cSLiam Girdwood if (ret) 163501d7584cSLiam Girdwood return ret; 163601d7584cSLiam Girdwood 163701d7584cSLiam Girdwood be->dpcm[stream].state = SND_SOC_DPCM_STATE_STOP; 163801d7584cSLiam Girdwood break; 163901d7584cSLiam Girdwood case SNDRV_PCM_TRIGGER_SUSPEND: 164001d7584cSLiam Girdwood if (be->dpcm[stream].state != SND_SOC_DPCM_STATE_STOP) 164101d7584cSLiam Girdwood continue; 164201d7584cSLiam Girdwood 164301d7584cSLiam Girdwood if (!snd_soc_dpcm_can_be_free_stop(fe, be, stream)) 164401d7584cSLiam Girdwood continue; 164501d7584cSLiam Girdwood 164601d7584cSLiam Girdwood ret = dpcm_do_trigger(dpcm, be_substream, cmd); 164701d7584cSLiam Girdwood if (ret) 164801d7584cSLiam Girdwood return ret; 164901d7584cSLiam Girdwood 165001d7584cSLiam Girdwood be->dpcm[stream].state = SND_SOC_DPCM_STATE_SUSPEND; 165101d7584cSLiam Girdwood break; 165201d7584cSLiam Girdwood case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 165301d7584cSLiam Girdwood if (be->dpcm[stream].state != SND_SOC_DPCM_STATE_START) 165401d7584cSLiam Girdwood continue; 165501d7584cSLiam Girdwood 165601d7584cSLiam Girdwood if (!snd_soc_dpcm_can_be_free_stop(fe, be, stream)) 165701d7584cSLiam Girdwood continue; 165801d7584cSLiam Girdwood 165901d7584cSLiam Girdwood ret = dpcm_do_trigger(dpcm, be_substream, cmd); 166001d7584cSLiam Girdwood if (ret) 166101d7584cSLiam Girdwood return ret; 166201d7584cSLiam Girdwood 166301d7584cSLiam Girdwood be->dpcm[stream].state = SND_SOC_DPCM_STATE_PAUSED; 166401d7584cSLiam Girdwood break; 166501d7584cSLiam Girdwood } 166601d7584cSLiam Girdwood } 166701d7584cSLiam Girdwood 166801d7584cSLiam Girdwood return ret; 166901d7584cSLiam Girdwood } 167001d7584cSLiam Girdwood EXPORT_SYMBOL_GPL(dpcm_be_dai_trigger); 167101d7584cSLiam Girdwood 167245c0a188SMark Brown static int dpcm_fe_dai_trigger(struct snd_pcm_substream *substream, int cmd) 167301d7584cSLiam Girdwood { 167401d7584cSLiam Girdwood struct snd_soc_pcm_runtime *fe = substream->private_data; 167501d7584cSLiam Girdwood int stream = substream->stream, ret; 167601d7584cSLiam Girdwood enum snd_soc_dpcm_trigger trigger = fe->dai_link->trigger[stream]; 167701d7584cSLiam Girdwood 167801d7584cSLiam Girdwood fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE; 167901d7584cSLiam Girdwood 168001d7584cSLiam Girdwood switch (trigger) { 168101d7584cSLiam Girdwood case SND_SOC_DPCM_TRIGGER_PRE: 168201d7584cSLiam Girdwood /* call trigger on the frontend before the backend. */ 168301d7584cSLiam Girdwood 1684103d84a3SLiam Girdwood dev_dbg(fe->dev, "ASoC: pre trigger FE %s cmd %d\n", 168501d7584cSLiam Girdwood fe->dai_link->name, cmd); 168601d7584cSLiam Girdwood 168701d7584cSLiam Girdwood ret = soc_pcm_trigger(substream, cmd); 168801d7584cSLiam Girdwood if (ret < 0) { 1689103d84a3SLiam Girdwood dev_err(fe->dev,"ASoC: trigger FE failed %d\n", ret); 169001d7584cSLiam Girdwood goto out; 169101d7584cSLiam Girdwood } 169201d7584cSLiam Girdwood 169301d7584cSLiam Girdwood ret = dpcm_be_dai_trigger(fe, substream->stream, cmd); 169401d7584cSLiam Girdwood break; 169501d7584cSLiam Girdwood case SND_SOC_DPCM_TRIGGER_POST: 169601d7584cSLiam Girdwood /* call trigger on the frontend after the backend. */ 169701d7584cSLiam Girdwood 169801d7584cSLiam Girdwood ret = dpcm_be_dai_trigger(fe, substream->stream, cmd); 169901d7584cSLiam Girdwood if (ret < 0) { 1700103d84a3SLiam Girdwood dev_err(fe->dev,"ASoC: trigger FE failed %d\n", ret); 170101d7584cSLiam Girdwood goto out; 170201d7584cSLiam Girdwood } 170301d7584cSLiam Girdwood 1704103d84a3SLiam Girdwood dev_dbg(fe->dev, "ASoC: post trigger FE %s cmd %d\n", 170501d7584cSLiam Girdwood fe->dai_link->name, cmd); 170601d7584cSLiam Girdwood 170701d7584cSLiam Girdwood ret = soc_pcm_trigger(substream, cmd); 170801d7584cSLiam Girdwood break; 170907bf84aaSLiam Girdwood case SND_SOC_DPCM_TRIGGER_BESPOKE: 171007bf84aaSLiam Girdwood /* bespoke trigger() - handles both FE and BEs */ 171107bf84aaSLiam Girdwood 1712103d84a3SLiam Girdwood dev_dbg(fe->dev, "ASoC: bespoke trigger FE %s cmd %d\n", 171307bf84aaSLiam Girdwood fe->dai_link->name, cmd); 171407bf84aaSLiam Girdwood 171507bf84aaSLiam Girdwood ret = soc_pcm_bespoke_trigger(substream, cmd); 171607bf84aaSLiam Girdwood if (ret < 0) { 1717103d84a3SLiam Girdwood dev_err(fe->dev,"ASoC: trigger FE failed %d\n", ret); 171807bf84aaSLiam Girdwood goto out; 171907bf84aaSLiam Girdwood } 172007bf84aaSLiam Girdwood break; 172101d7584cSLiam Girdwood default: 1722103d84a3SLiam Girdwood dev_err(fe->dev, "ASoC: invalid trigger cmd %d for %s\n", cmd, 172301d7584cSLiam Girdwood fe->dai_link->name); 172401d7584cSLiam Girdwood ret = -EINVAL; 172501d7584cSLiam Girdwood goto out; 172601d7584cSLiam Girdwood } 172701d7584cSLiam Girdwood 172801d7584cSLiam Girdwood switch (cmd) { 172901d7584cSLiam Girdwood case SNDRV_PCM_TRIGGER_START: 173001d7584cSLiam Girdwood case SNDRV_PCM_TRIGGER_RESUME: 173101d7584cSLiam Girdwood case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 173201d7584cSLiam Girdwood fe->dpcm[stream].state = SND_SOC_DPCM_STATE_START; 173301d7584cSLiam Girdwood break; 173401d7584cSLiam Girdwood case SNDRV_PCM_TRIGGER_STOP: 173501d7584cSLiam Girdwood case SNDRV_PCM_TRIGGER_SUSPEND: 173601d7584cSLiam Girdwood case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 173701d7584cSLiam Girdwood fe->dpcm[stream].state = SND_SOC_DPCM_STATE_STOP; 173801d7584cSLiam Girdwood break; 173901d7584cSLiam Girdwood } 174001d7584cSLiam Girdwood 174101d7584cSLiam Girdwood out: 174201d7584cSLiam Girdwood fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO; 174301d7584cSLiam Girdwood return ret; 174401d7584cSLiam Girdwood } 174501d7584cSLiam Girdwood 174623607025SLiam Girdwood int dpcm_be_dai_prepare(struct snd_soc_pcm_runtime *fe, int stream) 174701d7584cSLiam Girdwood { 174801d7584cSLiam Girdwood struct snd_soc_dpcm *dpcm; 174901d7584cSLiam Girdwood int ret = 0; 175001d7584cSLiam Girdwood 175101d7584cSLiam Girdwood list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) { 175201d7584cSLiam Girdwood 175301d7584cSLiam Girdwood struct snd_soc_pcm_runtime *be = dpcm->be; 175401d7584cSLiam Girdwood struct snd_pcm_substream *be_substream = 175501d7584cSLiam Girdwood snd_soc_dpcm_get_substream(be, stream); 175601d7584cSLiam Girdwood 175701d7584cSLiam Girdwood /* is this op for this BE ? */ 175801d7584cSLiam Girdwood if (!snd_soc_dpcm_be_can_update(fe, be, stream)) 175901d7584cSLiam Girdwood continue; 176001d7584cSLiam Girdwood 176101d7584cSLiam Girdwood if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_PARAMS) && 176201d7584cSLiam Girdwood (be->dpcm[stream].state != SND_SOC_DPCM_STATE_STOP)) 176301d7584cSLiam Girdwood continue; 176401d7584cSLiam Girdwood 1765103d84a3SLiam Girdwood dev_dbg(be->dev, "ASoC: prepare BE %s\n", 176601d7584cSLiam Girdwood dpcm->fe->dai_link->name); 176701d7584cSLiam Girdwood 176801d7584cSLiam Girdwood ret = soc_pcm_prepare(be_substream); 176901d7584cSLiam Girdwood if (ret < 0) { 1770103d84a3SLiam Girdwood dev_err(be->dev, "ASoC: backend prepare failed %d\n", 177101d7584cSLiam Girdwood ret); 177201d7584cSLiam Girdwood break; 177301d7584cSLiam Girdwood } 177401d7584cSLiam Girdwood 177501d7584cSLiam Girdwood be->dpcm[stream].state = SND_SOC_DPCM_STATE_PREPARE; 177601d7584cSLiam Girdwood } 177701d7584cSLiam Girdwood return ret; 177801d7584cSLiam Girdwood } 177901d7584cSLiam Girdwood 178045c0a188SMark Brown static int dpcm_fe_dai_prepare(struct snd_pcm_substream *substream) 178101d7584cSLiam Girdwood { 178201d7584cSLiam Girdwood struct snd_soc_pcm_runtime *fe = substream->private_data; 178301d7584cSLiam Girdwood int stream = substream->stream, ret = 0; 178401d7584cSLiam Girdwood 178501d7584cSLiam Girdwood mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME); 178601d7584cSLiam Girdwood 1787103d84a3SLiam Girdwood dev_dbg(fe->dev, "ASoC: prepare FE %s\n", fe->dai_link->name); 178801d7584cSLiam Girdwood 178901d7584cSLiam Girdwood fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE; 179001d7584cSLiam Girdwood 179101d7584cSLiam Girdwood /* there is no point preparing this FE if there are no BEs */ 179201d7584cSLiam Girdwood if (list_empty(&fe->dpcm[stream].be_clients)) { 1793103d84a3SLiam Girdwood dev_err(fe->dev, "ASoC: no backend DAIs enabled for %s\n", 179401d7584cSLiam Girdwood fe->dai_link->name); 179501d7584cSLiam Girdwood ret = -EINVAL; 179601d7584cSLiam Girdwood goto out; 179701d7584cSLiam Girdwood } 179801d7584cSLiam Girdwood 179901d7584cSLiam Girdwood ret = dpcm_be_dai_prepare(fe, substream->stream); 180001d7584cSLiam Girdwood if (ret < 0) 180101d7584cSLiam Girdwood goto out; 180201d7584cSLiam Girdwood 180301d7584cSLiam Girdwood /* call prepare on the frontend */ 180401d7584cSLiam Girdwood ret = soc_pcm_prepare(substream); 180501d7584cSLiam Girdwood if (ret < 0) { 1806103d84a3SLiam Girdwood dev_err(fe->dev,"ASoC: prepare FE %s failed\n", 180701d7584cSLiam Girdwood fe->dai_link->name); 180801d7584cSLiam Girdwood goto out; 180901d7584cSLiam Girdwood } 181001d7584cSLiam Girdwood 181101d7584cSLiam Girdwood /* run the stream event for each BE */ 181201d7584cSLiam Girdwood dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_START); 181301d7584cSLiam Girdwood fe->dpcm[stream].state = SND_SOC_DPCM_STATE_PREPARE; 181401d7584cSLiam Girdwood 181501d7584cSLiam Girdwood out: 181601d7584cSLiam Girdwood fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO; 181701d7584cSLiam Girdwood mutex_unlock(&fe->card->mutex); 181801d7584cSLiam Girdwood 181901d7584cSLiam Girdwood return ret; 182001d7584cSLiam Girdwood } 182101d7584cSLiam Girdwood 1822be3f3f2cSLiam Girdwood static int soc_pcm_ioctl(struct snd_pcm_substream *substream, 1823be3f3f2cSLiam Girdwood unsigned int cmd, void *arg) 1824be3f3f2cSLiam Girdwood { 1825be3f3f2cSLiam Girdwood struct snd_soc_pcm_runtime *rtd = substream->private_data; 1826be3f3f2cSLiam Girdwood struct snd_soc_platform *platform = rtd->platform; 1827be3f3f2cSLiam Girdwood 1828c5914b0aSMark Brown if (platform->driver->ops && platform->driver->ops->ioctl) 1829be3f3f2cSLiam Girdwood return platform->driver->ops->ioctl(substream, cmd, arg); 1830be3f3f2cSLiam Girdwood return snd_pcm_lib_ioctl(substream, cmd, arg); 1831be3f3f2cSLiam Girdwood } 1832be3f3f2cSLiam Girdwood 1833618dae11SLiam Girdwood static int dpcm_run_update_shutdown(struct snd_soc_pcm_runtime *fe, int stream) 1834618dae11SLiam Girdwood { 183507bf84aaSLiam Girdwood struct snd_pcm_substream *substream = 183607bf84aaSLiam Girdwood snd_soc_dpcm_get_substream(fe, stream); 183707bf84aaSLiam Girdwood enum snd_soc_dpcm_trigger trigger = fe->dai_link->trigger[stream]; 1838618dae11SLiam Girdwood int err; 183901d7584cSLiam Girdwood 1840103d84a3SLiam Girdwood dev_dbg(fe->dev, "ASoC: runtime %s close on FE %s\n", 1841618dae11SLiam Girdwood stream ? "capture" : "playback", fe->dai_link->name); 1842618dae11SLiam Girdwood 184307bf84aaSLiam Girdwood if (trigger == SND_SOC_DPCM_TRIGGER_BESPOKE) { 184407bf84aaSLiam Girdwood /* call bespoke trigger - FE takes care of all BE triggers */ 1845103d84a3SLiam Girdwood dev_dbg(fe->dev, "ASoC: bespoke trigger FE %s cmd stop\n", 184607bf84aaSLiam Girdwood fe->dai_link->name); 184707bf84aaSLiam Girdwood 184807bf84aaSLiam Girdwood err = soc_pcm_bespoke_trigger(substream, SNDRV_PCM_TRIGGER_STOP); 184907bf84aaSLiam Girdwood if (err < 0) 1850103d84a3SLiam Girdwood dev_err(fe->dev,"ASoC: trigger FE failed %d\n", err); 185107bf84aaSLiam Girdwood } else { 1852103d84a3SLiam Girdwood dev_dbg(fe->dev, "ASoC: trigger FE %s cmd stop\n", 185307bf84aaSLiam Girdwood fe->dai_link->name); 185407bf84aaSLiam Girdwood 1855618dae11SLiam Girdwood err = dpcm_be_dai_trigger(fe, stream, SNDRV_PCM_TRIGGER_STOP); 1856618dae11SLiam Girdwood if (err < 0) 1857103d84a3SLiam Girdwood dev_err(fe->dev,"ASoC: trigger FE failed %d\n", err); 185807bf84aaSLiam Girdwood } 1859618dae11SLiam Girdwood 1860618dae11SLiam Girdwood err = dpcm_be_dai_hw_free(fe, stream); 1861618dae11SLiam Girdwood if (err < 0) 1862103d84a3SLiam Girdwood dev_err(fe->dev,"ASoC: hw_free FE failed %d\n", err); 1863618dae11SLiam Girdwood 1864618dae11SLiam Girdwood err = dpcm_be_dai_shutdown(fe, stream); 1865618dae11SLiam Girdwood if (err < 0) 1866103d84a3SLiam Girdwood dev_err(fe->dev,"ASoC: shutdown FE failed %d\n", err); 1867618dae11SLiam Girdwood 1868618dae11SLiam Girdwood /* run the stream event for each BE */ 1869618dae11SLiam Girdwood dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_NOP); 1870618dae11SLiam Girdwood 1871618dae11SLiam Girdwood return 0; 1872618dae11SLiam Girdwood } 1873618dae11SLiam Girdwood 1874618dae11SLiam Girdwood static int dpcm_run_update_startup(struct snd_soc_pcm_runtime *fe, int stream) 1875618dae11SLiam Girdwood { 187607bf84aaSLiam Girdwood struct snd_pcm_substream *substream = 187707bf84aaSLiam Girdwood snd_soc_dpcm_get_substream(fe, stream); 1878618dae11SLiam Girdwood struct snd_soc_dpcm *dpcm; 187907bf84aaSLiam Girdwood enum snd_soc_dpcm_trigger trigger = fe->dai_link->trigger[stream]; 1880618dae11SLiam Girdwood int ret; 1881618dae11SLiam Girdwood 1882103d84a3SLiam Girdwood dev_dbg(fe->dev, "ASoC: runtime %s open on FE %s\n", 1883618dae11SLiam Girdwood stream ? "capture" : "playback", fe->dai_link->name); 1884618dae11SLiam Girdwood 1885618dae11SLiam Girdwood /* Only start the BE if the FE is ready */ 1886618dae11SLiam Girdwood if (fe->dpcm[stream].state == SND_SOC_DPCM_STATE_HW_FREE || 1887618dae11SLiam Girdwood fe->dpcm[stream].state == SND_SOC_DPCM_STATE_CLOSE) 1888618dae11SLiam Girdwood return -EINVAL; 1889618dae11SLiam Girdwood 1890618dae11SLiam Girdwood /* startup must always be called for new BEs */ 1891618dae11SLiam Girdwood ret = dpcm_be_dai_startup(fe, stream); 1892fffc0ca2SDan Carpenter if (ret < 0) 1893618dae11SLiam Girdwood goto disconnect; 1894618dae11SLiam Girdwood 1895618dae11SLiam Girdwood /* keep going if FE state is > open */ 1896618dae11SLiam Girdwood if (fe->dpcm[stream].state == SND_SOC_DPCM_STATE_OPEN) 1897618dae11SLiam Girdwood return 0; 1898618dae11SLiam Girdwood 1899618dae11SLiam Girdwood ret = dpcm_be_dai_hw_params(fe, stream); 1900fffc0ca2SDan Carpenter if (ret < 0) 1901618dae11SLiam Girdwood goto close; 1902618dae11SLiam Girdwood 1903618dae11SLiam Girdwood /* keep going if FE state is > hw_params */ 1904618dae11SLiam Girdwood if (fe->dpcm[stream].state == SND_SOC_DPCM_STATE_HW_PARAMS) 1905618dae11SLiam Girdwood return 0; 1906618dae11SLiam Girdwood 1907618dae11SLiam Girdwood 1908618dae11SLiam Girdwood ret = dpcm_be_dai_prepare(fe, stream); 1909fffc0ca2SDan Carpenter if (ret < 0) 1910618dae11SLiam Girdwood goto hw_free; 1911618dae11SLiam Girdwood 1912618dae11SLiam Girdwood /* run the stream event for each BE */ 1913618dae11SLiam Girdwood dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_NOP); 1914618dae11SLiam Girdwood 1915618dae11SLiam Girdwood /* keep going if FE state is > prepare */ 1916618dae11SLiam Girdwood if (fe->dpcm[stream].state == SND_SOC_DPCM_STATE_PREPARE || 1917618dae11SLiam Girdwood fe->dpcm[stream].state == SND_SOC_DPCM_STATE_STOP) 1918618dae11SLiam Girdwood return 0; 1919618dae11SLiam Girdwood 192007bf84aaSLiam Girdwood if (trigger == SND_SOC_DPCM_TRIGGER_BESPOKE) { 192107bf84aaSLiam Girdwood /* call trigger on the frontend - FE takes care of all BE triggers */ 1922103d84a3SLiam Girdwood dev_dbg(fe->dev, "ASoC: bespoke trigger FE %s cmd start\n", 192307bf84aaSLiam Girdwood fe->dai_link->name); 192407bf84aaSLiam Girdwood 192507bf84aaSLiam Girdwood ret = soc_pcm_bespoke_trigger(substream, SNDRV_PCM_TRIGGER_START); 192607bf84aaSLiam Girdwood if (ret < 0) { 1927103d84a3SLiam Girdwood dev_err(fe->dev,"ASoC: bespoke trigger FE failed %d\n", ret); 192807bf84aaSLiam Girdwood goto hw_free; 192907bf84aaSLiam Girdwood } 193007bf84aaSLiam Girdwood } else { 1931103d84a3SLiam Girdwood dev_dbg(fe->dev, "ASoC: trigger FE %s cmd start\n", 1932618dae11SLiam Girdwood fe->dai_link->name); 1933618dae11SLiam Girdwood 1934618dae11SLiam Girdwood ret = dpcm_be_dai_trigger(fe, stream, 1935618dae11SLiam Girdwood SNDRV_PCM_TRIGGER_START); 1936618dae11SLiam Girdwood if (ret < 0) { 1937103d84a3SLiam Girdwood dev_err(fe->dev,"ASoC: trigger FE failed %d\n", ret); 1938618dae11SLiam Girdwood goto hw_free; 1939618dae11SLiam Girdwood } 194007bf84aaSLiam Girdwood } 1941618dae11SLiam Girdwood 1942618dae11SLiam Girdwood return 0; 1943618dae11SLiam Girdwood 1944618dae11SLiam Girdwood hw_free: 1945618dae11SLiam Girdwood dpcm_be_dai_hw_free(fe, stream); 1946618dae11SLiam Girdwood close: 1947618dae11SLiam Girdwood dpcm_be_dai_shutdown(fe, stream); 1948618dae11SLiam Girdwood disconnect: 1949618dae11SLiam Girdwood /* disconnect any non started BEs */ 1950618dae11SLiam Girdwood list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) { 1951618dae11SLiam Girdwood struct snd_soc_pcm_runtime *be = dpcm->be; 1952618dae11SLiam Girdwood if (be->dpcm[stream].state != SND_SOC_DPCM_STATE_START) 1953618dae11SLiam Girdwood dpcm->state = SND_SOC_DPCM_LINK_STATE_FREE; 1954618dae11SLiam Girdwood } 1955618dae11SLiam Girdwood 1956618dae11SLiam Girdwood return ret; 1957618dae11SLiam Girdwood } 1958618dae11SLiam Girdwood 1959618dae11SLiam Girdwood static int dpcm_run_new_update(struct snd_soc_pcm_runtime *fe, int stream) 1960618dae11SLiam Girdwood { 1961618dae11SLiam Girdwood int ret; 1962618dae11SLiam Girdwood 1963618dae11SLiam Girdwood fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_BE; 1964618dae11SLiam Girdwood ret = dpcm_run_update_startup(fe, stream); 1965618dae11SLiam Girdwood if (ret < 0) 1966103d84a3SLiam Girdwood dev_err(fe->dev, "ASoC: failed to startup some BEs\n"); 1967618dae11SLiam Girdwood fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO; 1968618dae11SLiam Girdwood 1969618dae11SLiam Girdwood return ret; 1970618dae11SLiam Girdwood } 1971618dae11SLiam Girdwood 1972618dae11SLiam Girdwood static int dpcm_run_old_update(struct snd_soc_pcm_runtime *fe, int stream) 1973618dae11SLiam Girdwood { 1974618dae11SLiam Girdwood int ret; 1975618dae11SLiam Girdwood 1976618dae11SLiam Girdwood fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_BE; 1977618dae11SLiam Girdwood ret = dpcm_run_update_shutdown(fe, stream); 1978618dae11SLiam Girdwood if (ret < 0) 1979103d84a3SLiam Girdwood dev_err(fe->dev, "ASoC: failed to shutdown some BEs\n"); 1980618dae11SLiam Girdwood fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO; 1981618dae11SLiam Girdwood 1982618dae11SLiam Girdwood return ret; 1983618dae11SLiam Girdwood } 1984618dae11SLiam Girdwood 1985618dae11SLiam Girdwood /* Called by DAPM mixer/mux changes to update audio routing between PCMs and 1986618dae11SLiam Girdwood * any DAI links. 1987618dae11SLiam Girdwood */ 1988c3f48ae6SLars-Peter Clausen int soc_dpcm_runtime_update(struct snd_soc_card *card) 1989618dae11SLiam Girdwood { 1990618dae11SLiam Girdwood int i, old, new, paths; 1991618dae11SLiam Girdwood 1992618dae11SLiam Girdwood mutex_lock_nested(&card->mutex, SND_SOC_CARD_CLASS_RUNTIME); 1993618dae11SLiam Girdwood for (i = 0; i < card->num_rtd; i++) { 1994618dae11SLiam Girdwood struct snd_soc_dapm_widget_list *list; 1995618dae11SLiam Girdwood struct snd_soc_pcm_runtime *fe = &card->rtd[i]; 1996618dae11SLiam Girdwood 1997618dae11SLiam Girdwood /* make sure link is FE */ 1998618dae11SLiam Girdwood if (!fe->dai_link->dynamic) 1999618dae11SLiam Girdwood continue; 2000618dae11SLiam Girdwood 2001618dae11SLiam Girdwood /* only check active links */ 2002618dae11SLiam Girdwood if (!fe->cpu_dai->active) 2003618dae11SLiam Girdwood continue; 2004618dae11SLiam Girdwood 2005618dae11SLiam Girdwood /* DAPM sync will call this to update DSP paths */ 2006103d84a3SLiam Girdwood dev_dbg(fe->dev, "ASoC: DPCM runtime update for FE %s\n", 2007618dae11SLiam Girdwood fe->dai_link->name); 2008618dae11SLiam Girdwood 2009618dae11SLiam Girdwood /* skip if FE doesn't have playback capability */ 2010618dae11SLiam Girdwood if (!fe->cpu_dai->driver->playback.channels_min) 2011618dae11SLiam Girdwood goto capture; 2012618dae11SLiam Girdwood 2013618dae11SLiam Girdwood paths = dpcm_path_get(fe, SNDRV_PCM_STREAM_PLAYBACK, &list); 2014618dae11SLiam Girdwood if (paths < 0) { 2015103d84a3SLiam Girdwood dev_warn(fe->dev, "ASoC: %s no valid %s path\n", 2016618dae11SLiam Girdwood fe->dai_link->name, "playback"); 2017618dae11SLiam Girdwood mutex_unlock(&card->mutex); 2018618dae11SLiam Girdwood return paths; 2019618dae11SLiam Girdwood } 2020618dae11SLiam Girdwood 2021618dae11SLiam Girdwood /* update any new playback paths */ 2022618dae11SLiam Girdwood new = dpcm_process_paths(fe, SNDRV_PCM_STREAM_PLAYBACK, &list, 1); 2023618dae11SLiam Girdwood if (new) { 2024618dae11SLiam Girdwood dpcm_run_new_update(fe, SNDRV_PCM_STREAM_PLAYBACK); 2025618dae11SLiam Girdwood dpcm_clear_pending_state(fe, SNDRV_PCM_STREAM_PLAYBACK); 2026618dae11SLiam Girdwood dpcm_be_disconnect(fe, SNDRV_PCM_STREAM_PLAYBACK); 2027618dae11SLiam Girdwood } 2028618dae11SLiam Girdwood 2029618dae11SLiam Girdwood /* update any old playback paths */ 2030618dae11SLiam Girdwood old = dpcm_process_paths(fe, SNDRV_PCM_STREAM_PLAYBACK, &list, 0); 2031618dae11SLiam Girdwood if (old) { 2032618dae11SLiam Girdwood dpcm_run_old_update(fe, SNDRV_PCM_STREAM_PLAYBACK); 2033618dae11SLiam Girdwood dpcm_clear_pending_state(fe, SNDRV_PCM_STREAM_PLAYBACK); 2034618dae11SLiam Girdwood dpcm_be_disconnect(fe, SNDRV_PCM_STREAM_PLAYBACK); 2035618dae11SLiam Girdwood } 2036618dae11SLiam Girdwood 2037618dae11SLiam Girdwood capture: 2038618dae11SLiam Girdwood /* skip if FE doesn't have capture capability */ 2039618dae11SLiam Girdwood if (!fe->cpu_dai->driver->capture.channels_min) 2040618dae11SLiam Girdwood continue; 2041618dae11SLiam Girdwood 2042618dae11SLiam Girdwood paths = dpcm_path_get(fe, SNDRV_PCM_STREAM_CAPTURE, &list); 2043618dae11SLiam Girdwood if (paths < 0) { 2044103d84a3SLiam Girdwood dev_warn(fe->dev, "ASoC: %s no valid %s path\n", 2045618dae11SLiam Girdwood fe->dai_link->name, "capture"); 2046618dae11SLiam Girdwood mutex_unlock(&card->mutex); 2047618dae11SLiam Girdwood return paths; 2048618dae11SLiam Girdwood } 2049618dae11SLiam Girdwood 2050618dae11SLiam Girdwood /* update any new capture paths */ 2051618dae11SLiam Girdwood new = dpcm_process_paths(fe, SNDRV_PCM_STREAM_CAPTURE, &list, 1); 2052618dae11SLiam Girdwood if (new) { 2053618dae11SLiam Girdwood dpcm_run_new_update(fe, SNDRV_PCM_STREAM_CAPTURE); 2054618dae11SLiam Girdwood dpcm_clear_pending_state(fe, SNDRV_PCM_STREAM_CAPTURE); 2055618dae11SLiam Girdwood dpcm_be_disconnect(fe, SNDRV_PCM_STREAM_CAPTURE); 2056618dae11SLiam Girdwood } 2057618dae11SLiam Girdwood 2058618dae11SLiam Girdwood /* update any old capture paths */ 2059618dae11SLiam Girdwood old = dpcm_process_paths(fe, SNDRV_PCM_STREAM_CAPTURE, &list, 0); 2060618dae11SLiam Girdwood if (old) { 2061618dae11SLiam Girdwood dpcm_run_old_update(fe, SNDRV_PCM_STREAM_CAPTURE); 2062618dae11SLiam Girdwood dpcm_clear_pending_state(fe, SNDRV_PCM_STREAM_CAPTURE); 2063618dae11SLiam Girdwood dpcm_be_disconnect(fe, SNDRV_PCM_STREAM_CAPTURE); 2064618dae11SLiam Girdwood } 2065618dae11SLiam Girdwood 2066618dae11SLiam Girdwood dpcm_path_put(&list); 2067618dae11SLiam Girdwood } 2068618dae11SLiam Girdwood 2069618dae11SLiam Girdwood mutex_unlock(&card->mutex); 2070618dae11SLiam Girdwood return 0; 2071618dae11SLiam Girdwood } 207201d7584cSLiam Girdwood int soc_dpcm_be_digital_mute(struct snd_soc_pcm_runtime *fe, int mute) 207301d7584cSLiam Girdwood { 207401d7584cSLiam Girdwood struct snd_soc_dpcm *dpcm; 207501d7584cSLiam Girdwood struct list_head *clients = 207601d7584cSLiam Girdwood &fe->dpcm[SNDRV_PCM_STREAM_PLAYBACK].be_clients; 207701d7584cSLiam Girdwood 207801d7584cSLiam Girdwood list_for_each_entry(dpcm, clients, list_be) { 207901d7584cSLiam Girdwood 208001d7584cSLiam Girdwood struct snd_soc_pcm_runtime *be = dpcm->be; 208101d7584cSLiam Girdwood struct snd_soc_dai *dai = be->codec_dai; 208201d7584cSLiam Girdwood struct snd_soc_dai_driver *drv = dai->driver; 208301d7584cSLiam Girdwood 208401d7584cSLiam Girdwood if (be->dai_link->ignore_suspend) 208501d7584cSLiam Girdwood continue; 208601d7584cSLiam Girdwood 2087103d84a3SLiam Girdwood dev_dbg(be->dev, "ASoC: BE digital mute %s\n", be->dai_link->name); 208801d7584cSLiam Girdwood 2089c5914b0aSMark Brown if (drv->ops && drv->ops->digital_mute && dai->playback_active) 209001d7584cSLiam Girdwood drv->ops->digital_mute(dai, mute); 209101d7584cSLiam Girdwood } 209201d7584cSLiam Girdwood 209301d7584cSLiam Girdwood return 0; 209401d7584cSLiam Girdwood } 209501d7584cSLiam Girdwood 209645c0a188SMark Brown static int dpcm_fe_dai_open(struct snd_pcm_substream *fe_substream) 209701d7584cSLiam Girdwood { 209801d7584cSLiam Girdwood struct snd_soc_pcm_runtime *fe = fe_substream->private_data; 209901d7584cSLiam Girdwood struct snd_soc_dpcm *dpcm; 210001d7584cSLiam Girdwood struct snd_soc_dapm_widget_list *list; 210101d7584cSLiam Girdwood int ret; 210201d7584cSLiam Girdwood int stream = fe_substream->stream; 210301d7584cSLiam Girdwood 210401d7584cSLiam Girdwood mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME); 210501d7584cSLiam Girdwood fe->dpcm[stream].runtime = fe_substream->runtime; 210601d7584cSLiam Girdwood 210701d7584cSLiam Girdwood if (dpcm_path_get(fe, stream, &list) <= 0) { 2108103d84a3SLiam Girdwood dev_dbg(fe->dev, "ASoC: %s no valid %s route\n", 210901d7584cSLiam Girdwood fe->dai_link->name, stream ? "capture" : "playback"); 211001d7584cSLiam Girdwood } 211101d7584cSLiam Girdwood 211201d7584cSLiam Girdwood /* calculate valid and active FE <-> BE dpcms */ 211301d7584cSLiam Girdwood dpcm_process_paths(fe, stream, &list, 1); 211401d7584cSLiam Girdwood 211501d7584cSLiam Girdwood ret = dpcm_fe_dai_startup(fe_substream); 211601d7584cSLiam Girdwood if (ret < 0) { 211701d7584cSLiam Girdwood /* clean up all links */ 211801d7584cSLiam Girdwood list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) 211901d7584cSLiam Girdwood dpcm->state = SND_SOC_DPCM_LINK_STATE_FREE; 212001d7584cSLiam Girdwood 212101d7584cSLiam Girdwood dpcm_be_disconnect(fe, stream); 212201d7584cSLiam Girdwood fe->dpcm[stream].runtime = NULL; 212301d7584cSLiam Girdwood } 212401d7584cSLiam Girdwood 212501d7584cSLiam Girdwood dpcm_clear_pending_state(fe, stream); 212601d7584cSLiam Girdwood dpcm_path_put(&list); 212701d7584cSLiam Girdwood mutex_unlock(&fe->card->mutex); 212801d7584cSLiam Girdwood return ret; 212901d7584cSLiam Girdwood } 213001d7584cSLiam Girdwood 213145c0a188SMark Brown static int dpcm_fe_dai_close(struct snd_pcm_substream *fe_substream) 213201d7584cSLiam Girdwood { 213301d7584cSLiam Girdwood struct snd_soc_pcm_runtime *fe = fe_substream->private_data; 213401d7584cSLiam Girdwood struct snd_soc_dpcm *dpcm; 213501d7584cSLiam Girdwood int stream = fe_substream->stream, ret; 213601d7584cSLiam Girdwood 213701d7584cSLiam Girdwood mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME); 213801d7584cSLiam Girdwood ret = dpcm_fe_dai_shutdown(fe_substream); 213901d7584cSLiam Girdwood 214001d7584cSLiam Girdwood /* mark FE's links ready to prune */ 214101d7584cSLiam Girdwood list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) 214201d7584cSLiam Girdwood dpcm->state = SND_SOC_DPCM_LINK_STATE_FREE; 214301d7584cSLiam Girdwood 214401d7584cSLiam Girdwood dpcm_be_disconnect(fe, stream); 214501d7584cSLiam Girdwood 214601d7584cSLiam Girdwood fe->dpcm[stream].runtime = NULL; 214701d7584cSLiam Girdwood mutex_unlock(&fe->card->mutex); 214801d7584cSLiam Girdwood return ret; 214901d7584cSLiam Girdwood } 215001d7584cSLiam Girdwood 2151ddee627cSLiam Girdwood /* create a new pcm */ 2152ddee627cSLiam Girdwood int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num) 2153ddee627cSLiam Girdwood { 2154ddee627cSLiam Girdwood struct snd_soc_platform *platform = rtd->platform; 2155ddee627cSLiam Girdwood struct snd_soc_dai *codec_dai = rtd->codec_dai; 2156ddee627cSLiam Girdwood struct snd_soc_dai *cpu_dai = rtd->cpu_dai; 2157ddee627cSLiam Girdwood struct snd_pcm *pcm; 2158ddee627cSLiam Girdwood char new_name[64]; 2159ddee627cSLiam Girdwood int ret = 0, playback = 0, capture = 0; 2160ddee627cSLiam Girdwood 216101d7584cSLiam Girdwood if (rtd->dai_link->dynamic || rtd->dai_link->no_pcm) { 21621e9de42fSLiam Girdwood playback = rtd->dai_link->dpcm_playback; 21631e9de42fSLiam Girdwood capture = rtd->dai_link->dpcm_capture; 216401d7584cSLiam Girdwood } else { 216505679092SMark Brown if (codec_dai->driver->playback.channels_min && 216605679092SMark Brown cpu_dai->driver->playback.channels_min) 2167ddee627cSLiam Girdwood playback = 1; 216805679092SMark Brown if (codec_dai->driver->capture.channels_min && 216905679092SMark Brown cpu_dai->driver->capture.channels_min) 2170ddee627cSLiam Girdwood capture = 1; 217101d7584cSLiam Girdwood } 2172ddee627cSLiam Girdwood 2173d6bead02SFabio Estevam if (rtd->dai_link->playback_only) { 2174d6bead02SFabio Estevam playback = 1; 2175d6bead02SFabio Estevam capture = 0; 2176d6bead02SFabio Estevam } 2177d6bead02SFabio Estevam 2178d6bead02SFabio Estevam if (rtd->dai_link->capture_only) { 2179d6bead02SFabio Estevam playback = 0; 2180d6bead02SFabio Estevam capture = 1; 2181d6bead02SFabio Estevam } 2182d6bead02SFabio Estevam 218301d7584cSLiam Girdwood /* create the PCM */ 218401d7584cSLiam Girdwood if (rtd->dai_link->no_pcm) { 218501d7584cSLiam Girdwood snprintf(new_name, sizeof(new_name), "(%s)", 218601d7584cSLiam Girdwood rtd->dai_link->stream_name); 218701d7584cSLiam Girdwood 218801d7584cSLiam Girdwood ret = snd_pcm_new_internal(rtd->card->snd_card, new_name, num, 218901d7584cSLiam Girdwood playback, capture, &pcm); 219001d7584cSLiam Girdwood } else { 219101d7584cSLiam Girdwood if (rtd->dai_link->dynamic) 219201d7584cSLiam Girdwood snprintf(new_name, sizeof(new_name), "%s (*)", 219301d7584cSLiam Girdwood rtd->dai_link->stream_name); 219401d7584cSLiam Girdwood else 219501d7584cSLiam Girdwood snprintf(new_name, sizeof(new_name), "%s %s-%d", 219601d7584cSLiam Girdwood rtd->dai_link->stream_name, codec_dai->name, num); 219701d7584cSLiam Girdwood 219801d7584cSLiam Girdwood ret = snd_pcm_new(rtd->card->snd_card, new_name, num, playback, 219901d7584cSLiam Girdwood capture, &pcm); 220001d7584cSLiam Girdwood } 2201ddee627cSLiam Girdwood if (ret < 0) { 2202103d84a3SLiam Girdwood dev_err(rtd->card->dev, "ASoC: can't create pcm for %s\n", 22035cb9b748SLiam Girdwood rtd->dai_link->name); 2204ddee627cSLiam Girdwood return ret; 2205ddee627cSLiam Girdwood } 2206103d84a3SLiam Girdwood dev_dbg(rtd->card->dev, "ASoC: registered pcm #%d %s\n",num, new_name); 2207ddee627cSLiam Girdwood 2208ddee627cSLiam Girdwood /* DAPM dai link stream work */ 2209ddee627cSLiam Girdwood INIT_DELAYED_WORK(&rtd->delayed_work, close_delayed_work); 2210ddee627cSLiam Girdwood 2211ddee627cSLiam Girdwood rtd->pcm = pcm; 2212ddee627cSLiam Girdwood pcm->private_data = rtd; 221301d7584cSLiam Girdwood 221401d7584cSLiam Girdwood if (rtd->dai_link->no_pcm) { 221501d7584cSLiam Girdwood if (playback) 221601d7584cSLiam Girdwood pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream->private_data = rtd; 221701d7584cSLiam Girdwood if (capture) 221801d7584cSLiam Girdwood pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream->private_data = rtd; 221901d7584cSLiam Girdwood goto out; 222001d7584cSLiam Girdwood } 222101d7584cSLiam Girdwood 222201d7584cSLiam Girdwood /* ASoC PCM operations */ 222301d7584cSLiam Girdwood if (rtd->dai_link->dynamic) { 222401d7584cSLiam Girdwood rtd->ops.open = dpcm_fe_dai_open; 222501d7584cSLiam Girdwood rtd->ops.hw_params = dpcm_fe_dai_hw_params; 222601d7584cSLiam Girdwood rtd->ops.prepare = dpcm_fe_dai_prepare; 222701d7584cSLiam Girdwood rtd->ops.trigger = dpcm_fe_dai_trigger; 222801d7584cSLiam Girdwood rtd->ops.hw_free = dpcm_fe_dai_hw_free; 222901d7584cSLiam Girdwood rtd->ops.close = dpcm_fe_dai_close; 223001d7584cSLiam Girdwood rtd->ops.pointer = soc_pcm_pointer; 2231be3f3f2cSLiam Girdwood rtd->ops.ioctl = soc_pcm_ioctl; 223201d7584cSLiam Girdwood } else { 223301d7584cSLiam Girdwood rtd->ops.open = soc_pcm_open; 223401d7584cSLiam Girdwood rtd->ops.hw_params = soc_pcm_hw_params; 223501d7584cSLiam Girdwood rtd->ops.prepare = soc_pcm_prepare; 223601d7584cSLiam Girdwood rtd->ops.trigger = soc_pcm_trigger; 223701d7584cSLiam Girdwood rtd->ops.hw_free = soc_pcm_hw_free; 223801d7584cSLiam Girdwood rtd->ops.close = soc_pcm_close; 223901d7584cSLiam Girdwood rtd->ops.pointer = soc_pcm_pointer; 2240be3f3f2cSLiam Girdwood rtd->ops.ioctl = soc_pcm_ioctl; 224101d7584cSLiam Girdwood } 224201d7584cSLiam Girdwood 2243ddee627cSLiam Girdwood if (platform->driver->ops) { 224401d7584cSLiam Girdwood rtd->ops.ack = platform->driver->ops->ack; 224501d7584cSLiam Girdwood rtd->ops.copy = platform->driver->ops->copy; 224601d7584cSLiam Girdwood rtd->ops.silence = platform->driver->ops->silence; 224701d7584cSLiam Girdwood rtd->ops.page = platform->driver->ops->page; 224801d7584cSLiam Girdwood rtd->ops.mmap = platform->driver->ops->mmap; 2249ddee627cSLiam Girdwood } 2250ddee627cSLiam Girdwood 2251ddee627cSLiam Girdwood if (playback) 225201d7584cSLiam Girdwood snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &rtd->ops); 2253ddee627cSLiam Girdwood 2254ddee627cSLiam Girdwood if (capture) 225501d7584cSLiam Girdwood snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &rtd->ops); 2256ddee627cSLiam Girdwood 2257ddee627cSLiam Girdwood if (platform->driver->pcm_new) { 2258ddee627cSLiam Girdwood ret = platform->driver->pcm_new(rtd); 2259ddee627cSLiam Girdwood if (ret < 0) { 2260b1bc7b3cSMark Brown dev_err(platform->dev, 2261b1bc7b3cSMark Brown "ASoC: pcm constructor failed: %d\n", 2262b1bc7b3cSMark Brown ret); 2263ddee627cSLiam Girdwood return ret; 2264ddee627cSLiam Girdwood } 2265ddee627cSLiam Girdwood } 2266ddee627cSLiam Girdwood 2267ddee627cSLiam Girdwood pcm->private_free = platform->driver->pcm_free; 226801d7584cSLiam Girdwood out: 22695cb9b748SLiam Girdwood dev_info(rtd->card->dev, "%s <-> %s mapping ok\n", codec_dai->name, 2270ddee627cSLiam Girdwood cpu_dai->name); 2271ddee627cSLiam Girdwood return ret; 2272ddee627cSLiam Girdwood } 227301d7584cSLiam Girdwood 227401d7584cSLiam Girdwood /* is the current PCM operation for this FE ? */ 227501d7584cSLiam Girdwood int snd_soc_dpcm_fe_can_update(struct snd_soc_pcm_runtime *fe, int stream) 227601d7584cSLiam Girdwood { 227701d7584cSLiam Girdwood if (fe->dpcm[stream].runtime_update == SND_SOC_DPCM_UPDATE_FE) 227801d7584cSLiam Girdwood return 1; 227901d7584cSLiam Girdwood return 0; 228001d7584cSLiam Girdwood } 228101d7584cSLiam Girdwood EXPORT_SYMBOL_GPL(snd_soc_dpcm_fe_can_update); 228201d7584cSLiam Girdwood 228301d7584cSLiam Girdwood /* is the current PCM operation for this BE ? */ 228401d7584cSLiam Girdwood int snd_soc_dpcm_be_can_update(struct snd_soc_pcm_runtime *fe, 228501d7584cSLiam Girdwood struct snd_soc_pcm_runtime *be, int stream) 228601d7584cSLiam Girdwood { 228701d7584cSLiam Girdwood if ((fe->dpcm[stream].runtime_update == SND_SOC_DPCM_UPDATE_FE) || 228801d7584cSLiam Girdwood ((fe->dpcm[stream].runtime_update == SND_SOC_DPCM_UPDATE_BE) && 228901d7584cSLiam Girdwood be->dpcm[stream].runtime_update)) 229001d7584cSLiam Girdwood return 1; 229101d7584cSLiam Girdwood return 0; 229201d7584cSLiam Girdwood } 229301d7584cSLiam Girdwood EXPORT_SYMBOL_GPL(snd_soc_dpcm_be_can_update); 229401d7584cSLiam Girdwood 229501d7584cSLiam Girdwood /* get the substream for this BE */ 229601d7584cSLiam Girdwood struct snd_pcm_substream * 229701d7584cSLiam Girdwood snd_soc_dpcm_get_substream(struct snd_soc_pcm_runtime *be, int stream) 229801d7584cSLiam Girdwood { 229901d7584cSLiam Girdwood return be->pcm->streams[stream].substream; 230001d7584cSLiam Girdwood } 230101d7584cSLiam Girdwood EXPORT_SYMBOL_GPL(snd_soc_dpcm_get_substream); 230201d7584cSLiam Girdwood 230301d7584cSLiam Girdwood /* get the BE runtime state */ 230401d7584cSLiam Girdwood enum snd_soc_dpcm_state 230501d7584cSLiam Girdwood snd_soc_dpcm_be_get_state(struct snd_soc_pcm_runtime *be, int stream) 230601d7584cSLiam Girdwood { 230701d7584cSLiam Girdwood return be->dpcm[stream].state; 230801d7584cSLiam Girdwood } 230901d7584cSLiam Girdwood EXPORT_SYMBOL_GPL(snd_soc_dpcm_be_get_state); 231001d7584cSLiam Girdwood 231101d7584cSLiam Girdwood /* set the BE runtime state */ 231201d7584cSLiam Girdwood void snd_soc_dpcm_be_set_state(struct snd_soc_pcm_runtime *be, 231301d7584cSLiam Girdwood int stream, enum snd_soc_dpcm_state state) 231401d7584cSLiam Girdwood { 231501d7584cSLiam Girdwood be->dpcm[stream].state = state; 231601d7584cSLiam Girdwood } 231701d7584cSLiam Girdwood EXPORT_SYMBOL_GPL(snd_soc_dpcm_be_set_state); 231801d7584cSLiam Girdwood 231901d7584cSLiam Girdwood /* 232001d7584cSLiam Girdwood * We can only hw_free, stop, pause or suspend a BE DAI if any of it's FE 232101d7584cSLiam Girdwood * are not running, paused or suspended for the specified stream direction. 232201d7584cSLiam Girdwood */ 232301d7584cSLiam Girdwood int snd_soc_dpcm_can_be_free_stop(struct snd_soc_pcm_runtime *fe, 232401d7584cSLiam Girdwood struct snd_soc_pcm_runtime *be, int stream) 232501d7584cSLiam Girdwood { 232601d7584cSLiam Girdwood struct snd_soc_dpcm *dpcm; 232701d7584cSLiam Girdwood int state; 232801d7584cSLiam Girdwood 232901d7584cSLiam Girdwood list_for_each_entry(dpcm, &be->dpcm[stream].fe_clients, list_fe) { 233001d7584cSLiam Girdwood 233101d7584cSLiam Girdwood if (dpcm->fe == fe) 233201d7584cSLiam Girdwood continue; 233301d7584cSLiam Girdwood 233401d7584cSLiam Girdwood state = dpcm->fe->dpcm[stream].state; 233501d7584cSLiam Girdwood if (state == SND_SOC_DPCM_STATE_START || 233601d7584cSLiam Girdwood state == SND_SOC_DPCM_STATE_PAUSED || 233701d7584cSLiam Girdwood state == SND_SOC_DPCM_STATE_SUSPEND) 233801d7584cSLiam Girdwood return 0; 233901d7584cSLiam Girdwood } 234001d7584cSLiam Girdwood 234101d7584cSLiam Girdwood /* it's safe to free/stop this BE DAI */ 234201d7584cSLiam Girdwood return 1; 234301d7584cSLiam Girdwood } 234401d7584cSLiam Girdwood EXPORT_SYMBOL_GPL(snd_soc_dpcm_can_be_free_stop); 234501d7584cSLiam Girdwood 234601d7584cSLiam Girdwood /* 234701d7584cSLiam Girdwood * We can only change hw params a BE DAI if any of it's FE are not prepared, 234801d7584cSLiam Girdwood * running, paused or suspended for the specified stream direction. 234901d7584cSLiam Girdwood */ 235001d7584cSLiam Girdwood int snd_soc_dpcm_can_be_params(struct snd_soc_pcm_runtime *fe, 235101d7584cSLiam Girdwood struct snd_soc_pcm_runtime *be, int stream) 235201d7584cSLiam Girdwood { 235301d7584cSLiam Girdwood struct snd_soc_dpcm *dpcm; 235401d7584cSLiam Girdwood int state; 235501d7584cSLiam Girdwood 235601d7584cSLiam Girdwood list_for_each_entry(dpcm, &be->dpcm[stream].fe_clients, list_fe) { 235701d7584cSLiam Girdwood 235801d7584cSLiam Girdwood if (dpcm->fe == fe) 235901d7584cSLiam Girdwood continue; 236001d7584cSLiam Girdwood 236101d7584cSLiam Girdwood state = dpcm->fe->dpcm[stream].state; 236201d7584cSLiam Girdwood if (state == SND_SOC_DPCM_STATE_START || 236301d7584cSLiam Girdwood state == SND_SOC_DPCM_STATE_PAUSED || 236401d7584cSLiam Girdwood state == SND_SOC_DPCM_STATE_SUSPEND || 236501d7584cSLiam Girdwood state == SND_SOC_DPCM_STATE_PREPARE) 236601d7584cSLiam Girdwood return 0; 236701d7584cSLiam Girdwood } 236801d7584cSLiam Girdwood 236901d7584cSLiam Girdwood /* it's safe to change hw_params */ 237001d7584cSLiam Girdwood return 1; 237101d7584cSLiam Girdwood } 237201d7584cSLiam Girdwood EXPORT_SYMBOL_GPL(snd_soc_dpcm_can_be_params); 2373f86dcef8SLiam Girdwood 237407bf84aaSLiam Girdwood int snd_soc_platform_trigger(struct snd_pcm_substream *substream, 237507bf84aaSLiam Girdwood int cmd, struct snd_soc_platform *platform) 237607bf84aaSLiam Girdwood { 2377c5914b0aSMark Brown if (platform->driver->ops && platform->driver->ops->trigger) 237807bf84aaSLiam Girdwood return platform->driver->ops->trigger(substream, cmd); 237907bf84aaSLiam Girdwood return 0; 238007bf84aaSLiam Girdwood } 238107bf84aaSLiam Girdwood EXPORT_SYMBOL_GPL(snd_soc_platform_trigger); 238207bf84aaSLiam Girdwood 2383f86dcef8SLiam Girdwood #ifdef CONFIG_DEBUG_FS 2384f86dcef8SLiam Girdwood static char *dpcm_state_string(enum snd_soc_dpcm_state state) 2385f86dcef8SLiam Girdwood { 2386f86dcef8SLiam Girdwood switch (state) { 2387f86dcef8SLiam Girdwood case SND_SOC_DPCM_STATE_NEW: 2388f86dcef8SLiam Girdwood return "new"; 2389f86dcef8SLiam Girdwood case SND_SOC_DPCM_STATE_OPEN: 2390f86dcef8SLiam Girdwood return "open"; 2391f86dcef8SLiam Girdwood case SND_SOC_DPCM_STATE_HW_PARAMS: 2392f86dcef8SLiam Girdwood return "hw_params"; 2393f86dcef8SLiam Girdwood case SND_SOC_DPCM_STATE_PREPARE: 2394f86dcef8SLiam Girdwood return "prepare"; 2395f86dcef8SLiam Girdwood case SND_SOC_DPCM_STATE_START: 2396f86dcef8SLiam Girdwood return "start"; 2397f86dcef8SLiam Girdwood case SND_SOC_DPCM_STATE_STOP: 2398f86dcef8SLiam Girdwood return "stop"; 2399f86dcef8SLiam Girdwood case SND_SOC_DPCM_STATE_SUSPEND: 2400f86dcef8SLiam Girdwood return "suspend"; 2401f86dcef8SLiam Girdwood case SND_SOC_DPCM_STATE_PAUSED: 2402f86dcef8SLiam Girdwood return "paused"; 2403f86dcef8SLiam Girdwood case SND_SOC_DPCM_STATE_HW_FREE: 2404f86dcef8SLiam Girdwood return "hw_free"; 2405f86dcef8SLiam Girdwood case SND_SOC_DPCM_STATE_CLOSE: 2406f86dcef8SLiam Girdwood return "close"; 2407f86dcef8SLiam Girdwood } 2408f86dcef8SLiam Girdwood 2409f86dcef8SLiam Girdwood return "unknown"; 2410f86dcef8SLiam Girdwood } 2411f86dcef8SLiam Girdwood 2412f86dcef8SLiam Girdwood static ssize_t dpcm_show_state(struct snd_soc_pcm_runtime *fe, 2413f86dcef8SLiam Girdwood int stream, char *buf, size_t size) 2414f86dcef8SLiam Girdwood { 2415f86dcef8SLiam Girdwood struct snd_pcm_hw_params *params = &fe->dpcm[stream].hw_params; 2416f86dcef8SLiam Girdwood struct snd_soc_dpcm *dpcm; 2417f86dcef8SLiam Girdwood ssize_t offset = 0; 2418f86dcef8SLiam Girdwood 2419f86dcef8SLiam Girdwood /* FE state */ 2420f86dcef8SLiam Girdwood offset += snprintf(buf + offset, size - offset, 2421f86dcef8SLiam Girdwood "[%s - %s]\n", fe->dai_link->name, 2422f86dcef8SLiam Girdwood stream ? "Capture" : "Playback"); 2423f86dcef8SLiam Girdwood 2424f86dcef8SLiam Girdwood offset += snprintf(buf + offset, size - offset, "State: %s\n", 2425f86dcef8SLiam Girdwood dpcm_state_string(fe->dpcm[stream].state)); 2426f86dcef8SLiam Girdwood 2427f86dcef8SLiam Girdwood if ((fe->dpcm[stream].state >= SND_SOC_DPCM_STATE_HW_PARAMS) && 2428f86dcef8SLiam Girdwood (fe->dpcm[stream].state <= SND_SOC_DPCM_STATE_STOP)) 2429f86dcef8SLiam Girdwood offset += snprintf(buf + offset, size - offset, 2430f86dcef8SLiam Girdwood "Hardware Params: " 2431f86dcef8SLiam Girdwood "Format = %s, Channels = %d, Rate = %d\n", 2432f86dcef8SLiam Girdwood snd_pcm_format_name(params_format(params)), 2433f86dcef8SLiam Girdwood params_channels(params), 2434f86dcef8SLiam Girdwood params_rate(params)); 2435f86dcef8SLiam Girdwood 2436f86dcef8SLiam Girdwood /* BEs state */ 2437f86dcef8SLiam Girdwood offset += snprintf(buf + offset, size - offset, "Backends:\n"); 2438f86dcef8SLiam Girdwood 2439f86dcef8SLiam Girdwood if (list_empty(&fe->dpcm[stream].be_clients)) { 2440f86dcef8SLiam Girdwood offset += snprintf(buf + offset, size - offset, 2441f86dcef8SLiam Girdwood " No active DSP links\n"); 2442f86dcef8SLiam Girdwood goto out; 2443f86dcef8SLiam Girdwood } 2444f86dcef8SLiam Girdwood 2445f86dcef8SLiam Girdwood list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) { 2446f86dcef8SLiam Girdwood struct snd_soc_pcm_runtime *be = dpcm->be; 2447f86dcef8SLiam Girdwood params = &dpcm->hw_params; 2448f86dcef8SLiam Girdwood 2449f86dcef8SLiam Girdwood offset += snprintf(buf + offset, size - offset, 2450f86dcef8SLiam Girdwood "- %s\n", be->dai_link->name); 2451f86dcef8SLiam Girdwood 2452f86dcef8SLiam Girdwood offset += snprintf(buf + offset, size - offset, 2453f86dcef8SLiam Girdwood " State: %s\n", 2454f86dcef8SLiam Girdwood dpcm_state_string(be->dpcm[stream].state)); 2455f86dcef8SLiam Girdwood 2456f86dcef8SLiam Girdwood if ((be->dpcm[stream].state >= SND_SOC_DPCM_STATE_HW_PARAMS) && 2457f86dcef8SLiam Girdwood (be->dpcm[stream].state <= SND_SOC_DPCM_STATE_STOP)) 2458f86dcef8SLiam Girdwood offset += snprintf(buf + offset, size - offset, 2459f86dcef8SLiam Girdwood " Hardware Params: " 2460f86dcef8SLiam Girdwood "Format = %s, Channels = %d, Rate = %d\n", 2461f86dcef8SLiam Girdwood snd_pcm_format_name(params_format(params)), 2462f86dcef8SLiam Girdwood params_channels(params), 2463f86dcef8SLiam Girdwood params_rate(params)); 2464f86dcef8SLiam Girdwood } 2465f86dcef8SLiam Girdwood 2466f86dcef8SLiam Girdwood out: 2467f86dcef8SLiam Girdwood return offset; 2468f86dcef8SLiam Girdwood } 2469f86dcef8SLiam Girdwood 2470f86dcef8SLiam Girdwood static ssize_t dpcm_state_read_file(struct file *file, char __user *user_buf, 2471f86dcef8SLiam Girdwood size_t count, loff_t *ppos) 2472f86dcef8SLiam Girdwood { 2473f86dcef8SLiam Girdwood struct snd_soc_pcm_runtime *fe = file->private_data; 2474f86dcef8SLiam Girdwood ssize_t out_count = PAGE_SIZE, offset = 0, ret = 0; 2475f86dcef8SLiam Girdwood char *buf; 2476f86dcef8SLiam Girdwood 2477f86dcef8SLiam Girdwood buf = kmalloc(out_count, GFP_KERNEL); 2478f86dcef8SLiam Girdwood if (!buf) 2479f86dcef8SLiam Girdwood return -ENOMEM; 2480f86dcef8SLiam Girdwood 2481f86dcef8SLiam Girdwood if (fe->cpu_dai->driver->playback.channels_min) 2482f86dcef8SLiam Girdwood offset += dpcm_show_state(fe, SNDRV_PCM_STREAM_PLAYBACK, 2483f86dcef8SLiam Girdwood buf + offset, out_count - offset); 2484f86dcef8SLiam Girdwood 2485f86dcef8SLiam Girdwood if (fe->cpu_dai->driver->capture.channels_min) 2486f86dcef8SLiam Girdwood offset += dpcm_show_state(fe, SNDRV_PCM_STREAM_CAPTURE, 2487f86dcef8SLiam Girdwood buf + offset, out_count - offset); 2488f86dcef8SLiam Girdwood 2489f86dcef8SLiam Girdwood ret = simple_read_from_buffer(user_buf, count, ppos, buf, offset); 2490f86dcef8SLiam Girdwood 2491f86dcef8SLiam Girdwood kfree(buf); 2492f86dcef8SLiam Girdwood return ret; 2493f86dcef8SLiam Girdwood } 2494f86dcef8SLiam Girdwood 2495f86dcef8SLiam Girdwood static const struct file_operations dpcm_state_fops = { 2496f57b8488SLiam Girdwood .open = simple_open, 2497f86dcef8SLiam Girdwood .read = dpcm_state_read_file, 2498f86dcef8SLiam Girdwood .llseek = default_llseek, 2499f86dcef8SLiam Girdwood }; 2500f86dcef8SLiam Girdwood 2501f86dcef8SLiam Girdwood int soc_dpcm_debugfs_add(struct snd_soc_pcm_runtime *rtd) 2502f86dcef8SLiam Girdwood { 2503b3bba9a1SMark Brown if (!rtd->dai_link) 2504b3bba9a1SMark Brown return 0; 2505b3bba9a1SMark Brown 2506f86dcef8SLiam Girdwood rtd->debugfs_dpcm_root = debugfs_create_dir(rtd->dai_link->name, 2507f86dcef8SLiam Girdwood rtd->card->debugfs_card_root); 2508f86dcef8SLiam Girdwood if (!rtd->debugfs_dpcm_root) { 2509f86dcef8SLiam Girdwood dev_dbg(rtd->dev, 2510f86dcef8SLiam Girdwood "ASoC: Failed to create dpcm debugfs directory %s\n", 2511f86dcef8SLiam Girdwood rtd->dai_link->name); 2512f86dcef8SLiam Girdwood return -EINVAL; 2513f86dcef8SLiam Girdwood } 2514f86dcef8SLiam Girdwood 2515f57b8488SLiam Girdwood rtd->debugfs_dpcm_state = debugfs_create_file("state", 0444, 2516f86dcef8SLiam Girdwood rtd->debugfs_dpcm_root, 2517f86dcef8SLiam Girdwood rtd, &dpcm_state_fops); 2518f86dcef8SLiam Girdwood 2519f86dcef8SLiam Girdwood return 0; 2520f86dcef8SLiam Girdwood } 2521f86dcef8SLiam Girdwood #endif 2522