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 /** 3824894b76SLars-Peter Clausen * snd_soc_runtime_activate() - Increment active count for PCM runtime components 3924894b76SLars-Peter Clausen * @rtd: ASoC PCM runtime that is activated 4024894b76SLars-Peter Clausen * @stream: Direction of the PCM stream 4124894b76SLars-Peter Clausen * 4224894b76SLars-Peter Clausen * Increments the active count for all the DAIs and components attached to a PCM 4324894b76SLars-Peter Clausen * runtime. Should typically be called when a stream is opened. 4424894b76SLars-Peter Clausen * 4524894b76SLars-Peter Clausen * Must be called with the rtd->pcm_mutex being held 4624894b76SLars-Peter Clausen */ 4724894b76SLars-Peter Clausen void snd_soc_runtime_activate(struct snd_soc_pcm_runtime *rtd, int stream) 4824894b76SLars-Peter Clausen { 4924894b76SLars-Peter Clausen struct snd_soc_dai *cpu_dai = rtd->cpu_dai; 5024894b76SLars-Peter Clausen struct snd_soc_dai *codec_dai = rtd->codec_dai; 5124894b76SLars-Peter Clausen 5224894b76SLars-Peter Clausen lockdep_assert_held(&rtd->pcm_mutex); 5324894b76SLars-Peter Clausen 5424894b76SLars-Peter Clausen if (stream == SNDRV_PCM_STREAM_PLAYBACK) { 5524894b76SLars-Peter Clausen cpu_dai->playback_active++; 5624894b76SLars-Peter Clausen codec_dai->playback_active++; 5724894b76SLars-Peter Clausen } else { 5824894b76SLars-Peter Clausen cpu_dai->capture_active++; 5924894b76SLars-Peter Clausen codec_dai->capture_active++; 6024894b76SLars-Peter Clausen } 6124894b76SLars-Peter Clausen 6224894b76SLars-Peter Clausen cpu_dai->active++; 6324894b76SLars-Peter Clausen codec_dai->active++; 64cdde4ccbSLars-Peter Clausen cpu_dai->component->active++; 65cdde4ccbSLars-Peter Clausen codec_dai->component->active++; 6624894b76SLars-Peter Clausen } 6724894b76SLars-Peter Clausen 6824894b76SLars-Peter Clausen /** 6924894b76SLars-Peter Clausen * snd_soc_runtime_deactivate() - Decrement active count for PCM runtime components 7024894b76SLars-Peter Clausen * @rtd: ASoC PCM runtime that is deactivated 7124894b76SLars-Peter Clausen * @stream: Direction of the PCM stream 7224894b76SLars-Peter Clausen * 7324894b76SLars-Peter Clausen * Decrements the active count for all the DAIs and components attached to a PCM 7424894b76SLars-Peter Clausen * runtime. Should typically be called when a stream is closed. 7524894b76SLars-Peter Clausen * 7624894b76SLars-Peter Clausen * Must be called with the rtd->pcm_mutex being held 7724894b76SLars-Peter Clausen */ 7824894b76SLars-Peter Clausen void snd_soc_runtime_deactivate(struct snd_soc_pcm_runtime *rtd, int stream) 7924894b76SLars-Peter Clausen { 8024894b76SLars-Peter Clausen struct snd_soc_dai *cpu_dai = rtd->cpu_dai; 8124894b76SLars-Peter Clausen struct snd_soc_dai *codec_dai = rtd->codec_dai; 8224894b76SLars-Peter Clausen 8324894b76SLars-Peter Clausen lockdep_assert_held(&rtd->pcm_mutex); 8424894b76SLars-Peter Clausen 8524894b76SLars-Peter Clausen if (stream == SNDRV_PCM_STREAM_PLAYBACK) { 8624894b76SLars-Peter Clausen cpu_dai->playback_active--; 8724894b76SLars-Peter Clausen codec_dai->playback_active--; 8824894b76SLars-Peter Clausen } else { 8924894b76SLars-Peter Clausen cpu_dai->capture_active--; 9024894b76SLars-Peter Clausen codec_dai->capture_active--; 9124894b76SLars-Peter Clausen } 9224894b76SLars-Peter Clausen 9324894b76SLars-Peter Clausen cpu_dai->active--; 9424894b76SLars-Peter Clausen codec_dai->active--; 95cdde4ccbSLars-Peter Clausen cpu_dai->component->active--; 96cdde4ccbSLars-Peter Clausen codec_dai->component->active--; 9724894b76SLars-Peter Clausen } 9824894b76SLars-Peter Clausen 9924894b76SLars-Peter Clausen /** 100208a1589SLars-Peter Clausen * snd_soc_runtime_ignore_pmdown_time() - Check whether to ignore the power down delay 101208a1589SLars-Peter Clausen * @rtd: The ASoC PCM runtime that should be checked. 102208a1589SLars-Peter Clausen * 103208a1589SLars-Peter Clausen * This function checks whether the power down delay should be ignored for a 104208a1589SLars-Peter Clausen * specific PCM runtime. Returns true if the delay is 0, if it the DAI link has 105208a1589SLars-Peter Clausen * been configured to ignore the delay, or if none of the components benefits 106208a1589SLars-Peter Clausen * from having the delay. 107208a1589SLars-Peter Clausen */ 108208a1589SLars-Peter Clausen bool snd_soc_runtime_ignore_pmdown_time(struct snd_soc_pcm_runtime *rtd) 109208a1589SLars-Peter Clausen { 110208a1589SLars-Peter Clausen if (!rtd->pmdown_time || rtd->dai_link->ignore_pmdown_time) 111208a1589SLars-Peter Clausen return true; 112208a1589SLars-Peter Clausen 1133d59400fSLars-Peter Clausen return rtd->cpu_dai->component->ignore_pmdown_time && 1143d59400fSLars-Peter Clausen rtd->codec_dai->component->ignore_pmdown_time; 115208a1589SLars-Peter Clausen } 116208a1589SLars-Peter Clausen 117208a1589SLars-Peter Clausen /** 11890996f43SLars-Peter Clausen * snd_soc_set_runtime_hwparams - set the runtime hardware parameters 11990996f43SLars-Peter Clausen * @substream: the pcm substream 12090996f43SLars-Peter Clausen * @hw: the hardware parameters 12190996f43SLars-Peter Clausen * 12290996f43SLars-Peter Clausen * Sets the substream runtime hardware parameters. 12390996f43SLars-Peter Clausen */ 12490996f43SLars-Peter Clausen int snd_soc_set_runtime_hwparams(struct snd_pcm_substream *substream, 12590996f43SLars-Peter Clausen const struct snd_pcm_hardware *hw) 12690996f43SLars-Peter Clausen { 12790996f43SLars-Peter Clausen struct snd_pcm_runtime *runtime = substream->runtime; 12890996f43SLars-Peter Clausen runtime->hw.info = hw->info; 12990996f43SLars-Peter Clausen runtime->hw.formats = hw->formats; 13090996f43SLars-Peter Clausen runtime->hw.period_bytes_min = hw->period_bytes_min; 13190996f43SLars-Peter Clausen runtime->hw.period_bytes_max = hw->period_bytes_max; 13290996f43SLars-Peter Clausen runtime->hw.periods_min = hw->periods_min; 13390996f43SLars-Peter Clausen runtime->hw.periods_max = hw->periods_max; 13490996f43SLars-Peter Clausen runtime->hw.buffer_bytes_max = hw->buffer_bytes_max; 13590996f43SLars-Peter Clausen runtime->hw.fifo_size = hw->fifo_size; 13690996f43SLars-Peter Clausen return 0; 13790996f43SLars-Peter Clausen } 13890996f43SLars-Peter Clausen EXPORT_SYMBOL_GPL(snd_soc_set_runtime_hwparams); 13990996f43SLars-Peter Clausen 14001d7584cSLiam Girdwood /* DPCM stream event, send event to FE and all active BEs. */ 14123607025SLiam Girdwood int dpcm_dapm_stream_event(struct snd_soc_pcm_runtime *fe, int dir, 14201d7584cSLiam Girdwood int event) 14301d7584cSLiam Girdwood { 14401d7584cSLiam Girdwood struct snd_soc_dpcm *dpcm; 14501d7584cSLiam Girdwood 14601d7584cSLiam Girdwood list_for_each_entry(dpcm, &fe->dpcm[dir].be_clients, list_be) { 14701d7584cSLiam Girdwood 14801d7584cSLiam Girdwood struct snd_soc_pcm_runtime *be = dpcm->be; 14901d7584cSLiam Girdwood 150103d84a3SLiam Girdwood dev_dbg(be->dev, "ASoC: BE %s event %d dir %d\n", 15101d7584cSLiam Girdwood be->dai_link->name, event, dir); 15201d7584cSLiam Girdwood 15301d7584cSLiam Girdwood snd_soc_dapm_stream_event(be, dir, event); 15401d7584cSLiam Girdwood } 15501d7584cSLiam Girdwood 15601d7584cSLiam Girdwood snd_soc_dapm_stream_event(fe, dir, event); 15701d7584cSLiam Girdwood 15801d7584cSLiam Girdwood return 0; 15901d7584cSLiam Girdwood } 16001d7584cSLiam Girdwood 16117841020SDong Aisheng static int soc_pcm_apply_symmetry(struct snd_pcm_substream *substream, 16217841020SDong Aisheng struct snd_soc_dai *soc_dai) 163ddee627cSLiam Girdwood { 164ddee627cSLiam Girdwood struct snd_soc_pcm_runtime *rtd = substream->private_data; 165ddee627cSLiam Girdwood int ret; 166ddee627cSLiam Girdwood 1673635bf09SNicolin Chen if (soc_dai->rate && (soc_dai->driver->symmetric_rates || 1683635bf09SNicolin Chen rtd->dai_link->symmetric_rates)) { 1693635bf09SNicolin Chen dev_dbg(soc_dai->dev, "ASoC: Symmetry forces %dHz rate\n", 1703635bf09SNicolin Chen soc_dai->rate); 171ddee627cSLiam Girdwood 172ddee627cSLiam Girdwood ret = snd_pcm_hw_constraint_minmax(substream->runtime, 173ddee627cSLiam Girdwood SNDRV_PCM_HW_PARAM_RATE, 17417841020SDong Aisheng soc_dai->rate, soc_dai->rate); 175ddee627cSLiam Girdwood if (ret < 0) { 17617841020SDong Aisheng dev_err(soc_dai->dev, 1773635bf09SNicolin Chen "ASoC: Unable to apply rate constraint: %d\n", 178103d84a3SLiam Girdwood ret); 179ddee627cSLiam Girdwood return ret; 180ddee627cSLiam Girdwood } 1813635bf09SNicolin Chen } 1823635bf09SNicolin Chen 1833635bf09SNicolin Chen if (soc_dai->channels && (soc_dai->driver->symmetric_channels || 1843635bf09SNicolin Chen rtd->dai_link->symmetric_channels)) { 1853635bf09SNicolin Chen dev_dbg(soc_dai->dev, "ASoC: Symmetry forces %d channel(s)\n", 1863635bf09SNicolin Chen soc_dai->channels); 1873635bf09SNicolin Chen 1883635bf09SNicolin Chen ret = snd_pcm_hw_constraint_minmax(substream->runtime, 1893635bf09SNicolin Chen SNDRV_PCM_HW_PARAM_CHANNELS, 1903635bf09SNicolin Chen soc_dai->channels, 1913635bf09SNicolin Chen soc_dai->channels); 1923635bf09SNicolin Chen if (ret < 0) { 1933635bf09SNicolin Chen dev_err(soc_dai->dev, 1943635bf09SNicolin Chen "ASoC: Unable to apply channel symmetry constraint: %d\n", 1953635bf09SNicolin Chen ret); 1963635bf09SNicolin Chen return ret; 1973635bf09SNicolin Chen } 1983635bf09SNicolin Chen } 1993635bf09SNicolin Chen 2003635bf09SNicolin Chen if (soc_dai->sample_bits && (soc_dai->driver->symmetric_samplebits || 2013635bf09SNicolin Chen rtd->dai_link->symmetric_samplebits)) { 2023635bf09SNicolin Chen dev_dbg(soc_dai->dev, "ASoC: Symmetry forces %d sample bits\n", 2033635bf09SNicolin Chen soc_dai->sample_bits); 2043635bf09SNicolin Chen 2053635bf09SNicolin Chen ret = snd_pcm_hw_constraint_minmax(substream->runtime, 2063635bf09SNicolin Chen SNDRV_PCM_HW_PARAM_SAMPLE_BITS, 2073635bf09SNicolin Chen soc_dai->sample_bits, 2083635bf09SNicolin Chen soc_dai->sample_bits); 2093635bf09SNicolin Chen if (ret < 0) { 2103635bf09SNicolin Chen dev_err(soc_dai->dev, 2113635bf09SNicolin Chen "ASoC: Unable to apply sample bits symmetry constraint: %d\n", 2123635bf09SNicolin Chen ret); 2133635bf09SNicolin Chen return ret; 2143635bf09SNicolin Chen } 2153635bf09SNicolin Chen } 216ddee627cSLiam Girdwood 217ddee627cSLiam Girdwood return 0; 218ddee627cSLiam Girdwood } 219ddee627cSLiam Girdwood 2203635bf09SNicolin Chen static int soc_pcm_params_symmetry(struct snd_pcm_substream *substream, 2213635bf09SNicolin Chen struct snd_pcm_hw_params *params) 2223635bf09SNicolin Chen { 2233635bf09SNicolin Chen struct snd_soc_pcm_runtime *rtd = substream->private_data; 2243635bf09SNicolin Chen struct snd_soc_dai *cpu_dai = rtd->cpu_dai; 2253635bf09SNicolin Chen struct snd_soc_dai *codec_dai = rtd->codec_dai; 2263635bf09SNicolin Chen unsigned int rate, channels, sample_bits, symmetry; 2273635bf09SNicolin Chen 2283635bf09SNicolin Chen rate = params_rate(params); 2293635bf09SNicolin Chen channels = params_channels(params); 2303635bf09SNicolin Chen sample_bits = snd_pcm_format_physical_width(params_format(params)); 2313635bf09SNicolin Chen 2323635bf09SNicolin Chen /* reject unmatched parameters when applying symmetry */ 2333635bf09SNicolin Chen symmetry = cpu_dai->driver->symmetric_rates || 2343635bf09SNicolin Chen codec_dai->driver->symmetric_rates || 2353635bf09SNicolin Chen rtd->dai_link->symmetric_rates; 2363635bf09SNicolin Chen if (symmetry && cpu_dai->rate && cpu_dai->rate != rate) { 2373635bf09SNicolin Chen dev_err(rtd->dev, "ASoC: unmatched rate symmetry: %d - %d\n", 2383635bf09SNicolin Chen cpu_dai->rate, rate); 2393635bf09SNicolin Chen return -EINVAL; 2403635bf09SNicolin Chen } 2413635bf09SNicolin Chen 2423635bf09SNicolin Chen symmetry = cpu_dai->driver->symmetric_channels || 2433635bf09SNicolin Chen codec_dai->driver->symmetric_channels || 2443635bf09SNicolin Chen rtd->dai_link->symmetric_channels; 2453635bf09SNicolin Chen if (symmetry && cpu_dai->channels && cpu_dai->channels != channels) { 2463635bf09SNicolin Chen dev_err(rtd->dev, "ASoC: unmatched channel symmetry: %d - %d\n", 2473635bf09SNicolin Chen cpu_dai->channels, channels); 2483635bf09SNicolin Chen return -EINVAL; 2493635bf09SNicolin Chen } 2503635bf09SNicolin Chen 2513635bf09SNicolin Chen symmetry = cpu_dai->driver->symmetric_samplebits || 2523635bf09SNicolin Chen codec_dai->driver->symmetric_samplebits || 2533635bf09SNicolin Chen rtd->dai_link->symmetric_samplebits; 2543635bf09SNicolin Chen if (symmetry && cpu_dai->sample_bits && cpu_dai->sample_bits != sample_bits) { 2553635bf09SNicolin Chen dev_err(rtd->dev, "ASoC: unmatched sample bits symmetry: %d - %d\n", 2563635bf09SNicolin Chen cpu_dai->sample_bits, sample_bits); 2573635bf09SNicolin Chen return -EINVAL; 2583635bf09SNicolin Chen } 259ddee627cSLiam Girdwood 260ddee627cSLiam Girdwood return 0; 261ddee627cSLiam Girdwood } 262ddee627cSLiam Girdwood 26362e5f676SLars-Peter Clausen static bool soc_pcm_has_symmetry(struct snd_pcm_substream *substream) 26462e5f676SLars-Peter Clausen { 26562e5f676SLars-Peter Clausen struct snd_soc_pcm_runtime *rtd = substream->private_data; 26662e5f676SLars-Peter Clausen struct snd_soc_dai_driver *cpu_driver = rtd->cpu_dai->driver; 26762e5f676SLars-Peter Clausen struct snd_soc_dai_driver *codec_driver = rtd->codec_dai->driver; 26862e5f676SLars-Peter Clausen struct snd_soc_dai_link *link = rtd->dai_link; 26962e5f676SLars-Peter Clausen 27062e5f676SLars-Peter Clausen return cpu_driver->symmetric_rates || codec_driver->symmetric_rates || 27162e5f676SLars-Peter Clausen link->symmetric_rates || cpu_driver->symmetric_channels || 27262e5f676SLars-Peter Clausen codec_driver->symmetric_channels || link->symmetric_channels || 27362e5f676SLars-Peter Clausen cpu_driver->symmetric_samplebits || 27462e5f676SLars-Peter Clausen codec_driver->symmetric_samplebits || 27562e5f676SLars-Peter Clausen link->symmetric_samplebits; 27662e5f676SLars-Peter Clausen } 27762e5f676SLars-Peter Clausen 278ddee627cSLiam Girdwood /* 27958ba9b25SMark Brown * List of sample sizes that might go over the bus for parameter 28058ba9b25SMark Brown * application. There ought to be a wildcard sample size for things 28158ba9b25SMark Brown * like the DAC/ADC resolution to use but there isn't right now. 28258ba9b25SMark Brown */ 28358ba9b25SMark Brown static int sample_sizes[] = { 28488e33954SPeter Ujfalusi 24, 32, 28558ba9b25SMark Brown }; 28658ba9b25SMark Brown 28758ba9b25SMark Brown static void soc_pcm_apply_msb(struct snd_pcm_substream *substream, 28858ba9b25SMark Brown struct snd_soc_dai *dai) 28958ba9b25SMark Brown { 29058ba9b25SMark Brown int ret, i, bits; 29158ba9b25SMark Brown 29258ba9b25SMark Brown if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 29358ba9b25SMark Brown bits = dai->driver->playback.sig_bits; 29458ba9b25SMark Brown else 29558ba9b25SMark Brown bits = dai->driver->capture.sig_bits; 29658ba9b25SMark Brown 29758ba9b25SMark Brown if (!bits) 29858ba9b25SMark Brown return; 29958ba9b25SMark Brown 30058ba9b25SMark Brown for (i = 0; i < ARRAY_SIZE(sample_sizes); i++) { 301278047fdSMark Brown if (bits >= sample_sizes[i]) 302278047fdSMark Brown continue; 303278047fdSMark Brown 304278047fdSMark Brown ret = snd_pcm_hw_constraint_msbits(substream->runtime, 0, 305278047fdSMark Brown sample_sizes[i], bits); 30658ba9b25SMark Brown if (ret != 0) 30758ba9b25SMark Brown dev_warn(dai->dev, 308103d84a3SLiam Girdwood "ASoC: Failed to set MSB %d/%d: %d\n", 30958ba9b25SMark Brown bits, sample_sizes[i], ret); 31058ba9b25SMark Brown } 31158ba9b25SMark Brown } 31258ba9b25SMark Brown 31378e45c99SLars-Peter Clausen static void soc_pcm_init_runtime_hw(struct snd_pcm_runtime *runtime, 314bd477c31SLars-Peter Clausen struct snd_soc_pcm_stream *codec_stream, 315bd477c31SLars-Peter Clausen struct snd_soc_pcm_stream *cpu_stream) 316bd477c31SLars-Peter Clausen { 31778e45c99SLars-Peter Clausen struct snd_pcm_hardware *hw = &runtime->hw; 31878e45c99SLars-Peter Clausen 319bd477c31SLars-Peter Clausen hw->channels_min = max(codec_stream->channels_min, 320bd477c31SLars-Peter Clausen cpu_stream->channels_min); 321bd477c31SLars-Peter Clausen hw->channels_max = min(codec_stream->channels_max, 322bd477c31SLars-Peter Clausen cpu_stream->channels_max); 32316d7ea91SLars-Peter Clausen if (hw->formats) 32416d7ea91SLars-Peter Clausen hw->formats &= codec_stream->formats & cpu_stream->formats; 32516d7ea91SLars-Peter Clausen else 326bd477c31SLars-Peter Clausen hw->formats = codec_stream->formats & cpu_stream->formats; 32755dcdb50SLars-Peter Clausen hw->rates = snd_pcm_rate_mask_intersect(codec_stream->rates, 32855dcdb50SLars-Peter Clausen cpu_stream->rates); 32978e45c99SLars-Peter Clausen 330817873f4SLars-Peter Clausen hw->rate_min = 0; 331817873f4SLars-Peter Clausen hw->rate_max = UINT_MAX; 33278e45c99SLars-Peter Clausen 33378e45c99SLars-Peter Clausen snd_pcm_limit_hw_rates(runtime); 33478e45c99SLars-Peter Clausen 33578e45c99SLars-Peter Clausen hw->rate_min = max(hw->rate_min, cpu_stream->rate_min); 33678e45c99SLars-Peter Clausen hw->rate_min = max(hw->rate_min, codec_stream->rate_min); 33778e45c99SLars-Peter Clausen hw->rate_max = min_not_zero(hw->rate_max, cpu_stream->rate_max); 33878e45c99SLars-Peter Clausen hw->rate_max = min_not_zero(hw->rate_max, codec_stream->rate_max); 339bd477c31SLars-Peter Clausen } 340bd477c31SLars-Peter Clausen 34158ba9b25SMark Brown /* 342ddee627cSLiam Girdwood * Called by ALSA when a PCM substream is opened, the runtime->hw record is 343ddee627cSLiam Girdwood * then initialized and any private data can be allocated. This also calls 344ddee627cSLiam Girdwood * startup for the cpu DAI, platform, machine and codec DAI. 345ddee627cSLiam Girdwood */ 346ddee627cSLiam Girdwood static int soc_pcm_open(struct snd_pcm_substream *substream) 347ddee627cSLiam Girdwood { 348ddee627cSLiam Girdwood struct snd_soc_pcm_runtime *rtd = substream->private_data; 349ddee627cSLiam Girdwood struct snd_pcm_runtime *runtime = substream->runtime; 350ddee627cSLiam Girdwood struct snd_soc_platform *platform = rtd->platform; 351ddee627cSLiam Girdwood struct snd_soc_dai *cpu_dai = rtd->cpu_dai; 352ddee627cSLiam Girdwood struct snd_soc_dai *codec_dai = rtd->codec_dai; 353ddee627cSLiam Girdwood struct snd_soc_dai_driver *cpu_dai_drv = cpu_dai->driver; 354ddee627cSLiam Girdwood struct snd_soc_dai_driver *codec_dai_drv = codec_dai->driver; 355ddee627cSLiam Girdwood int ret = 0; 356ddee627cSLiam Girdwood 357988e8cc4SNicolin Chen pinctrl_pm_select_default_state(cpu_dai->dev); 358988e8cc4SNicolin Chen pinctrl_pm_select_default_state(codec_dai->dev); 359d6652ef8SMark Brown pm_runtime_get_sync(cpu_dai->dev); 360d6652ef8SMark Brown pm_runtime_get_sync(codec_dai->dev); 361d6652ef8SMark Brown pm_runtime_get_sync(platform->dev); 362d6652ef8SMark Brown 363b8c0dab9SLiam Girdwood mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); 364ddee627cSLiam Girdwood 365ddee627cSLiam Girdwood /* startup the audio subsystem */ 366c5914b0aSMark Brown if (cpu_dai->driver->ops && cpu_dai->driver->ops->startup) { 367ddee627cSLiam Girdwood ret = cpu_dai->driver->ops->startup(substream, cpu_dai); 368ddee627cSLiam Girdwood if (ret < 0) { 369103d84a3SLiam Girdwood dev_err(cpu_dai->dev, "ASoC: can't open interface" 370103d84a3SLiam Girdwood " %s: %d\n", cpu_dai->name, ret); 371ddee627cSLiam Girdwood goto out; 372ddee627cSLiam Girdwood } 373ddee627cSLiam Girdwood } 374ddee627cSLiam Girdwood 375ddee627cSLiam Girdwood if (platform->driver->ops && platform->driver->ops->open) { 376ddee627cSLiam Girdwood ret = platform->driver->ops->open(substream); 377ddee627cSLiam Girdwood if (ret < 0) { 378103d84a3SLiam Girdwood dev_err(platform->dev, "ASoC: can't open platform" 379103d84a3SLiam Girdwood " %s: %d\n", platform->name, ret); 380ddee627cSLiam Girdwood goto platform_err; 381ddee627cSLiam Girdwood } 382ddee627cSLiam Girdwood } 383ddee627cSLiam Girdwood 384c5914b0aSMark Brown if (codec_dai->driver->ops && codec_dai->driver->ops->startup) { 385ddee627cSLiam Girdwood ret = codec_dai->driver->ops->startup(substream, codec_dai); 386ddee627cSLiam Girdwood if (ret < 0) { 387103d84a3SLiam Girdwood dev_err(codec_dai->dev, "ASoC: can't open codec" 388103d84a3SLiam Girdwood " %s: %d\n", codec_dai->name, ret); 389ddee627cSLiam Girdwood goto codec_dai_err; 390ddee627cSLiam Girdwood } 391ddee627cSLiam Girdwood } 392ddee627cSLiam Girdwood 393ddee627cSLiam Girdwood if (rtd->dai_link->ops && rtd->dai_link->ops->startup) { 394ddee627cSLiam Girdwood ret = rtd->dai_link->ops->startup(substream); 395ddee627cSLiam Girdwood if (ret < 0) { 396103d84a3SLiam Girdwood pr_err("ASoC: %s startup failed: %d\n", 39725bfe662SMark Brown rtd->dai_link->name, ret); 398ddee627cSLiam Girdwood goto machine_err; 399ddee627cSLiam Girdwood } 400ddee627cSLiam Girdwood } 401ddee627cSLiam Girdwood 40201d7584cSLiam Girdwood /* Dynamic PCM DAI links compat checks use dynamic capabilities */ 40301d7584cSLiam Girdwood if (rtd->dai_link->dynamic || rtd->dai_link->no_pcm) 40401d7584cSLiam Girdwood goto dynamic; 40501d7584cSLiam Girdwood 406ddee627cSLiam Girdwood /* Check that the codec and cpu DAIs are compatible */ 407ddee627cSLiam Girdwood if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 40878e45c99SLars-Peter Clausen soc_pcm_init_runtime_hw(runtime, &codec_dai_drv->playback, 409bd477c31SLars-Peter Clausen &cpu_dai_drv->playback); 410ddee627cSLiam Girdwood } else { 41178e45c99SLars-Peter Clausen soc_pcm_init_runtime_hw(runtime, &codec_dai_drv->capture, 412bd477c31SLars-Peter Clausen &cpu_dai_drv->capture); 413ddee627cSLiam Girdwood } 414ddee627cSLiam Girdwood 41562e5f676SLars-Peter Clausen if (soc_pcm_has_symmetry(substream)) 41662e5f676SLars-Peter Clausen runtime->hw.info |= SNDRV_PCM_INFO_JOINT_DUPLEX; 41762e5f676SLars-Peter Clausen 418ddee627cSLiam Girdwood ret = -EINVAL; 419ddee627cSLiam Girdwood if (!runtime->hw.rates) { 420103d84a3SLiam Girdwood printk(KERN_ERR "ASoC: %s <-> %s No matching rates\n", 421ddee627cSLiam Girdwood codec_dai->name, cpu_dai->name); 422ddee627cSLiam Girdwood goto config_err; 423ddee627cSLiam Girdwood } 424ddee627cSLiam Girdwood if (!runtime->hw.formats) { 425103d84a3SLiam Girdwood printk(KERN_ERR "ASoC: %s <-> %s No matching formats\n", 426ddee627cSLiam Girdwood codec_dai->name, cpu_dai->name); 427ddee627cSLiam Girdwood goto config_err; 428ddee627cSLiam Girdwood } 429ddee627cSLiam Girdwood if (!runtime->hw.channels_min || !runtime->hw.channels_max || 430ddee627cSLiam Girdwood runtime->hw.channels_min > runtime->hw.channels_max) { 431103d84a3SLiam Girdwood printk(KERN_ERR "ASoC: %s <-> %s No matching channels\n", 432ddee627cSLiam Girdwood codec_dai->name, cpu_dai->name); 433ddee627cSLiam Girdwood goto config_err; 434ddee627cSLiam Girdwood } 435ddee627cSLiam Girdwood 43658ba9b25SMark Brown soc_pcm_apply_msb(substream, codec_dai); 43758ba9b25SMark Brown soc_pcm_apply_msb(substream, cpu_dai); 43858ba9b25SMark Brown 439ddee627cSLiam Girdwood /* Symmetry only applies if we've already got an active stream. */ 44017841020SDong Aisheng if (cpu_dai->active) { 44117841020SDong Aisheng ret = soc_pcm_apply_symmetry(substream, cpu_dai); 44217841020SDong Aisheng if (ret != 0) 44317841020SDong Aisheng goto config_err; 44417841020SDong Aisheng } 44517841020SDong Aisheng 44617841020SDong Aisheng if (codec_dai->active) { 44717841020SDong Aisheng ret = soc_pcm_apply_symmetry(substream, codec_dai); 448ddee627cSLiam Girdwood if (ret != 0) 449ddee627cSLiam Girdwood goto config_err; 450ddee627cSLiam Girdwood } 451ddee627cSLiam Girdwood 452103d84a3SLiam Girdwood pr_debug("ASoC: %s <-> %s info:\n", 453ddee627cSLiam Girdwood codec_dai->name, cpu_dai->name); 454103d84a3SLiam Girdwood pr_debug("ASoC: rate mask 0x%x\n", runtime->hw.rates); 455103d84a3SLiam Girdwood pr_debug("ASoC: min ch %d max ch %d\n", runtime->hw.channels_min, 456ddee627cSLiam Girdwood runtime->hw.channels_max); 457103d84a3SLiam Girdwood pr_debug("ASoC: min rate %d max rate %d\n", runtime->hw.rate_min, 458ddee627cSLiam Girdwood runtime->hw.rate_max); 459ddee627cSLiam Girdwood 46001d7584cSLiam Girdwood dynamic: 46124894b76SLars-Peter Clausen 46224894b76SLars-Peter Clausen snd_soc_runtime_activate(rtd, substream->stream); 46324894b76SLars-Peter Clausen 464b8c0dab9SLiam Girdwood mutex_unlock(&rtd->pcm_mutex); 465ddee627cSLiam Girdwood return 0; 466ddee627cSLiam Girdwood 467ddee627cSLiam Girdwood config_err: 468ddee627cSLiam Girdwood if (rtd->dai_link->ops && rtd->dai_link->ops->shutdown) 469ddee627cSLiam Girdwood rtd->dai_link->ops->shutdown(substream); 470ddee627cSLiam Girdwood 471ddee627cSLiam Girdwood machine_err: 472ddee627cSLiam Girdwood if (codec_dai->driver->ops->shutdown) 473ddee627cSLiam Girdwood codec_dai->driver->ops->shutdown(substream, codec_dai); 474ddee627cSLiam Girdwood 475ddee627cSLiam Girdwood codec_dai_err: 476ddee627cSLiam Girdwood if (platform->driver->ops && platform->driver->ops->close) 477ddee627cSLiam Girdwood platform->driver->ops->close(substream); 478ddee627cSLiam Girdwood 479ddee627cSLiam Girdwood platform_err: 480ddee627cSLiam Girdwood if (cpu_dai->driver->ops->shutdown) 481ddee627cSLiam Girdwood cpu_dai->driver->ops->shutdown(substream, cpu_dai); 482ddee627cSLiam Girdwood out: 483b8c0dab9SLiam Girdwood mutex_unlock(&rtd->pcm_mutex); 484d6652ef8SMark Brown 485d6652ef8SMark Brown pm_runtime_put(platform->dev); 486d6652ef8SMark Brown pm_runtime_put(codec_dai->dev); 487d6652ef8SMark Brown pm_runtime_put(cpu_dai->dev); 488988e8cc4SNicolin Chen if (!codec_dai->active) 489988e8cc4SNicolin Chen pinctrl_pm_select_sleep_state(codec_dai->dev); 490988e8cc4SNicolin Chen if (!cpu_dai->active) 491988e8cc4SNicolin Chen pinctrl_pm_select_sleep_state(cpu_dai->dev); 492d6652ef8SMark Brown 493ddee627cSLiam Girdwood return ret; 494ddee627cSLiam Girdwood } 495ddee627cSLiam Girdwood 496ddee627cSLiam Girdwood /* 497ddee627cSLiam Girdwood * Power down the audio subsystem pmdown_time msecs after close is called. 498ddee627cSLiam Girdwood * This is to ensure there are no pops or clicks in between any music tracks 499ddee627cSLiam Girdwood * due to DAPM power cycling. 500ddee627cSLiam Girdwood */ 501ddee627cSLiam Girdwood static void close_delayed_work(struct work_struct *work) 502ddee627cSLiam Girdwood { 503ddee627cSLiam Girdwood struct snd_soc_pcm_runtime *rtd = 504ddee627cSLiam Girdwood container_of(work, struct snd_soc_pcm_runtime, delayed_work.work); 505ddee627cSLiam Girdwood struct snd_soc_dai *codec_dai = rtd->codec_dai; 506ddee627cSLiam Girdwood 507b8c0dab9SLiam Girdwood mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); 508ddee627cSLiam Girdwood 509103d84a3SLiam Girdwood dev_dbg(rtd->dev, "ASoC: pop wq checking: %s status: %s waiting: %s\n", 510ddee627cSLiam Girdwood codec_dai->driver->playback.stream_name, 511ddee627cSLiam Girdwood codec_dai->playback_active ? "active" : "inactive", 5129bffb1fbSMisael Lopez Cruz rtd->pop_wait ? "yes" : "no"); 513ddee627cSLiam Girdwood 514ddee627cSLiam Girdwood /* are we waiting on this codec DAI stream */ 5159bffb1fbSMisael Lopez Cruz if (rtd->pop_wait == 1) { 5169bffb1fbSMisael Lopez Cruz rtd->pop_wait = 0; 5177bd3a6f3SMark Brown snd_soc_dapm_stream_event(rtd, SNDRV_PCM_STREAM_PLAYBACK, 518d9b0951bSLiam Girdwood SND_SOC_DAPM_STREAM_STOP); 519ddee627cSLiam Girdwood } 520ddee627cSLiam Girdwood 521b8c0dab9SLiam Girdwood mutex_unlock(&rtd->pcm_mutex); 522ddee627cSLiam Girdwood } 523ddee627cSLiam Girdwood 524ddee627cSLiam Girdwood /* 525ddee627cSLiam Girdwood * Called by ALSA when a PCM substream is closed. Private data can be 526ddee627cSLiam Girdwood * freed here. The cpu DAI, codec DAI, machine and platform are also 527ddee627cSLiam Girdwood * shutdown. 528ddee627cSLiam Girdwood */ 52991d5e6b4SLiam Girdwood static int soc_pcm_close(struct snd_pcm_substream *substream) 530ddee627cSLiam Girdwood { 531ddee627cSLiam Girdwood struct snd_soc_pcm_runtime *rtd = substream->private_data; 532ddee627cSLiam Girdwood struct snd_soc_platform *platform = rtd->platform; 533ddee627cSLiam Girdwood struct snd_soc_dai *cpu_dai = rtd->cpu_dai; 534ddee627cSLiam Girdwood struct snd_soc_dai *codec_dai = rtd->codec_dai; 535ddee627cSLiam Girdwood 536b8c0dab9SLiam Girdwood mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); 537ddee627cSLiam Girdwood 53824894b76SLars-Peter Clausen snd_soc_runtime_deactivate(rtd, substream->stream); 539ddee627cSLiam Girdwood 54017841020SDong Aisheng /* clear the corresponding DAIs rate when inactive */ 54117841020SDong Aisheng if (!cpu_dai->active) 54217841020SDong Aisheng cpu_dai->rate = 0; 54317841020SDong Aisheng 54417841020SDong Aisheng if (!codec_dai->active) 54517841020SDong Aisheng codec_dai->rate = 0; 54625b76791SSascha Hauer 547ddee627cSLiam Girdwood if (cpu_dai->driver->ops->shutdown) 548ddee627cSLiam Girdwood cpu_dai->driver->ops->shutdown(substream, cpu_dai); 549ddee627cSLiam Girdwood 550ddee627cSLiam Girdwood if (codec_dai->driver->ops->shutdown) 551ddee627cSLiam Girdwood codec_dai->driver->ops->shutdown(substream, codec_dai); 552ddee627cSLiam Girdwood 553ddee627cSLiam Girdwood if (rtd->dai_link->ops && rtd->dai_link->ops->shutdown) 554ddee627cSLiam Girdwood rtd->dai_link->ops->shutdown(substream); 555ddee627cSLiam Girdwood 556ddee627cSLiam Girdwood if (platform->driver->ops && platform->driver->ops->close) 557ddee627cSLiam Girdwood platform->driver->ops->close(substream); 558ddee627cSLiam Girdwood 559ddee627cSLiam Girdwood if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 560208a1589SLars-Peter Clausen if (snd_soc_runtime_ignore_pmdown_time(rtd)) { 5611d69c5c5SPeter Ujfalusi /* powered down playback stream now */ 5621d69c5c5SPeter Ujfalusi snd_soc_dapm_stream_event(rtd, 5637bd3a6f3SMark Brown SNDRV_PCM_STREAM_PLAYBACK, 5641d69c5c5SPeter Ujfalusi SND_SOC_DAPM_STREAM_STOP); 5651d69c5c5SPeter Ujfalusi } else { 566ddee627cSLiam Girdwood /* start delayed pop wq here for playback streams */ 5679bffb1fbSMisael Lopez Cruz rtd->pop_wait = 1; 568d4e1a73aSMark Brown queue_delayed_work(system_power_efficient_wq, 569d4e1a73aSMark Brown &rtd->delayed_work, 570ddee627cSLiam Girdwood msecs_to_jiffies(rtd->pmdown_time)); 5711d69c5c5SPeter Ujfalusi } 572ddee627cSLiam Girdwood } else { 573ddee627cSLiam Girdwood /* capture streams can be powered down now */ 5747bd3a6f3SMark Brown snd_soc_dapm_stream_event(rtd, SNDRV_PCM_STREAM_CAPTURE, 575d9b0951bSLiam Girdwood SND_SOC_DAPM_STREAM_STOP); 576ddee627cSLiam Girdwood } 577ddee627cSLiam Girdwood 578b8c0dab9SLiam Girdwood mutex_unlock(&rtd->pcm_mutex); 579d6652ef8SMark Brown 580d6652ef8SMark Brown pm_runtime_put(platform->dev); 581d6652ef8SMark Brown pm_runtime_put(codec_dai->dev); 582d6652ef8SMark Brown pm_runtime_put(cpu_dai->dev); 583988e8cc4SNicolin Chen if (!codec_dai->active) 584988e8cc4SNicolin Chen pinctrl_pm_select_sleep_state(codec_dai->dev); 585988e8cc4SNicolin Chen if (!cpu_dai->active) 586988e8cc4SNicolin Chen pinctrl_pm_select_sleep_state(cpu_dai->dev); 587d6652ef8SMark Brown 588ddee627cSLiam Girdwood return 0; 589ddee627cSLiam Girdwood } 590ddee627cSLiam Girdwood 591ddee627cSLiam Girdwood /* 592ddee627cSLiam Girdwood * Called by ALSA when the PCM substream is prepared, can set format, sample 593ddee627cSLiam Girdwood * rate, etc. This function is non atomic and can be called multiple times, 594ddee627cSLiam Girdwood * it can refer to the runtime info. 595ddee627cSLiam Girdwood */ 596ddee627cSLiam Girdwood static int soc_pcm_prepare(struct snd_pcm_substream *substream) 597ddee627cSLiam Girdwood { 598ddee627cSLiam Girdwood struct snd_soc_pcm_runtime *rtd = substream->private_data; 599ddee627cSLiam Girdwood struct snd_soc_platform *platform = rtd->platform; 600ddee627cSLiam Girdwood struct snd_soc_dai *cpu_dai = rtd->cpu_dai; 601ddee627cSLiam Girdwood struct snd_soc_dai *codec_dai = rtd->codec_dai; 602ddee627cSLiam Girdwood int ret = 0; 603ddee627cSLiam Girdwood 604b8c0dab9SLiam Girdwood mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); 605ddee627cSLiam Girdwood 606ddee627cSLiam Girdwood if (rtd->dai_link->ops && rtd->dai_link->ops->prepare) { 607ddee627cSLiam Girdwood ret = rtd->dai_link->ops->prepare(substream); 608ddee627cSLiam Girdwood if (ret < 0) { 609103d84a3SLiam Girdwood dev_err(rtd->card->dev, "ASoC: machine prepare error:" 610103d84a3SLiam Girdwood " %d\n", ret); 611ddee627cSLiam Girdwood goto out; 612ddee627cSLiam Girdwood } 613ddee627cSLiam Girdwood } 614ddee627cSLiam Girdwood 615ddee627cSLiam Girdwood if (platform->driver->ops && platform->driver->ops->prepare) { 616ddee627cSLiam Girdwood ret = platform->driver->ops->prepare(substream); 617ddee627cSLiam Girdwood if (ret < 0) { 618103d84a3SLiam Girdwood dev_err(platform->dev, "ASoC: platform prepare error:" 619103d84a3SLiam Girdwood " %d\n", ret); 620ddee627cSLiam Girdwood goto out; 621ddee627cSLiam Girdwood } 622ddee627cSLiam Girdwood } 623ddee627cSLiam Girdwood 624c5914b0aSMark Brown if (codec_dai->driver->ops && codec_dai->driver->ops->prepare) { 625ddee627cSLiam Girdwood ret = codec_dai->driver->ops->prepare(substream, codec_dai); 626ddee627cSLiam Girdwood if (ret < 0) { 627103d84a3SLiam Girdwood dev_err(codec_dai->dev, "ASoC: DAI prepare error: %d\n", 62825bfe662SMark Brown ret); 629ddee627cSLiam Girdwood goto out; 630ddee627cSLiam Girdwood } 631ddee627cSLiam Girdwood } 632ddee627cSLiam Girdwood 633c5914b0aSMark Brown if (cpu_dai->driver->ops && cpu_dai->driver->ops->prepare) { 634ddee627cSLiam Girdwood ret = cpu_dai->driver->ops->prepare(substream, cpu_dai); 635ddee627cSLiam Girdwood if (ret < 0) { 636103d84a3SLiam Girdwood dev_err(cpu_dai->dev, "ASoC: DAI prepare error: %d\n", 63725bfe662SMark Brown ret); 638ddee627cSLiam Girdwood goto out; 639ddee627cSLiam Girdwood } 640ddee627cSLiam Girdwood } 641ddee627cSLiam Girdwood 642ddee627cSLiam Girdwood /* cancel any delayed stream shutdown that is pending */ 643ddee627cSLiam Girdwood if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && 6449bffb1fbSMisael Lopez Cruz rtd->pop_wait) { 6459bffb1fbSMisael Lopez Cruz rtd->pop_wait = 0; 646ddee627cSLiam Girdwood cancel_delayed_work(&rtd->delayed_work); 647ddee627cSLiam Girdwood } 648ddee627cSLiam Girdwood 649d9b0951bSLiam Girdwood snd_soc_dapm_stream_event(rtd, substream->stream, 650ddee627cSLiam Girdwood SND_SOC_DAPM_STREAM_START); 651ddee627cSLiam Girdwood 652da18396fSMark Brown snd_soc_dai_digital_mute(codec_dai, 0, substream->stream); 653ddee627cSLiam Girdwood 654ddee627cSLiam Girdwood out: 655b8c0dab9SLiam Girdwood mutex_unlock(&rtd->pcm_mutex); 656ddee627cSLiam Girdwood return ret; 657ddee627cSLiam Girdwood } 658ddee627cSLiam Girdwood 659ddee627cSLiam Girdwood /* 660ddee627cSLiam Girdwood * Called by ALSA when the hardware params are set by application. This 661ddee627cSLiam Girdwood * function can also be called multiple times and can allocate buffers 662ddee627cSLiam Girdwood * (using snd_pcm_lib_* ). It's non-atomic. 663ddee627cSLiam Girdwood */ 664ddee627cSLiam Girdwood static int soc_pcm_hw_params(struct snd_pcm_substream *substream, 665ddee627cSLiam Girdwood struct snd_pcm_hw_params *params) 666ddee627cSLiam Girdwood { 667ddee627cSLiam Girdwood struct snd_soc_pcm_runtime *rtd = substream->private_data; 668ddee627cSLiam Girdwood struct snd_soc_platform *platform = rtd->platform; 669ddee627cSLiam Girdwood struct snd_soc_dai *cpu_dai = rtd->cpu_dai; 670ddee627cSLiam Girdwood struct snd_soc_dai *codec_dai = rtd->codec_dai; 671ddee627cSLiam Girdwood int ret = 0; 672ddee627cSLiam Girdwood 673b8c0dab9SLiam Girdwood mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); 674ddee627cSLiam Girdwood 6753635bf09SNicolin Chen ret = soc_pcm_params_symmetry(substream, params); 6763635bf09SNicolin Chen if (ret) 6773635bf09SNicolin Chen goto out; 6783635bf09SNicolin Chen 679ddee627cSLiam Girdwood if (rtd->dai_link->ops && rtd->dai_link->ops->hw_params) { 680ddee627cSLiam Girdwood ret = rtd->dai_link->ops->hw_params(substream, params); 681ddee627cSLiam Girdwood if (ret < 0) { 682103d84a3SLiam Girdwood dev_err(rtd->card->dev, "ASoC: machine hw_params" 683103d84a3SLiam Girdwood " failed: %d\n", ret); 684ddee627cSLiam Girdwood goto out; 685ddee627cSLiam Girdwood } 686ddee627cSLiam Girdwood } 687ddee627cSLiam Girdwood 688c5914b0aSMark Brown if (codec_dai->driver->ops && codec_dai->driver->ops->hw_params) { 689ddee627cSLiam Girdwood ret = codec_dai->driver->ops->hw_params(substream, params, codec_dai); 690ddee627cSLiam Girdwood if (ret < 0) { 691103d84a3SLiam Girdwood dev_err(codec_dai->dev, "ASoC: can't set %s hw params:" 692103d84a3SLiam Girdwood " %d\n", codec_dai->name, ret); 693ddee627cSLiam Girdwood goto codec_err; 694ddee627cSLiam Girdwood } 695ddee627cSLiam Girdwood } 696ddee627cSLiam Girdwood 697c5914b0aSMark Brown if (cpu_dai->driver->ops && cpu_dai->driver->ops->hw_params) { 698ddee627cSLiam Girdwood ret = cpu_dai->driver->ops->hw_params(substream, params, cpu_dai); 699ddee627cSLiam Girdwood if (ret < 0) { 700103d84a3SLiam Girdwood dev_err(cpu_dai->dev, "ASoC: %s hw params failed: %d\n", 70125bfe662SMark Brown cpu_dai->name, ret); 702ddee627cSLiam Girdwood goto interface_err; 703ddee627cSLiam Girdwood } 704ddee627cSLiam Girdwood } 705ddee627cSLiam Girdwood 706ddee627cSLiam Girdwood if (platform->driver->ops && platform->driver->ops->hw_params) { 707ddee627cSLiam Girdwood ret = platform->driver->ops->hw_params(substream, params); 708ddee627cSLiam Girdwood if (ret < 0) { 709103d84a3SLiam Girdwood dev_err(platform->dev, "ASoC: %s hw params failed: %d\n", 71025bfe662SMark Brown platform->name, ret); 711ddee627cSLiam Girdwood goto platform_err; 712ddee627cSLiam Girdwood } 713ddee627cSLiam Girdwood } 714ddee627cSLiam Girdwood 7153635bf09SNicolin Chen /* store the parameters for each DAIs */ 71617841020SDong Aisheng cpu_dai->rate = params_rate(params); 7173635bf09SNicolin Chen cpu_dai->channels = params_channels(params); 7183635bf09SNicolin Chen cpu_dai->sample_bits = 7193635bf09SNicolin Chen snd_pcm_format_physical_width(params_format(params)); 7203635bf09SNicolin Chen 72117841020SDong Aisheng codec_dai->rate = params_rate(params); 7223635bf09SNicolin Chen codec_dai->channels = params_channels(params); 7233635bf09SNicolin Chen codec_dai->sample_bits = 7243635bf09SNicolin Chen snd_pcm_format_physical_width(params_format(params)); 725ddee627cSLiam Girdwood 726ddee627cSLiam Girdwood out: 727b8c0dab9SLiam Girdwood mutex_unlock(&rtd->pcm_mutex); 728ddee627cSLiam Girdwood return ret; 729ddee627cSLiam Girdwood 730ddee627cSLiam Girdwood platform_err: 731c5914b0aSMark Brown if (cpu_dai->driver->ops && cpu_dai->driver->ops->hw_free) 732ddee627cSLiam Girdwood cpu_dai->driver->ops->hw_free(substream, cpu_dai); 733ddee627cSLiam Girdwood 734ddee627cSLiam Girdwood interface_err: 735c5914b0aSMark Brown if (codec_dai->driver->ops && codec_dai->driver->ops->hw_free) 736ddee627cSLiam Girdwood codec_dai->driver->ops->hw_free(substream, codec_dai); 737ddee627cSLiam Girdwood 738ddee627cSLiam Girdwood codec_err: 739ddee627cSLiam Girdwood if (rtd->dai_link->ops && rtd->dai_link->ops->hw_free) 740ddee627cSLiam Girdwood rtd->dai_link->ops->hw_free(substream); 741ddee627cSLiam Girdwood 742b8c0dab9SLiam Girdwood mutex_unlock(&rtd->pcm_mutex); 743ddee627cSLiam Girdwood return ret; 744ddee627cSLiam Girdwood } 745ddee627cSLiam Girdwood 746ddee627cSLiam Girdwood /* 747ddee627cSLiam Girdwood * Frees resources allocated by hw_params, can be called multiple times 748ddee627cSLiam Girdwood */ 749ddee627cSLiam Girdwood static int soc_pcm_hw_free(struct snd_pcm_substream *substream) 750ddee627cSLiam Girdwood { 751ddee627cSLiam Girdwood struct snd_soc_pcm_runtime *rtd = substream->private_data; 752ddee627cSLiam Girdwood struct snd_soc_platform *platform = rtd->platform; 753ddee627cSLiam Girdwood struct snd_soc_dai *cpu_dai = rtd->cpu_dai; 754ddee627cSLiam Girdwood struct snd_soc_dai *codec_dai = rtd->codec_dai; 7557f62b6eeSNicolin Chen bool playback = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; 756ddee627cSLiam Girdwood 757b8c0dab9SLiam Girdwood mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); 758ddee627cSLiam Girdwood 759d3383420SNicolin Chen /* clear the corresponding DAIs parameters when going to be inactive */ 760d3383420SNicolin Chen if (cpu_dai->active == 1) { 761d3383420SNicolin Chen cpu_dai->rate = 0; 762d3383420SNicolin Chen cpu_dai->channels = 0; 763d3383420SNicolin Chen cpu_dai->sample_bits = 0; 764d3383420SNicolin Chen } 765d3383420SNicolin Chen 766d3383420SNicolin Chen if (codec_dai->active == 1) { 767d3383420SNicolin Chen codec_dai->rate = 0; 768d3383420SNicolin Chen codec_dai->channels = 0; 769d3383420SNicolin Chen codec_dai->sample_bits = 0; 770d3383420SNicolin Chen } 771d3383420SNicolin Chen 772ddee627cSLiam Girdwood /* apply codec digital mute */ 7737f62b6eeSNicolin Chen if ((playback && codec_dai->playback_active == 1) || 7747f62b6eeSNicolin Chen (!playback && codec_dai->capture_active == 1)) 775da18396fSMark Brown snd_soc_dai_digital_mute(codec_dai, 1, substream->stream); 776ddee627cSLiam Girdwood 777ddee627cSLiam Girdwood /* free any machine hw params */ 778ddee627cSLiam Girdwood if (rtd->dai_link->ops && rtd->dai_link->ops->hw_free) 779ddee627cSLiam Girdwood rtd->dai_link->ops->hw_free(substream); 780ddee627cSLiam Girdwood 781ddee627cSLiam Girdwood /* free any DMA resources */ 782ddee627cSLiam Girdwood if (platform->driver->ops && platform->driver->ops->hw_free) 783ddee627cSLiam Girdwood platform->driver->ops->hw_free(substream); 784ddee627cSLiam Girdwood 785ddee627cSLiam Girdwood /* now free hw params for the DAIs */ 786c5914b0aSMark Brown if (codec_dai->driver->ops && codec_dai->driver->ops->hw_free) 787ddee627cSLiam Girdwood codec_dai->driver->ops->hw_free(substream, codec_dai); 788ddee627cSLiam Girdwood 789c5914b0aSMark Brown if (cpu_dai->driver->ops && cpu_dai->driver->ops->hw_free) 790ddee627cSLiam Girdwood cpu_dai->driver->ops->hw_free(substream, cpu_dai); 791ddee627cSLiam Girdwood 792b8c0dab9SLiam Girdwood mutex_unlock(&rtd->pcm_mutex); 793ddee627cSLiam Girdwood return 0; 794ddee627cSLiam Girdwood } 795ddee627cSLiam Girdwood 796ddee627cSLiam Girdwood static int soc_pcm_trigger(struct snd_pcm_substream *substream, int cmd) 797ddee627cSLiam Girdwood { 798ddee627cSLiam Girdwood struct snd_soc_pcm_runtime *rtd = substream->private_data; 799ddee627cSLiam Girdwood struct snd_soc_platform *platform = rtd->platform; 800ddee627cSLiam Girdwood struct snd_soc_dai *cpu_dai = rtd->cpu_dai; 801ddee627cSLiam Girdwood struct snd_soc_dai *codec_dai = rtd->codec_dai; 802ddee627cSLiam Girdwood int ret; 803ddee627cSLiam Girdwood 804c5914b0aSMark Brown if (codec_dai->driver->ops && codec_dai->driver->ops->trigger) { 805ddee627cSLiam Girdwood ret = codec_dai->driver->ops->trigger(substream, cmd, codec_dai); 806ddee627cSLiam Girdwood if (ret < 0) 807ddee627cSLiam Girdwood return ret; 808ddee627cSLiam Girdwood } 809ddee627cSLiam Girdwood 810ddee627cSLiam Girdwood if (platform->driver->ops && platform->driver->ops->trigger) { 811ddee627cSLiam Girdwood ret = platform->driver->ops->trigger(substream, cmd); 812ddee627cSLiam Girdwood if (ret < 0) 813ddee627cSLiam Girdwood return ret; 814ddee627cSLiam Girdwood } 815ddee627cSLiam Girdwood 816c5914b0aSMark Brown if (cpu_dai->driver->ops && cpu_dai->driver->ops->trigger) { 817ddee627cSLiam Girdwood ret = cpu_dai->driver->ops->trigger(substream, cmd, cpu_dai); 818ddee627cSLiam Girdwood if (ret < 0) 819ddee627cSLiam Girdwood return ret; 820ddee627cSLiam Girdwood } 8214792b0dbSJarkko Nikula 8224792b0dbSJarkko Nikula if (rtd->dai_link->ops && rtd->dai_link->ops->trigger) { 8234792b0dbSJarkko Nikula ret = rtd->dai_link->ops->trigger(substream, cmd); 8244792b0dbSJarkko Nikula if (ret < 0) 8254792b0dbSJarkko Nikula return ret; 8264792b0dbSJarkko Nikula } 8274792b0dbSJarkko Nikula 828ddee627cSLiam Girdwood return 0; 829ddee627cSLiam Girdwood } 830ddee627cSLiam Girdwood 83145c0a188SMark Brown static int soc_pcm_bespoke_trigger(struct snd_pcm_substream *substream, 83245c0a188SMark Brown int cmd) 83307bf84aaSLiam Girdwood { 83407bf84aaSLiam Girdwood struct snd_soc_pcm_runtime *rtd = substream->private_data; 83507bf84aaSLiam Girdwood struct snd_soc_platform *platform = rtd->platform; 83607bf84aaSLiam Girdwood struct snd_soc_dai *cpu_dai = rtd->cpu_dai; 83707bf84aaSLiam Girdwood struct snd_soc_dai *codec_dai = rtd->codec_dai; 83807bf84aaSLiam Girdwood int ret; 83907bf84aaSLiam Girdwood 840c5914b0aSMark Brown if (codec_dai->driver->ops && 841c5914b0aSMark Brown codec_dai->driver->ops->bespoke_trigger) { 84207bf84aaSLiam Girdwood ret = codec_dai->driver->ops->bespoke_trigger(substream, cmd, codec_dai); 84307bf84aaSLiam Girdwood if (ret < 0) 84407bf84aaSLiam Girdwood return ret; 84507bf84aaSLiam Girdwood } 84607bf84aaSLiam Girdwood 847dcf0fa27SJean-Francois Moine if (platform->driver->bespoke_trigger) { 84807bf84aaSLiam Girdwood ret = platform->driver->bespoke_trigger(substream, cmd); 84907bf84aaSLiam Girdwood if (ret < 0) 85007bf84aaSLiam Girdwood return ret; 85107bf84aaSLiam Girdwood } 85207bf84aaSLiam Girdwood 853c5914b0aSMark Brown if (cpu_dai->driver->ops && cpu_dai->driver->ops->bespoke_trigger) { 85407bf84aaSLiam Girdwood ret = cpu_dai->driver->ops->bespoke_trigger(substream, cmd, cpu_dai); 85507bf84aaSLiam Girdwood if (ret < 0) 85607bf84aaSLiam Girdwood return ret; 85707bf84aaSLiam Girdwood } 85807bf84aaSLiam Girdwood return 0; 85907bf84aaSLiam Girdwood } 860ddee627cSLiam Girdwood /* 861ddee627cSLiam Girdwood * soc level wrapper for pointer callback 862ddee627cSLiam Girdwood * If cpu_dai, codec_dai, platform driver has the delay callback, than 863ddee627cSLiam Girdwood * the runtime->delay will be updated accordingly. 864ddee627cSLiam Girdwood */ 865ddee627cSLiam Girdwood static snd_pcm_uframes_t soc_pcm_pointer(struct snd_pcm_substream *substream) 866ddee627cSLiam Girdwood { 867ddee627cSLiam Girdwood struct snd_soc_pcm_runtime *rtd = substream->private_data; 868ddee627cSLiam Girdwood struct snd_soc_platform *platform = rtd->platform; 869ddee627cSLiam Girdwood struct snd_soc_dai *cpu_dai = rtd->cpu_dai; 870ddee627cSLiam Girdwood struct snd_soc_dai *codec_dai = rtd->codec_dai; 871ddee627cSLiam Girdwood struct snd_pcm_runtime *runtime = substream->runtime; 872ddee627cSLiam Girdwood snd_pcm_uframes_t offset = 0; 873ddee627cSLiam Girdwood snd_pcm_sframes_t delay = 0; 874ddee627cSLiam Girdwood 875ddee627cSLiam Girdwood if (platform->driver->ops && platform->driver->ops->pointer) 876ddee627cSLiam Girdwood offset = platform->driver->ops->pointer(substream); 877ddee627cSLiam Girdwood 878c5914b0aSMark Brown if (cpu_dai->driver->ops && cpu_dai->driver->ops->delay) 879ddee627cSLiam Girdwood delay += cpu_dai->driver->ops->delay(substream, cpu_dai); 880ddee627cSLiam Girdwood 881c5914b0aSMark Brown if (codec_dai->driver->ops && codec_dai->driver->ops->delay) 882ddee627cSLiam Girdwood delay += codec_dai->driver->ops->delay(substream, codec_dai); 883ddee627cSLiam Girdwood 884ddee627cSLiam Girdwood if (platform->driver->delay) 885ddee627cSLiam Girdwood delay += platform->driver->delay(substream, codec_dai); 886ddee627cSLiam Girdwood 887ddee627cSLiam Girdwood runtime->delay = delay; 888ddee627cSLiam Girdwood 889ddee627cSLiam Girdwood return offset; 890ddee627cSLiam Girdwood } 891ddee627cSLiam Girdwood 89201d7584cSLiam Girdwood /* connect a FE and BE */ 89301d7584cSLiam Girdwood static int dpcm_be_connect(struct snd_soc_pcm_runtime *fe, 89401d7584cSLiam Girdwood struct snd_soc_pcm_runtime *be, int stream) 89501d7584cSLiam Girdwood { 89601d7584cSLiam Girdwood struct snd_soc_dpcm *dpcm; 89701d7584cSLiam Girdwood 89801d7584cSLiam Girdwood /* only add new dpcms */ 89901d7584cSLiam Girdwood list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) { 90001d7584cSLiam Girdwood if (dpcm->be == be && dpcm->fe == fe) 90101d7584cSLiam Girdwood return 0; 90201d7584cSLiam Girdwood } 90301d7584cSLiam Girdwood 90401d7584cSLiam Girdwood dpcm = kzalloc(sizeof(struct snd_soc_dpcm), GFP_KERNEL); 90501d7584cSLiam Girdwood if (!dpcm) 90601d7584cSLiam Girdwood return -ENOMEM; 90701d7584cSLiam Girdwood 90801d7584cSLiam Girdwood dpcm->be = be; 90901d7584cSLiam Girdwood dpcm->fe = fe; 91001d7584cSLiam Girdwood be->dpcm[stream].runtime = fe->dpcm[stream].runtime; 91101d7584cSLiam Girdwood dpcm->state = SND_SOC_DPCM_LINK_STATE_NEW; 91201d7584cSLiam Girdwood list_add(&dpcm->list_be, &fe->dpcm[stream].be_clients); 91301d7584cSLiam Girdwood list_add(&dpcm->list_fe, &be->dpcm[stream].fe_clients); 91401d7584cSLiam Girdwood 91501d7584cSLiam Girdwood dev_dbg(fe->dev, "connected new DPCM %s path %s %s %s\n", 91601d7584cSLiam Girdwood stream ? "capture" : "playback", fe->dai_link->name, 91701d7584cSLiam Girdwood stream ? "<-" : "->", be->dai_link->name); 91801d7584cSLiam Girdwood 919f86dcef8SLiam Girdwood #ifdef CONFIG_DEBUG_FS 920f86dcef8SLiam Girdwood dpcm->debugfs_state = debugfs_create_u32(be->dai_link->name, 0644, 921f86dcef8SLiam Girdwood fe->debugfs_dpcm_root, &dpcm->state); 922f86dcef8SLiam Girdwood #endif 92301d7584cSLiam Girdwood return 1; 92401d7584cSLiam Girdwood } 92501d7584cSLiam Girdwood 92601d7584cSLiam Girdwood /* reparent a BE onto another FE */ 92701d7584cSLiam Girdwood static void dpcm_be_reparent(struct snd_soc_pcm_runtime *fe, 92801d7584cSLiam Girdwood struct snd_soc_pcm_runtime *be, int stream) 92901d7584cSLiam Girdwood { 93001d7584cSLiam Girdwood struct snd_soc_dpcm *dpcm; 93101d7584cSLiam Girdwood struct snd_pcm_substream *fe_substream, *be_substream; 93201d7584cSLiam Girdwood 93301d7584cSLiam Girdwood /* reparent if BE is connected to other FEs */ 93401d7584cSLiam Girdwood if (!be->dpcm[stream].users) 93501d7584cSLiam Girdwood return; 93601d7584cSLiam Girdwood 93701d7584cSLiam Girdwood be_substream = snd_soc_dpcm_get_substream(be, stream); 93801d7584cSLiam Girdwood 93901d7584cSLiam Girdwood list_for_each_entry(dpcm, &be->dpcm[stream].fe_clients, list_fe) { 94001d7584cSLiam Girdwood if (dpcm->fe == fe) 94101d7584cSLiam Girdwood continue; 94201d7584cSLiam Girdwood 94301d7584cSLiam Girdwood dev_dbg(fe->dev, "reparent %s path %s %s %s\n", 94401d7584cSLiam Girdwood stream ? "capture" : "playback", 94501d7584cSLiam Girdwood dpcm->fe->dai_link->name, 94601d7584cSLiam Girdwood stream ? "<-" : "->", dpcm->be->dai_link->name); 94701d7584cSLiam Girdwood 94801d7584cSLiam Girdwood fe_substream = snd_soc_dpcm_get_substream(dpcm->fe, stream); 94901d7584cSLiam Girdwood be_substream->runtime = fe_substream->runtime; 95001d7584cSLiam Girdwood break; 95101d7584cSLiam Girdwood } 95201d7584cSLiam Girdwood } 95301d7584cSLiam Girdwood 95401d7584cSLiam Girdwood /* disconnect a BE and FE */ 95523607025SLiam Girdwood void dpcm_be_disconnect(struct snd_soc_pcm_runtime *fe, int stream) 95601d7584cSLiam Girdwood { 95701d7584cSLiam Girdwood struct snd_soc_dpcm *dpcm, *d; 95801d7584cSLiam Girdwood 95901d7584cSLiam Girdwood list_for_each_entry_safe(dpcm, d, &fe->dpcm[stream].be_clients, list_be) { 960103d84a3SLiam Girdwood dev_dbg(fe->dev, "ASoC: BE %s disconnect check for %s\n", 96101d7584cSLiam Girdwood stream ? "capture" : "playback", 96201d7584cSLiam Girdwood dpcm->be->dai_link->name); 96301d7584cSLiam Girdwood 96401d7584cSLiam Girdwood if (dpcm->state != SND_SOC_DPCM_LINK_STATE_FREE) 96501d7584cSLiam Girdwood continue; 96601d7584cSLiam Girdwood 96701d7584cSLiam Girdwood dev_dbg(fe->dev, "freed DSP %s path %s %s %s\n", 96801d7584cSLiam Girdwood stream ? "capture" : "playback", fe->dai_link->name, 96901d7584cSLiam Girdwood stream ? "<-" : "->", dpcm->be->dai_link->name); 97001d7584cSLiam Girdwood 97101d7584cSLiam Girdwood /* BEs still alive need new FE */ 97201d7584cSLiam Girdwood dpcm_be_reparent(fe, dpcm->be, stream); 97301d7584cSLiam Girdwood 974f86dcef8SLiam Girdwood #ifdef CONFIG_DEBUG_FS 975f86dcef8SLiam Girdwood debugfs_remove(dpcm->debugfs_state); 976f86dcef8SLiam Girdwood #endif 97701d7584cSLiam Girdwood list_del(&dpcm->list_be); 97801d7584cSLiam Girdwood list_del(&dpcm->list_fe); 97901d7584cSLiam Girdwood kfree(dpcm); 98001d7584cSLiam Girdwood } 98101d7584cSLiam Girdwood } 98201d7584cSLiam Girdwood 98301d7584cSLiam Girdwood /* get BE for DAI widget and stream */ 98401d7584cSLiam Girdwood static struct snd_soc_pcm_runtime *dpcm_get_be(struct snd_soc_card *card, 98501d7584cSLiam Girdwood struct snd_soc_dapm_widget *widget, int stream) 98601d7584cSLiam Girdwood { 98701d7584cSLiam Girdwood struct snd_soc_pcm_runtime *be; 98801d7584cSLiam Girdwood int i; 98901d7584cSLiam Girdwood 99001d7584cSLiam Girdwood if (stream == SNDRV_PCM_STREAM_PLAYBACK) { 99101d7584cSLiam Girdwood for (i = 0; i < card->num_links; i++) { 99201d7584cSLiam Girdwood be = &card->rtd[i]; 99301d7584cSLiam Girdwood 99435ea0655SLiam Girdwood if (!be->dai_link->no_pcm) 99535ea0655SLiam Girdwood continue; 99635ea0655SLiam Girdwood 99701d7584cSLiam Girdwood if (be->cpu_dai->playback_widget == widget || 99801d7584cSLiam Girdwood be->codec_dai->playback_widget == widget) 99901d7584cSLiam Girdwood return be; 100001d7584cSLiam Girdwood } 100101d7584cSLiam Girdwood } else { 100201d7584cSLiam Girdwood 100301d7584cSLiam Girdwood for (i = 0; i < card->num_links; i++) { 100401d7584cSLiam Girdwood be = &card->rtd[i]; 100501d7584cSLiam Girdwood 100635ea0655SLiam Girdwood if (!be->dai_link->no_pcm) 100735ea0655SLiam Girdwood continue; 100835ea0655SLiam Girdwood 100901d7584cSLiam Girdwood if (be->cpu_dai->capture_widget == widget || 101001d7584cSLiam Girdwood be->codec_dai->capture_widget == widget) 101101d7584cSLiam Girdwood return be; 101201d7584cSLiam Girdwood } 101301d7584cSLiam Girdwood } 101401d7584cSLiam Girdwood 1015103d84a3SLiam Girdwood dev_err(card->dev, "ASoC: can't get %s BE for %s\n", 101601d7584cSLiam Girdwood stream ? "capture" : "playback", widget->name); 101701d7584cSLiam Girdwood return NULL; 101801d7584cSLiam Girdwood } 101901d7584cSLiam Girdwood 102001d7584cSLiam Girdwood static inline struct snd_soc_dapm_widget * 102137018610SBenoit Cousson dai_get_widget(struct snd_soc_dai *dai, int stream) 102201d7584cSLiam Girdwood { 102301d7584cSLiam Girdwood if (stream == SNDRV_PCM_STREAM_PLAYBACK) 102437018610SBenoit Cousson return dai->playback_widget; 102501d7584cSLiam Girdwood else 102637018610SBenoit Cousson return dai->capture_widget; 102701d7584cSLiam Girdwood } 102801d7584cSLiam Girdwood 102901d7584cSLiam Girdwood static int widget_in_list(struct snd_soc_dapm_widget_list *list, 103001d7584cSLiam Girdwood struct snd_soc_dapm_widget *widget) 103101d7584cSLiam Girdwood { 103201d7584cSLiam Girdwood int i; 103301d7584cSLiam Girdwood 103401d7584cSLiam Girdwood for (i = 0; i < list->num_widgets; i++) { 103501d7584cSLiam Girdwood if (widget == list->widgets[i]) 103601d7584cSLiam Girdwood return 1; 103701d7584cSLiam Girdwood } 103801d7584cSLiam Girdwood 103901d7584cSLiam Girdwood return 0; 104001d7584cSLiam Girdwood } 104101d7584cSLiam Girdwood 104223607025SLiam Girdwood int dpcm_path_get(struct snd_soc_pcm_runtime *fe, 104301d7584cSLiam Girdwood int stream, struct snd_soc_dapm_widget_list **list_) 104401d7584cSLiam Girdwood { 104501d7584cSLiam Girdwood struct snd_soc_dai *cpu_dai = fe->cpu_dai; 104601d7584cSLiam Girdwood struct snd_soc_dapm_widget_list *list; 104701d7584cSLiam Girdwood int paths; 104801d7584cSLiam Girdwood 104901d7584cSLiam Girdwood list = kzalloc(sizeof(struct snd_soc_dapm_widget_list) + 105001d7584cSLiam Girdwood sizeof(struct snd_soc_dapm_widget *), GFP_KERNEL); 105101d7584cSLiam Girdwood if (list == NULL) 105201d7584cSLiam Girdwood return -ENOMEM; 105301d7584cSLiam Girdwood 105401d7584cSLiam Girdwood /* get number of valid DAI paths and their widgets */ 105501d7584cSLiam Girdwood paths = snd_soc_dapm_dai_get_connected_widgets(cpu_dai, stream, &list); 105601d7584cSLiam Girdwood 1057103d84a3SLiam Girdwood dev_dbg(fe->dev, "ASoC: found %d audio %s paths\n", paths, 105801d7584cSLiam Girdwood stream ? "capture" : "playback"); 105901d7584cSLiam Girdwood 106001d7584cSLiam Girdwood *list_ = list; 106101d7584cSLiam Girdwood return paths; 106201d7584cSLiam Girdwood } 106301d7584cSLiam Girdwood 106401d7584cSLiam Girdwood static int dpcm_prune_paths(struct snd_soc_pcm_runtime *fe, int stream, 106501d7584cSLiam Girdwood struct snd_soc_dapm_widget_list **list_) 106601d7584cSLiam Girdwood { 106701d7584cSLiam Girdwood struct snd_soc_dpcm *dpcm; 106801d7584cSLiam Girdwood struct snd_soc_dapm_widget_list *list = *list_; 106901d7584cSLiam Girdwood struct snd_soc_dapm_widget *widget; 107001d7584cSLiam Girdwood int prune = 0; 107101d7584cSLiam Girdwood 107201d7584cSLiam Girdwood /* Destroy any old FE <--> BE connections */ 107301d7584cSLiam Girdwood list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) { 107401d7584cSLiam Girdwood 107501d7584cSLiam Girdwood /* is there a valid CPU DAI widget for this BE */ 107637018610SBenoit Cousson widget = dai_get_widget(dpcm->be->cpu_dai, stream); 107701d7584cSLiam Girdwood 107801d7584cSLiam Girdwood /* prune the BE if it's no longer in our active list */ 107901d7584cSLiam Girdwood if (widget && widget_in_list(list, widget)) 108001d7584cSLiam Girdwood continue; 108101d7584cSLiam Girdwood 108201d7584cSLiam Girdwood /* is there a valid CODEC DAI widget for this BE */ 108337018610SBenoit Cousson widget = dai_get_widget(dpcm->be->codec_dai, stream); 108401d7584cSLiam Girdwood 108501d7584cSLiam Girdwood /* prune the BE if it's no longer in our active list */ 108601d7584cSLiam Girdwood if (widget && widget_in_list(list, widget)) 108701d7584cSLiam Girdwood continue; 108801d7584cSLiam Girdwood 1089103d84a3SLiam Girdwood dev_dbg(fe->dev, "ASoC: pruning %s BE %s for %s\n", 109001d7584cSLiam Girdwood stream ? "capture" : "playback", 109101d7584cSLiam Girdwood dpcm->be->dai_link->name, fe->dai_link->name); 109201d7584cSLiam Girdwood dpcm->state = SND_SOC_DPCM_LINK_STATE_FREE; 109301d7584cSLiam Girdwood dpcm->be->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_BE; 109401d7584cSLiam Girdwood prune++; 109501d7584cSLiam Girdwood } 109601d7584cSLiam Girdwood 1097103d84a3SLiam Girdwood dev_dbg(fe->dev, "ASoC: found %d old BE paths for pruning\n", prune); 109801d7584cSLiam Girdwood return prune; 109901d7584cSLiam Girdwood } 110001d7584cSLiam Girdwood 110101d7584cSLiam Girdwood static int dpcm_add_paths(struct snd_soc_pcm_runtime *fe, int stream, 110201d7584cSLiam Girdwood struct snd_soc_dapm_widget_list **list_) 110301d7584cSLiam Girdwood { 110401d7584cSLiam Girdwood struct snd_soc_card *card = fe->card; 110501d7584cSLiam Girdwood struct snd_soc_dapm_widget_list *list = *list_; 110601d7584cSLiam Girdwood struct snd_soc_pcm_runtime *be; 110701d7584cSLiam Girdwood int i, new = 0, err; 110801d7584cSLiam Girdwood 110901d7584cSLiam Girdwood /* Create any new FE <--> BE connections */ 111001d7584cSLiam Girdwood for (i = 0; i < list->num_widgets; i++) { 111101d7584cSLiam Girdwood 11124616274dSMark Brown switch (list->widgets[i]->id) { 11134616274dSMark Brown case snd_soc_dapm_dai_in: 11144616274dSMark Brown case snd_soc_dapm_dai_out: 11154616274dSMark Brown break; 11164616274dSMark Brown default: 111701d7584cSLiam Girdwood continue; 11184616274dSMark Brown } 111901d7584cSLiam Girdwood 112001d7584cSLiam Girdwood /* is there a valid BE rtd for this widget */ 112101d7584cSLiam Girdwood be = dpcm_get_be(card, list->widgets[i], stream); 112201d7584cSLiam Girdwood if (!be) { 1123103d84a3SLiam Girdwood dev_err(fe->dev, "ASoC: no BE found for %s\n", 112401d7584cSLiam Girdwood list->widgets[i]->name); 112501d7584cSLiam Girdwood continue; 112601d7584cSLiam Girdwood } 112701d7584cSLiam Girdwood 112801d7584cSLiam Girdwood /* make sure BE is a real BE */ 112901d7584cSLiam Girdwood if (!be->dai_link->no_pcm) 113001d7584cSLiam Girdwood continue; 113101d7584cSLiam Girdwood 113201d7584cSLiam Girdwood /* don't connect if FE is not running */ 113323607025SLiam Girdwood if (!fe->dpcm[stream].runtime && !fe->fe_compr) 113401d7584cSLiam Girdwood continue; 113501d7584cSLiam Girdwood 113601d7584cSLiam Girdwood /* newly connected FE and BE */ 113701d7584cSLiam Girdwood err = dpcm_be_connect(fe, be, stream); 113801d7584cSLiam Girdwood if (err < 0) { 1139103d84a3SLiam Girdwood dev_err(fe->dev, "ASoC: can't connect %s\n", 114001d7584cSLiam Girdwood list->widgets[i]->name); 114101d7584cSLiam Girdwood break; 114201d7584cSLiam Girdwood } else if (err == 0) /* already connected */ 114301d7584cSLiam Girdwood continue; 114401d7584cSLiam Girdwood 114501d7584cSLiam Girdwood /* new */ 114601d7584cSLiam Girdwood be->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_BE; 114701d7584cSLiam Girdwood new++; 114801d7584cSLiam Girdwood } 114901d7584cSLiam Girdwood 1150103d84a3SLiam Girdwood dev_dbg(fe->dev, "ASoC: found %d new BE paths\n", new); 115101d7584cSLiam Girdwood return new; 115201d7584cSLiam Girdwood } 115301d7584cSLiam Girdwood 115401d7584cSLiam Girdwood /* 115501d7584cSLiam Girdwood * Find the corresponding BE DAIs that source or sink audio to this 115601d7584cSLiam Girdwood * FE substream. 115701d7584cSLiam Girdwood */ 115823607025SLiam Girdwood int dpcm_process_paths(struct snd_soc_pcm_runtime *fe, 115901d7584cSLiam Girdwood int stream, struct snd_soc_dapm_widget_list **list, int new) 116001d7584cSLiam Girdwood { 116101d7584cSLiam Girdwood if (new) 116201d7584cSLiam Girdwood return dpcm_add_paths(fe, stream, list); 116301d7584cSLiam Girdwood else 116401d7584cSLiam Girdwood return dpcm_prune_paths(fe, stream, list); 116501d7584cSLiam Girdwood } 116601d7584cSLiam Girdwood 116723607025SLiam Girdwood void dpcm_clear_pending_state(struct snd_soc_pcm_runtime *fe, int stream) 116801d7584cSLiam Girdwood { 116901d7584cSLiam Girdwood struct snd_soc_dpcm *dpcm; 117001d7584cSLiam Girdwood 117101d7584cSLiam Girdwood list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) 117201d7584cSLiam Girdwood dpcm->be->dpcm[stream].runtime_update = 117301d7584cSLiam Girdwood SND_SOC_DPCM_UPDATE_NO; 117401d7584cSLiam Girdwood } 117501d7584cSLiam Girdwood 117601d7584cSLiam Girdwood static void dpcm_be_dai_startup_unwind(struct snd_soc_pcm_runtime *fe, 117701d7584cSLiam Girdwood int stream) 117801d7584cSLiam Girdwood { 117901d7584cSLiam Girdwood struct snd_soc_dpcm *dpcm; 118001d7584cSLiam Girdwood 118101d7584cSLiam Girdwood /* disable any enabled and non active backends */ 118201d7584cSLiam Girdwood list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) { 118301d7584cSLiam Girdwood 118401d7584cSLiam Girdwood struct snd_soc_pcm_runtime *be = dpcm->be; 118501d7584cSLiam Girdwood struct snd_pcm_substream *be_substream = 118601d7584cSLiam Girdwood snd_soc_dpcm_get_substream(be, stream); 118701d7584cSLiam Girdwood 118801d7584cSLiam Girdwood if (be->dpcm[stream].users == 0) 1189103d84a3SLiam Girdwood dev_err(be->dev, "ASoC: no users %s at close - state %d\n", 119001d7584cSLiam Girdwood stream ? "capture" : "playback", 119101d7584cSLiam Girdwood be->dpcm[stream].state); 119201d7584cSLiam Girdwood 119301d7584cSLiam Girdwood if (--be->dpcm[stream].users != 0) 119401d7584cSLiam Girdwood continue; 119501d7584cSLiam Girdwood 119601d7584cSLiam Girdwood if (be->dpcm[stream].state != SND_SOC_DPCM_STATE_OPEN) 119701d7584cSLiam Girdwood continue; 119801d7584cSLiam Girdwood 119901d7584cSLiam Girdwood soc_pcm_close(be_substream); 120001d7584cSLiam Girdwood be_substream->runtime = NULL; 120101d7584cSLiam Girdwood be->dpcm[stream].state = SND_SOC_DPCM_STATE_CLOSE; 120201d7584cSLiam Girdwood } 120301d7584cSLiam Girdwood } 120401d7584cSLiam Girdwood 120523607025SLiam Girdwood int dpcm_be_dai_startup(struct snd_soc_pcm_runtime *fe, int stream) 120601d7584cSLiam Girdwood { 120701d7584cSLiam Girdwood struct snd_soc_dpcm *dpcm; 120801d7584cSLiam Girdwood int err, count = 0; 120901d7584cSLiam Girdwood 121001d7584cSLiam Girdwood /* only startup BE DAIs that are either sinks or sources to this FE DAI */ 121101d7584cSLiam Girdwood list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) { 121201d7584cSLiam Girdwood 121301d7584cSLiam Girdwood struct snd_soc_pcm_runtime *be = dpcm->be; 121401d7584cSLiam Girdwood struct snd_pcm_substream *be_substream = 121501d7584cSLiam Girdwood snd_soc_dpcm_get_substream(be, stream); 121601d7584cSLiam Girdwood 12172062b4c5SRussell King - ARM Linux if (!be_substream) { 12182062b4c5SRussell King - ARM Linux dev_err(be->dev, "ASoC: no backend %s stream\n", 12192062b4c5SRussell King - ARM Linux stream ? "capture" : "playback"); 12202062b4c5SRussell King - ARM Linux continue; 12212062b4c5SRussell King - ARM Linux } 12222062b4c5SRussell King - ARM Linux 122301d7584cSLiam Girdwood /* is this op for this BE ? */ 122401d7584cSLiam Girdwood if (!snd_soc_dpcm_be_can_update(fe, be, stream)) 122501d7584cSLiam Girdwood continue; 122601d7584cSLiam Girdwood 122701d7584cSLiam Girdwood /* first time the dpcm is open ? */ 122801d7584cSLiam Girdwood if (be->dpcm[stream].users == DPCM_MAX_BE_USERS) 1229103d84a3SLiam Girdwood dev_err(be->dev, "ASoC: too many users %s at open %d\n", 123001d7584cSLiam Girdwood stream ? "capture" : "playback", 123101d7584cSLiam Girdwood be->dpcm[stream].state); 123201d7584cSLiam Girdwood 123301d7584cSLiam Girdwood if (be->dpcm[stream].users++ != 0) 123401d7584cSLiam Girdwood continue; 123501d7584cSLiam Girdwood 123601d7584cSLiam Girdwood if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_NEW) && 123701d7584cSLiam Girdwood (be->dpcm[stream].state != SND_SOC_DPCM_STATE_CLOSE)) 123801d7584cSLiam Girdwood continue; 123901d7584cSLiam Girdwood 12402062b4c5SRussell King - ARM Linux dev_dbg(be->dev, "ASoC: open %s BE %s\n", 12412062b4c5SRussell King - ARM Linux stream ? "capture" : "playback", be->dai_link->name); 124201d7584cSLiam Girdwood 124301d7584cSLiam Girdwood be_substream->runtime = be->dpcm[stream].runtime; 124401d7584cSLiam Girdwood err = soc_pcm_open(be_substream); 124501d7584cSLiam Girdwood if (err < 0) { 1246103d84a3SLiam Girdwood dev_err(be->dev, "ASoC: BE open failed %d\n", err); 124701d7584cSLiam Girdwood be->dpcm[stream].users--; 124801d7584cSLiam Girdwood if (be->dpcm[stream].users < 0) 1249103d84a3SLiam Girdwood dev_err(be->dev, "ASoC: no users %s at unwind %d\n", 125001d7584cSLiam Girdwood stream ? "capture" : "playback", 125101d7584cSLiam Girdwood be->dpcm[stream].state); 125201d7584cSLiam Girdwood 125301d7584cSLiam Girdwood be->dpcm[stream].state = SND_SOC_DPCM_STATE_CLOSE; 125401d7584cSLiam Girdwood goto unwind; 125501d7584cSLiam Girdwood } 125601d7584cSLiam Girdwood 125701d7584cSLiam Girdwood be->dpcm[stream].state = SND_SOC_DPCM_STATE_OPEN; 125801d7584cSLiam Girdwood count++; 125901d7584cSLiam Girdwood } 126001d7584cSLiam Girdwood 126101d7584cSLiam Girdwood return count; 126201d7584cSLiam Girdwood 126301d7584cSLiam Girdwood unwind: 126401d7584cSLiam Girdwood /* disable any enabled and non active backends */ 126501d7584cSLiam Girdwood list_for_each_entry_continue_reverse(dpcm, &fe->dpcm[stream].be_clients, list_be) { 126601d7584cSLiam Girdwood struct snd_soc_pcm_runtime *be = dpcm->be; 126701d7584cSLiam Girdwood struct snd_pcm_substream *be_substream = 126801d7584cSLiam Girdwood snd_soc_dpcm_get_substream(be, stream); 126901d7584cSLiam Girdwood 127001d7584cSLiam Girdwood if (!snd_soc_dpcm_be_can_update(fe, be, stream)) 127101d7584cSLiam Girdwood continue; 127201d7584cSLiam Girdwood 127301d7584cSLiam Girdwood if (be->dpcm[stream].users == 0) 1274103d84a3SLiam Girdwood dev_err(be->dev, "ASoC: no users %s at close %d\n", 127501d7584cSLiam Girdwood stream ? "capture" : "playback", 127601d7584cSLiam Girdwood be->dpcm[stream].state); 127701d7584cSLiam Girdwood 127801d7584cSLiam Girdwood if (--be->dpcm[stream].users != 0) 127901d7584cSLiam Girdwood continue; 128001d7584cSLiam Girdwood 128101d7584cSLiam Girdwood if (be->dpcm[stream].state != SND_SOC_DPCM_STATE_OPEN) 128201d7584cSLiam Girdwood continue; 128301d7584cSLiam Girdwood 128401d7584cSLiam Girdwood soc_pcm_close(be_substream); 128501d7584cSLiam Girdwood be_substream->runtime = NULL; 128601d7584cSLiam Girdwood be->dpcm[stream].state = SND_SOC_DPCM_STATE_CLOSE; 128701d7584cSLiam Girdwood } 128801d7584cSLiam Girdwood 128901d7584cSLiam Girdwood return err; 129001d7584cSLiam Girdwood } 129101d7584cSLiam Girdwood 129208ae9b45SLars-Peter Clausen static void dpcm_init_runtime_hw(struct snd_pcm_runtime *runtime, 129308ae9b45SLars-Peter Clausen struct snd_soc_pcm_stream *stream) 129408ae9b45SLars-Peter Clausen { 129508ae9b45SLars-Peter Clausen runtime->hw.rate_min = stream->rate_min; 129608ae9b45SLars-Peter Clausen runtime->hw.rate_max = stream->rate_max; 129708ae9b45SLars-Peter Clausen runtime->hw.channels_min = stream->channels_min; 129808ae9b45SLars-Peter Clausen runtime->hw.channels_max = stream->channels_max; 1299002220a9SLars-Peter Clausen if (runtime->hw.formats) 130008ae9b45SLars-Peter Clausen runtime->hw.formats &= stream->formats; 1301002220a9SLars-Peter Clausen else 1302002220a9SLars-Peter Clausen runtime->hw.formats = stream->formats; 130308ae9b45SLars-Peter Clausen runtime->hw.rates = stream->rates; 130408ae9b45SLars-Peter Clausen } 130508ae9b45SLars-Peter Clausen 130645c0a188SMark Brown static void dpcm_set_fe_runtime(struct snd_pcm_substream *substream) 130701d7584cSLiam Girdwood { 130801d7584cSLiam Girdwood struct snd_pcm_runtime *runtime = substream->runtime; 130901d7584cSLiam Girdwood struct snd_soc_pcm_runtime *rtd = substream->private_data; 131001d7584cSLiam Girdwood struct snd_soc_dai *cpu_dai = rtd->cpu_dai; 131101d7584cSLiam Girdwood struct snd_soc_dai_driver *cpu_dai_drv = cpu_dai->driver; 131201d7584cSLiam Girdwood 131308ae9b45SLars-Peter Clausen if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 131408ae9b45SLars-Peter Clausen dpcm_init_runtime_hw(runtime, &cpu_dai_drv->playback); 131508ae9b45SLars-Peter Clausen else 131608ae9b45SLars-Peter Clausen dpcm_init_runtime_hw(runtime, &cpu_dai_drv->capture); 131701d7584cSLiam Girdwood } 131801d7584cSLiam Girdwood 131901d7584cSLiam Girdwood static int dpcm_fe_dai_startup(struct snd_pcm_substream *fe_substream) 132001d7584cSLiam Girdwood { 132101d7584cSLiam Girdwood struct snd_soc_pcm_runtime *fe = fe_substream->private_data; 132201d7584cSLiam Girdwood struct snd_pcm_runtime *runtime = fe_substream->runtime; 132301d7584cSLiam Girdwood int stream = fe_substream->stream, ret = 0; 132401d7584cSLiam Girdwood 132501d7584cSLiam Girdwood fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE; 132601d7584cSLiam Girdwood 132701d7584cSLiam Girdwood ret = dpcm_be_dai_startup(fe, fe_substream->stream); 132801d7584cSLiam Girdwood if (ret < 0) { 1329103d84a3SLiam Girdwood dev_err(fe->dev,"ASoC: failed to start some BEs %d\n", ret); 133001d7584cSLiam Girdwood goto be_err; 133101d7584cSLiam Girdwood } 133201d7584cSLiam Girdwood 1333103d84a3SLiam Girdwood dev_dbg(fe->dev, "ASoC: open FE %s\n", fe->dai_link->name); 133401d7584cSLiam Girdwood 133501d7584cSLiam Girdwood /* start the DAI frontend */ 133601d7584cSLiam Girdwood ret = soc_pcm_open(fe_substream); 133701d7584cSLiam Girdwood if (ret < 0) { 1338103d84a3SLiam Girdwood dev_err(fe->dev,"ASoC: failed to start FE %d\n", ret); 133901d7584cSLiam Girdwood goto unwind; 134001d7584cSLiam Girdwood } 134101d7584cSLiam Girdwood 134201d7584cSLiam Girdwood fe->dpcm[stream].state = SND_SOC_DPCM_STATE_OPEN; 134301d7584cSLiam Girdwood 134401d7584cSLiam Girdwood dpcm_set_fe_runtime(fe_substream); 134501d7584cSLiam Girdwood snd_pcm_limit_hw_rates(runtime); 134601d7584cSLiam Girdwood 134701d7584cSLiam Girdwood fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO; 134801d7584cSLiam Girdwood return 0; 134901d7584cSLiam Girdwood 135001d7584cSLiam Girdwood unwind: 135101d7584cSLiam Girdwood dpcm_be_dai_startup_unwind(fe, fe_substream->stream); 135201d7584cSLiam Girdwood be_err: 135301d7584cSLiam Girdwood fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO; 135401d7584cSLiam Girdwood return ret; 135501d7584cSLiam Girdwood } 135601d7584cSLiam Girdwood 135723607025SLiam Girdwood int dpcm_be_dai_shutdown(struct snd_soc_pcm_runtime *fe, int stream) 135801d7584cSLiam Girdwood { 135901d7584cSLiam Girdwood struct snd_soc_dpcm *dpcm; 136001d7584cSLiam Girdwood 136101d7584cSLiam Girdwood /* only shutdown BEs that are either sinks or sources to this FE DAI */ 136201d7584cSLiam Girdwood list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) { 136301d7584cSLiam Girdwood 136401d7584cSLiam Girdwood struct snd_soc_pcm_runtime *be = dpcm->be; 136501d7584cSLiam Girdwood struct snd_pcm_substream *be_substream = 136601d7584cSLiam Girdwood snd_soc_dpcm_get_substream(be, stream); 136701d7584cSLiam Girdwood 136801d7584cSLiam Girdwood /* is this op for this BE ? */ 136901d7584cSLiam Girdwood if (!snd_soc_dpcm_be_can_update(fe, be, stream)) 137001d7584cSLiam Girdwood continue; 137101d7584cSLiam Girdwood 137201d7584cSLiam Girdwood if (be->dpcm[stream].users == 0) 1373103d84a3SLiam Girdwood dev_err(be->dev, "ASoC: no users %s at close - state %d\n", 137401d7584cSLiam Girdwood stream ? "capture" : "playback", 137501d7584cSLiam Girdwood be->dpcm[stream].state); 137601d7584cSLiam Girdwood 137701d7584cSLiam Girdwood if (--be->dpcm[stream].users != 0) 137801d7584cSLiam Girdwood continue; 137901d7584cSLiam Girdwood 138001d7584cSLiam Girdwood if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_FREE) && 138101d7584cSLiam Girdwood (be->dpcm[stream].state != SND_SOC_DPCM_STATE_OPEN)) 138201d7584cSLiam Girdwood continue; 138301d7584cSLiam Girdwood 1384103d84a3SLiam Girdwood dev_dbg(be->dev, "ASoC: close BE %s\n", 138501d7584cSLiam Girdwood dpcm->fe->dai_link->name); 138601d7584cSLiam Girdwood 138701d7584cSLiam Girdwood soc_pcm_close(be_substream); 138801d7584cSLiam Girdwood be_substream->runtime = NULL; 138901d7584cSLiam Girdwood 139001d7584cSLiam Girdwood be->dpcm[stream].state = SND_SOC_DPCM_STATE_CLOSE; 139101d7584cSLiam Girdwood } 139201d7584cSLiam Girdwood return 0; 139301d7584cSLiam Girdwood } 139401d7584cSLiam Girdwood 139501d7584cSLiam Girdwood static int dpcm_fe_dai_shutdown(struct snd_pcm_substream *substream) 139601d7584cSLiam Girdwood { 139701d7584cSLiam Girdwood struct snd_soc_pcm_runtime *fe = substream->private_data; 139801d7584cSLiam Girdwood int stream = substream->stream; 139901d7584cSLiam Girdwood 140001d7584cSLiam Girdwood fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE; 140101d7584cSLiam Girdwood 140201d7584cSLiam Girdwood /* shutdown the BEs */ 140301d7584cSLiam Girdwood dpcm_be_dai_shutdown(fe, substream->stream); 140401d7584cSLiam Girdwood 1405103d84a3SLiam Girdwood dev_dbg(fe->dev, "ASoC: close FE %s\n", fe->dai_link->name); 140601d7584cSLiam Girdwood 140701d7584cSLiam Girdwood /* now shutdown the frontend */ 140801d7584cSLiam Girdwood soc_pcm_close(substream); 140901d7584cSLiam Girdwood 141001d7584cSLiam Girdwood /* run the stream event for each BE */ 141101d7584cSLiam Girdwood dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_STOP); 141201d7584cSLiam Girdwood 141301d7584cSLiam Girdwood fe->dpcm[stream].state = SND_SOC_DPCM_STATE_CLOSE; 141401d7584cSLiam Girdwood fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO; 141501d7584cSLiam Girdwood return 0; 141601d7584cSLiam Girdwood } 141701d7584cSLiam Girdwood 141823607025SLiam Girdwood int dpcm_be_dai_hw_free(struct snd_soc_pcm_runtime *fe, int stream) 141901d7584cSLiam Girdwood { 142001d7584cSLiam Girdwood struct snd_soc_dpcm *dpcm; 142101d7584cSLiam Girdwood 142201d7584cSLiam Girdwood /* only hw_params backends that are either sinks or sources 142301d7584cSLiam Girdwood * to this frontend DAI */ 142401d7584cSLiam Girdwood list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) { 142501d7584cSLiam Girdwood 142601d7584cSLiam Girdwood struct snd_soc_pcm_runtime *be = dpcm->be; 142701d7584cSLiam Girdwood struct snd_pcm_substream *be_substream = 142801d7584cSLiam Girdwood snd_soc_dpcm_get_substream(be, stream); 142901d7584cSLiam Girdwood 143001d7584cSLiam Girdwood /* is this op for this BE ? */ 143101d7584cSLiam Girdwood if (!snd_soc_dpcm_be_can_update(fe, be, stream)) 143201d7584cSLiam Girdwood continue; 143301d7584cSLiam Girdwood 143401d7584cSLiam Girdwood /* only free hw when no longer used - check all FEs */ 143501d7584cSLiam Girdwood if (!snd_soc_dpcm_can_be_free_stop(fe, be, stream)) 143601d7584cSLiam Girdwood continue; 143701d7584cSLiam Girdwood 143801d7584cSLiam Girdwood if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_PARAMS) && 143901d7584cSLiam Girdwood (be->dpcm[stream].state != SND_SOC_DPCM_STATE_PREPARE) && 144001d7584cSLiam Girdwood (be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_FREE) && 144108b27848SPatrick Lai (be->dpcm[stream].state != SND_SOC_DPCM_STATE_PAUSED) && 144201d7584cSLiam Girdwood (be->dpcm[stream].state != SND_SOC_DPCM_STATE_STOP)) 144301d7584cSLiam Girdwood continue; 144401d7584cSLiam Girdwood 1445103d84a3SLiam Girdwood dev_dbg(be->dev, "ASoC: hw_free BE %s\n", 144601d7584cSLiam Girdwood dpcm->fe->dai_link->name); 144701d7584cSLiam Girdwood 144801d7584cSLiam Girdwood soc_pcm_hw_free(be_substream); 144901d7584cSLiam Girdwood 145001d7584cSLiam Girdwood be->dpcm[stream].state = SND_SOC_DPCM_STATE_HW_FREE; 145101d7584cSLiam Girdwood } 145201d7584cSLiam Girdwood 145301d7584cSLiam Girdwood return 0; 145401d7584cSLiam Girdwood } 145501d7584cSLiam Girdwood 145645c0a188SMark Brown static int dpcm_fe_dai_hw_free(struct snd_pcm_substream *substream) 145701d7584cSLiam Girdwood { 145801d7584cSLiam Girdwood struct snd_soc_pcm_runtime *fe = substream->private_data; 145901d7584cSLiam Girdwood int err, stream = substream->stream; 146001d7584cSLiam Girdwood 146101d7584cSLiam Girdwood mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME); 146201d7584cSLiam Girdwood fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE; 146301d7584cSLiam Girdwood 1464103d84a3SLiam Girdwood dev_dbg(fe->dev, "ASoC: hw_free FE %s\n", fe->dai_link->name); 146501d7584cSLiam Girdwood 146601d7584cSLiam Girdwood /* call hw_free on the frontend */ 146701d7584cSLiam Girdwood err = soc_pcm_hw_free(substream); 146801d7584cSLiam Girdwood if (err < 0) 1469103d84a3SLiam Girdwood dev_err(fe->dev,"ASoC: hw_free FE %s failed\n", 147001d7584cSLiam Girdwood fe->dai_link->name); 147101d7584cSLiam Girdwood 147201d7584cSLiam Girdwood /* only hw_params backends that are either sinks or sources 147301d7584cSLiam Girdwood * to this frontend DAI */ 147401d7584cSLiam Girdwood err = dpcm_be_dai_hw_free(fe, stream); 147501d7584cSLiam Girdwood 147601d7584cSLiam Girdwood fe->dpcm[stream].state = SND_SOC_DPCM_STATE_HW_FREE; 147701d7584cSLiam Girdwood fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO; 147801d7584cSLiam Girdwood 147901d7584cSLiam Girdwood mutex_unlock(&fe->card->mutex); 148001d7584cSLiam Girdwood return 0; 148101d7584cSLiam Girdwood } 148201d7584cSLiam Girdwood 148323607025SLiam Girdwood int dpcm_be_dai_hw_params(struct snd_soc_pcm_runtime *fe, int stream) 148401d7584cSLiam Girdwood { 148501d7584cSLiam Girdwood struct snd_soc_dpcm *dpcm; 148601d7584cSLiam Girdwood int ret; 148701d7584cSLiam Girdwood 148801d7584cSLiam Girdwood list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) { 148901d7584cSLiam Girdwood 149001d7584cSLiam Girdwood struct snd_soc_pcm_runtime *be = dpcm->be; 149101d7584cSLiam Girdwood struct snd_pcm_substream *be_substream = 149201d7584cSLiam Girdwood snd_soc_dpcm_get_substream(be, stream); 149301d7584cSLiam Girdwood 149401d7584cSLiam Girdwood /* is this op for this BE ? */ 149501d7584cSLiam Girdwood if (!snd_soc_dpcm_be_can_update(fe, be, stream)) 149601d7584cSLiam Girdwood continue; 149701d7584cSLiam Girdwood 149801d7584cSLiam Girdwood /* only allow hw_params() if no connected FEs are running */ 149901d7584cSLiam Girdwood if (!snd_soc_dpcm_can_be_params(fe, be, stream)) 150001d7584cSLiam Girdwood continue; 150101d7584cSLiam Girdwood 150201d7584cSLiam Girdwood if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_OPEN) && 150301d7584cSLiam Girdwood (be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_PARAMS) && 150401d7584cSLiam Girdwood (be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_FREE)) 150501d7584cSLiam Girdwood continue; 150601d7584cSLiam Girdwood 1507103d84a3SLiam Girdwood dev_dbg(be->dev, "ASoC: hw_params BE %s\n", 150801d7584cSLiam Girdwood dpcm->fe->dai_link->name); 150901d7584cSLiam Girdwood 151001d7584cSLiam Girdwood /* copy params for each dpcm */ 151101d7584cSLiam Girdwood memcpy(&dpcm->hw_params, &fe->dpcm[stream].hw_params, 151201d7584cSLiam Girdwood sizeof(struct snd_pcm_hw_params)); 151301d7584cSLiam Girdwood 151401d7584cSLiam Girdwood /* perform any hw_params fixups */ 151501d7584cSLiam Girdwood if (be->dai_link->be_hw_params_fixup) { 151601d7584cSLiam Girdwood ret = be->dai_link->be_hw_params_fixup(be, 151701d7584cSLiam Girdwood &dpcm->hw_params); 151801d7584cSLiam Girdwood if (ret < 0) { 151901d7584cSLiam Girdwood dev_err(be->dev, 1520103d84a3SLiam Girdwood "ASoC: hw_params BE fixup failed %d\n", 152101d7584cSLiam Girdwood ret); 152201d7584cSLiam Girdwood goto unwind; 152301d7584cSLiam Girdwood } 152401d7584cSLiam Girdwood } 152501d7584cSLiam Girdwood 152601d7584cSLiam Girdwood ret = soc_pcm_hw_params(be_substream, &dpcm->hw_params); 152701d7584cSLiam Girdwood if (ret < 0) { 152801d7584cSLiam Girdwood dev_err(dpcm->be->dev, 1529103d84a3SLiam Girdwood "ASoC: hw_params BE failed %d\n", ret); 153001d7584cSLiam Girdwood goto unwind; 153101d7584cSLiam Girdwood } 153201d7584cSLiam Girdwood 153301d7584cSLiam Girdwood be->dpcm[stream].state = SND_SOC_DPCM_STATE_HW_PARAMS; 153401d7584cSLiam Girdwood } 153501d7584cSLiam Girdwood return 0; 153601d7584cSLiam Girdwood 153701d7584cSLiam Girdwood unwind: 153801d7584cSLiam Girdwood /* disable any enabled and non active backends */ 153901d7584cSLiam Girdwood list_for_each_entry_continue_reverse(dpcm, &fe->dpcm[stream].be_clients, list_be) { 154001d7584cSLiam Girdwood struct snd_soc_pcm_runtime *be = dpcm->be; 154101d7584cSLiam Girdwood struct snd_pcm_substream *be_substream = 154201d7584cSLiam Girdwood snd_soc_dpcm_get_substream(be, stream); 154301d7584cSLiam Girdwood 154401d7584cSLiam Girdwood if (!snd_soc_dpcm_be_can_update(fe, be, stream)) 154501d7584cSLiam Girdwood continue; 154601d7584cSLiam Girdwood 154701d7584cSLiam Girdwood /* only allow hw_free() if no connected FEs are running */ 154801d7584cSLiam Girdwood if (!snd_soc_dpcm_can_be_free_stop(fe, be, stream)) 154901d7584cSLiam Girdwood continue; 155001d7584cSLiam Girdwood 155101d7584cSLiam Girdwood if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_OPEN) && 155201d7584cSLiam Girdwood (be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_PARAMS) && 155301d7584cSLiam Girdwood (be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_FREE) && 155401d7584cSLiam Girdwood (be->dpcm[stream].state != SND_SOC_DPCM_STATE_STOP)) 155501d7584cSLiam Girdwood continue; 155601d7584cSLiam Girdwood 155701d7584cSLiam Girdwood soc_pcm_hw_free(be_substream); 155801d7584cSLiam Girdwood } 155901d7584cSLiam Girdwood 156001d7584cSLiam Girdwood return ret; 156101d7584cSLiam Girdwood } 156201d7584cSLiam Girdwood 156345c0a188SMark Brown static int dpcm_fe_dai_hw_params(struct snd_pcm_substream *substream, 156401d7584cSLiam Girdwood struct snd_pcm_hw_params *params) 156501d7584cSLiam Girdwood { 156601d7584cSLiam Girdwood struct snd_soc_pcm_runtime *fe = substream->private_data; 156701d7584cSLiam Girdwood int ret, stream = substream->stream; 156801d7584cSLiam Girdwood 156901d7584cSLiam Girdwood mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME); 157001d7584cSLiam Girdwood fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE; 157101d7584cSLiam Girdwood 157201d7584cSLiam Girdwood memcpy(&fe->dpcm[substream->stream].hw_params, params, 157301d7584cSLiam Girdwood sizeof(struct snd_pcm_hw_params)); 157401d7584cSLiam Girdwood ret = dpcm_be_dai_hw_params(fe, substream->stream); 157501d7584cSLiam Girdwood if (ret < 0) { 1576103d84a3SLiam Girdwood dev_err(fe->dev,"ASoC: hw_params BE failed %d\n", ret); 157701d7584cSLiam Girdwood goto out; 157801d7584cSLiam Girdwood } 157901d7584cSLiam Girdwood 1580103d84a3SLiam Girdwood dev_dbg(fe->dev, "ASoC: hw_params FE %s rate %d chan %x fmt %d\n", 158101d7584cSLiam Girdwood fe->dai_link->name, params_rate(params), 158201d7584cSLiam Girdwood params_channels(params), params_format(params)); 158301d7584cSLiam Girdwood 158401d7584cSLiam Girdwood /* call hw_params on the frontend */ 158501d7584cSLiam Girdwood ret = soc_pcm_hw_params(substream, params); 158601d7584cSLiam Girdwood if (ret < 0) { 1587103d84a3SLiam Girdwood dev_err(fe->dev,"ASoC: hw_params FE failed %d\n", ret); 158801d7584cSLiam Girdwood dpcm_be_dai_hw_free(fe, stream); 158901d7584cSLiam Girdwood } else 159001d7584cSLiam Girdwood fe->dpcm[stream].state = SND_SOC_DPCM_STATE_HW_PARAMS; 159101d7584cSLiam Girdwood 159201d7584cSLiam Girdwood out: 159301d7584cSLiam Girdwood fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO; 159401d7584cSLiam Girdwood mutex_unlock(&fe->card->mutex); 159501d7584cSLiam Girdwood return ret; 159601d7584cSLiam Girdwood } 159701d7584cSLiam Girdwood 159801d7584cSLiam Girdwood static int dpcm_do_trigger(struct snd_soc_dpcm *dpcm, 159901d7584cSLiam Girdwood struct snd_pcm_substream *substream, int cmd) 160001d7584cSLiam Girdwood { 160101d7584cSLiam Girdwood int ret; 160201d7584cSLiam Girdwood 1603103d84a3SLiam Girdwood dev_dbg(dpcm->be->dev, "ASoC: trigger BE %s cmd %d\n", 160401d7584cSLiam Girdwood dpcm->fe->dai_link->name, cmd); 160501d7584cSLiam Girdwood 160601d7584cSLiam Girdwood ret = soc_pcm_trigger(substream, cmd); 160701d7584cSLiam Girdwood if (ret < 0) 1608103d84a3SLiam Girdwood dev_err(dpcm->be->dev,"ASoC: trigger BE failed %d\n", ret); 160901d7584cSLiam Girdwood 161001d7584cSLiam Girdwood return ret; 161101d7584cSLiam Girdwood } 161201d7584cSLiam Girdwood 161323607025SLiam Girdwood int dpcm_be_dai_trigger(struct snd_soc_pcm_runtime *fe, int stream, 161445c0a188SMark Brown int cmd) 161501d7584cSLiam Girdwood { 161601d7584cSLiam Girdwood struct snd_soc_dpcm *dpcm; 161701d7584cSLiam Girdwood int ret = 0; 161801d7584cSLiam Girdwood 161901d7584cSLiam Girdwood list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) { 162001d7584cSLiam Girdwood 162101d7584cSLiam Girdwood struct snd_soc_pcm_runtime *be = dpcm->be; 162201d7584cSLiam Girdwood struct snd_pcm_substream *be_substream = 162301d7584cSLiam Girdwood snd_soc_dpcm_get_substream(be, stream); 162401d7584cSLiam Girdwood 162501d7584cSLiam Girdwood /* is this op for this BE ? */ 162601d7584cSLiam Girdwood if (!snd_soc_dpcm_be_can_update(fe, be, stream)) 162701d7584cSLiam Girdwood continue; 162801d7584cSLiam Girdwood 162901d7584cSLiam Girdwood switch (cmd) { 163001d7584cSLiam Girdwood case SNDRV_PCM_TRIGGER_START: 163101d7584cSLiam Girdwood if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_PREPARE) && 163201d7584cSLiam Girdwood (be->dpcm[stream].state != SND_SOC_DPCM_STATE_STOP)) 163301d7584cSLiam Girdwood continue; 163401d7584cSLiam Girdwood 163501d7584cSLiam Girdwood ret = dpcm_do_trigger(dpcm, be_substream, cmd); 163601d7584cSLiam Girdwood if (ret) 163701d7584cSLiam Girdwood return ret; 163801d7584cSLiam Girdwood 163901d7584cSLiam Girdwood be->dpcm[stream].state = SND_SOC_DPCM_STATE_START; 164001d7584cSLiam Girdwood break; 164101d7584cSLiam Girdwood case SNDRV_PCM_TRIGGER_RESUME: 164201d7584cSLiam Girdwood if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_SUSPEND)) 164301d7584cSLiam Girdwood continue; 164401d7584cSLiam Girdwood 164501d7584cSLiam Girdwood ret = dpcm_do_trigger(dpcm, be_substream, cmd); 164601d7584cSLiam Girdwood if (ret) 164701d7584cSLiam Girdwood return ret; 164801d7584cSLiam Girdwood 164901d7584cSLiam Girdwood be->dpcm[stream].state = SND_SOC_DPCM_STATE_START; 165001d7584cSLiam Girdwood break; 165101d7584cSLiam Girdwood case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 165201d7584cSLiam Girdwood if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_PAUSED)) 165301d7584cSLiam Girdwood continue; 165401d7584cSLiam Girdwood 165501d7584cSLiam Girdwood ret = dpcm_do_trigger(dpcm, be_substream, cmd); 165601d7584cSLiam Girdwood if (ret) 165701d7584cSLiam Girdwood return ret; 165801d7584cSLiam Girdwood 165901d7584cSLiam Girdwood be->dpcm[stream].state = SND_SOC_DPCM_STATE_START; 166001d7584cSLiam Girdwood break; 166101d7584cSLiam Girdwood case SNDRV_PCM_TRIGGER_STOP: 166201d7584cSLiam Girdwood if (be->dpcm[stream].state != SND_SOC_DPCM_STATE_START) 166301d7584cSLiam Girdwood continue; 166401d7584cSLiam Girdwood 166501d7584cSLiam Girdwood if (!snd_soc_dpcm_can_be_free_stop(fe, be, stream)) 166601d7584cSLiam Girdwood continue; 166701d7584cSLiam Girdwood 166801d7584cSLiam Girdwood ret = dpcm_do_trigger(dpcm, be_substream, cmd); 166901d7584cSLiam Girdwood if (ret) 167001d7584cSLiam Girdwood return ret; 167101d7584cSLiam Girdwood 167201d7584cSLiam Girdwood be->dpcm[stream].state = SND_SOC_DPCM_STATE_STOP; 167301d7584cSLiam Girdwood break; 167401d7584cSLiam Girdwood case SNDRV_PCM_TRIGGER_SUSPEND: 1675868a6ca8SNicolin Chen if (be->dpcm[stream].state != SND_SOC_DPCM_STATE_START) 167601d7584cSLiam Girdwood continue; 167701d7584cSLiam Girdwood 167801d7584cSLiam Girdwood if (!snd_soc_dpcm_can_be_free_stop(fe, be, stream)) 167901d7584cSLiam Girdwood continue; 168001d7584cSLiam Girdwood 168101d7584cSLiam Girdwood ret = dpcm_do_trigger(dpcm, be_substream, cmd); 168201d7584cSLiam Girdwood if (ret) 168301d7584cSLiam Girdwood return ret; 168401d7584cSLiam Girdwood 168501d7584cSLiam Girdwood be->dpcm[stream].state = SND_SOC_DPCM_STATE_SUSPEND; 168601d7584cSLiam Girdwood break; 168701d7584cSLiam Girdwood case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 168801d7584cSLiam Girdwood if (be->dpcm[stream].state != SND_SOC_DPCM_STATE_START) 168901d7584cSLiam Girdwood continue; 169001d7584cSLiam Girdwood 169101d7584cSLiam Girdwood if (!snd_soc_dpcm_can_be_free_stop(fe, be, stream)) 169201d7584cSLiam Girdwood continue; 169301d7584cSLiam Girdwood 169401d7584cSLiam Girdwood ret = dpcm_do_trigger(dpcm, be_substream, cmd); 169501d7584cSLiam Girdwood if (ret) 169601d7584cSLiam Girdwood return ret; 169701d7584cSLiam Girdwood 169801d7584cSLiam Girdwood be->dpcm[stream].state = SND_SOC_DPCM_STATE_PAUSED; 169901d7584cSLiam Girdwood break; 170001d7584cSLiam Girdwood } 170101d7584cSLiam Girdwood } 170201d7584cSLiam Girdwood 170301d7584cSLiam Girdwood return ret; 170401d7584cSLiam Girdwood } 170501d7584cSLiam Girdwood EXPORT_SYMBOL_GPL(dpcm_be_dai_trigger); 170601d7584cSLiam Girdwood 170745c0a188SMark Brown static int dpcm_fe_dai_trigger(struct snd_pcm_substream *substream, int cmd) 170801d7584cSLiam Girdwood { 170901d7584cSLiam Girdwood struct snd_soc_pcm_runtime *fe = substream->private_data; 171001d7584cSLiam Girdwood int stream = substream->stream, ret; 171101d7584cSLiam Girdwood enum snd_soc_dpcm_trigger trigger = fe->dai_link->trigger[stream]; 171201d7584cSLiam Girdwood 171301d7584cSLiam Girdwood fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE; 171401d7584cSLiam Girdwood 171501d7584cSLiam Girdwood switch (trigger) { 171601d7584cSLiam Girdwood case SND_SOC_DPCM_TRIGGER_PRE: 171701d7584cSLiam Girdwood /* call trigger on the frontend before the backend. */ 171801d7584cSLiam Girdwood 1719103d84a3SLiam Girdwood dev_dbg(fe->dev, "ASoC: pre trigger FE %s cmd %d\n", 172001d7584cSLiam Girdwood fe->dai_link->name, cmd); 172101d7584cSLiam Girdwood 172201d7584cSLiam Girdwood ret = soc_pcm_trigger(substream, cmd); 172301d7584cSLiam Girdwood if (ret < 0) { 1724103d84a3SLiam Girdwood dev_err(fe->dev,"ASoC: trigger FE failed %d\n", ret); 172501d7584cSLiam Girdwood goto out; 172601d7584cSLiam Girdwood } 172701d7584cSLiam Girdwood 172801d7584cSLiam Girdwood ret = dpcm_be_dai_trigger(fe, substream->stream, cmd); 172901d7584cSLiam Girdwood break; 173001d7584cSLiam Girdwood case SND_SOC_DPCM_TRIGGER_POST: 173101d7584cSLiam Girdwood /* call trigger on the frontend after the backend. */ 173201d7584cSLiam Girdwood 173301d7584cSLiam Girdwood ret = dpcm_be_dai_trigger(fe, substream->stream, cmd); 173401d7584cSLiam Girdwood if (ret < 0) { 1735103d84a3SLiam Girdwood dev_err(fe->dev,"ASoC: trigger FE failed %d\n", ret); 173601d7584cSLiam Girdwood goto out; 173701d7584cSLiam Girdwood } 173801d7584cSLiam Girdwood 1739103d84a3SLiam Girdwood dev_dbg(fe->dev, "ASoC: post trigger FE %s cmd %d\n", 174001d7584cSLiam Girdwood fe->dai_link->name, cmd); 174101d7584cSLiam Girdwood 174201d7584cSLiam Girdwood ret = soc_pcm_trigger(substream, cmd); 174301d7584cSLiam Girdwood break; 174407bf84aaSLiam Girdwood case SND_SOC_DPCM_TRIGGER_BESPOKE: 174507bf84aaSLiam Girdwood /* bespoke trigger() - handles both FE and BEs */ 174607bf84aaSLiam Girdwood 1747103d84a3SLiam Girdwood dev_dbg(fe->dev, "ASoC: bespoke trigger FE %s cmd %d\n", 174807bf84aaSLiam Girdwood fe->dai_link->name, cmd); 174907bf84aaSLiam Girdwood 175007bf84aaSLiam Girdwood ret = soc_pcm_bespoke_trigger(substream, cmd); 175107bf84aaSLiam Girdwood if (ret < 0) { 1752103d84a3SLiam Girdwood dev_err(fe->dev,"ASoC: trigger FE failed %d\n", ret); 175307bf84aaSLiam Girdwood goto out; 175407bf84aaSLiam Girdwood } 175507bf84aaSLiam Girdwood break; 175601d7584cSLiam Girdwood default: 1757103d84a3SLiam Girdwood dev_err(fe->dev, "ASoC: invalid trigger cmd %d for %s\n", cmd, 175801d7584cSLiam Girdwood fe->dai_link->name); 175901d7584cSLiam Girdwood ret = -EINVAL; 176001d7584cSLiam Girdwood goto out; 176101d7584cSLiam Girdwood } 176201d7584cSLiam Girdwood 176301d7584cSLiam Girdwood switch (cmd) { 176401d7584cSLiam Girdwood case SNDRV_PCM_TRIGGER_START: 176501d7584cSLiam Girdwood case SNDRV_PCM_TRIGGER_RESUME: 176601d7584cSLiam Girdwood case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 176701d7584cSLiam Girdwood fe->dpcm[stream].state = SND_SOC_DPCM_STATE_START; 176801d7584cSLiam Girdwood break; 176901d7584cSLiam Girdwood case SNDRV_PCM_TRIGGER_STOP: 177001d7584cSLiam Girdwood case SNDRV_PCM_TRIGGER_SUSPEND: 177101d7584cSLiam Girdwood case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 177201d7584cSLiam Girdwood fe->dpcm[stream].state = SND_SOC_DPCM_STATE_STOP; 177301d7584cSLiam Girdwood break; 177401d7584cSLiam Girdwood } 177501d7584cSLiam Girdwood 177601d7584cSLiam Girdwood out: 177701d7584cSLiam Girdwood fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO; 177801d7584cSLiam Girdwood return ret; 177901d7584cSLiam Girdwood } 178001d7584cSLiam Girdwood 178123607025SLiam Girdwood int dpcm_be_dai_prepare(struct snd_soc_pcm_runtime *fe, int stream) 178201d7584cSLiam Girdwood { 178301d7584cSLiam Girdwood struct snd_soc_dpcm *dpcm; 178401d7584cSLiam Girdwood int ret = 0; 178501d7584cSLiam Girdwood 178601d7584cSLiam Girdwood list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) { 178701d7584cSLiam Girdwood 178801d7584cSLiam Girdwood struct snd_soc_pcm_runtime *be = dpcm->be; 178901d7584cSLiam Girdwood struct snd_pcm_substream *be_substream = 179001d7584cSLiam Girdwood snd_soc_dpcm_get_substream(be, stream); 179101d7584cSLiam Girdwood 179201d7584cSLiam Girdwood /* is this op for this BE ? */ 179301d7584cSLiam Girdwood if (!snd_soc_dpcm_be_can_update(fe, be, stream)) 179401d7584cSLiam Girdwood continue; 179501d7584cSLiam Girdwood 179601d7584cSLiam Girdwood if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_PARAMS) && 179701d7584cSLiam Girdwood (be->dpcm[stream].state != SND_SOC_DPCM_STATE_STOP)) 179801d7584cSLiam Girdwood continue; 179901d7584cSLiam Girdwood 1800103d84a3SLiam Girdwood dev_dbg(be->dev, "ASoC: prepare BE %s\n", 180101d7584cSLiam Girdwood dpcm->fe->dai_link->name); 180201d7584cSLiam Girdwood 180301d7584cSLiam Girdwood ret = soc_pcm_prepare(be_substream); 180401d7584cSLiam Girdwood if (ret < 0) { 1805103d84a3SLiam Girdwood dev_err(be->dev, "ASoC: backend prepare failed %d\n", 180601d7584cSLiam Girdwood ret); 180701d7584cSLiam Girdwood break; 180801d7584cSLiam Girdwood } 180901d7584cSLiam Girdwood 181001d7584cSLiam Girdwood be->dpcm[stream].state = SND_SOC_DPCM_STATE_PREPARE; 181101d7584cSLiam Girdwood } 181201d7584cSLiam Girdwood return ret; 181301d7584cSLiam Girdwood } 181401d7584cSLiam Girdwood 181545c0a188SMark Brown static int dpcm_fe_dai_prepare(struct snd_pcm_substream *substream) 181601d7584cSLiam Girdwood { 181701d7584cSLiam Girdwood struct snd_soc_pcm_runtime *fe = substream->private_data; 181801d7584cSLiam Girdwood int stream = substream->stream, ret = 0; 181901d7584cSLiam Girdwood 182001d7584cSLiam Girdwood mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME); 182101d7584cSLiam Girdwood 1822103d84a3SLiam Girdwood dev_dbg(fe->dev, "ASoC: prepare FE %s\n", fe->dai_link->name); 182301d7584cSLiam Girdwood 182401d7584cSLiam Girdwood fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE; 182501d7584cSLiam Girdwood 182601d7584cSLiam Girdwood /* there is no point preparing this FE if there are no BEs */ 182701d7584cSLiam Girdwood if (list_empty(&fe->dpcm[stream].be_clients)) { 1828103d84a3SLiam Girdwood dev_err(fe->dev, "ASoC: no backend DAIs enabled for %s\n", 182901d7584cSLiam Girdwood fe->dai_link->name); 183001d7584cSLiam Girdwood ret = -EINVAL; 183101d7584cSLiam Girdwood goto out; 183201d7584cSLiam Girdwood } 183301d7584cSLiam Girdwood 183401d7584cSLiam Girdwood ret = dpcm_be_dai_prepare(fe, substream->stream); 183501d7584cSLiam Girdwood if (ret < 0) 183601d7584cSLiam Girdwood goto out; 183701d7584cSLiam Girdwood 183801d7584cSLiam Girdwood /* call prepare on the frontend */ 183901d7584cSLiam Girdwood ret = soc_pcm_prepare(substream); 184001d7584cSLiam Girdwood if (ret < 0) { 1841103d84a3SLiam Girdwood dev_err(fe->dev,"ASoC: prepare FE %s failed\n", 184201d7584cSLiam Girdwood fe->dai_link->name); 184301d7584cSLiam Girdwood goto out; 184401d7584cSLiam Girdwood } 184501d7584cSLiam Girdwood 184601d7584cSLiam Girdwood /* run the stream event for each BE */ 184701d7584cSLiam Girdwood dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_START); 184801d7584cSLiam Girdwood fe->dpcm[stream].state = SND_SOC_DPCM_STATE_PREPARE; 184901d7584cSLiam Girdwood 185001d7584cSLiam Girdwood out: 185101d7584cSLiam Girdwood fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO; 185201d7584cSLiam Girdwood mutex_unlock(&fe->card->mutex); 185301d7584cSLiam Girdwood 185401d7584cSLiam Girdwood return ret; 185501d7584cSLiam Girdwood } 185601d7584cSLiam Girdwood 1857be3f3f2cSLiam Girdwood static int soc_pcm_ioctl(struct snd_pcm_substream *substream, 1858be3f3f2cSLiam Girdwood unsigned int cmd, void *arg) 1859be3f3f2cSLiam Girdwood { 1860be3f3f2cSLiam Girdwood struct snd_soc_pcm_runtime *rtd = substream->private_data; 1861be3f3f2cSLiam Girdwood struct snd_soc_platform *platform = rtd->platform; 1862be3f3f2cSLiam Girdwood 1863c5914b0aSMark Brown if (platform->driver->ops && platform->driver->ops->ioctl) 1864be3f3f2cSLiam Girdwood return platform->driver->ops->ioctl(substream, cmd, arg); 1865be3f3f2cSLiam Girdwood return snd_pcm_lib_ioctl(substream, cmd, arg); 1866be3f3f2cSLiam Girdwood } 1867be3f3f2cSLiam Girdwood 1868618dae11SLiam Girdwood static int dpcm_run_update_shutdown(struct snd_soc_pcm_runtime *fe, int stream) 1869618dae11SLiam Girdwood { 187007bf84aaSLiam Girdwood struct snd_pcm_substream *substream = 187107bf84aaSLiam Girdwood snd_soc_dpcm_get_substream(fe, stream); 187207bf84aaSLiam Girdwood enum snd_soc_dpcm_trigger trigger = fe->dai_link->trigger[stream]; 1873618dae11SLiam Girdwood int err; 187401d7584cSLiam Girdwood 1875103d84a3SLiam Girdwood dev_dbg(fe->dev, "ASoC: runtime %s close on FE %s\n", 1876618dae11SLiam Girdwood stream ? "capture" : "playback", fe->dai_link->name); 1877618dae11SLiam Girdwood 187807bf84aaSLiam Girdwood if (trigger == SND_SOC_DPCM_TRIGGER_BESPOKE) { 187907bf84aaSLiam Girdwood /* call bespoke trigger - FE takes care of all BE triggers */ 1880103d84a3SLiam Girdwood dev_dbg(fe->dev, "ASoC: bespoke trigger FE %s cmd stop\n", 188107bf84aaSLiam Girdwood fe->dai_link->name); 188207bf84aaSLiam Girdwood 188307bf84aaSLiam Girdwood err = soc_pcm_bespoke_trigger(substream, SNDRV_PCM_TRIGGER_STOP); 188407bf84aaSLiam Girdwood if (err < 0) 1885103d84a3SLiam Girdwood dev_err(fe->dev,"ASoC: trigger FE failed %d\n", err); 188607bf84aaSLiam Girdwood } else { 1887103d84a3SLiam Girdwood dev_dbg(fe->dev, "ASoC: trigger FE %s cmd stop\n", 188807bf84aaSLiam Girdwood fe->dai_link->name); 188907bf84aaSLiam Girdwood 1890618dae11SLiam Girdwood err = dpcm_be_dai_trigger(fe, stream, SNDRV_PCM_TRIGGER_STOP); 1891618dae11SLiam Girdwood if (err < 0) 1892103d84a3SLiam Girdwood dev_err(fe->dev,"ASoC: trigger FE failed %d\n", err); 189307bf84aaSLiam Girdwood } 1894618dae11SLiam Girdwood 1895618dae11SLiam Girdwood err = dpcm_be_dai_hw_free(fe, stream); 1896618dae11SLiam Girdwood if (err < 0) 1897103d84a3SLiam Girdwood dev_err(fe->dev,"ASoC: hw_free FE failed %d\n", err); 1898618dae11SLiam Girdwood 1899618dae11SLiam Girdwood err = dpcm_be_dai_shutdown(fe, stream); 1900618dae11SLiam Girdwood if (err < 0) 1901103d84a3SLiam Girdwood dev_err(fe->dev,"ASoC: shutdown FE failed %d\n", err); 1902618dae11SLiam Girdwood 1903618dae11SLiam Girdwood /* run the stream event for each BE */ 1904618dae11SLiam Girdwood dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_NOP); 1905618dae11SLiam Girdwood 1906618dae11SLiam Girdwood return 0; 1907618dae11SLiam Girdwood } 1908618dae11SLiam Girdwood 1909618dae11SLiam Girdwood static int dpcm_run_update_startup(struct snd_soc_pcm_runtime *fe, int stream) 1910618dae11SLiam Girdwood { 191107bf84aaSLiam Girdwood struct snd_pcm_substream *substream = 191207bf84aaSLiam Girdwood snd_soc_dpcm_get_substream(fe, stream); 1913618dae11SLiam Girdwood struct snd_soc_dpcm *dpcm; 191407bf84aaSLiam Girdwood enum snd_soc_dpcm_trigger trigger = fe->dai_link->trigger[stream]; 1915618dae11SLiam Girdwood int ret; 1916618dae11SLiam Girdwood 1917103d84a3SLiam Girdwood dev_dbg(fe->dev, "ASoC: runtime %s open on FE %s\n", 1918618dae11SLiam Girdwood stream ? "capture" : "playback", fe->dai_link->name); 1919618dae11SLiam Girdwood 1920618dae11SLiam Girdwood /* Only start the BE if the FE is ready */ 1921618dae11SLiam Girdwood if (fe->dpcm[stream].state == SND_SOC_DPCM_STATE_HW_FREE || 1922618dae11SLiam Girdwood fe->dpcm[stream].state == SND_SOC_DPCM_STATE_CLOSE) 1923618dae11SLiam Girdwood return -EINVAL; 1924618dae11SLiam Girdwood 1925618dae11SLiam Girdwood /* startup must always be called for new BEs */ 1926618dae11SLiam Girdwood ret = dpcm_be_dai_startup(fe, stream); 1927fffc0ca2SDan Carpenter if (ret < 0) 1928618dae11SLiam Girdwood goto disconnect; 1929618dae11SLiam Girdwood 1930618dae11SLiam Girdwood /* keep going if FE state is > open */ 1931618dae11SLiam Girdwood if (fe->dpcm[stream].state == SND_SOC_DPCM_STATE_OPEN) 1932618dae11SLiam Girdwood return 0; 1933618dae11SLiam Girdwood 1934618dae11SLiam Girdwood ret = dpcm_be_dai_hw_params(fe, stream); 1935fffc0ca2SDan Carpenter if (ret < 0) 1936618dae11SLiam Girdwood goto close; 1937618dae11SLiam Girdwood 1938618dae11SLiam Girdwood /* keep going if FE state is > hw_params */ 1939618dae11SLiam Girdwood if (fe->dpcm[stream].state == SND_SOC_DPCM_STATE_HW_PARAMS) 1940618dae11SLiam Girdwood return 0; 1941618dae11SLiam Girdwood 1942618dae11SLiam Girdwood 1943618dae11SLiam Girdwood ret = dpcm_be_dai_prepare(fe, stream); 1944fffc0ca2SDan Carpenter if (ret < 0) 1945618dae11SLiam Girdwood goto hw_free; 1946618dae11SLiam Girdwood 1947618dae11SLiam Girdwood /* run the stream event for each BE */ 1948618dae11SLiam Girdwood dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_NOP); 1949618dae11SLiam Girdwood 1950618dae11SLiam Girdwood /* keep going if FE state is > prepare */ 1951618dae11SLiam Girdwood if (fe->dpcm[stream].state == SND_SOC_DPCM_STATE_PREPARE || 1952618dae11SLiam Girdwood fe->dpcm[stream].state == SND_SOC_DPCM_STATE_STOP) 1953618dae11SLiam Girdwood return 0; 1954618dae11SLiam Girdwood 195507bf84aaSLiam Girdwood if (trigger == SND_SOC_DPCM_TRIGGER_BESPOKE) { 195607bf84aaSLiam Girdwood /* call trigger on the frontend - FE takes care of all BE triggers */ 1957103d84a3SLiam Girdwood dev_dbg(fe->dev, "ASoC: bespoke trigger FE %s cmd start\n", 195807bf84aaSLiam Girdwood fe->dai_link->name); 195907bf84aaSLiam Girdwood 196007bf84aaSLiam Girdwood ret = soc_pcm_bespoke_trigger(substream, SNDRV_PCM_TRIGGER_START); 196107bf84aaSLiam Girdwood if (ret < 0) { 1962103d84a3SLiam Girdwood dev_err(fe->dev,"ASoC: bespoke trigger FE failed %d\n", ret); 196307bf84aaSLiam Girdwood goto hw_free; 196407bf84aaSLiam Girdwood } 196507bf84aaSLiam Girdwood } else { 1966103d84a3SLiam Girdwood dev_dbg(fe->dev, "ASoC: trigger FE %s cmd start\n", 1967618dae11SLiam Girdwood fe->dai_link->name); 1968618dae11SLiam Girdwood 1969618dae11SLiam Girdwood ret = dpcm_be_dai_trigger(fe, stream, 1970618dae11SLiam Girdwood SNDRV_PCM_TRIGGER_START); 1971618dae11SLiam Girdwood if (ret < 0) { 1972103d84a3SLiam Girdwood dev_err(fe->dev,"ASoC: trigger FE failed %d\n", ret); 1973618dae11SLiam Girdwood goto hw_free; 1974618dae11SLiam Girdwood } 197507bf84aaSLiam Girdwood } 1976618dae11SLiam Girdwood 1977618dae11SLiam Girdwood return 0; 1978618dae11SLiam Girdwood 1979618dae11SLiam Girdwood hw_free: 1980618dae11SLiam Girdwood dpcm_be_dai_hw_free(fe, stream); 1981618dae11SLiam Girdwood close: 1982618dae11SLiam Girdwood dpcm_be_dai_shutdown(fe, stream); 1983618dae11SLiam Girdwood disconnect: 1984618dae11SLiam Girdwood /* disconnect any non started BEs */ 1985618dae11SLiam Girdwood list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) { 1986618dae11SLiam Girdwood struct snd_soc_pcm_runtime *be = dpcm->be; 1987618dae11SLiam Girdwood if (be->dpcm[stream].state != SND_SOC_DPCM_STATE_START) 1988618dae11SLiam Girdwood dpcm->state = SND_SOC_DPCM_LINK_STATE_FREE; 1989618dae11SLiam Girdwood } 1990618dae11SLiam Girdwood 1991618dae11SLiam Girdwood return ret; 1992618dae11SLiam Girdwood } 1993618dae11SLiam Girdwood 1994618dae11SLiam Girdwood static int dpcm_run_new_update(struct snd_soc_pcm_runtime *fe, int stream) 1995618dae11SLiam Girdwood { 1996618dae11SLiam Girdwood int ret; 1997618dae11SLiam Girdwood 1998618dae11SLiam Girdwood fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_BE; 1999618dae11SLiam Girdwood ret = dpcm_run_update_startup(fe, stream); 2000618dae11SLiam Girdwood if (ret < 0) 2001103d84a3SLiam Girdwood dev_err(fe->dev, "ASoC: failed to startup some BEs\n"); 2002618dae11SLiam Girdwood fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO; 2003618dae11SLiam Girdwood 2004618dae11SLiam Girdwood return ret; 2005618dae11SLiam Girdwood } 2006618dae11SLiam Girdwood 2007618dae11SLiam Girdwood static int dpcm_run_old_update(struct snd_soc_pcm_runtime *fe, int stream) 2008618dae11SLiam Girdwood { 2009618dae11SLiam Girdwood int ret; 2010618dae11SLiam Girdwood 2011618dae11SLiam Girdwood fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_BE; 2012618dae11SLiam Girdwood ret = dpcm_run_update_shutdown(fe, stream); 2013618dae11SLiam Girdwood if (ret < 0) 2014103d84a3SLiam Girdwood dev_err(fe->dev, "ASoC: failed to shutdown some BEs\n"); 2015618dae11SLiam Girdwood fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO; 2016618dae11SLiam Girdwood 2017618dae11SLiam Girdwood return ret; 2018618dae11SLiam Girdwood } 2019618dae11SLiam Girdwood 2020618dae11SLiam Girdwood /* Called by DAPM mixer/mux changes to update audio routing between PCMs and 2021618dae11SLiam Girdwood * any DAI links. 2022618dae11SLiam Girdwood */ 2023c3f48ae6SLars-Peter Clausen int soc_dpcm_runtime_update(struct snd_soc_card *card) 2024618dae11SLiam Girdwood { 2025618dae11SLiam Girdwood int i, old, new, paths; 2026618dae11SLiam Girdwood 2027618dae11SLiam Girdwood mutex_lock_nested(&card->mutex, SND_SOC_CARD_CLASS_RUNTIME); 2028618dae11SLiam Girdwood for (i = 0; i < card->num_rtd; i++) { 2029618dae11SLiam Girdwood struct snd_soc_dapm_widget_list *list; 2030618dae11SLiam Girdwood struct snd_soc_pcm_runtime *fe = &card->rtd[i]; 2031618dae11SLiam Girdwood 2032618dae11SLiam Girdwood /* make sure link is FE */ 2033618dae11SLiam Girdwood if (!fe->dai_link->dynamic) 2034618dae11SLiam Girdwood continue; 2035618dae11SLiam Girdwood 2036618dae11SLiam Girdwood /* only check active links */ 2037618dae11SLiam Girdwood if (!fe->cpu_dai->active) 2038618dae11SLiam Girdwood continue; 2039618dae11SLiam Girdwood 2040618dae11SLiam Girdwood /* DAPM sync will call this to update DSP paths */ 2041103d84a3SLiam Girdwood dev_dbg(fe->dev, "ASoC: DPCM runtime update for FE %s\n", 2042618dae11SLiam Girdwood fe->dai_link->name); 2043618dae11SLiam Girdwood 2044618dae11SLiam Girdwood /* skip if FE doesn't have playback capability */ 2045618dae11SLiam Girdwood if (!fe->cpu_dai->driver->playback.channels_min) 2046618dae11SLiam Girdwood goto capture; 2047618dae11SLiam Girdwood 2048618dae11SLiam Girdwood paths = dpcm_path_get(fe, SNDRV_PCM_STREAM_PLAYBACK, &list); 2049618dae11SLiam Girdwood if (paths < 0) { 2050103d84a3SLiam Girdwood dev_warn(fe->dev, "ASoC: %s no valid %s path\n", 2051618dae11SLiam Girdwood fe->dai_link->name, "playback"); 2052618dae11SLiam Girdwood mutex_unlock(&card->mutex); 2053618dae11SLiam Girdwood return paths; 2054618dae11SLiam Girdwood } 2055618dae11SLiam Girdwood 2056618dae11SLiam Girdwood /* update any new playback paths */ 2057618dae11SLiam Girdwood new = dpcm_process_paths(fe, SNDRV_PCM_STREAM_PLAYBACK, &list, 1); 2058618dae11SLiam Girdwood if (new) { 2059618dae11SLiam Girdwood dpcm_run_new_update(fe, SNDRV_PCM_STREAM_PLAYBACK); 2060618dae11SLiam Girdwood dpcm_clear_pending_state(fe, SNDRV_PCM_STREAM_PLAYBACK); 2061618dae11SLiam Girdwood dpcm_be_disconnect(fe, SNDRV_PCM_STREAM_PLAYBACK); 2062618dae11SLiam Girdwood } 2063618dae11SLiam Girdwood 2064618dae11SLiam Girdwood /* update any old playback paths */ 2065618dae11SLiam Girdwood old = dpcm_process_paths(fe, SNDRV_PCM_STREAM_PLAYBACK, &list, 0); 2066618dae11SLiam Girdwood if (old) { 2067618dae11SLiam Girdwood dpcm_run_old_update(fe, SNDRV_PCM_STREAM_PLAYBACK); 2068618dae11SLiam Girdwood dpcm_clear_pending_state(fe, SNDRV_PCM_STREAM_PLAYBACK); 2069618dae11SLiam Girdwood dpcm_be_disconnect(fe, SNDRV_PCM_STREAM_PLAYBACK); 2070618dae11SLiam Girdwood } 2071618dae11SLiam Girdwood 2072*7ed9de76SQiao Zhou dpcm_path_put(&list); 2073618dae11SLiam Girdwood capture: 2074618dae11SLiam Girdwood /* skip if FE doesn't have capture capability */ 2075618dae11SLiam Girdwood if (!fe->cpu_dai->driver->capture.channels_min) 2076618dae11SLiam Girdwood continue; 2077618dae11SLiam Girdwood 2078618dae11SLiam Girdwood paths = dpcm_path_get(fe, SNDRV_PCM_STREAM_CAPTURE, &list); 2079618dae11SLiam Girdwood if (paths < 0) { 2080103d84a3SLiam Girdwood dev_warn(fe->dev, "ASoC: %s no valid %s path\n", 2081618dae11SLiam Girdwood fe->dai_link->name, "capture"); 2082618dae11SLiam Girdwood mutex_unlock(&card->mutex); 2083618dae11SLiam Girdwood return paths; 2084618dae11SLiam Girdwood } 2085618dae11SLiam Girdwood 2086618dae11SLiam Girdwood /* update any new capture paths */ 2087618dae11SLiam Girdwood new = dpcm_process_paths(fe, SNDRV_PCM_STREAM_CAPTURE, &list, 1); 2088618dae11SLiam Girdwood if (new) { 2089618dae11SLiam Girdwood dpcm_run_new_update(fe, SNDRV_PCM_STREAM_CAPTURE); 2090618dae11SLiam Girdwood dpcm_clear_pending_state(fe, SNDRV_PCM_STREAM_CAPTURE); 2091618dae11SLiam Girdwood dpcm_be_disconnect(fe, SNDRV_PCM_STREAM_CAPTURE); 2092618dae11SLiam Girdwood } 2093618dae11SLiam Girdwood 2094618dae11SLiam Girdwood /* update any old capture paths */ 2095618dae11SLiam Girdwood old = dpcm_process_paths(fe, SNDRV_PCM_STREAM_CAPTURE, &list, 0); 2096618dae11SLiam Girdwood if (old) { 2097618dae11SLiam Girdwood dpcm_run_old_update(fe, SNDRV_PCM_STREAM_CAPTURE); 2098618dae11SLiam Girdwood dpcm_clear_pending_state(fe, SNDRV_PCM_STREAM_CAPTURE); 2099618dae11SLiam Girdwood dpcm_be_disconnect(fe, SNDRV_PCM_STREAM_CAPTURE); 2100618dae11SLiam Girdwood } 2101618dae11SLiam Girdwood 2102618dae11SLiam Girdwood dpcm_path_put(&list); 2103618dae11SLiam Girdwood } 2104618dae11SLiam Girdwood 2105618dae11SLiam Girdwood mutex_unlock(&card->mutex); 2106618dae11SLiam Girdwood return 0; 2107618dae11SLiam Girdwood } 210801d7584cSLiam Girdwood int soc_dpcm_be_digital_mute(struct snd_soc_pcm_runtime *fe, int mute) 210901d7584cSLiam Girdwood { 211001d7584cSLiam Girdwood struct snd_soc_dpcm *dpcm; 211101d7584cSLiam Girdwood struct list_head *clients = 211201d7584cSLiam Girdwood &fe->dpcm[SNDRV_PCM_STREAM_PLAYBACK].be_clients; 211301d7584cSLiam Girdwood 211401d7584cSLiam Girdwood list_for_each_entry(dpcm, clients, list_be) { 211501d7584cSLiam Girdwood 211601d7584cSLiam Girdwood struct snd_soc_pcm_runtime *be = dpcm->be; 211701d7584cSLiam Girdwood struct snd_soc_dai *dai = be->codec_dai; 211801d7584cSLiam Girdwood struct snd_soc_dai_driver *drv = dai->driver; 211901d7584cSLiam Girdwood 212001d7584cSLiam Girdwood if (be->dai_link->ignore_suspend) 212101d7584cSLiam Girdwood continue; 212201d7584cSLiam Girdwood 2123103d84a3SLiam Girdwood dev_dbg(be->dev, "ASoC: BE digital mute %s\n", be->dai_link->name); 212401d7584cSLiam Girdwood 2125c5914b0aSMark Brown if (drv->ops && drv->ops->digital_mute && dai->playback_active) 212601d7584cSLiam Girdwood drv->ops->digital_mute(dai, mute); 212701d7584cSLiam Girdwood } 212801d7584cSLiam Girdwood 212901d7584cSLiam Girdwood return 0; 213001d7584cSLiam Girdwood } 213101d7584cSLiam Girdwood 213245c0a188SMark Brown static int dpcm_fe_dai_open(struct snd_pcm_substream *fe_substream) 213301d7584cSLiam Girdwood { 213401d7584cSLiam Girdwood struct snd_soc_pcm_runtime *fe = fe_substream->private_data; 213501d7584cSLiam Girdwood struct snd_soc_dpcm *dpcm; 213601d7584cSLiam Girdwood struct snd_soc_dapm_widget_list *list; 213701d7584cSLiam Girdwood int ret; 213801d7584cSLiam Girdwood int stream = fe_substream->stream; 213901d7584cSLiam Girdwood 214001d7584cSLiam Girdwood mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME); 214101d7584cSLiam Girdwood fe->dpcm[stream].runtime = fe_substream->runtime; 214201d7584cSLiam Girdwood 214301d7584cSLiam Girdwood if (dpcm_path_get(fe, stream, &list) <= 0) { 2144103d84a3SLiam Girdwood dev_dbg(fe->dev, "ASoC: %s no valid %s route\n", 214501d7584cSLiam Girdwood fe->dai_link->name, stream ? "capture" : "playback"); 214601d7584cSLiam Girdwood } 214701d7584cSLiam Girdwood 214801d7584cSLiam Girdwood /* calculate valid and active FE <-> BE dpcms */ 214901d7584cSLiam Girdwood dpcm_process_paths(fe, stream, &list, 1); 215001d7584cSLiam Girdwood 215101d7584cSLiam Girdwood ret = dpcm_fe_dai_startup(fe_substream); 215201d7584cSLiam Girdwood if (ret < 0) { 215301d7584cSLiam Girdwood /* clean up all links */ 215401d7584cSLiam Girdwood list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) 215501d7584cSLiam Girdwood dpcm->state = SND_SOC_DPCM_LINK_STATE_FREE; 215601d7584cSLiam Girdwood 215701d7584cSLiam Girdwood dpcm_be_disconnect(fe, stream); 215801d7584cSLiam Girdwood fe->dpcm[stream].runtime = NULL; 215901d7584cSLiam Girdwood } 216001d7584cSLiam Girdwood 216101d7584cSLiam Girdwood dpcm_clear_pending_state(fe, stream); 216201d7584cSLiam Girdwood dpcm_path_put(&list); 216301d7584cSLiam Girdwood mutex_unlock(&fe->card->mutex); 216401d7584cSLiam Girdwood return ret; 216501d7584cSLiam Girdwood } 216601d7584cSLiam Girdwood 216745c0a188SMark Brown static int dpcm_fe_dai_close(struct snd_pcm_substream *fe_substream) 216801d7584cSLiam Girdwood { 216901d7584cSLiam Girdwood struct snd_soc_pcm_runtime *fe = fe_substream->private_data; 217001d7584cSLiam Girdwood struct snd_soc_dpcm *dpcm; 217101d7584cSLiam Girdwood int stream = fe_substream->stream, ret; 217201d7584cSLiam Girdwood 217301d7584cSLiam Girdwood mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME); 217401d7584cSLiam Girdwood ret = dpcm_fe_dai_shutdown(fe_substream); 217501d7584cSLiam Girdwood 217601d7584cSLiam Girdwood /* mark FE's links ready to prune */ 217701d7584cSLiam Girdwood list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) 217801d7584cSLiam Girdwood dpcm->state = SND_SOC_DPCM_LINK_STATE_FREE; 217901d7584cSLiam Girdwood 218001d7584cSLiam Girdwood dpcm_be_disconnect(fe, stream); 218101d7584cSLiam Girdwood 218201d7584cSLiam Girdwood fe->dpcm[stream].runtime = NULL; 218301d7584cSLiam Girdwood mutex_unlock(&fe->card->mutex); 218401d7584cSLiam Girdwood return ret; 218501d7584cSLiam Girdwood } 218601d7584cSLiam Girdwood 2187ddee627cSLiam Girdwood /* create a new pcm */ 2188ddee627cSLiam Girdwood int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num) 2189ddee627cSLiam Girdwood { 2190ddee627cSLiam Girdwood struct snd_soc_platform *platform = rtd->platform; 2191ddee627cSLiam Girdwood struct snd_soc_dai *codec_dai = rtd->codec_dai; 2192ddee627cSLiam Girdwood struct snd_soc_dai *cpu_dai = rtd->cpu_dai; 2193ddee627cSLiam Girdwood struct snd_pcm *pcm; 2194ddee627cSLiam Girdwood char new_name[64]; 2195ddee627cSLiam Girdwood int ret = 0, playback = 0, capture = 0; 2196ddee627cSLiam Girdwood 219701d7584cSLiam Girdwood if (rtd->dai_link->dynamic || rtd->dai_link->no_pcm) { 21981e9de42fSLiam Girdwood playback = rtd->dai_link->dpcm_playback; 21991e9de42fSLiam Girdwood capture = rtd->dai_link->dpcm_capture; 220001d7584cSLiam Girdwood } else { 220105679092SMark Brown if (codec_dai->driver->playback.channels_min && 220205679092SMark Brown cpu_dai->driver->playback.channels_min) 2203ddee627cSLiam Girdwood playback = 1; 220405679092SMark Brown if (codec_dai->driver->capture.channels_min && 220505679092SMark Brown cpu_dai->driver->capture.channels_min) 2206ddee627cSLiam Girdwood capture = 1; 220701d7584cSLiam Girdwood } 2208ddee627cSLiam Girdwood 2209d6bead02SFabio Estevam if (rtd->dai_link->playback_only) { 2210d6bead02SFabio Estevam playback = 1; 2211d6bead02SFabio Estevam capture = 0; 2212d6bead02SFabio Estevam } 2213d6bead02SFabio Estevam 2214d6bead02SFabio Estevam if (rtd->dai_link->capture_only) { 2215d6bead02SFabio Estevam playback = 0; 2216d6bead02SFabio Estevam capture = 1; 2217d6bead02SFabio Estevam } 2218d6bead02SFabio Estevam 221901d7584cSLiam Girdwood /* create the PCM */ 222001d7584cSLiam Girdwood if (rtd->dai_link->no_pcm) { 222101d7584cSLiam Girdwood snprintf(new_name, sizeof(new_name), "(%s)", 222201d7584cSLiam Girdwood rtd->dai_link->stream_name); 222301d7584cSLiam Girdwood 222401d7584cSLiam Girdwood ret = snd_pcm_new_internal(rtd->card->snd_card, new_name, num, 222501d7584cSLiam Girdwood playback, capture, &pcm); 222601d7584cSLiam Girdwood } else { 222701d7584cSLiam Girdwood if (rtd->dai_link->dynamic) 222801d7584cSLiam Girdwood snprintf(new_name, sizeof(new_name), "%s (*)", 222901d7584cSLiam Girdwood rtd->dai_link->stream_name); 223001d7584cSLiam Girdwood else 223101d7584cSLiam Girdwood snprintf(new_name, sizeof(new_name), "%s %s-%d", 223201d7584cSLiam Girdwood rtd->dai_link->stream_name, codec_dai->name, num); 223301d7584cSLiam Girdwood 223401d7584cSLiam Girdwood ret = snd_pcm_new(rtd->card->snd_card, new_name, num, playback, 223501d7584cSLiam Girdwood capture, &pcm); 223601d7584cSLiam Girdwood } 2237ddee627cSLiam Girdwood if (ret < 0) { 2238103d84a3SLiam Girdwood dev_err(rtd->card->dev, "ASoC: can't create pcm for %s\n", 22395cb9b748SLiam Girdwood rtd->dai_link->name); 2240ddee627cSLiam Girdwood return ret; 2241ddee627cSLiam Girdwood } 2242103d84a3SLiam Girdwood dev_dbg(rtd->card->dev, "ASoC: registered pcm #%d %s\n",num, new_name); 2243ddee627cSLiam Girdwood 2244ddee627cSLiam Girdwood /* DAPM dai link stream work */ 2245ddee627cSLiam Girdwood INIT_DELAYED_WORK(&rtd->delayed_work, close_delayed_work); 2246ddee627cSLiam Girdwood 2247ddee627cSLiam Girdwood rtd->pcm = pcm; 2248ddee627cSLiam Girdwood pcm->private_data = rtd; 224901d7584cSLiam Girdwood 225001d7584cSLiam Girdwood if (rtd->dai_link->no_pcm) { 225101d7584cSLiam Girdwood if (playback) 225201d7584cSLiam Girdwood pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream->private_data = rtd; 225301d7584cSLiam Girdwood if (capture) 225401d7584cSLiam Girdwood pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream->private_data = rtd; 225501d7584cSLiam Girdwood goto out; 225601d7584cSLiam Girdwood } 225701d7584cSLiam Girdwood 225801d7584cSLiam Girdwood /* ASoC PCM operations */ 225901d7584cSLiam Girdwood if (rtd->dai_link->dynamic) { 226001d7584cSLiam Girdwood rtd->ops.open = dpcm_fe_dai_open; 226101d7584cSLiam Girdwood rtd->ops.hw_params = dpcm_fe_dai_hw_params; 226201d7584cSLiam Girdwood rtd->ops.prepare = dpcm_fe_dai_prepare; 226301d7584cSLiam Girdwood rtd->ops.trigger = dpcm_fe_dai_trigger; 226401d7584cSLiam Girdwood rtd->ops.hw_free = dpcm_fe_dai_hw_free; 226501d7584cSLiam Girdwood rtd->ops.close = dpcm_fe_dai_close; 226601d7584cSLiam Girdwood rtd->ops.pointer = soc_pcm_pointer; 2267be3f3f2cSLiam Girdwood rtd->ops.ioctl = soc_pcm_ioctl; 226801d7584cSLiam Girdwood } else { 226901d7584cSLiam Girdwood rtd->ops.open = soc_pcm_open; 227001d7584cSLiam Girdwood rtd->ops.hw_params = soc_pcm_hw_params; 227101d7584cSLiam Girdwood rtd->ops.prepare = soc_pcm_prepare; 227201d7584cSLiam Girdwood rtd->ops.trigger = soc_pcm_trigger; 227301d7584cSLiam Girdwood rtd->ops.hw_free = soc_pcm_hw_free; 227401d7584cSLiam Girdwood rtd->ops.close = soc_pcm_close; 227501d7584cSLiam Girdwood rtd->ops.pointer = soc_pcm_pointer; 2276be3f3f2cSLiam Girdwood rtd->ops.ioctl = soc_pcm_ioctl; 227701d7584cSLiam Girdwood } 227801d7584cSLiam Girdwood 2279ddee627cSLiam Girdwood if (platform->driver->ops) { 228001d7584cSLiam Girdwood rtd->ops.ack = platform->driver->ops->ack; 228101d7584cSLiam Girdwood rtd->ops.copy = platform->driver->ops->copy; 228201d7584cSLiam Girdwood rtd->ops.silence = platform->driver->ops->silence; 228301d7584cSLiam Girdwood rtd->ops.page = platform->driver->ops->page; 228401d7584cSLiam Girdwood rtd->ops.mmap = platform->driver->ops->mmap; 2285ddee627cSLiam Girdwood } 2286ddee627cSLiam Girdwood 2287ddee627cSLiam Girdwood if (playback) 228801d7584cSLiam Girdwood snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &rtd->ops); 2289ddee627cSLiam Girdwood 2290ddee627cSLiam Girdwood if (capture) 229101d7584cSLiam Girdwood snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &rtd->ops); 2292ddee627cSLiam Girdwood 2293ddee627cSLiam Girdwood if (platform->driver->pcm_new) { 2294ddee627cSLiam Girdwood ret = platform->driver->pcm_new(rtd); 2295ddee627cSLiam Girdwood if (ret < 0) { 2296b1bc7b3cSMark Brown dev_err(platform->dev, 2297b1bc7b3cSMark Brown "ASoC: pcm constructor failed: %d\n", 2298b1bc7b3cSMark Brown ret); 2299ddee627cSLiam Girdwood return ret; 2300ddee627cSLiam Girdwood } 2301ddee627cSLiam Girdwood } 2302ddee627cSLiam Girdwood 2303ddee627cSLiam Girdwood pcm->private_free = platform->driver->pcm_free; 230401d7584cSLiam Girdwood out: 23055cb9b748SLiam Girdwood dev_info(rtd->card->dev, "%s <-> %s mapping ok\n", codec_dai->name, 2306ddee627cSLiam Girdwood cpu_dai->name); 2307ddee627cSLiam Girdwood return ret; 2308ddee627cSLiam Girdwood } 230901d7584cSLiam Girdwood 231001d7584cSLiam Girdwood /* is the current PCM operation for this FE ? */ 231101d7584cSLiam Girdwood int snd_soc_dpcm_fe_can_update(struct snd_soc_pcm_runtime *fe, int stream) 231201d7584cSLiam Girdwood { 231301d7584cSLiam Girdwood if (fe->dpcm[stream].runtime_update == SND_SOC_DPCM_UPDATE_FE) 231401d7584cSLiam Girdwood return 1; 231501d7584cSLiam Girdwood return 0; 231601d7584cSLiam Girdwood } 231701d7584cSLiam Girdwood EXPORT_SYMBOL_GPL(snd_soc_dpcm_fe_can_update); 231801d7584cSLiam Girdwood 231901d7584cSLiam Girdwood /* is the current PCM operation for this BE ? */ 232001d7584cSLiam Girdwood int snd_soc_dpcm_be_can_update(struct snd_soc_pcm_runtime *fe, 232101d7584cSLiam Girdwood struct snd_soc_pcm_runtime *be, int stream) 232201d7584cSLiam Girdwood { 232301d7584cSLiam Girdwood if ((fe->dpcm[stream].runtime_update == SND_SOC_DPCM_UPDATE_FE) || 232401d7584cSLiam Girdwood ((fe->dpcm[stream].runtime_update == SND_SOC_DPCM_UPDATE_BE) && 232501d7584cSLiam Girdwood be->dpcm[stream].runtime_update)) 232601d7584cSLiam Girdwood return 1; 232701d7584cSLiam Girdwood return 0; 232801d7584cSLiam Girdwood } 232901d7584cSLiam Girdwood EXPORT_SYMBOL_GPL(snd_soc_dpcm_be_can_update); 233001d7584cSLiam Girdwood 233101d7584cSLiam Girdwood /* get the substream for this BE */ 233201d7584cSLiam Girdwood struct snd_pcm_substream * 233301d7584cSLiam Girdwood snd_soc_dpcm_get_substream(struct snd_soc_pcm_runtime *be, int stream) 233401d7584cSLiam Girdwood { 233501d7584cSLiam Girdwood return be->pcm->streams[stream].substream; 233601d7584cSLiam Girdwood } 233701d7584cSLiam Girdwood EXPORT_SYMBOL_GPL(snd_soc_dpcm_get_substream); 233801d7584cSLiam Girdwood 233901d7584cSLiam Girdwood /* get the BE runtime state */ 234001d7584cSLiam Girdwood enum snd_soc_dpcm_state 234101d7584cSLiam Girdwood snd_soc_dpcm_be_get_state(struct snd_soc_pcm_runtime *be, int stream) 234201d7584cSLiam Girdwood { 234301d7584cSLiam Girdwood return be->dpcm[stream].state; 234401d7584cSLiam Girdwood } 234501d7584cSLiam Girdwood EXPORT_SYMBOL_GPL(snd_soc_dpcm_be_get_state); 234601d7584cSLiam Girdwood 234701d7584cSLiam Girdwood /* set the BE runtime state */ 234801d7584cSLiam Girdwood void snd_soc_dpcm_be_set_state(struct snd_soc_pcm_runtime *be, 234901d7584cSLiam Girdwood int stream, enum snd_soc_dpcm_state state) 235001d7584cSLiam Girdwood { 235101d7584cSLiam Girdwood be->dpcm[stream].state = state; 235201d7584cSLiam Girdwood } 235301d7584cSLiam Girdwood EXPORT_SYMBOL_GPL(snd_soc_dpcm_be_set_state); 235401d7584cSLiam Girdwood 235501d7584cSLiam Girdwood /* 235601d7584cSLiam Girdwood * We can only hw_free, stop, pause or suspend a BE DAI if any of it's FE 235701d7584cSLiam Girdwood * are not running, paused or suspended for the specified stream direction. 235801d7584cSLiam Girdwood */ 235901d7584cSLiam Girdwood int snd_soc_dpcm_can_be_free_stop(struct snd_soc_pcm_runtime *fe, 236001d7584cSLiam Girdwood struct snd_soc_pcm_runtime *be, int stream) 236101d7584cSLiam Girdwood { 236201d7584cSLiam Girdwood struct snd_soc_dpcm *dpcm; 236301d7584cSLiam Girdwood int state; 236401d7584cSLiam Girdwood 236501d7584cSLiam Girdwood list_for_each_entry(dpcm, &be->dpcm[stream].fe_clients, list_fe) { 236601d7584cSLiam Girdwood 236701d7584cSLiam Girdwood if (dpcm->fe == fe) 236801d7584cSLiam Girdwood continue; 236901d7584cSLiam Girdwood 237001d7584cSLiam Girdwood state = dpcm->fe->dpcm[stream].state; 237101d7584cSLiam Girdwood if (state == SND_SOC_DPCM_STATE_START || 237201d7584cSLiam Girdwood state == SND_SOC_DPCM_STATE_PAUSED || 237301d7584cSLiam Girdwood state == SND_SOC_DPCM_STATE_SUSPEND) 237401d7584cSLiam Girdwood return 0; 237501d7584cSLiam Girdwood } 237601d7584cSLiam Girdwood 237701d7584cSLiam Girdwood /* it's safe to free/stop this BE DAI */ 237801d7584cSLiam Girdwood return 1; 237901d7584cSLiam Girdwood } 238001d7584cSLiam Girdwood EXPORT_SYMBOL_GPL(snd_soc_dpcm_can_be_free_stop); 238101d7584cSLiam Girdwood 238201d7584cSLiam Girdwood /* 238301d7584cSLiam Girdwood * We can only change hw params a BE DAI if any of it's FE are not prepared, 238401d7584cSLiam Girdwood * running, paused or suspended for the specified stream direction. 238501d7584cSLiam Girdwood */ 238601d7584cSLiam Girdwood int snd_soc_dpcm_can_be_params(struct snd_soc_pcm_runtime *fe, 238701d7584cSLiam Girdwood struct snd_soc_pcm_runtime *be, int stream) 238801d7584cSLiam Girdwood { 238901d7584cSLiam Girdwood struct snd_soc_dpcm *dpcm; 239001d7584cSLiam Girdwood int state; 239101d7584cSLiam Girdwood 239201d7584cSLiam Girdwood list_for_each_entry(dpcm, &be->dpcm[stream].fe_clients, list_fe) { 239301d7584cSLiam Girdwood 239401d7584cSLiam Girdwood if (dpcm->fe == fe) 239501d7584cSLiam Girdwood continue; 239601d7584cSLiam Girdwood 239701d7584cSLiam Girdwood state = dpcm->fe->dpcm[stream].state; 239801d7584cSLiam Girdwood if (state == SND_SOC_DPCM_STATE_START || 239901d7584cSLiam Girdwood state == SND_SOC_DPCM_STATE_PAUSED || 240001d7584cSLiam Girdwood state == SND_SOC_DPCM_STATE_SUSPEND || 240101d7584cSLiam Girdwood state == SND_SOC_DPCM_STATE_PREPARE) 240201d7584cSLiam Girdwood return 0; 240301d7584cSLiam Girdwood } 240401d7584cSLiam Girdwood 240501d7584cSLiam Girdwood /* it's safe to change hw_params */ 240601d7584cSLiam Girdwood return 1; 240701d7584cSLiam Girdwood } 240801d7584cSLiam Girdwood EXPORT_SYMBOL_GPL(snd_soc_dpcm_can_be_params); 2409f86dcef8SLiam Girdwood 241007bf84aaSLiam Girdwood int snd_soc_platform_trigger(struct snd_pcm_substream *substream, 241107bf84aaSLiam Girdwood int cmd, struct snd_soc_platform *platform) 241207bf84aaSLiam Girdwood { 2413c5914b0aSMark Brown if (platform->driver->ops && platform->driver->ops->trigger) 241407bf84aaSLiam Girdwood return platform->driver->ops->trigger(substream, cmd); 241507bf84aaSLiam Girdwood return 0; 241607bf84aaSLiam Girdwood } 241707bf84aaSLiam Girdwood EXPORT_SYMBOL_GPL(snd_soc_platform_trigger); 241807bf84aaSLiam Girdwood 2419f86dcef8SLiam Girdwood #ifdef CONFIG_DEBUG_FS 2420f86dcef8SLiam Girdwood static char *dpcm_state_string(enum snd_soc_dpcm_state state) 2421f86dcef8SLiam Girdwood { 2422f86dcef8SLiam Girdwood switch (state) { 2423f86dcef8SLiam Girdwood case SND_SOC_DPCM_STATE_NEW: 2424f86dcef8SLiam Girdwood return "new"; 2425f86dcef8SLiam Girdwood case SND_SOC_DPCM_STATE_OPEN: 2426f86dcef8SLiam Girdwood return "open"; 2427f86dcef8SLiam Girdwood case SND_SOC_DPCM_STATE_HW_PARAMS: 2428f86dcef8SLiam Girdwood return "hw_params"; 2429f86dcef8SLiam Girdwood case SND_SOC_DPCM_STATE_PREPARE: 2430f86dcef8SLiam Girdwood return "prepare"; 2431f86dcef8SLiam Girdwood case SND_SOC_DPCM_STATE_START: 2432f86dcef8SLiam Girdwood return "start"; 2433f86dcef8SLiam Girdwood case SND_SOC_DPCM_STATE_STOP: 2434f86dcef8SLiam Girdwood return "stop"; 2435f86dcef8SLiam Girdwood case SND_SOC_DPCM_STATE_SUSPEND: 2436f86dcef8SLiam Girdwood return "suspend"; 2437f86dcef8SLiam Girdwood case SND_SOC_DPCM_STATE_PAUSED: 2438f86dcef8SLiam Girdwood return "paused"; 2439f86dcef8SLiam Girdwood case SND_SOC_DPCM_STATE_HW_FREE: 2440f86dcef8SLiam Girdwood return "hw_free"; 2441f86dcef8SLiam Girdwood case SND_SOC_DPCM_STATE_CLOSE: 2442f86dcef8SLiam Girdwood return "close"; 2443f86dcef8SLiam Girdwood } 2444f86dcef8SLiam Girdwood 2445f86dcef8SLiam Girdwood return "unknown"; 2446f86dcef8SLiam Girdwood } 2447f86dcef8SLiam Girdwood 2448f86dcef8SLiam Girdwood static ssize_t dpcm_show_state(struct snd_soc_pcm_runtime *fe, 2449f86dcef8SLiam Girdwood int stream, char *buf, size_t size) 2450f86dcef8SLiam Girdwood { 2451f86dcef8SLiam Girdwood struct snd_pcm_hw_params *params = &fe->dpcm[stream].hw_params; 2452f86dcef8SLiam Girdwood struct snd_soc_dpcm *dpcm; 2453f86dcef8SLiam Girdwood ssize_t offset = 0; 2454f86dcef8SLiam Girdwood 2455f86dcef8SLiam Girdwood /* FE state */ 2456f86dcef8SLiam Girdwood offset += snprintf(buf + offset, size - offset, 2457f86dcef8SLiam Girdwood "[%s - %s]\n", fe->dai_link->name, 2458f86dcef8SLiam Girdwood stream ? "Capture" : "Playback"); 2459f86dcef8SLiam Girdwood 2460f86dcef8SLiam Girdwood offset += snprintf(buf + offset, size - offset, "State: %s\n", 2461f86dcef8SLiam Girdwood dpcm_state_string(fe->dpcm[stream].state)); 2462f86dcef8SLiam Girdwood 2463f86dcef8SLiam Girdwood if ((fe->dpcm[stream].state >= SND_SOC_DPCM_STATE_HW_PARAMS) && 2464f86dcef8SLiam Girdwood (fe->dpcm[stream].state <= SND_SOC_DPCM_STATE_STOP)) 2465f86dcef8SLiam Girdwood offset += snprintf(buf + offset, size - offset, 2466f86dcef8SLiam Girdwood "Hardware Params: " 2467f86dcef8SLiam Girdwood "Format = %s, Channels = %d, Rate = %d\n", 2468f86dcef8SLiam Girdwood snd_pcm_format_name(params_format(params)), 2469f86dcef8SLiam Girdwood params_channels(params), 2470f86dcef8SLiam Girdwood params_rate(params)); 2471f86dcef8SLiam Girdwood 2472f86dcef8SLiam Girdwood /* BEs state */ 2473f86dcef8SLiam Girdwood offset += snprintf(buf + offset, size - offset, "Backends:\n"); 2474f86dcef8SLiam Girdwood 2475f86dcef8SLiam Girdwood if (list_empty(&fe->dpcm[stream].be_clients)) { 2476f86dcef8SLiam Girdwood offset += snprintf(buf + offset, size - offset, 2477f86dcef8SLiam Girdwood " No active DSP links\n"); 2478f86dcef8SLiam Girdwood goto out; 2479f86dcef8SLiam Girdwood } 2480f86dcef8SLiam Girdwood 2481f86dcef8SLiam Girdwood list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) { 2482f86dcef8SLiam Girdwood struct snd_soc_pcm_runtime *be = dpcm->be; 2483f86dcef8SLiam Girdwood params = &dpcm->hw_params; 2484f86dcef8SLiam Girdwood 2485f86dcef8SLiam Girdwood offset += snprintf(buf + offset, size - offset, 2486f86dcef8SLiam Girdwood "- %s\n", be->dai_link->name); 2487f86dcef8SLiam Girdwood 2488f86dcef8SLiam Girdwood offset += snprintf(buf + offset, size - offset, 2489f86dcef8SLiam Girdwood " State: %s\n", 2490f86dcef8SLiam Girdwood dpcm_state_string(be->dpcm[stream].state)); 2491f86dcef8SLiam Girdwood 2492f86dcef8SLiam Girdwood if ((be->dpcm[stream].state >= SND_SOC_DPCM_STATE_HW_PARAMS) && 2493f86dcef8SLiam Girdwood (be->dpcm[stream].state <= SND_SOC_DPCM_STATE_STOP)) 2494f86dcef8SLiam Girdwood offset += snprintf(buf + offset, size - offset, 2495f86dcef8SLiam Girdwood " Hardware Params: " 2496f86dcef8SLiam Girdwood "Format = %s, Channels = %d, Rate = %d\n", 2497f86dcef8SLiam Girdwood snd_pcm_format_name(params_format(params)), 2498f86dcef8SLiam Girdwood params_channels(params), 2499f86dcef8SLiam Girdwood params_rate(params)); 2500f86dcef8SLiam Girdwood } 2501f86dcef8SLiam Girdwood 2502f86dcef8SLiam Girdwood out: 2503f86dcef8SLiam Girdwood return offset; 2504f86dcef8SLiam Girdwood } 2505f86dcef8SLiam Girdwood 2506f86dcef8SLiam Girdwood static ssize_t dpcm_state_read_file(struct file *file, char __user *user_buf, 2507f86dcef8SLiam Girdwood size_t count, loff_t *ppos) 2508f86dcef8SLiam Girdwood { 2509f86dcef8SLiam Girdwood struct snd_soc_pcm_runtime *fe = file->private_data; 2510f86dcef8SLiam Girdwood ssize_t out_count = PAGE_SIZE, offset = 0, ret = 0; 2511f86dcef8SLiam Girdwood char *buf; 2512f86dcef8SLiam Girdwood 2513f86dcef8SLiam Girdwood buf = kmalloc(out_count, GFP_KERNEL); 2514f86dcef8SLiam Girdwood if (!buf) 2515f86dcef8SLiam Girdwood return -ENOMEM; 2516f86dcef8SLiam Girdwood 2517f86dcef8SLiam Girdwood if (fe->cpu_dai->driver->playback.channels_min) 2518f86dcef8SLiam Girdwood offset += dpcm_show_state(fe, SNDRV_PCM_STREAM_PLAYBACK, 2519f86dcef8SLiam Girdwood buf + offset, out_count - offset); 2520f86dcef8SLiam Girdwood 2521f86dcef8SLiam Girdwood if (fe->cpu_dai->driver->capture.channels_min) 2522f86dcef8SLiam Girdwood offset += dpcm_show_state(fe, SNDRV_PCM_STREAM_CAPTURE, 2523f86dcef8SLiam Girdwood buf + offset, out_count - offset); 2524f86dcef8SLiam Girdwood 2525f86dcef8SLiam Girdwood ret = simple_read_from_buffer(user_buf, count, ppos, buf, offset); 2526f86dcef8SLiam Girdwood 2527f86dcef8SLiam Girdwood kfree(buf); 2528f86dcef8SLiam Girdwood return ret; 2529f86dcef8SLiam Girdwood } 2530f86dcef8SLiam Girdwood 2531f86dcef8SLiam Girdwood static const struct file_operations dpcm_state_fops = { 2532f57b8488SLiam Girdwood .open = simple_open, 2533f86dcef8SLiam Girdwood .read = dpcm_state_read_file, 2534f86dcef8SLiam Girdwood .llseek = default_llseek, 2535f86dcef8SLiam Girdwood }; 2536f86dcef8SLiam Girdwood 2537f86dcef8SLiam Girdwood int soc_dpcm_debugfs_add(struct snd_soc_pcm_runtime *rtd) 2538f86dcef8SLiam Girdwood { 2539b3bba9a1SMark Brown if (!rtd->dai_link) 2540b3bba9a1SMark Brown return 0; 2541b3bba9a1SMark Brown 2542f86dcef8SLiam Girdwood rtd->debugfs_dpcm_root = debugfs_create_dir(rtd->dai_link->name, 2543f86dcef8SLiam Girdwood rtd->card->debugfs_card_root); 2544f86dcef8SLiam Girdwood if (!rtd->debugfs_dpcm_root) { 2545f86dcef8SLiam Girdwood dev_dbg(rtd->dev, 2546f86dcef8SLiam Girdwood "ASoC: Failed to create dpcm debugfs directory %s\n", 2547f86dcef8SLiam Girdwood rtd->dai_link->name); 2548f86dcef8SLiam Girdwood return -EINVAL; 2549f86dcef8SLiam Girdwood } 2550f86dcef8SLiam Girdwood 2551f57b8488SLiam Girdwood rtd->debugfs_dpcm_state = debugfs_create_file("state", 0444, 2552f86dcef8SLiam Girdwood rtd->debugfs_dpcm_root, 2553f86dcef8SLiam Girdwood rtd, &dpcm_state_fops); 2554f86dcef8SLiam Girdwood 2555f86dcef8SLiam Girdwood return 0; 2556f86dcef8SLiam Girdwood } 2557f86dcef8SLiam Girdwood #endif 2558