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 /** 3890996f43SLars-Peter Clausen * snd_soc_set_runtime_hwparams - set the runtime hardware parameters 3990996f43SLars-Peter Clausen * @substream: the pcm substream 4090996f43SLars-Peter Clausen * @hw: the hardware parameters 4190996f43SLars-Peter Clausen * 4290996f43SLars-Peter Clausen * Sets the substream runtime hardware parameters. 4390996f43SLars-Peter Clausen */ 4490996f43SLars-Peter Clausen int snd_soc_set_runtime_hwparams(struct snd_pcm_substream *substream, 4590996f43SLars-Peter Clausen const struct snd_pcm_hardware *hw) 4690996f43SLars-Peter Clausen { 4790996f43SLars-Peter Clausen struct snd_pcm_runtime *runtime = substream->runtime; 4890996f43SLars-Peter Clausen runtime->hw.info = hw->info; 4990996f43SLars-Peter Clausen runtime->hw.formats = hw->formats; 5090996f43SLars-Peter Clausen runtime->hw.period_bytes_min = hw->period_bytes_min; 5190996f43SLars-Peter Clausen runtime->hw.period_bytes_max = hw->period_bytes_max; 5290996f43SLars-Peter Clausen runtime->hw.periods_min = hw->periods_min; 5390996f43SLars-Peter Clausen runtime->hw.periods_max = hw->periods_max; 5490996f43SLars-Peter Clausen runtime->hw.buffer_bytes_max = hw->buffer_bytes_max; 5590996f43SLars-Peter Clausen runtime->hw.fifo_size = hw->fifo_size; 5690996f43SLars-Peter Clausen return 0; 5790996f43SLars-Peter Clausen } 5890996f43SLars-Peter Clausen EXPORT_SYMBOL_GPL(snd_soc_set_runtime_hwparams); 5990996f43SLars-Peter Clausen 6001d7584cSLiam Girdwood /* DPCM stream event, send event to FE and all active BEs. */ 6101d7584cSLiam Girdwood static int dpcm_dapm_stream_event(struct snd_soc_pcm_runtime *fe, int dir, 6201d7584cSLiam Girdwood int event) 6301d7584cSLiam Girdwood { 6401d7584cSLiam Girdwood struct snd_soc_dpcm *dpcm; 6501d7584cSLiam Girdwood 6601d7584cSLiam Girdwood list_for_each_entry(dpcm, &fe->dpcm[dir].be_clients, list_be) { 6701d7584cSLiam Girdwood 6801d7584cSLiam Girdwood struct snd_soc_pcm_runtime *be = dpcm->be; 6901d7584cSLiam Girdwood 70103d84a3SLiam Girdwood dev_dbg(be->dev, "ASoC: BE %s event %d dir %d\n", 7101d7584cSLiam Girdwood be->dai_link->name, event, dir); 7201d7584cSLiam Girdwood 7301d7584cSLiam Girdwood snd_soc_dapm_stream_event(be, dir, event); 7401d7584cSLiam Girdwood } 7501d7584cSLiam Girdwood 7601d7584cSLiam Girdwood snd_soc_dapm_stream_event(fe, dir, event); 7701d7584cSLiam Girdwood 7801d7584cSLiam Girdwood return 0; 7901d7584cSLiam Girdwood } 8001d7584cSLiam Girdwood 8117841020SDong Aisheng static int soc_pcm_apply_symmetry(struct snd_pcm_substream *substream, 8217841020SDong Aisheng struct snd_soc_dai *soc_dai) 83ddee627cSLiam Girdwood { 84ddee627cSLiam Girdwood struct snd_soc_pcm_runtime *rtd = substream->private_data; 85ddee627cSLiam Girdwood int ret; 86ddee627cSLiam Girdwood 87*3635bf09SNicolin Chen if (soc_dai->rate && (soc_dai->driver->symmetric_rates || 88*3635bf09SNicolin Chen rtd->dai_link->symmetric_rates)) { 89*3635bf09SNicolin Chen dev_dbg(soc_dai->dev, "ASoC: Symmetry forces %dHz rate\n", 90*3635bf09SNicolin Chen soc_dai->rate); 91ddee627cSLiam Girdwood 92ddee627cSLiam Girdwood ret = snd_pcm_hw_constraint_minmax(substream->runtime, 93ddee627cSLiam Girdwood SNDRV_PCM_HW_PARAM_RATE, 9417841020SDong Aisheng soc_dai->rate, soc_dai->rate); 95ddee627cSLiam Girdwood if (ret < 0) { 9617841020SDong Aisheng dev_err(soc_dai->dev, 97*3635bf09SNicolin Chen "ASoC: Unable to apply rate constraint: %d\n", 98103d84a3SLiam Girdwood ret); 99ddee627cSLiam Girdwood return ret; 100ddee627cSLiam Girdwood } 101*3635bf09SNicolin Chen } 102*3635bf09SNicolin Chen 103*3635bf09SNicolin Chen if (soc_dai->channels && (soc_dai->driver->symmetric_channels || 104*3635bf09SNicolin Chen rtd->dai_link->symmetric_channels)) { 105*3635bf09SNicolin Chen dev_dbg(soc_dai->dev, "ASoC: Symmetry forces %d channel(s)\n", 106*3635bf09SNicolin Chen soc_dai->channels); 107*3635bf09SNicolin Chen 108*3635bf09SNicolin Chen ret = snd_pcm_hw_constraint_minmax(substream->runtime, 109*3635bf09SNicolin Chen SNDRV_PCM_HW_PARAM_CHANNELS, 110*3635bf09SNicolin Chen soc_dai->channels, 111*3635bf09SNicolin Chen soc_dai->channels); 112*3635bf09SNicolin Chen if (ret < 0) { 113*3635bf09SNicolin Chen dev_err(soc_dai->dev, 114*3635bf09SNicolin Chen "ASoC: Unable to apply channel symmetry constraint: %d\n", 115*3635bf09SNicolin Chen ret); 116*3635bf09SNicolin Chen return ret; 117*3635bf09SNicolin Chen } 118*3635bf09SNicolin Chen } 119*3635bf09SNicolin Chen 120*3635bf09SNicolin Chen if (soc_dai->sample_bits && (soc_dai->driver->symmetric_samplebits || 121*3635bf09SNicolin Chen rtd->dai_link->symmetric_samplebits)) { 122*3635bf09SNicolin Chen dev_dbg(soc_dai->dev, "ASoC: Symmetry forces %d sample bits\n", 123*3635bf09SNicolin Chen soc_dai->sample_bits); 124*3635bf09SNicolin Chen 125*3635bf09SNicolin Chen ret = snd_pcm_hw_constraint_minmax(substream->runtime, 126*3635bf09SNicolin Chen SNDRV_PCM_HW_PARAM_SAMPLE_BITS, 127*3635bf09SNicolin Chen soc_dai->sample_bits, 128*3635bf09SNicolin Chen soc_dai->sample_bits); 129*3635bf09SNicolin Chen if (ret < 0) { 130*3635bf09SNicolin Chen dev_err(soc_dai->dev, 131*3635bf09SNicolin Chen "ASoC: Unable to apply sample bits symmetry constraint: %d\n", 132*3635bf09SNicolin Chen ret); 133*3635bf09SNicolin Chen return ret; 134*3635bf09SNicolin Chen } 135*3635bf09SNicolin Chen } 136*3635bf09SNicolin Chen 137*3635bf09SNicolin Chen return 0; 138*3635bf09SNicolin Chen } 139*3635bf09SNicolin Chen 140*3635bf09SNicolin Chen static int soc_pcm_params_symmetry(struct snd_pcm_substream *substream, 141*3635bf09SNicolin Chen struct snd_pcm_hw_params *params) 142*3635bf09SNicolin Chen { 143*3635bf09SNicolin Chen struct snd_soc_pcm_runtime *rtd = substream->private_data; 144*3635bf09SNicolin Chen struct snd_soc_dai *cpu_dai = rtd->cpu_dai; 145*3635bf09SNicolin Chen struct snd_soc_dai *codec_dai = rtd->codec_dai; 146*3635bf09SNicolin Chen unsigned int rate, channels, sample_bits, symmetry; 147*3635bf09SNicolin Chen 148*3635bf09SNicolin Chen rate = params_rate(params); 149*3635bf09SNicolin Chen channels = params_channels(params); 150*3635bf09SNicolin Chen sample_bits = snd_pcm_format_physical_width(params_format(params)); 151*3635bf09SNicolin Chen 152*3635bf09SNicolin Chen /* reject unmatched parameters when applying symmetry */ 153*3635bf09SNicolin Chen symmetry = cpu_dai->driver->symmetric_rates || 154*3635bf09SNicolin Chen codec_dai->driver->symmetric_rates || 155*3635bf09SNicolin Chen rtd->dai_link->symmetric_rates; 156*3635bf09SNicolin Chen if (symmetry && cpu_dai->rate && cpu_dai->rate != rate) { 157*3635bf09SNicolin Chen dev_err(rtd->dev, "ASoC: unmatched rate symmetry: %d - %d\n", 158*3635bf09SNicolin Chen cpu_dai->rate, rate); 159*3635bf09SNicolin Chen return -EINVAL; 160*3635bf09SNicolin Chen } 161*3635bf09SNicolin Chen 162*3635bf09SNicolin Chen symmetry = cpu_dai->driver->symmetric_channels || 163*3635bf09SNicolin Chen codec_dai->driver->symmetric_channels || 164*3635bf09SNicolin Chen rtd->dai_link->symmetric_channels; 165*3635bf09SNicolin Chen if (symmetry && cpu_dai->channels && cpu_dai->channels != channels) { 166*3635bf09SNicolin Chen dev_err(rtd->dev, "ASoC: unmatched channel symmetry: %d - %d\n", 167*3635bf09SNicolin Chen cpu_dai->channels, channels); 168*3635bf09SNicolin Chen return -EINVAL; 169*3635bf09SNicolin Chen } 170*3635bf09SNicolin Chen 171*3635bf09SNicolin Chen symmetry = cpu_dai->driver->symmetric_samplebits || 172*3635bf09SNicolin Chen codec_dai->driver->symmetric_samplebits || 173*3635bf09SNicolin Chen rtd->dai_link->symmetric_samplebits; 174*3635bf09SNicolin Chen if (symmetry && cpu_dai->sample_bits && cpu_dai->sample_bits != sample_bits) { 175*3635bf09SNicolin Chen dev_err(rtd->dev, "ASoC: unmatched sample bits symmetry: %d - %d\n", 176*3635bf09SNicolin Chen cpu_dai->sample_bits, sample_bits); 177*3635bf09SNicolin Chen return -EINVAL; 178*3635bf09SNicolin Chen } 179ddee627cSLiam Girdwood 180ddee627cSLiam Girdwood return 0; 181ddee627cSLiam Girdwood } 182ddee627cSLiam Girdwood 183ddee627cSLiam Girdwood /* 18458ba9b25SMark Brown * List of sample sizes that might go over the bus for parameter 18558ba9b25SMark Brown * application. There ought to be a wildcard sample size for things 18658ba9b25SMark Brown * like the DAC/ADC resolution to use but there isn't right now. 18758ba9b25SMark Brown */ 18858ba9b25SMark Brown static int sample_sizes[] = { 18988e33954SPeter Ujfalusi 24, 32, 19058ba9b25SMark Brown }; 19158ba9b25SMark Brown 19258ba9b25SMark Brown static void soc_pcm_apply_msb(struct snd_pcm_substream *substream, 19358ba9b25SMark Brown struct snd_soc_dai *dai) 19458ba9b25SMark Brown { 19558ba9b25SMark Brown int ret, i, bits; 19658ba9b25SMark Brown 19758ba9b25SMark Brown if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 19858ba9b25SMark Brown bits = dai->driver->playback.sig_bits; 19958ba9b25SMark Brown else 20058ba9b25SMark Brown bits = dai->driver->capture.sig_bits; 20158ba9b25SMark Brown 20258ba9b25SMark Brown if (!bits) 20358ba9b25SMark Brown return; 20458ba9b25SMark Brown 20558ba9b25SMark Brown for (i = 0; i < ARRAY_SIZE(sample_sizes); i++) { 206278047fdSMark Brown if (bits >= sample_sizes[i]) 207278047fdSMark Brown continue; 208278047fdSMark Brown 209278047fdSMark Brown ret = snd_pcm_hw_constraint_msbits(substream->runtime, 0, 210278047fdSMark Brown sample_sizes[i], bits); 21158ba9b25SMark Brown if (ret != 0) 21258ba9b25SMark Brown dev_warn(dai->dev, 213103d84a3SLiam Girdwood "ASoC: Failed to set MSB %d/%d: %d\n", 21458ba9b25SMark Brown bits, sample_sizes[i], ret); 21558ba9b25SMark Brown } 21658ba9b25SMark Brown } 21758ba9b25SMark Brown 218bd477c31SLars-Peter Clausen static void soc_pcm_init_runtime_hw(struct snd_pcm_hardware *hw, 219bd477c31SLars-Peter Clausen struct snd_soc_pcm_stream *codec_stream, 220bd477c31SLars-Peter Clausen struct snd_soc_pcm_stream *cpu_stream) 221bd477c31SLars-Peter Clausen { 222bd477c31SLars-Peter Clausen hw->rate_min = max(codec_stream->rate_min, cpu_stream->rate_min); 223bd477c31SLars-Peter Clausen hw->rate_max = max(codec_stream->rate_max, cpu_stream->rate_max); 224bd477c31SLars-Peter Clausen hw->channels_min = max(codec_stream->channels_min, 225bd477c31SLars-Peter Clausen cpu_stream->channels_min); 226bd477c31SLars-Peter Clausen hw->channels_max = min(codec_stream->channels_max, 227bd477c31SLars-Peter Clausen cpu_stream->channels_max); 228bd477c31SLars-Peter Clausen hw->formats = codec_stream->formats & cpu_stream->formats; 229bd477c31SLars-Peter Clausen hw->rates = codec_stream->rates & cpu_stream->rates; 230bd477c31SLars-Peter Clausen if (codec_stream->rates 231bd477c31SLars-Peter Clausen & (SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_CONTINUOUS)) 232bd477c31SLars-Peter Clausen hw->rates |= cpu_stream->rates; 233bd477c31SLars-Peter Clausen if (cpu_stream->rates 234bd477c31SLars-Peter Clausen & (SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_CONTINUOUS)) 235bd477c31SLars-Peter Clausen hw->rates |= codec_stream->rates; 236bd477c31SLars-Peter Clausen } 237bd477c31SLars-Peter Clausen 23858ba9b25SMark Brown /* 239ddee627cSLiam Girdwood * Called by ALSA when a PCM substream is opened, the runtime->hw record is 240ddee627cSLiam Girdwood * then initialized and any private data can be allocated. This also calls 241ddee627cSLiam Girdwood * startup for the cpu DAI, platform, machine and codec DAI. 242ddee627cSLiam Girdwood */ 243ddee627cSLiam Girdwood static int soc_pcm_open(struct snd_pcm_substream *substream) 244ddee627cSLiam Girdwood { 245ddee627cSLiam Girdwood struct snd_soc_pcm_runtime *rtd = substream->private_data; 246ddee627cSLiam Girdwood struct snd_pcm_runtime *runtime = substream->runtime; 247ddee627cSLiam Girdwood struct snd_soc_platform *platform = rtd->platform; 248ddee627cSLiam Girdwood struct snd_soc_dai *cpu_dai = rtd->cpu_dai; 249ddee627cSLiam Girdwood struct snd_soc_dai *codec_dai = rtd->codec_dai; 250ddee627cSLiam Girdwood struct snd_soc_dai_driver *cpu_dai_drv = cpu_dai->driver; 251ddee627cSLiam Girdwood struct snd_soc_dai_driver *codec_dai_drv = codec_dai->driver; 252ddee627cSLiam Girdwood int ret = 0; 253ddee627cSLiam Girdwood 254988e8cc4SNicolin Chen pinctrl_pm_select_default_state(cpu_dai->dev); 255988e8cc4SNicolin Chen pinctrl_pm_select_default_state(codec_dai->dev); 256d6652ef8SMark Brown pm_runtime_get_sync(cpu_dai->dev); 257d6652ef8SMark Brown pm_runtime_get_sync(codec_dai->dev); 258d6652ef8SMark Brown pm_runtime_get_sync(platform->dev); 259d6652ef8SMark Brown 260b8c0dab9SLiam Girdwood mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); 261ddee627cSLiam Girdwood 262ddee627cSLiam Girdwood /* startup the audio subsystem */ 263c5914b0aSMark Brown if (cpu_dai->driver->ops && cpu_dai->driver->ops->startup) { 264ddee627cSLiam Girdwood ret = cpu_dai->driver->ops->startup(substream, cpu_dai); 265ddee627cSLiam Girdwood if (ret < 0) { 266103d84a3SLiam Girdwood dev_err(cpu_dai->dev, "ASoC: can't open interface" 267103d84a3SLiam Girdwood " %s: %d\n", cpu_dai->name, ret); 268ddee627cSLiam Girdwood goto out; 269ddee627cSLiam Girdwood } 270ddee627cSLiam Girdwood } 271ddee627cSLiam Girdwood 272ddee627cSLiam Girdwood if (platform->driver->ops && platform->driver->ops->open) { 273ddee627cSLiam Girdwood ret = platform->driver->ops->open(substream); 274ddee627cSLiam Girdwood if (ret < 0) { 275103d84a3SLiam Girdwood dev_err(platform->dev, "ASoC: can't open platform" 276103d84a3SLiam Girdwood " %s: %d\n", platform->name, ret); 277ddee627cSLiam Girdwood goto platform_err; 278ddee627cSLiam Girdwood } 279ddee627cSLiam Girdwood } 280ddee627cSLiam Girdwood 281c5914b0aSMark Brown if (codec_dai->driver->ops && codec_dai->driver->ops->startup) { 282ddee627cSLiam Girdwood ret = codec_dai->driver->ops->startup(substream, codec_dai); 283ddee627cSLiam Girdwood if (ret < 0) { 284103d84a3SLiam Girdwood dev_err(codec_dai->dev, "ASoC: can't open codec" 285103d84a3SLiam Girdwood " %s: %d\n", codec_dai->name, ret); 286ddee627cSLiam Girdwood goto codec_dai_err; 287ddee627cSLiam Girdwood } 288ddee627cSLiam Girdwood } 289ddee627cSLiam Girdwood 290ddee627cSLiam Girdwood if (rtd->dai_link->ops && rtd->dai_link->ops->startup) { 291ddee627cSLiam Girdwood ret = rtd->dai_link->ops->startup(substream); 292ddee627cSLiam Girdwood if (ret < 0) { 293103d84a3SLiam Girdwood pr_err("ASoC: %s startup failed: %d\n", 29425bfe662SMark Brown rtd->dai_link->name, ret); 295ddee627cSLiam Girdwood goto machine_err; 296ddee627cSLiam Girdwood } 297ddee627cSLiam Girdwood } 298ddee627cSLiam Girdwood 29901d7584cSLiam Girdwood /* Dynamic PCM DAI links compat checks use dynamic capabilities */ 30001d7584cSLiam Girdwood if (rtd->dai_link->dynamic || rtd->dai_link->no_pcm) 30101d7584cSLiam Girdwood goto dynamic; 30201d7584cSLiam Girdwood 303ddee627cSLiam Girdwood /* Check that the codec and cpu DAIs are compatible */ 304ddee627cSLiam Girdwood if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 305bd477c31SLars-Peter Clausen soc_pcm_init_runtime_hw(&runtime->hw, &codec_dai_drv->playback, 306bd477c31SLars-Peter Clausen &cpu_dai_drv->playback); 307ddee627cSLiam Girdwood } else { 308bd477c31SLars-Peter Clausen soc_pcm_init_runtime_hw(&runtime->hw, &codec_dai_drv->capture, 309bd477c31SLars-Peter Clausen &cpu_dai_drv->capture); 310ddee627cSLiam Girdwood } 311ddee627cSLiam Girdwood 312ddee627cSLiam Girdwood ret = -EINVAL; 313ddee627cSLiam Girdwood snd_pcm_limit_hw_rates(runtime); 314ddee627cSLiam Girdwood if (!runtime->hw.rates) { 315103d84a3SLiam Girdwood printk(KERN_ERR "ASoC: %s <-> %s No matching rates\n", 316ddee627cSLiam Girdwood codec_dai->name, cpu_dai->name); 317ddee627cSLiam Girdwood goto config_err; 318ddee627cSLiam Girdwood } 319ddee627cSLiam Girdwood if (!runtime->hw.formats) { 320103d84a3SLiam Girdwood printk(KERN_ERR "ASoC: %s <-> %s No matching formats\n", 321ddee627cSLiam Girdwood codec_dai->name, cpu_dai->name); 322ddee627cSLiam Girdwood goto config_err; 323ddee627cSLiam Girdwood } 324ddee627cSLiam Girdwood if (!runtime->hw.channels_min || !runtime->hw.channels_max || 325ddee627cSLiam Girdwood runtime->hw.channels_min > runtime->hw.channels_max) { 326103d84a3SLiam Girdwood printk(KERN_ERR "ASoC: %s <-> %s No matching channels\n", 327ddee627cSLiam Girdwood codec_dai->name, cpu_dai->name); 328ddee627cSLiam Girdwood goto config_err; 329ddee627cSLiam Girdwood } 330ddee627cSLiam Girdwood 33158ba9b25SMark Brown soc_pcm_apply_msb(substream, codec_dai); 33258ba9b25SMark Brown soc_pcm_apply_msb(substream, cpu_dai); 33358ba9b25SMark Brown 334ddee627cSLiam Girdwood /* Symmetry only applies if we've already got an active stream. */ 33517841020SDong Aisheng if (cpu_dai->active) { 33617841020SDong Aisheng ret = soc_pcm_apply_symmetry(substream, cpu_dai); 33717841020SDong Aisheng if (ret != 0) 33817841020SDong Aisheng goto config_err; 33917841020SDong Aisheng } 34017841020SDong Aisheng 34117841020SDong Aisheng if (codec_dai->active) { 34217841020SDong Aisheng ret = soc_pcm_apply_symmetry(substream, codec_dai); 343ddee627cSLiam Girdwood if (ret != 0) 344ddee627cSLiam Girdwood goto config_err; 345ddee627cSLiam Girdwood } 346ddee627cSLiam Girdwood 347103d84a3SLiam Girdwood pr_debug("ASoC: %s <-> %s info:\n", 348ddee627cSLiam Girdwood codec_dai->name, cpu_dai->name); 349103d84a3SLiam Girdwood pr_debug("ASoC: rate mask 0x%x\n", runtime->hw.rates); 350103d84a3SLiam Girdwood pr_debug("ASoC: min ch %d max ch %d\n", runtime->hw.channels_min, 351ddee627cSLiam Girdwood runtime->hw.channels_max); 352103d84a3SLiam Girdwood pr_debug("ASoC: min rate %d max rate %d\n", runtime->hw.rate_min, 353ddee627cSLiam Girdwood runtime->hw.rate_max); 354ddee627cSLiam Girdwood 35501d7584cSLiam Girdwood dynamic: 356ddee627cSLiam Girdwood if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 357ddee627cSLiam Girdwood cpu_dai->playback_active++; 358ddee627cSLiam Girdwood codec_dai->playback_active++; 359ddee627cSLiam Girdwood } else { 360ddee627cSLiam Girdwood cpu_dai->capture_active++; 361ddee627cSLiam Girdwood codec_dai->capture_active++; 362ddee627cSLiam Girdwood } 363ddee627cSLiam Girdwood cpu_dai->active++; 364ddee627cSLiam Girdwood codec_dai->active++; 365ddee627cSLiam Girdwood rtd->codec->active++; 366b8c0dab9SLiam Girdwood mutex_unlock(&rtd->pcm_mutex); 367ddee627cSLiam Girdwood return 0; 368ddee627cSLiam Girdwood 369ddee627cSLiam Girdwood config_err: 370ddee627cSLiam Girdwood if (rtd->dai_link->ops && rtd->dai_link->ops->shutdown) 371ddee627cSLiam Girdwood rtd->dai_link->ops->shutdown(substream); 372ddee627cSLiam Girdwood 373ddee627cSLiam Girdwood machine_err: 374ddee627cSLiam Girdwood if (codec_dai->driver->ops->shutdown) 375ddee627cSLiam Girdwood codec_dai->driver->ops->shutdown(substream, codec_dai); 376ddee627cSLiam Girdwood 377ddee627cSLiam Girdwood codec_dai_err: 378ddee627cSLiam Girdwood if (platform->driver->ops && platform->driver->ops->close) 379ddee627cSLiam Girdwood platform->driver->ops->close(substream); 380ddee627cSLiam Girdwood 381ddee627cSLiam Girdwood platform_err: 382ddee627cSLiam Girdwood if (cpu_dai->driver->ops->shutdown) 383ddee627cSLiam Girdwood cpu_dai->driver->ops->shutdown(substream, cpu_dai); 384ddee627cSLiam Girdwood out: 385b8c0dab9SLiam Girdwood mutex_unlock(&rtd->pcm_mutex); 386d6652ef8SMark Brown 387d6652ef8SMark Brown pm_runtime_put(platform->dev); 388d6652ef8SMark Brown pm_runtime_put(codec_dai->dev); 389d6652ef8SMark Brown pm_runtime_put(cpu_dai->dev); 390988e8cc4SNicolin Chen if (!codec_dai->active) 391988e8cc4SNicolin Chen pinctrl_pm_select_sleep_state(codec_dai->dev); 392988e8cc4SNicolin Chen if (!cpu_dai->active) 393988e8cc4SNicolin Chen pinctrl_pm_select_sleep_state(cpu_dai->dev); 394d6652ef8SMark Brown 395ddee627cSLiam Girdwood return ret; 396ddee627cSLiam Girdwood } 397ddee627cSLiam Girdwood 398ddee627cSLiam Girdwood /* 399ddee627cSLiam Girdwood * Power down the audio subsystem pmdown_time msecs after close is called. 400ddee627cSLiam Girdwood * This is to ensure there are no pops or clicks in between any music tracks 401ddee627cSLiam Girdwood * due to DAPM power cycling. 402ddee627cSLiam Girdwood */ 403ddee627cSLiam Girdwood static void close_delayed_work(struct work_struct *work) 404ddee627cSLiam Girdwood { 405ddee627cSLiam Girdwood struct snd_soc_pcm_runtime *rtd = 406ddee627cSLiam Girdwood container_of(work, struct snd_soc_pcm_runtime, delayed_work.work); 407ddee627cSLiam Girdwood struct snd_soc_dai *codec_dai = rtd->codec_dai; 408ddee627cSLiam Girdwood 409b8c0dab9SLiam Girdwood mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); 410ddee627cSLiam Girdwood 411103d84a3SLiam Girdwood dev_dbg(rtd->dev, "ASoC: pop wq checking: %s status: %s waiting: %s\n", 412ddee627cSLiam Girdwood codec_dai->driver->playback.stream_name, 413ddee627cSLiam Girdwood codec_dai->playback_active ? "active" : "inactive", 4149bffb1fbSMisael Lopez Cruz rtd->pop_wait ? "yes" : "no"); 415ddee627cSLiam Girdwood 416ddee627cSLiam Girdwood /* are we waiting on this codec DAI stream */ 4179bffb1fbSMisael Lopez Cruz if (rtd->pop_wait == 1) { 4189bffb1fbSMisael Lopez Cruz rtd->pop_wait = 0; 4197bd3a6f3SMark Brown snd_soc_dapm_stream_event(rtd, SNDRV_PCM_STREAM_PLAYBACK, 420d9b0951bSLiam Girdwood SND_SOC_DAPM_STREAM_STOP); 421ddee627cSLiam Girdwood } 422ddee627cSLiam Girdwood 423b8c0dab9SLiam Girdwood mutex_unlock(&rtd->pcm_mutex); 424ddee627cSLiam Girdwood } 425ddee627cSLiam Girdwood 426ddee627cSLiam Girdwood /* 427ddee627cSLiam Girdwood * Called by ALSA when a PCM substream is closed. Private data can be 428ddee627cSLiam Girdwood * freed here. The cpu DAI, codec DAI, machine and platform are also 429ddee627cSLiam Girdwood * shutdown. 430ddee627cSLiam Girdwood */ 43191d5e6b4SLiam Girdwood static int soc_pcm_close(struct snd_pcm_substream *substream) 432ddee627cSLiam Girdwood { 433ddee627cSLiam Girdwood struct snd_soc_pcm_runtime *rtd = substream->private_data; 434ddee627cSLiam Girdwood struct snd_soc_platform *platform = rtd->platform; 435ddee627cSLiam Girdwood struct snd_soc_dai *cpu_dai = rtd->cpu_dai; 436ddee627cSLiam Girdwood struct snd_soc_dai *codec_dai = rtd->codec_dai; 437ddee627cSLiam Girdwood struct snd_soc_codec *codec = rtd->codec; 438ddee627cSLiam Girdwood 439b8c0dab9SLiam Girdwood mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); 440ddee627cSLiam Girdwood 441ddee627cSLiam Girdwood if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 442ddee627cSLiam Girdwood cpu_dai->playback_active--; 443ddee627cSLiam Girdwood codec_dai->playback_active--; 444ddee627cSLiam Girdwood } else { 445ddee627cSLiam Girdwood cpu_dai->capture_active--; 446ddee627cSLiam Girdwood codec_dai->capture_active--; 447ddee627cSLiam Girdwood } 448ddee627cSLiam Girdwood 449ddee627cSLiam Girdwood cpu_dai->active--; 450ddee627cSLiam Girdwood codec_dai->active--; 451ddee627cSLiam Girdwood codec->active--; 452ddee627cSLiam Girdwood 45317841020SDong Aisheng /* clear the corresponding DAIs rate when inactive */ 454*3635bf09SNicolin Chen if (!cpu_dai->active) { 45517841020SDong Aisheng cpu_dai->rate = 0; 456*3635bf09SNicolin Chen cpu_dai->channels = 0; 457*3635bf09SNicolin Chen cpu_dai->sample_bits = 0; 458*3635bf09SNicolin Chen } 45917841020SDong Aisheng 460*3635bf09SNicolin Chen if (!codec_dai->active) { 46117841020SDong Aisheng codec_dai->rate = 0; 462*3635bf09SNicolin Chen codec_dai->channels = 0; 463*3635bf09SNicolin Chen codec_dai->sample_bits = 0; 464*3635bf09SNicolin Chen } 46525b76791SSascha Hauer 466ddee627cSLiam Girdwood /* Muting the DAC suppresses artifacts caused during digital 467ddee627cSLiam Girdwood * shutdown, for example from stopping clocks. 468ddee627cSLiam Girdwood */ 469da18396fSMark Brown snd_soc_dai_digital_mute(codec_dai, 1, substream->stream); 470ddee627cSLiam Girdwood 471ddee627cSLiam Girdwood if (cpu_dai->driver->ops->shutdown) 472ddee627cSLiam Girdwood cpu_dai->driver->ops->shutdown(substream, cpu_dai); 473ddee627cSLiam Girdwood 474ddee627cSLiam Girdwood if (codec_dai->driver->ops->shutdown) 475ddee627cSLiam Girdwood codec_dai->driver->ops->shutdown(substream, codec_dai); 476ddee627cSLiam Girdwood 477ddee627cSLiam Girdwood if (rtd->dai_link->ops && rtd->dai_link->ops->shutdown) 478ddee627cSLiam Girdwood rtd->dai_link->ops->shutdown(substream); 479ddee627cSLiam Girdwood 480ddee627cSLiam Girdwood if (platform->driver->ops && platform->driver->ops->close) 481ddee627cSLiam Girdwood platform->driver->ops->close(substream); 482ddee627cSLiam Girdwood cpu_dai->runtime = NULL; 483ddee627cSLiam Girdwood 484ddee627cSLiam Girdwood if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 485b5d1d036SMark Brown if (!rtd->pmdown_time || codec->ignore_pmdown_time || 4865b6247abSMark Brown rtd->dai_link->ignore_pmdown_time) { 4871d69c5c5SPeter Ujfalusi /* powered down playback stream now */ 4881d69c5c5SPeter Ujfalusi snd_soc_dapm_stream_event(rtd, 4897bd3a6f3SMark Brown SNDRV_PCM_STREAM_PLAYBACK, 4901d69c5c5SPeter Ujfalusi SND_SOC_DAPM_STREAM_STOP); 4911d69c5c5SPeter Ujfalusi } else { 492ddee627cSLiam Girdwood /* start delayed pop wq here for playback streams */ 4939bffb1fbSMisael Lopez Cruz rtd->pop_wait = 1; 494d4e1a73aSMark Brown queue_delayed_work(system_power_efficient_wq, 495d4e1a73aSMark Brown &rtd->delayed_work, 496ddee627cSLiam Girdwood msecs_to_jiffies(rtd->pmdown_time)); 4971d69c5c5SPeter Ujfalusi } 498ddee627cSLiam Girdwood } else { 499ddee627cSLiam Girdwood /* capture streams can be powered down now */ 5007bd3a6f3SMark Brown snd_soc_dapm_stream_event(rtd, SNDRV_PCM_STREAM_CAPTURE, 501d9b0951bSLiam Girdwood SND_SOC_DAPM_STREAM_STOP); 502ddee627cSLiam Girdwood } 503ddee627cSLiam Girdwood 504b8c0dab9SLiam Girdwood mutex_unlock(&rtd->pcm_mutex); 505d6652ef8SMark Brown 506d6652ef8SMark Brown pm_runtime_put(platform->dev); 507d6652ef8SMark Brown pm_runtime_put(codec_dai->dev); 508d6652ef8SMark Brown pm_runtime_put(cpu_dai->dev); 509988e8cc4SNicolin Chen if (!codec_dai->active) 510988e8cc4SNicolin Chen pinctrl_pm_select_sleep_state(codec_dai->dev); 511988e8cc4SNicolin Chen if (!cpu_dai->active) 512988e8cc4SNicolin Chen pinctrl_pm_select_sleep_state(cpu_dai->dev); 513d6652ef8SMark Brown 514ddee627cSLiam Girdwood return 0; 515ddee627cSLiam Girdwood } 516ddee627cSLiam Girdwood 517ddee627cSLiam Girdwood /* 518ddee627cSLiam Girdwood * Called by ALSA when the PCM substream is prepared, can set format, sample 519ddee627cSLiam Girdwood * rate, etc. This function is non atomic and can be called multiple times, 520ddee627cSLiam Girdwood * it can refer to the runtime info. 521ddee627cSLiam Girdwood */ 522ddee627cSLiam Girdwood static int soc_pcm_prepare(struct snd_pcm_substream *substream) 523ddee627cSLiam Girdwood { 524ddee627cSLiam Girdwood struct snd_soc_pcm_runtime *rtd = substream->private_data; 525ddee627cSLiam Girdwood struct snd_soc_platform *platform = rtd->platform; 526ddee627cSLiam Girdwood struct snd_soc_dai *cpu_dai = rtd->cpu_dai; 527ddee627cSLiam Girdwood struct snd_soc_dai *codec_dai = rtd->codec_dai; 528ddee627cSLiam Girdwood int ret = 0; 529ddee627cSLiam Girdwood 530b8c0dab9SLiam Girdwood mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); 531ddee627cSLiam Girdwood 532ddee627cSLiam Girdwood if (rtd->dai_link->ops && rtd->dai_link->ops->prepare) { 533ddee627cSLiam Girdwood ret = rtd->dai_link->ops->prepare(substream); 534ddee627cSLiam Girdwood if (ret < 0) { 535103d84a3SLiam Girdwood dev_err(rtd->card->dev, "ASoC: machine prepare error:" 536103d84a3SLiam Girdwood " %d\n", ret); 537ddee627cSLiam Girdwood goto out; 538ddee627cSLiam Girdwood } 539ddee627cSLiam Girdwood } 540ddee627cSLiam Girdwood 541ddee627cSLiam Girdwood if (platform->driver->ops && platform->driver->ops->prepare) { 542ddee627cSLiam Girdwood ret = platform->driver->ops->prepare(substream); 543ddee627cSLiam Girdwood if (ret < 0) { 544103d84a3SLiam Girdwood dev_err(platform->dev, "ASoC: platform prepare error:" 545103d84a3SLiam Girdwood " %d\n", ret); 546ddee627cSLiam Girdwood goto out; 547ddee627cSLiam Girdwood } 548ddee627cSLiam Girdwood } 549ddee627cSLiam Girdwood 550c5914b0aSMark Brown if (codec_dai->driver->ops && codec_dai->driver->ops->prepare) { 551ddee627cSLiam Girdwood ret = codec_dai->driver->ops->prepare(substream, codec_dai); 552ddee627cSLiam Girdwood if (ret < 0) { 553103d84a3SLiam Girdwood dev_err(codec_dai->dev, "ASoC: DAI prepare error: %d\n", 55425bfe662SMark Brown ret); 555ddee627cSLiam Girdwood goto out; 556ddee627cSLiam Girdwood } 557ddee627cSLiam Girdwood } 558ddee627cSLiam Girdwood 559c5914b0aSMark Brown if (cpu_dai->driver->ops && cpu_dai->driver->ops->prepare) { 560ddee627cSLiam Girdwood ret = cpu_dai->driver->ops->prepare(substream, cpu_dai); 561ddee627cSLiam Girdwood if (ret < 0) { 562103d84a3SLiam Girdwood dev_err(cpu_dai->dev, "ASoC: DAI prepare error: %d\n", 56325bfe662SMark Brown ret); 564ddee627cSLiam Girdwood goto out; 565ddee627cSLiam Girdwood } 566ddee627cSLiam Girdwood } 567ddee627cSLiam Girdwood 568ddee627cSLiam Girdwood /* cancel any delayed stream shutdown that is pending */ 569ddee627cSLiam Girdwood if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && 5709bffb1fbSMisael Lopez Cruz rtd->pop_wait) { 5719bffb1fbSMisael Lopez Cruz rtd->pop_wait = 0; 572ddee627cSLiam Girdwood cancel_delayed_work(&rtd->delayed_work); 573ddee627cSLiam Girdwood } 574ddee627cSLiam Girdwood 575d9b0951bSLiam Girdwood snd_soc_dapm_stream_event(rtd, substream->stream, 576ddee627cSLiam Girdwood SND_SOC_DAPM_STREAM_START); 577ddee627cSLiam Girdwood 578da18396fSMark Brown snd_soc_dai_digital_mute(codec_dai, 0, substream->stream); 579ddee627cSLiam Girdwood 580ddee627cSLiam Girdwood out: 581b8c0dab9SLiam Girdwood mutex_unlock(&rtd->pcm_mutex); 582ddee627cSLiam Girdwood return ret; 583ddee627cSLiam Girdwood } 584ddee627cSLiam Girdwood 585ddee627cSLiam Girdwood /* 586ddee627cSLiam Girdwood * Called by ALSA when the hardware params are set by application. This 587ddee627cSLiam Girdwood * function can also be called multiple times and can allocate buffers 588ddee627cSLiam Girdwood * (using snd_pcm_lib_* ). It's non-atomic. 589ddee627cSLiam Girdwood */ 590ddee627cSLiam Girdwood static int soc_pcm_hw_params(struct snd_pcm_substream *substream, 591ddee627cSLiam Girdwood struct snd_pcm_hw_params *params) 592ddee627cSLiam Girdwood { 593ddee627cSLiam Girdwood struct snd_soc_pcm_runtime *rtd = substream->private_data; 594ddee627cSLiam Girdwood struct snd_soc_platform *platform = rtd->platform; 595ddee627cSLiam Girdwood struct snd_soc_dai *cpu_dai = rtd->cpu_dai; 596ddee627cSLiam Girdwood struct snd_soc_dai *codec_dai = rtd->codec_dai; 597ddee627cSLiam Girdwood int ret = 0; 598ddee627cSLiam Girdwood 599b8c0dab9SLiam Girdwood mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); 600ddee627cSLiam Girdwood 601*3635bf09SNicolin Chen ret = soc_pcm_params_symmetry(substream, params); 602*3635bf09SNicolin Chen if (ret) 603*3635bf09SNicolin Chen goto out; 604*3635bf09SNicolin Chen 605ddee627cSLiam Girdwood if (rtd->dai_link->ops && rtd->dai_link->ops->hw_params) { 606ddee627cSLiam Girdwood ret = rtd->dai_link->ops->hw_params(substream, params); 607ddee627cSLiam Girdwood if (ret < 0) { 608103d84a3SLiam Girdwood dev_err(rtd->card->dev, "ASoC: machine hw_params" 609103d84a3SLiam Girdwood " failed: %d\n", ret); 610ddee627cSLiam Girdwood goto out; 611ddee627cSLiam Girdwood } 612ddee627cSLiam Girdwood } 613ddee627cSLiam Girdwood 614c5914b0aSMark Brown if (codec_dai->driver->ops && codec_dai->driver->ops->hw_params) { 615ddee627cSLiam Girdwood ret = codec_dai->driver->ops->hw_params(substream, params, codec_dai); 616ddee627cSLiam Girdwood if (ret < 0) { 617103d84a3SLiam Girdwood dev_err(codec_dai->dev, "ASoC: can't set %s hw params:" 618103d84a3SLiam Girdwood " %d\n", codec_dai->name, ret); 619ddee627cSLiam Girdwood goto codec_err; 620ddee627cSLiam Girdwood } 621ddee627cSLiam Girdwood } 622ddee627cSLiam Girdwood 623c5914b0aSMark Brown if (cpu_dai->driver->ops && cpu_dai->driver->ops->hw_params) { 624ddee627cSLiam Girdwood ret = cpu_dai->driver->ops->hw_params(substream, params, cpu_dai); 625ddee627cSLiam Girdwood if (ret < 0) { 626103d84a3SLiam Girdwood dev_err(cpu_dai->dev, "ASoC: %s hw params failed: %d\n", 62725bfe662SMark Brown cpu_dai->name, ret); 628ddee627cSLiam Girdwood goto interface_err; 629ddee627cSLiam Girdwood } 630ddee627cSLiam Girdwood } 631ddee627cSLiam Girdwood 632ddee627cSLiam Girdwood if (platform->driver->ops && platform->driver->ops->hw_params) { 633ddee627cSLiam Girdwood ret = platform->driver->ops->hw_params(substream, params); 634ddee627cSLiam Girdwood if (ret < 0) { 635103d84a3SLiam Girdwood dev_err(platform->dev, "ASoC: %s hw params failed: %d\n", 63625bfe662SMark Brown platform->name, ret); 637ddee627cSLiam Girdwood goto platform_err; 638ddee627cSLiam Girdwood } 639ddee627cSLiam Girdwood } 640ddee627cSLiam Girdwood 641*3635bf09SNicolin Chen /* store the parameters for each DAIs */ 64217841020SDong Aisheng cpu_dai->rate = params_rate(params); 643*3635bf09SNicolin Chen cpu_dai->channels = params_channels(params); 644*3635bf09SNicolin Chen cpu_dai->sample_bits = 645*3635bf09SNicolin Chen snd_pcm_format_physical_width(params_format(params)); 646*3635bf09SNicolin Chen 64717841020SDong Aisheng codec_dai->rate = params_rate(params); 648*3635bf09SNicolin Chen codec_dai->channels = params_channels(params); 649*3635bf09SNicolin Chen codec_dai->sample_bits = 650*3635bf09SNicolin Chen snd_pcm_format_physical_width(params_format(params)); 651ddee627cSLiam Girdwood 652ddee627cSLiam Girdwood out: 653b8c0dab9SLiam Girdwood mutex_unlock(&rtd->pcm_mutex); 654ddee627cSLiam Girdwood return ret; 655ddee627cSLiam Girdwood 656ddee627cSLiam Girdwood platform_err: 657c5914b0aSMark Brown if (cpu_dai->driver->ops && cpu_dai->driver->ops->hw_free) 658ddee627cSLiam Girdwood cpu_dai->driver->ops->hw_free(substream, cpu_dai); 659ddee627cSLiam Girdwood 660ddee627cSLiam Girdwood interface_err: 661c5914b0aSMark Brown if (codec_dai->driver->ops && codec_dai->driver->ops->hw_free) 662ddee627cSLiam Girdwood codec_dai->driver->ops->hw_free(substream, codec_dai); 663ddee627cSLiam Girdwood 664ddee627cSLiam Girdwood codec_err: 665ddee627cSLiam Girdwood if (rtd->dai_link->ops && rtd->dai_link->ops->hw_free) 666ddee627cSLiam Girdwood rtd->dai_link->ops->hw_free(substream); 667ddee627cSLiam Girdwood 668b8c0dab9SLiam Girdwood mutex_unlock(&rtd->pcm_mutex); 669ddee627cSLiam Girdwood return ret; 670ddee627cSLiam Girdwood } 671ddee627cSLiam Girdwood 672ddee627cSLiam Girdwood /* 673ddee627cSLiam Girdwood * Frees resources allocated by hw_params, can be called multiple times 674ddee627cSLiam Girdwood */ 675ddee627cSLiam Girdwood static int soc_pcm_hw_free(struct snd_pcm_substream *substream) 676ddee627cSLiam Girdwood { 677ddee627cSLiam Girdwood struct snd_soc_pcm_runtime *rtd = substream->private_data; 678ddee627cSLiam Girdwood struct snd_soc_platform *platform = rtd->platform; 679ddee627cSLiam Girdwood struct snd_soc_dai *cpu_dai = rtd->cpu_dai; 680ddee627cSLiam Girdwood struct snd_soc_dai *codec_dai = rtd->codec_dai; 681ddee627cSLiam Girdwood struct snd_soc_codec *codec = rtd->codec; 682ddee627cSLiam Girdwood 683b8c0dab9SLiam Girdwood mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); 684ddee627cSLiam Girdwood 685ddee627cSLiam Girdwood /* apply codec digital mute */ 686ddee627cSLiam Girdwood if (!codec->active) 687da18396fSMark Brown snd_soc_dai_digital_mute(codec_dai, 1, substream->stream); 688ddee627cSLiam Girdwood 689ddee627cSLiam Girdwood /* free any machine hw params */ 690ddee627cSLiam Girdwood if (rtd->dai_link->ops && rtd->dai_link->ops->hw_free) 691ddee627cSLiam Girdwood rtd->dai_link->ops->hw_free(substream); 692ddee627cSLiam Girdwood 693ddee627cSLiam Girdwood /* free any DMA resources */ 694ddee627cSLiam Girdwood if (platform->driver->ops && platform->driver->ops->hw_free) 695ddee627cSLiam Girdwood platform->driver->ops->hw_free(substream); 696ddee627cSLiam Girdwood 697ddee627cSLiam Girdwood /* now free hw params for the DAIs */ 698c5914b0aSMark Brown if (codec_dai->driver->ops && codec_dai->driver->ops->hw_free) 699ddee627cSLiam Girdwood codec_dai->driver->ops->hw_free(substream, codec_dai); 700ddee627cSLiam Girdwood 701c5914b0aSMark Brown if (cpu_dai->driver->ops && cpu_dai->driver->ops->hw_free) 702ddee627cSLiam Girdwood cpu_dai->driver->ops->hw_free(substream, cpu_dai); 703ddee627cSLiam Girdwood 704b8c0dab9SLiam Girdwood mutex_unlock(&rtd->pcm_mutex); 705ddee627cSLiam Girdwood return 0; 706ddee627cSLiam Girdwood } 707ddee627cSLiam Girdwood 708ddee627cSLiam Girdwood static int soc_pcm_trigger(struct snd_pcm_substream *substream, int cmd) 709ddee627cSLiam Girdwood { 710ddee627cSLiam Girdwood struct snd_soc_pcm_runtime *rtd = substream->private_data; 711ddee627cSLiam Girdwood struct snd_soc_platform *platform = rtd->platform; 712ddee627cSLiam Girdwood struct snd_soc_dai *cpu_dai = rtd->cpu_dai; 713ddee627cSLiam Girdwood struct snd_soc_dai *codec_dai = rtd->codec_dai; 714ddee627cSLiam Girdwood int ret; 715ddee627cSLiam Girdwood 716c5914b0aSMark Brown if (codec_dai->driver->ops && codec_dai->driver->ops->trigger) { 717ddee627cSLiam Girdwood ret = codec_dai->driver->ops->trigger(substream, cmd, codec_dai); 718ddee627cSLiam Girdwood if (ret < 0) 719ddee627cSLiam Girdwood return ret; 720ddee627cSLiam Girdwood } 721ddee627cSLiam Girdwood 722ddee627cSLiam Girdwood if (platform->driver->ops && platform->driver->ops->trigger) { 723ddee627cSLiam Girdwood ret = platform->driver->ops->trigger(substream, cmd); 724ddee627cSLiam Girdwood if (ret < 0) 725ddee627cSLiam Girdwood return ret; 726ddee627cSLiam Girdwood } 727ddee627cSLiam Girdwood 728c5914b0aSMark Brown if (cpu_dai->driver->ops && cpu_dai->driver->ops->trigger) { 729ddee627cSLiam Girdwood ret = cpu_dai->driver->ops->trigger(substream, cmd, cpu_dai); 730ddee627cSLiam Girdwood if (ret < 0) 731ddee627cSLiam Girdwood return ret; 732ddee627cSLiam Girdwood } 733ddee627cSLiam Girdwood return 0; 734ddee627cSLiam Girdwood } 735ddee627cSLiam Girdwood 73645c0a188SMark Brown static int soc_pcm_bespoke_trigger(struct snd_pcm_substream *substream, 73745c0a188SMark Brown int cmd) 73807bf84aaSLiam Girdwood { 73907bf84aaSLiam Girdwood struct snd_soc_pcm_runtime *rtd = substream->private_data; 74007bf84aaSLiam Girdwood struct snd_soc_platform *platform = rtd->platform; 74107bf84aaSLiam Girdwood struct snd_soc_dai *cpu_dai = rtd->cpu_dai; 74207bf84aaSLiam Girdwood struct snd_soc_dai *codec_dai = rtd->codec_dai; 74307bf84aaSLiam Girdwood int ret; 74407bf84aaSLiam Girdwood 745c5914b0aSMark Brown if (codec_dai->driver->ops && 746c5914b0aSMark Brown codec_dai->driver->ops->bespoke_trigger) { 74707bf84aaSLiam Girdwood ret = codec_dai->driver->ops->bespoke_trigger(substream, cmd, codec_dai); 74807bf84aaSLiam Girdwood if (ret < 0) 74907bf84aaSLiam Girdwood return ret; 75007bf84aaSLiam Girdwood } 75107bf84aaSLiam Girdwood 752c5914b0aSMark Brown if (platform->driver->ops && platform->driver->bespoke_trigger) { 75307bf84aaSLiam Girdwood ret = platform->driver->bespoke_trigger(substream, cmd); 75407bf84aaSLiam Girdwood if (ret < 0) 75507bf84aaSLiam Girdwood return ret; 75607bf84aaSLiam Girdwood } 75707bf84aaSLiam Girdwood 758c5914b0aSMark Brown if (cpu_dai->driver->ops && cpu_dai->driver->ops->bespoke_trigger) { 75907bf84aaSLiam Girdwood ret = cpu_dai->driver->ops->bespoke_trigger(substream, cmd, cpu_dai); 76007bf84aaSLiam Girdwood if (ret < 0) 76107bf84aaSLiam Girdwood return ret; 76207bf84aaSLiam Girdwood } 76307bf84aaSLiam Girdwood return 0; 76407bf84aaSLiam Girdwood } 765ddee627cSLiam Girdwood /* 766ddee627cSLiam Girdwood * soc level wrapper for pointer callback 767ddee627cSLiam Girdwood * If cpu_dai, codec_dai, platform driver has the delay callback, than 768ddee627cSLiam Girdwood * the runtime->delay will be updated accordingly. 769ddee627cSLiam Girdwood */ 770ddee627cSLiam Girdwood static snd_pcm_uframes_t soc_pcm_pointer(struct snd_pcm_substream *substream) 771ddee627cSLiam Girdwood { 772ddee627cSLiam Girdwood struct snd_soc_pcm_runtime *rtd = substream->private_data; 773ddee627cSLiam Girdwood struct snd_soc_platform *platform = rtd->platform; 774ddee627cSLiam Girdwood struct snd_soc_dai *cpu_dai = rtd->cpu_dai; 775ddee627cSLiam Girdwood struct snd_soc_dai *codec_dai = rtd->codec_dai; 776ddee627cSLiam Girdwood struct snd_pcm_runtime *runtime = substream->runtime; 777ddee627cSLiam Girdwood snd_pcm_uframes_t offset = 0; 778ddee627cSLiam Girdwood snd_pcm_sframes_t delay = 0; 779ddee627cSLiam Girdwood 780ddee627cSLiam Girdwood if (platform->driver->ops && platform->driver->ops->pointer) 781ddee627cSLiam Girdwood offset = platform->driver->ops->pointer(substream); 782ddee627cSLiam Girdwood 783c5914b0aSMark Brown if (cpu_dai->driver->ops && cpu_dai->driver->ops->delay) 784ddee627cSLiam Girdwood delay += cpu_dai->driver->ops->delay(substream, cpu_dai); 785ddee627cSLiam Girdwood 786c5914b0aSMark Brown if (codec_dai->driver->ops && codec_dai->driver->ops->delay) 787ddee627cSLiam Girdwood delay += codec_dai->driver->ops->delay(substream, codec_dai); 788ddee627cSLiam Girdwood 789ddee627cSLiam Girdwood if (platform->driver->delay) 790ddee627cSLiam Girdwood delay += platform->driver->delay(substream, codec_dai); 791ddee627cSLiam Girdwood 792ddee627cSLiam Girdwood runtime->delay = delay; 793ddee627cSLiam Girdwood 794ddee627cSLiam Girdwood return offset; 795ddee627cSLiam Girdwood } 796ddee627cSLiam Girdwood 79701d7584cSLiam Girdwood /* connect a FE and BE */ 79801d7584cSLiam Girdwood static int dpcm_be_connect(struct snd_soc_pcm_runtime *fe, 79901d7584cSLiam Girdwood struct snd_soc_pcm_runtime *be, int stream) 80001d7584cSLiam Girdwood { 80101d7584cSLiam Girdwood struct snd_soc_dpcm *dpcm; 80201d7584cSLiam Girdwood 80301d7584cSLiam Girdwood /* only add new dpcms */ 80401d7584cSLiam Girdwood list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) { 80501d7584cSLiam Girdwood if (dpcm->be == be && dpcm->fe == fe) 80601d7584cSLiam Girdwood return 0; 80701d7584cSLiam Girdwood } 80801d7584cSLiam Girdwood 80901d7584cSLiam Girdwood dpcm = kzalloc(sizeof(struct snd_soc_dpcm), GFP_KERNEL); 81001d7584cSLiam Girdwood if (!dpcm) 81101d7584cSLiam Girdwood return -ENOMEM; 81201d7584cSLiam Girdwood 81301d7584cSLiam Girdwood dpcm->be = be; 81401d7584cSLiam Girdwood dpcm->fe = fe; 81501d7584cSLiam Girdwood be->dpcm[stream].runtime = fe->dpcm[stream].runtime; 81601d7584cSLiam Girdwood dpcm->state = SND_SOC_DPCM_LINK_STATE_NEW; 81701d7584cSLiam Girdwood list_add(&dpcm->list_be, &fe->dpcm[stream].be_clients); 81801d7584cSLiam Girdwood list_add(&dpcm->list_fe, &be->dpcm[stream].fe_clients); 81901d7584cSLiam Girdwood 82001d7584cSLiam Girdwood dev_dbg(fe->dev, "connected new DPCM %s path %s %s %s\n", 82101d7584cSLiam Girdwood stream ? "capture" : "playback", fe->dai_link->name, 82201d7584cSLiam Girdwood stream ? "<-" : "->", be->dai_link->name); 82301d7584cSLiam Girdwood 824f86dcef8SLiam Girdwood #ifdef CONFIG_DEBUG_FS 825f86dcef8SLiam Girdwood dpcm->debugfs_state = debugfs_create_u32(be->dai_link->name, 0644, 826f86dcef8SLiam Girdwood fe->debugfs_dpcm_root, &dpcm->state); 827f86dcef8SLiam Girdwood #endif 82801d7584cSLiam Girdwood return 1; 82901d7584cSLiam Girdwood } 83001d7584cSLiam Girdwood 83101d7584cSLiam Girdwood /* reparent a BE onto another FE */ 83201d7584cSLiam Girdwood static void dpcm_be_reparent(struct snd_soc_pcm_runtime *fe, 83301d7584cSLiam Girdwood struct snd_soc_pcm_runtime *be, int stream) 83401d7584cSLiam Girdwood { 83501d7584cSLiam Girdwood struct snd_soc_dpcm *dpcm; 83601d7584cSLiam Girdwood struct snd_pcm_substream *fe_substream, *be_substream; 83701d7584cSLiam Girdwood 83801d7584cSLiam Girdwood /* reparent if BE is connected to other FEs */ 83901d7584cSLiam Girdwood if (!be->dpcm[stream].users) 84001d7584cSLiam Girdwood return; 84101d7584cSLiam Girdwood 84201d7584cSLiam Girdwood be_substream = snd_soc_dpcm_get_substream(be, stream); 84301d7584cSLiam Girdwood 84401d7584cSLiam Girdwood list_for_each_entry(dpcm, &be->dpcm[stream].fe_clients, list_fe) { 84501d7584cSLiam Girdwood if (dpcm->fe == fe) 84601d7584cSLiam Girdwood continue; 84701d7584cSLiam Girdwood 84801d7584cSLiam Girdwood dev_dbg(fe->dev, "reparent %s path %s %s %s\n", 84901d7584cSLiam Girdwood stream ? "capture" : "playback", 85001d7584cSLiam Girdwood dpcm->fe->dai_link->name, 85101d7584cSLiam Girdwood stream ? "<-" : "->", dpcm->be->dai_link->name); 85201d7584cSLiam Girdwood 85301d7584cSLiam Girdwood fe_substream = snd_soc_dpcm_get_substream(dpcm->fe, stream); 85401d7584cSLiam Girdwood be_substream->runtime = fe_substream->runtime; 85501d7584cSLiam Girdwood break; 85601d7584cSLiam Girdwood } 85701d7584cSLiam Girdwood } 85801d7584cSLiam Girdwood 85901d7584cSLiam Girdwood /* disconnect a BE and FE */ 86001d7584cSLiam Girdwood static void dpcm_be_disconnect(struct snd_soc_pcm_runtime *fe, int stream) 86101d7584cSLiam Girdwood { 86201d7584cSLiam Girdwood struct snd_soc_dpcm *dpcm, *d; 86301d7584cSLiam Girdwood 86401d7584cSLiam Girdwood list_for_each_entry_safe(dpcm, d, &fe->dpcm[stream].be_clients, list_be) { 865103d84a3SLiam Girdwood dev_dbg(fe->dev, "ASoC: BE %s disconnect check for %s\n", 86601d7584cSLiam Girdwood stream ? "capture" : "playback", 86701d7584cSLiam Girdwood dpcm->be->dai_link->name); 86801d7584cSLiam Girdwood 86901d7584cSLiam Girdwood if (dpcm->state != SND_SOC_DPCM_LINK_STATE_FREE) 87001d7584cSLiam Girdwood continue; 87101d7584cSLiam Girdwood 87201d7584cSLiam Girdwood dev_dbg(fe->dev, "freed DSP %s path %s %s %s\n", 87301d7584cSLiam Girdwood stream ? "capture" : "playback", fe->dai_link->name, 87401d7584cSLiam Girdwood stream ? "<-" : "->", dpcm->be->dai_link->name); 87501d7584cSLiam Girdwood 87601d7584cSLiam Girdwood /* BEs still alive need new FE */ 87701d7584cSLiam Girdwood dpcm_be_reparent(fe, dpcm->be, stream); 87801d7584cSLiam Girdwood 879f86dcef8SLiam Girdwood #ifdef CONFIG_DEBUG_FS 880f86dcef8SLiam Girdwood debugfs_remove(dpcm->debugfs_state); 881f86dcef8SLiam Girdwood #endif 88201d7584cSLiam Girdwood list_del(&dpcm->list_be); 88301d7584cSLiam Girdwood list_del(&dpcm->list_fe); 88401d7584cSLiam Girdwood kfree(dpcm); 88501d7584cSLiam Girdwood } 88601d7584cSLiam Girdwood } 88701d7584cSLiam Girdwood 88801d7584cSLiam Girdwood /* get BE for DAI widget and stream */ 88901d7584cSLiam Girdwood static struct snd_soc_pcm_runtime *dpcm_get_be(struct snd_soc_card *card, 89001d7584cSLiam Girdwood struct snd_soc_dapm_widget *widget, int stream) 89101d7584cSLiam Girdwood { 89201d7584cSLiam Girdwood struct snd_soc_pcm_runtime *be; 89301d7584cSLiam Girdwood int i; 89401d7584cSLiam Girdwood 89501d7584cSLiam Girdwood if (stream == SNDRV_PCM_STREAM_PLAYBACK) { 89601d7584cSLiam Girdwood for (i = 0; i < card->num_links; i++) { 89701d7584cSLiam Girdwood be = &card->rtd[i]; 89801d7584cSLiam Girdwood 89935ea0655SLiam Girdwood if (!be->dai_link->no_pcm) 90035ea0655SLiam Girdwood continue; 90135ea0655SLiam Girdwood 90201d7584cSLiam Girdwood if (be->cpu_dai->playback_widget == widget || 90301d7584cSLiam Girdwood be->codec_dai->playback_widget == widget) 90401d7584cSLiam Girdwood return be; 90501d7584cSLiam Girdwood } 90601d7584cSLiam Girdwood } else { 90701d7584cSLiam Girdwood 90801d7584cSLiam Girdwood for (i = 0; i < card->num_links; i++) { 90901d7584cSLiam Girdwood be = &card->rtd[i]; 91001d7584cSLiam Girdwood 91135ea0655SLiam Girdwood if (!be->dai_link->no_pcm) 91235ea0655SLiam Girdwood continue; 91335ea0655SLiam Girdwood 91401d7584cSLiam Girdwood if (be->cpu_dai->capture_widget == widget || 91501d7584cSLiam Girdwood be->codec_dai->capture_widget == widget) 91601d7584cSLiam Girdwood return be; 91701d7584cSLiam Girdwood } 91801d7584cSLiam Girdwood } 91901d7584cSLiam Girdwood 920103d84a3SLiam Girdwood dev_err(card->dev, "ASoC: can't get %s BE for %s\n", 92101d7584cSLiam Girdwood stream ? "capture" : "playback", widget->name); 92201d7584cSLiam Girdwood return NULL; 92301d7584cSLiam Girdwood } 92401d7584cSLiam Girdwood 92501d7584cSLiam Girdwood static inline struct snd_soc_dapm_widget * 92601d7584cSLiam Girdwood rtd_get_cpu_widget(struct snd_soc_pcm_runtime *rtd, int stream) 92701d7584cSLiam Girdwood { 92801d7584cSLiam Girdwood if (stream == SNDRV_PCM_STREAM_PLAYBACK) 92901d7584cSLiam Girdwood return rtd->cpu_dai->playback_widget; 93001d7584cSLiam Girdwood else 93101d7584cSLiam Girdwood return rtd->cpu_dai->capture_widget; 93201d7584cSLiam Girdwood } 93301d7584cSLiam Girdwood 93401d7584cSLiam Girdwood static inline struct snd_soc_dapm_widget * 93501d7584cSLiam Girdwood rtd_get_codec_widget(struct snd_soc_pcm_runtime *rtd, int stream) 93601d7584cSLiam Girdwood { 93701d7584cSLiam Girdwood if (stream == SNDRV_PCM_STREAM_PLAYBACK) 93801d7584cSLiam Girdwood return rtd->codec_dai->playback_widget; 93901d7584cSLiam Girdwood else 94001d7584cSLiam Girdwood return rtd->codec_dai->capture_widget; 94101d7584cSLiam Girdwood } 94201d7584cSLiam Girdwood 94301d7584cSLiam Girdwood static int widget_in_list(struct snd_soc_dapm_widget_list *list, 94401d7584cSLiam Girdwood struct snd_soc_dapm_widget *widget) 94501d7584cSLiam Girdwood { 94601d7584cSLiam Girdwood int i; 94701d7584cSLiam Girdwood 94801d7584cSLiam Girdwood for (i = 0; i < list->num_widgets; i++) { 94901d7584cSLiam Girdwood if (widget == list->widgets[i]) 95001d7584cSLiam Girdwood return 1; 95101d7584cSLiam Girdwood } 95201d7584cSLiam Girdwood 95301d7584cSLiam Girdwood return 0; 95401d7584cSLiam Girdwood } 95501d7584cSLiam Girdwood 95601d7584cSLiam Girdwood static int dpcm_path_get(struct snd_soc_pcm_runtime *fe, 95701d7584cSLiam Girdwood int stream, struct snd_soc_dapm_widget_list **list_) 95801d7584cSLiam Girdwood { 95901d7584cSLiam Girdwood struct snd_soc_dai *cpu_dai = fe->cpu_dai; 96001d7584cSLiam Girdwood struct snd_soc_dapm_widget_list *list; 96101d7584cSLiam Girdwood int paths; 96201d7584cSLiam Girdwood 96301d7584cSLiam Girdwood list = kzalloc(sizeof(struct snd_soc_dapm_widget_list) + 96401d7584cSLiam Girdwood sizeof(struct snd_soc_dapm_widget *), GFP_KERNEL); 96501d7584cSLiam Girdwood if (list == NULL) 96601d7584cSLiam Girdwood return -ENOMEM; 96701d7584cSLiam Girdwood 96801d7584cSLiam Girdwood /* get number of valid DAI paths and their widgets */ 96901d7584cSLiam Girdwood paths = snd_soc_dapm_dai_get_connected_widgets(cpu_dai, stream, &list); 97001d7584cSLiam Girdwood 971103d84a3SLiam Girdwood dev_dbg(fe->dev, "ASoC: found %d audio %s paths\n", paths, 97201d7584cSLiam Girdwood stream ? "capture" : "playback"); 97301d7584cSLiam Girdwood 97401d7584cSLiam Girdwood *list_ = list; 97501d7584cSLiam Girdwood return paths; 97601d7584cSLiam Girdwood } 97701d7584cSLiam Girdwood 97801d7584cSLiam Girdwood static inline void dpcm_path_put(struct snd_soc_dapm_widget_list **list) 97901d7584cSLiam Girdwood { 98001d7584cSLiam Girdwood kfree(*list); 98101d7584cSLiam Girdwood } 98201d7584cSLiam Girdwood 98301d7584cSLiam Girdwood static int dpcm_prune_paths(struct snd_soc_pcm_runtime *fe, int stream, 98401d7584cSLiam Girdwood struct snd_soc_dapm_widget_list **list_) 98501d7584cSLiam Girdwood { 98601d7584cSLiam Girdwood struct snd_soc_dpcm *dpcm; 98701d7584cSLiam Girdwood struct snd_soc_dapm_widget_list *list = *list_; 98801d7584cSLiam Girdwood struct snd_soc_dapm_widget *widget; 98901d7584cSLiam Girdwood int prune = 0; 99001d7584cSLiam Girdwood 99101d7584cSLiam Girdwood /* Destroy any old FE <--> BE connections */ 99201d7584cSLiam Girdwood list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) { 99301d7584cSLiam Girdwood 99401d7584cSLiam Girdwood /* is there a valid CPU DAI widget for this BE */ 99501d7584cSLiam Girdwood widget = rtd_get_cpu_widget(dpcm->be, stream); 99601d7584cSLiam Girdwood 99701d7584cSLiam Girdwood /* prune the BE if it's no longer in our active list */ 99801d7584cSLiam Girdwood if (widget && widget_in_list(list, widget)) 99901d7584cSLiam Girdwood continue; 100001d7584cSLiam Girdwood 100101d7584cSLiam Girdwood /* is there a valid CODEC DAI widget for this BE */ 100201d7584cSLiam Girdwood widget = rtd_get_codec_widget(dpcm->be, stream); 100301d7584cSLiam Girdwood 100401d7584cSLiam Girdwood /* prune the BE if it's no longer in our active list */ 100501d7584cSLiam Girdwood if (widget && widget_in_list(list, widget)) 100601d7584cSLiam Girdwood continue; 100701d7584cSLiam Girdwood 1008103d84a3SLiam Girdwood dev_dbg(fe->dev, "ASoC: pruning %s BE %s for %s\n", 100901d7584cSLiam Girdwood stream ? "capture" : "playback", 101001d7584cSLiam Girdwood dpcm->be->dai_link->name, fe->dai_link->name); 101101d7584cSLiam Girdwood dpcm->state = SND_SOC_DPCM_LINK_STATE_FREE; 101201d7584cSLiam Girdwood dpcm->be->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_BE; 101301d7584cSLiam Girdwood prune++; 101401d7584cSLiam Girdwood } 101501d7584cSLiam Girdwood 1016103d84a3SLiam Girdwood dev_dbg(fe->dev, "ASoC: found %d old BE paths for pruning\n", prune); 101701d7584cSLiam Girdwood return prune; 101801d7584cSLiam Girdwood } 101901d7584cSLiam Girdwood 102001d7584cSLiam Girdwood static int dpcm_add_paths(struct snd_soc_pcm_runtime *fe, int stream, 102101d7584cSLiam Girdwood struct snd_soc_dapm_widget_list **list_) 102201d7584cSLiam Girdwood { 102301d7584cSLiam Girdwood struct snd_soc_card *card = fe->card; 102401d7584cSLiam Girdwood struct snd_soc_dapm_widget_list *list = *list_; 102501d7584cSLiam Girdwood struct snd_soc_pcm_runtime *be; 102601d7584cSLiam Girdwood int i, new = 0, err; 102701d7584cSLiam Girdwood 102801d7584cSLiam Girdwood /* Create any new FE <--> BE connections */ 102901d7584cSLiam Girdwood for (i = 0; i < list->num_widgets; i++) { 103001d7584cSLiam Girdwood 10314616274dSMark Brown switch (list->widgets[i]->id) { 10324616274dSMark Brown case snd_soc_dapm_dai_in: 10334616274dSMark Brown case snd_soc_dapm_dai_out: 10344616274dSMark Brown break; 10354616274dSMark Brown default: 103601d7584cSLiam Girdwood continue; 10374616274dSMark Brown } 103801d7584cSLiam Girdwood 103901d7584cSLiam Girdwood /* is there a valid BE rtd for this widget */ 104001d7584cSLiam Girdwood be = dpcm_get_be(card, list->widgets[i], stream); 104101d7584cSLiam Girdwood if (!be) { 1042103d84a3SLiam Girdwood dev_err(fe->dev, "ASoC: no BE found for %s\n", 104301d7584cSLiam Girdwood list->widgets[i]->name); 104401d7584cSLiam Girdwood continue; 104501d7584cSLiam Girdwood } 104601d7584cSLiam Girdwood 104701d7584cSLiam Girdwood /* make sure BE is a real BE */ 104801d7584cSLiam Girdwood if (!be->dai_link->no_pcm) 104901d7584cSLiam Girdwood continue; 105001d7584cSLiam Girdwood 105101d7584cSLiam Girdwood /* don't connect if FE is not running */ 105201d7584cSLiam Girdwood if (!fe->dpcm[stream].runtime) 105301d7584cSLiam Girdwood continue; 105401d7584cSLiam Girdwood 105501d7584cSLiam Girdwood /* newly connected FE and BE */ 105601d7584cSLiam Girdwood err = dpcm_be_connect(fe, be, stream); 105701d7584cSLiam Girdwood if (err < 0) { 1058103d84a3SLiam Girdwood dev_err(fe->dev, "ASoC: can't connect %s\n", 105901d7584cSLiam Girdwood list->widgets[i]->name); 106001d7584cSLiam Girdwood break; 106101d7584cSLiam Girdwood } else if (err == 0) /* already connected */ 106201d7584cSLiam Girdwood continue; 106301d7584cSLiam Girdwood 106401d7584cSLiam Girdwood /* new */ 106501d7584cSLiam Girdwood be->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_BE; 106601d7584cSLiam Girdwood new++; 106701d7584cSLiam Girdwood } 106801d7584cSLiam Girdwood 1069103d84a3SLiam Girdwood dev_dbg(fe->dev, "ASoC: found %d new BE paths\n", new); 107001d7584cSLiam Girdwood return new; 107101d7584cSLiam Girdwood } 107201d7584cSLiam Girdwood 107301d7584cSLiam Girdwood /* 107401d7584cSLiam Girdwood * Find the corresponding BE DAIs that source or sink audio to this 107501d7584cSLiam Girdwood * FE substream. 107601d7584cSLiam Girdwood */ 107701d7584cSLiam Girdwood static int dpcm_process_paths(struct snd_soc_pcm_runtime *fe, 107801d7584cSLiam Girdwood int stream, struct snd_soc_dapm_widget_list **list, int new) 107901d7584cSLiam Girdwood { 108001d7584cSLiam Girdwood if (new) 108101d7584cSLiam Girdwood return dpcm_add_paths(fe, stream, list); 108201d7584cSLiam Girdwood else 108301d7584cSLiam Girdwood return dpcm_prune_paths(fe, stream, list); 108401d7584cSLiam Girdwood } 108501d7584cSLiam Girdwood 108601d7584cSLiam Girdwood static void dpcm_clear_pending_state(struct snd_soc_pcm_runtime *fe, int stream) 108701d7584cSLiam Girdwood { 108801d7584cSLiam Girdwood struct snd_soc_dpcm *dpcm; 108901d7584cSLiam Girdwood 109001d7584cSLiam Girdwood list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) 109101d7584cSLiam Girdwood dpcm->be->dpcm[stream].runtime_update = 109201d7584cSLiam Girdwood SND_SOC_DPCM_UPDATE_NO; 109301d7584cSLiam Girdwood } 109401d7584cSLiam Girdwood 109501d7584cSLiam Girdwood static void dpcm_be_dai_startup_unwind(struct snd_soc_pcm_runtime *fe, 109601d7584cSLiam Girdwood int stream) 109701d7584cSLiam Girdwood { 109801d7584cSLiam Girdwood struct snd_soc_dpcm *dpcm; 109901d7584cSLiam Girdwood 110001d7584cSLiam Girdwood /* disable any enabled and non active backends */ 110101d7584cSLiam Girdwood list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) { 110201d7584cSLiam Girdwood 110301d7584cSLiam Girdwood struct snd_soc_pcm_runtime *be = dpcm->be; 110401d7584cSLiam Girdwood struct snd_pcm_substream *be_substream = 110501d7584cSLiam Girdwood snd_soc_dpcm_get_substream(be, stream); 110601d7584cSLiam Girdwood 110701d7584cSLiam Girdwood if (be->dpcm[stream].users == 0) 1108103d84a3SLiam Girdwood dev_err(be->dev, "ASoC: no users %s at close - state %d\n", 110901d7584cSLiam Girdwood stream ? "capture" : "playback", 111001d7584cSLiam Girdwood be->dpcm[stream].state); 111101d7584cSLiam Girdwood 111201d7584cSLiam Girdwood if (--be->dpcm[stream].users != 0) 111301d7584cSLiam Girdwood continue; 111401d7584cSLiam Girdwood 111501d7584cSLiam Girdwood if (be->dpcm[stream].state != SND_SOC_DPCM_STATE_OPEN) 111601d7584cSLiam Girdwood continue; 111701d7584cSLiam Girdwood 111801d7584cSLiam Girdwood soc_pcm_close(be_substream); 111901d7584cSLiam Girdwood be_substream->runtime = NULL; 112001d7584cSLiam Girdwood be->dpcm[stream].state = SND_SOC_DPCM_STATE_CLOSE; 112101d7584cSLiam Girdwood } 112201d7584cSLiam Girdwood } 112301d7584cSLiam Girdwood 112401d7584cSLiam Girdwood static int dpcm_be_dai_startup(struct snd_soc_pcm_runtime *fe, int stream) 112501d7584cSLiam Girdwood { 112601d7584cSLiam Girdwood struct snd_soc_dpcm *dpcm; 112701d7584cSLiam Girdwood int err, count = 0; 112801d7584cSLiam Girdwood 112901d7584cSLiam Girdwood /* only startup BE DAIs that are either sinks or sources to this FE DAI */ 113001d7584cSLiam Girdwood list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) { 113101d7584cSLiam Girdwood 113201d7584cSLiam Girdwood struct snd_soc_pcm_runtime *be = dpcm->be; 113301d7584cSLiam Girdwood struct snd_pcm_substream *be_substream = 113401d7584cSLiam Girdwood snd_soc_dpcm_get_substream(be, stream); 113501d7584cSLiam Girdwood 11362062b4c5SRussell King - ARM Linux if (!be_substream) { 11372062b4c5SRussell King - ARM Linux dev_err(be->dev, "ASoC: no backend %s stream\n", 11382062b4c5SRussell King - ARM Linux stream ? "capture" : "playback"); 11392062b4c5SRussell King - ARM Linux continue; 11402062b4c5SRussell King - ARM Linux } 11412062b4c5SRussell King - ARM Linux 114201d7584cSLiam Girdwood /* is this op for this BE ? */ 114301d7584cSLiam Girdwood if (!snd_soc_dpcm_be_can_update(fe, be, stream)) 114401d7584cSLiam Girdwood continue; 114501d7584cSLiam Girdwood 114601d7584cSLiam Girdwood /* first time the dpcm is open ? */ 114701d7584cSLiam Girdwood if (be->dpcm[stream].users == DPCM_MAX_BE_USERS) 1148103d84a3SLiam Girdwood dev_err(be->dev, "ASoC: too many users %s at open %d\n", 114901d7584cSLiam Girdwood stream ? "capture" : "playback", 115001d7584cSLiam Girdwood be->dpcm[stream].state); 115101d7584cSLiam Girdwood 115201d7584cSLiam Girdwood if (be->dpcm[stream].users++ != 0) 115301d7584cSLiam Girdwood continue; 115401d7584cSLiam Girdwood 115501d7584cSLiam Girdwood if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_NEW) && 115601d7584cSLiam Girdwood (be->dpcm[stream].state != SND_SOC_DPCM_STATE_CLOSE)) 115701d7584cSLiam Girdwood continue; 115801d7584cSLiam Girdwood 11592062b4c5SRussell King - ARM Linux dev_dbg(be->dev, "ASoC: open %s BE %s\n", 11602062b4c5SRussell King - ARM Linux stream ? "capture" : "playback", be->dai_link->name); 116101d7584cSLiam Girdwood 116201d7584cSLiam Girdwood be_substream->runtime = be->dpcm[stream].runtime; 116301d7584cSLiam Girdwood err = soc_pcm_open(be_substream); 116401d7584cSLiam Girdwood if (err < 0) { 1165103d84a3SLiam Girdwood dev_err(be->dev, "ASoC: BE open failed %d\n", err); 116601d7584cSLiam Girdwood be->dpcm[stream].users--; 116701d7584cSLiam Girdwood if (be->dpcm[stream].users < 0) 1168103d84a3SLiam Girdwood dev_err(be->dev, "ASoC: no users %s at unwind %d\n", 116901d7584cSLiam Girdwood stream ? "capture" : "playback", 117001d7584cSLiam Girdwood be->dpcm[stream].state); 117101d7584cSLiam Girdwood 117201d7584cSLiam Girdwood be->dpcm[stream].state = SND_SOC_DPCM_STATE_CLOSE; 117301d7584cSLiam Girdwood goto unwind; 117401d7584cSLiam Girdwood } 117501d7584cSLiam Girdwood 117601d7584cSLiam Girdwood be->dpcm[stream].state = SND_SOC_DPCM_STATE_OPEN; 117701d7584cSLiam Girdwood count++; 117801d7584cSLiam Girdwood } 117901d7584cSLiam Girdwood 118001d7584cSLiam Girdwood return count; 118101d7584cSLiam Girdwood 118201d7584cSLiam Girdwood unwind: 118301d7584cSLiam Girdwood /* disable any enabled and non active backends */ 118401d7584cSLiam Girdwood list_for_each_entry_continue_reverse(dpcm, &fe->dpcm[stream].be_clients, list_be) { 118501d7584cSLiam Girdwood struct snd_soc_pcm_runtime *be = dpcm->be; 118601d7584cSLiam Girdwood struct snd_pcm_substream *be_substream = 118701d7584cSLiam Girdwood snd_soc_dpcm_get_substream(be, stream); 118801d7584cSLiam Girdwood 118901d7584cSLiam Girdwood if (!snd_soc_dpcm_be_can_update(fe, be, stream)) 119001d7584cSLiam Girdwood continue; 119101d7584cSLiam Girdwood 119201d7584cSLiam Girdwood if (be->dpcm[stream].users == 0) 1193103d84a3SLiam Girdwood dev_err(be->dev, "ASoC: no users %s at close %d\n", 119401d7584cSLiam Girdwood stream ? "capture" : "playback", 119501d7584cSLiam Girdwood be->dpcm[stream].state); 119601d7584cSLiam Girdwood 119701d7584cSLiam Girdwood if (--be->dpcm[stream].users != 0) 119801d7584cSLiam Girdwood continue; 119901d7584cSLiam Girdwood 120001d7584cSLiam Girdwood if (be->dpcm[stream].state != SND_SOC_DPCM_STATE_OPEN) 120101d7584cSLiam Girdwood continue; 120201d7584cSLiam Girdwood 120301d7584cSLiam Girdwood soc_pcm_close(be_substream); 120401d7584cSLiam Girdwood be_substream->runtime = NULL; 120501d7584cSLiam Girdwood be->dpcm[stream].state = SND_SOC_DPCM_STATE_CLOSE; 120601d7584cSLiam Girdwood } 120701d7584cSLiam Girdwood 120801d7584cSLiam Girdwood return err; 120901d7584cSLiam Girdwood } 121001d7584cSLiam Girdwood 121145c0a188SMark Brown static void dpcm_set_fe_runtime(struct snd_pcm_substream *substream) 121201d7584cSLiam Girdwood { 121301d7584cSLiam Girdwood struct snd_pcm_runtime *runtime = substream->runtime; 121401d7584cSLiam Girdwood struct snd_soc_pcm_runtime *rtd = substream->private_data; 121501d7584cSLiam Girdwood struct snd_soc_dai *cpu_dai = rtd->cpu_dai; 121601d7584cSLiam Girdwood struct snd_soc_dai_driver *cpu_dai_drv = cpu_dai->driver; 121701d7584cSLiam Girdwood 121801d7584cSLiam Girdwood if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 121901d7584cSLiam Girdwood runtime->hw.rate_min = cpu_dai_drv->playback.rate_min; 122001d7584cSLiam Girdwood runtime->hw.rate_max = cpu_dai_drv->playback.rate_max; 122101d7584cSLiam Girdwood runtime->hw.channels_min = cpu_dai_drv->playback.channels_min; 122201d7584cSLiam Girdwood runtime->hw.channels_max = cpu_dai_drv->playback.channels_max; 122301d7584cSLiam Girdwood runtime->hw.formats &= cpu_dai_drv->playback.formats; 122401d7584cSLiam Girdwood runtime->hw.rates = cpu_dai_drv->playback.rates; 122501d7584cSLiam Girdwood } else { 122601d7584cSLiam Girdwood runtime->hw.rate_min = cpu_dai_drv->capture.rate_min; 122701d7584cSLiam Girdwood runtime->hw.rate_max = cpu_dai_drv->capture.rate_max; 122801d7584cSLiam Girdwood runtime->hw.channels_min = cpu_dai_drv->capture.channels_min; 122901d7584cSLiam Girdwood runtime->hw.channels_max = cpu_dai_drv->capture.channels_max; 123001d7584cSLiam Girdwood runtime->hw.formats &= cpu_dai_drv->capture.formats; 123101d7584cSLiam Girdwood runtime->hw.rates = cpu_dai_drv->capture.rates; 123201d7584cSLiam Girdwood } 123301d7584cSLiam Girdwood } 123401d7584cSLiam Girdwood 123501d7584cSLiam Girdwood static int dpcm_fe_dai_startup(struct snd_pcm_substream *fe_substream) 123601d7584cSLiam Girdwood { 123701d7584cSLiam Girdwood struct snd_soc_pcm_runtime *fe = fe_substream->private_data; 123801d7584cSLiam Girdwood struct snd_pcm_runtime *runtime = fe_substream->runtime; 123901d7584cSLiam Girdwood int stream = fe_substream->stream, ret = 0; 124001d7584cSLiam Girdwood 124101d7584cSLiam Girdwood fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE; 124201d7584cSLiam Girdwood 124301d7584cSLiam Girdwood ret = dpcm_be_dai_startup(fe, fe_substream->stream); 124401d7584cSLiam Girdwood if (ret < 0) { 1245103d84a3SLiam Girdwood dev_err(fe->dev,"ASoC: failed to start some BEs %d\n", ret); 124601d7584cSLiam Girdwood goto be_err; 124701d7584cSLiam Girdwood } 124801d7584cSLiam Girdwood 1249103d84a3SLiam Girdwood dev_dbg(fe->dev, "ASoC: open FE %s\n", fe->dai_link->name); 125001d7584cSLiam Girdwood 125101d7584cSLiam Girdwood /* start the DAI frontend */ 125201d7584cSLiam Girdwood ret = soc_pcm_open(fe_substream); 125301d7584cSLiam Girdwood if (ret < 0) { 1254103d84a3SLiam Girdwood dev_err(fe->dev,"ASoC: failed to start FE %d\n", ret); 125501d7584cSLiam Girdwood goto unwind; 125601d7584cSLiam Girdwood } 125701d7584cSLiam Girdwood 125801d7584cSLiam Girdwood fe->dpcm[stream].state = SND_SOC_DPCM_STATE_OPEN; 125901d7584cSLiam Girdwood 126001d7584cSLiam Girdwood dpcm_set_fe_runtime(fe_substream); 126101d7584cSLiam Girdwood snd_pcm_limit_hw_rates(runtime); 126201d7584cSLiam Girdwood 126301d7584cSLiam Girdwood fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO; 126401d7584cSLiam Girdwood return 0; 126501d7584cSLiam Girdwood 126601d7584cSLiam Girdwood unwind: 126701d7584cSLiam Girdwood dpcm_be_dai_startup_unwind(fe, fe_substream->stream); 126801d7584cSLiam Girdwood be_err: 126901d7584cSLiam Girdwood fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO; 127001d7584cSLiam Girdwood return ret; 127101d7584cSLiam Girdwood } 127201d7584cSLiam Girdwood 127301d7584cSLiam Girdwood static int dpcm_be_dai_shutdown(struct snd_soc_pcm_runtime *fe, int stream) 127401d7584cSLiam Girdwood { 127501d7584cSLiam Girdwood struct snd_soc_dpcm *dpcm; 127601d7584cSLiam Girdwood 127701d7584cSLiam Girdwood /* only shutdown BEs that are either sinks or sources to this FE DAI */ 127801d7584cSLiam Girdwood list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) { 127901d7584cSLiam Girdwood 128001d7584cSLiam Girdwood struct snd_soc_pcm_runtime *be = dpcm->be; 128101d7584cSLiam Girdwood struct snd_pcm_substream *be_substream = 128201d7584cSLiam Girdwood snd_soc_dpcm_get_substream(be, stream); 128301d7584cSLiam Girdwood 128401d7584cSLiam Girdwood /* is this op for this BE ? */ 128501d7584cSLiam Girdwood if (!snd_soc_dpcm_be_can_update(fe, be, stream)) 128601d7584cSLiam Girdwood continue; 128701d7584cSLiam Girdwood 128801d7584cSLiam Girdwood if (be->dpcm[stream].users == 0) 1289103d84a3SLiam Girdwood dev_err(be->dev, "ASoC: no users %s at close - state %d\n", 129001d7584cSLiam Girdwood stream ? "capture" : "playback", 129101d7584cSLiam Girdwood be->dpcm[stream].state); 129201d7584cSLiam Girdwood 129301d7584cSLiam Girdwood if (--be->dpcm[stream].users != 0) 129401d7584cSLiam Girdwood continue; 129501d7584cSLiam Girdwood 129601d7584cSLiam Girdwood if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_FREE) && 129701d7584cSLiam Girdwood (be->dpcm[stream].state != SND_SOC_DPCM_STATE_OPEN)) 129801d7584cSLiam Girdwood continue; 129901d7584cSLiam Girdwood 1300103d84a3SLiam Girdwood dev_dbg(be->dev, "ASoC: close BE %s\n", 130101d7584cSLiam Girdwood dpcm->fe->dai_link->name); 130201d7584cSLiam Girdwood 130301d7584cSLiam Girdwood soc_pcm_close(be_substream); 130401d7584cSLiam Girdwood be_substream->runtime = NULL; 130501d7584cSLiam Girdwood 130601d7584cSLiam Girdwood be->dpcm[stream].state = SND_SOC_DPCM_STATE_CLOSE; 130701d7584cSLiam Girdwood } 130801d7584cSLiam Girdwood return 0; 130901d7584cSLiam Girdwood } 131001d7584cSLiam Girdwood 131101d7584cSLiam Girdwood static int dpcm_fe_dai_shutdown(struct snd_pcm_substream *substream) 131201d7584cSLiam Girdwood { 131301d7584cSLiam Girdwood struct snd_soc_pcm_runtime *fe = substream->private_data; 131401d7584cSLiam Girdwood int stream = substream->stream; 131501d7584cSLiam Girdwood 131601d7584cSLiam Girdwood fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE; 131701d7584cSLiam Girdwood 131801d7584cSLiam Girdwood /* shutdown the BEs */ 131901d7584cSLiam Girdwood dpcm_be_dai_shutdown(fe, substream->stream); 132001d7584cSLiam Girdwood 1321103d84a3SLiam Girdwood dev_dbg(fe->dev, "ASoC: close FE %s\n", fe->dai_link->name); 132201d7584cSLiam Girdwood 132301d7584cSLiam Girdwood /* now shutdown the frontend */ 132401d7584cSLiam Girdwood soc_pcm_close(substream); 132501d7584cSLiam Girdwood 132601d7584cSLiam Girdwood /* run the stream event for each BE */ 132701d7584cSLiam Girdwood dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_STOP); 132801d7584cSLiam Girdwood 132901d7584cSLiam Girdwood fe->dpcm[stream].state = SND_SOC_DPCM_STATE_CLOSE; 133001d7584cSLiam Girdwood fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO; 133101d7584cSLiam Girdwood return 0; 133201d7584cSLiam Girdwood } 133301d7584cSLiam Girdwood 133401d7584cSLiam Girdwood static int dpcm_be_dai_hw_free(struct snd_soc_pcm_runtime *fe, int stream) 133501d7584cSLiam Girdwood { 133601d7584cSLiam Girdwood struct snd_soc_dpcm *dpcm; 133701d7584cSLiam Girdwood 133801d7584cSLiam Girdwood /* only hw_params backends that are either sinks or sources 133901d7584cSLiam Girdwood * to this frontend DAI */ 134001d7584cSLiam Girdwood list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) { 134101d7584cSLiam Girdwood 134201d7584cSLiam Girdwood struct snd_soc_pcm_runtime *be = dpcm->be; 134301d7584cSLiam Girdwood struct snd_pcm_substream *be_substream = 134401d7584cSLiam Girdwood snd_soc_dpcm_get_substream(be, stream); 134501d7584cSLiam Girdwood 134601d7584cSLiam Girdwood /* is this op for this BE ? */ 134701d7584cSLiam Girdwood if (!snd_soc_dpcm_be_can_update(fe, be, stream)) 134801d7584cSLiam Girdwood continue; 134901d7584cSLiam Girdwood 135001d7584cSLiam Girdwood /* only free hw when no longer used - check all FEs */ 135101d7584cSLiam Girdwood if (!snd_soc_dpcm_can_be_free_stop(fe, be, stream)) 135201d7584cSLiam Girdwood continue; 135301d7584cSLiam Girdwood 135401d7584cSLiam Girdwood if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_PARAMS) && 135501d7584cSLiam Girdwood (be->dpcm[stream].state != SND_SOC_DPCM_STATE_PREPARE) && 135601d7584cSLiam Girdwood (be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_FREE) && 135708b27848SPatrick Lai (be->dpcm[stream].state != SND_SOC_DPCM_STATE_PAUSED) && 135801d7584cSLiam Girdwood (be->dpcm[stream].state != SND_SOC_DPCM_STATE_STOP)) 135901d7584cSLiam Girdwood continue; 136001d7584cSLiam Girdwood 1361103d84a3SLiam Girdwood dev_dbg(be->dev, "ASoC: hw_free BE %s\n", 136201d7584cSLiam Girdwood dpcm->fe->dai_link->name); 136301d7584cSLiam Girdwood 136401d7584cSLiam Girdwood soc_pcm_hw_free(be_substream); 136501d7584cSLiam Girdwood 136601d7584cSLiam Girdwood be->dpcm[stream].state = SND_SOC_DPCM_STATE_HW_FREE; 136701d7584cSLiam Girdwood } 136801d7584cSLiam Girdwood 136901d7584cSLiam Girdwood return 0; 137001d7584cSLiam Girdwood } 137101d7584cSLiam Girdwood 137245c0a188SMark Brown static int dpcm_fe_dai_hw_free(struct snd_pcm_substream *substream) 137301d7584cSLiam Girdwood { 137401d7584cSLiam Girdwood struct snd_soc_pcm_runtime *fe = substream->private_data; 137501d7584cSLiam Girdwood int err, stream = substream->stream; 137601d7584cSLiam Girdwood 137701d7584cSLiam Girdwood mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME); 137801d7584cSLiam Girdwood fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE; 137901d7584cSLiam Girdwood 1380103d84a3SLiam Girdwood dev_dbg(fe->dev, "ASoC: hw_free FE %s\n", fe->dai_link->name); 138101d7584cSLiam Girdwood 138201d7584cSLiam Girdwood /* call hw_free on the frontend */ 138301d7584cSLiam Girdwood err = soc_pcm_hw_free(substream); 138401d7584cSLiam Girdwood if (err < 0) 1385103d84a3SLiam Girdwood dev_err(fe->dev,"ASoC: hw_free FE %s failed\n", 138601d7584cSLiam Girdwood fe->dai_link->name); 138701d7584cSLiam Girdwood 138801d7584cSLiam Girdwood /* only hw_params backends that are either sinks or sources 138901d7584cSLiam Girdwood * to this frontend DAI */ 139001d7584cSLiam Girdwood err = dpcm_be_dai_hw_free(fe, stream); 139101d7584cSLiam Girdwood 139201d7584cSLiam Girdwood fe->dpcm[stream].state = SND_SOC_DPCM_STATE_HW_FREE; 139301d7584cSLiam Girdwood fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO; 139401d7584cSLiam Girdwood 139501d7584cSLiam Girdwood mutex_unlock(&fe->card->mutex); 139601d7584cSLiam Girdwood return 0; 139701d7584cSLiam Girdwood } 139801d7584cSLiam Girdwood 139901d7584cSLiam Girdwood static int dpcm_be_dai_hw_params(struct snd_soc_pcm_runtime *fe, int stream) 140001d7584cSLiam Girdwood { 140101d7584cSLiam Girdwood struct snd_soc_dpcm *dpcm; 140201d7584cSLiam Girdwood int ret; 140301d7584cSLiam Girdwood 140401d7584cSLiam Girdwood list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) { 140501d7584cSLiam Girdwood 140601d7584cSLiam Girdwood struct snd_soc_pcm_runtime *be = dpcm->be; 140701d7584cSLiam Girdwood struct snd_pcm_substream *be_substream = 140801d7584cSLiam Girdwood snd_soc_dpcm_get_substream(be, stream); 140901d7584cSLiam Girdwood 141001d7584cSLiam Girdwood /* is this op for this BE ? */ 141101d7584cSLiam Girdwood if (!snd_soc_dpcm_be_can_update(fe, be, stream)) 141201d7584cSLiam Girdwood continue; 141301d7584cSLiam Girdwood 141401d7584cSLiam Girdwood /* only allow hw_params() if no connected FEs are running */ 141501d7584cSLiam Girdwood if (!snd_soc_dpcm_can_be_params(fe, be, stream)) 141601d7584cSLiam Girdwood continue; 141701d7584cSLiam Girdwood 141801d7584cSLiam Girdwood if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_OPEN) && 141901d7584cSLiam Girdwood (be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_PARAMS) && 142001d7584cSLiam Girdwood (be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_FREE)) 142101d7584cSLiam Girdwood continue; 142201d7584cSLiam Girdwood 1423103d84a3SLiam Girdwood dev_dbg(be->dev, "ASoC: hw_params BE %s\n", 142401d7584cSLiam Girdwood dpcm->fe->dai_link->name); 142501d7584cSLiam Girdwood 142601d7584cSLiam Girdwood /* copy params for each dpcm */ 142701d7584cSLiam Girdwood memcpy(&dpcm->hw_params, &fe->dpcm[stream].hw_params, 142801d7584cSLiam Girdwood sizeof(struct snd_pcm_hw_params)); 142901d7584cSLiam Girdwood 143001d7584cSLiam Girdwood /* perform any hw_params fixups */ 143101d7584cSLiam Girdwood if (be->dai_link->be_hw_params_fixup) { 143201d7584cSLiam Girdwood ret = be->dai_link->be_hw_params_fixup(be, 143301d7584cSLiam Girdwood &dpcm->hw_params); 143401d7584cSLiam Girdwood if (ret < 0) { 143501d7584cSLiam Girdwood dev_err(be->dev, 1436103d84a3SLiam Girdwood "ASoC: hw_params BE fixup failed %d\n", 143701d7584cSLiam Girdwood ret); 143801d7584cSLiam Girdwood goto unwind; 143901d7584cSLiam Girdwood } 144001d7584cSLiam Girdwood } 144101d7584cSLiam Girdwood 144201d7584cSLiam Girdwood ret = soc_pcm_hw_params(be_substream, &dpcm->hw_params); 144301d7584cSLiam Girdwood if (ret < 0) { 144401d7584cSLiam Girdwood dev_err(dpcm->be->dev, 1445103d84a3SLiam Girdwood "ASoC: hw_params BE failed %d\n", ret); 144601d7584cSLiam Girdwood goto unwind; 144701d7584cSLiam Girdwood } 144801d7584cSLiam Girdwood 144901d7584cSLiam Girdwood be->dpcm[stream].state = SND_SOC_DPCM_STATE_HW_PARAMS; 145001d7584cSLiam Girdwood } 145101d7584cSLiam Girdwood return 0; 145201d7584cSLiam Girdwood 145301d7584cSLiam Girdwood unwind: 145401d7584cSLiam Girdwood /* disable any enabled and non active backends */ 145501d7584cSLiam Girdwood list_for_each_entry_continue_reverse(dpcm, &fe->dpcm[stream].be_clients, list_be) { 145601d7584cSLiam Girdwood struct snd_soc_pcm_runtime *be = dpcm->be; 145701d7584cSLiam Girdwood struct snd_pcm_substream *be_substream = 145801d7584cSLiam Girdwood snd_soc_dpcm_get_substream(be, stream); 145901d7584cSLiam Girdwood 146001d7584cSLiam Girdwood if (!snd_soc_dpcm_be_can_update(fe, be, stream)) 146101d7584cSLiam Girdwood continue; 146201d7584cSLiam Girdwood 146301d7584cSLiam Girdwood /* only allow hw_free() if no connected FEs are running */ 146401d7584cSLiam Girdwood if (!snd_soc_dpcm_can_be_free_stop(fe, be, stream)) 146501d7584cSLiam Girdwood continue; 146601d7584cSLiam Girdwood 146701d7584cSLiam Girdwood if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_OPEN) && 146801d7584cSLiam Girdwood (be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_PARAMS) && 146901d7584cSLiam Girdwood (be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_FREE) && 147001d7584cSLiam Girdwood (be->dpcm[stream].state != SND_SOC_DPCM_STATE_STOP)) 147101d7584cSLiam Girdwood continue; 147201d7584cSLiam Girdwood 147301d7584cSLiam Girdwood soc_pcm_hw_free(be_substream); 147401d7584cSLiam Girdwood } 147501d7584cSLiam Girdwood 147601d7584cSLiam Girdwood return ret; 147701d7584cSLiam Girdwood } 147801d7584cSLiam Girdwood 147945c0a188SMark Brown static int dpcm_fe_dai_hw_params(struct snd_pcm_substream *substream, 148001d7584cSLiam Girdwood struct snd_pcm_hw_params *params) 148101d7584cSLiam Girdwood { 148201d7584cSLiam Girdwood struct snd_soc_pcm_runtime *fe = substream->private_data; 148301d7584cSLiam Girdwood int ret, stream = substream->stream; 148401d7584cSLiam Girdwood 148501d7584cSLiam Girdwood mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME); 148601d7584cSLiam Girdwood fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE; 148701d7584cSLiam Girdwood 148801d7584cSLiam Girdwood memcpy(&fe->dpcm[substream->stream].hw_params, params, 148901d7584cSLiam Girdwood sizeof(struct snd_pcm_hw_params)); 149001d7584cSLiam Girdwood ret = dpcm_be_dai_hw_params(fe, substream->stream); 149101d7584cSLiam Girdwood if (ret < 0) { 1492103d84a3SLiam Girdwood dev_err(fe->dev,"ASoC: hw_params BE failed %d\n", ret); 149301d7584cSLiam Girdwood goto out; 149401d7584cSLiam Girdwood } 149501d7584cSLiam Girdwood 1496103d84a3SLiam Girdwood dev_dbg(fe->dev, "ASoC: hw_params FE %s rate %d chan %x fmt %d\n", 149701d7584cSLiam Girdwood fe->dai_link->name, params_rate(params), 149801d7584cSLiam Girdwood params_channels(params), params_format(params)); 149901d7584cSLiam Girdwood 150001d7584cSLiam Girdwood /* call hw_params on the frontend */ 150101d7584cSLiam Girdwood ret = soc_pcm_hw_params(substream, params); 150201d7584cSLiam Girdwood if (ret < 0) { 1503103d84a3SLiam Girdwood dev_err(fe->dev,"ASoC: hw_params FE failed %d\n", ret); 150401d7584cSLiam Girdwood dpcm_be_dai_hw_free(fe, stream); 150501d7584cSLiam Girdwood } else 150601d7584cSLiam Girdwood fe->dpcm[stream].state = SND_SOC_DPCM_STATE_HW_PARAMS; 150701d7584cSLiam Girdwood 150801d7584cSLiam Girdwood out: 150901d7584cSLiam Girdwood fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO; 151001d7584cSLiam Girdwood mutex_unlock(&fe->card->mutex); 151101d7584cSLiam Girdwood return ret; 151201d7584cSLiam Girdwood } 151301d7584cSLiam Girdwood 151401d7584cSLiam Girdwood static int dpcm_do_trigger(struct snd_soc_dpcm *dpcm, 151501d7584cSLiam Girdwood struct snd_pcm_substream *substream, int cmd) 151601d7584cSLiam Girdwood { 151701d7584cSLiam Girdwood int ret; 151801d7584cSLiam Girdwood 1519103d84a3SLiam Girdwood dev_dbg(dpcm->be->dev, "ASoC: trigger BE %s cmd %d\n", 152001d7584cSLiam Girdwood dpcm->fe->dai_link->name, cmd); 152101d7584cSLiam Girdwood 152201d7584cSLiam Girdwood ret = soc_pcm_trigger(substream, cmd); 152301d7584cSLiam Girdwood if (ret < 0) 1524103d84a3SLiam Girdwood dev_err(dpcm->be->dev,"ASoC: trigger BE failed %d\n", ret); 152501d7584cSLiam Girdwood 152601d7584cSLiam Girdwood return ret; 152701d7584cSLiam Girdwood } 152801d7584cSLiam Girdwood 152945c0a188SMark Brown static int dpcm_be_dai_trigger(struct snd_soc_pcm_runtime *fe, int stream, 153045c0a188SMark Brown int cmd) 153101d7584cSLiam Girdwood { 153201d7584cSLiam Girdwood struct snd_soc_dpcm *dpcm; 153301d7584cSLiam Girdwood int ret = 0; 153401d7584cSLiam Girdwood 153501d7584cSLiam Girdwood list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) { 153601d7584cSLiam Girdwood 153701d7584cSLiam Girdwood struct snd_soc_pcm_runtime *be = dpcm->be; 153801d7584cSLiam Girdwood struct snd_pcm_substream *be_substream = 153901d7584cSLiam Girdwood snd_soc_dpcm_get_substream(be, stream); 154001d7584cSLiam Girdwood 154101d7584cSLiam Girdwood /* is this op for this BE ? */ 154201d7584cSLiam Girdwood if (!snd_soc_dpcm_be_can_update(fe, be, stream)) 154301d7584cSLiam Girdwood continue; 154401d7584cSLiam Girdwood 154501d7584cSLiam Girdwood switch (cmd) { 154601d7584cSLiam Girdwood case SNDRV_PCM_TRIGGER_START: 154701d7584cSLiam Girdwood if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_PREPARE) && 154801d7584cSLiam Girdwood (be->dpcm[stream].state != SND_SOC_DPCM_STATE_STOP)) 154901d7584cSLiam Girdwood continue; 155001d7584cSLiam Girdwood 155101d7584cSLiam Girdwood ret = dpcm_do_trigger(dpcm, be_substream, cmd); 155201d7584cSLiam Girdwood if (ret) 155301d7584cSLiam Girdwood return ret; 155401d7584cSLiam Girdwood 155501d7584cSLiam Girdwood be->dpcm[stream].state = SND_SOC_DPCM_STATE_START; 155601d7584cSLiam Girdwood break; 155701d7584cSLiam Girdwood case SNDRV_PCM_TRIGGER_RESUME: 155801d7584cSLiam Girdwood if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_SUSPEND)) 155901d7584cSLiam Girdwood continue; 156001d7584cSLiam Girdwood 156101d7584cSLiam Girdwood ret = dpcm_do_trigger(dpcm, be_substream, cmd); 156201d7584cSLiam Girdwood if (ret) 156301d7584cSLiam Girdwood return ret; 156401d7584cSLiam Girdwood 156501d7584cSLiam Girdwood be->dpcm[stream].state = SND_SOC_DPCM_STATE_START; 156601d7584cSLiam Girdwood break; 156701d7584cSLiam Girdwood case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 156801d7584cSLiam Girdwood if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_PAUSED)) 156901d7584cSLiam Girdwood continue; 157001d7584cSLiam Girdwood 157101d7584cSLiam Girdwood ret = dpcm_do_trigger(dpcm, be_substream, cmd); 157201d7584cSLiam Girdwood if (ret) 157301d7584cSLiam Girdwood return ret; 157401d7584cSLiam Girdwood 157501d7584cSLiam Girdwood be->dpcm[stream].state = SND_SOC_DPCM_STATE_START; 157601d7584cSLiam Girdwood break; 157701d7584cSLiam Girdwood case SNDRV_PCM_TRIGGER_STOP: 157801d7584cSLiam Girdwood if (be->dpcm[stream].state != SND_SOC_DPCM_STATE_START) 157901d7584cSLiam Girdwood continue; 158001d7584cSLiam Girdwood 158101d7584cSLiam Girdwood if (!snd_soc_dpcm_can_be_free_stop(fe, be, stream)) 158201d7584cSLiam Girdwood continue; 158301d7584cSLiam Girdwood 158401d7584cSLiam Girdwood ret = dpcm_do_trigger(dpcm, be_substream, cmd); 158501d7584cSLiam Girdwood if (ret) 158601d7584cSLiam Girdwood return ret; 158701d7584cSLiam Girdwood 158801d7584cSLiam Girdwood be->dpcm[stream].state = SND_SOC_DPCM_STATE_STOP; 158901d7584cSLiam Girdwood break; 159001d7584cSLiam Girdwood case SNDRV_PCM_TRIGGER_SUSPEND: 159101d7584cSLiam Girdwood if (be->dpcm[stream].state != SND_SOC_DPCM_STATE_STOP) 159201d7584cSLiam Girdwood continue; 159301d7584cSLiam Girdwood 159401d7584cSLiam Girdwood if (!snd_soc_dpcm_can_be_free_stop(fe, be, stream)) 159501d7584cSLiam Girdwood continue; 159601d7584cSLiam Girdwood 159701d7584cSLiam Girdwood ret = dpcm_do_trigger(dpcm, be_substream, cmd); 159801d7584cSLiam Girdwood if (ret) 159901d7584cSLiam Girdwood return ret; 160001d7584cSLiam Girdwood 160101d7584cSLiam Girdwood be->dpcm[stream].state = SND_SOC_DPCM_STATE_SUSPEND; 160201d7584cSLiam Girdwood break; 160301d7584cSLiam Girdwood case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 160401d7584cSLiam Girdwood if (be->dpcm[stream].state != SND_SOC_DPCM_STATE_START) 160501d7584cSLiam Girdwood continue; 160601d7584cSLiam Girdwood 160701d7584cSLiam Girdwood if (!snd_soc_dpcm_can_be_free_stop(fe, be, stream)) 160801d7584cSLiam Girdwood continue; 160901d7584cSLiam Girdwood 161001d7584cSLiam Girdwood ret = dpcm_do_trigger(dpcm, be_substream, cmd); 161101d7584cSLiam Girdwood if (ret) 161201d7584cSLiam Girdwood return ret; 161301d7584cSLiam Girdwood 161401d7584cSLiam Girdwood be->dpcm[stream].state = SND_SOC_DPCM_STATE_PAUSED; 161501d7584cSLiam Girdwood break; 161601d7584cSLiam Girdwood } 161701d7584cSLiam Girdwood } 161801d7584cSLiam Girdwood 161901d7584cSLiam Girdwood return ret; 162001d7584cSLiam Girdwood } 162101d7584cSLiam Girdwood EXPORT_SYMBOL_GPL(dpcm_be_dai_trigger); 162201d7584cSLiam Girdwood 162345c0a188SMark Brown static int dpcm_fe_dai_trigger(struct snd_pcm_substream *substream, int cmd) 162401d7584cSLiam Girdwood { 162501d7584cSLiam Girdwood struct snd_soc_pcm_runtime *fe = substream->private_data; 162601d7584cSLiam Girdwood int stream = substream->stream, ret; 162701d7584cSLiam Girdwood enum snd_soc_dpcm_trigger trigger = fe->dai_link->trigger[stream]; 162801d7584cSLiam Girdwood 162901d7584cSLiam Girdwood fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE; 163001d7584cSLiam Girdwood 163101d7584cSLiam Girdwood switch (trigger) { 163201d7584cSLiam Girdwood case SND_SOC_DPCM_TRIGGER_PRE: 163301d7584cSLiam Girdwood /* call trigger on the frontend before the backend. */ 163401d7584cSLiam Girdwood 1635103d84a3SLiam Girdwood dev_dbg(fe->dev, "ASoC: pre trigger FE %s cmd %d\n", 163601d7584cSLiam Girdwood fe->dai_link->name, cmd); 163701d7584cSLiam Girdwood 163801d7584cSLiam Girdwood ret = soc_pcm_trigger(substream, cmd); 163901d7584cSLiam Girdwood if (ret < 0) { 1640103d84a3SLiam Girdwood dev_err(fe->dev,"ASoC: trigger FE failed %d\n", ret); 164101d7584cSLiam Girdwood goto out; 164201d7584cSLiam Girdwood } 164301d7584cSLiam Girdwood 164401d7584cSLiam Girdwood ret = dpcm_be_dai_trigger(fe, substream->stream, cmd); 164501d7584cSLiam Girdwood break; 164601d7584cSLiam Girdwood case SND_SOC_DPCM_TRIGGER_POST: 164701d7584cSLiam Girdwood /* call trigger on the frontend after the backend. */ 164801d7584cSLiam Girdwood 164901d7584cSLiam Girdwood ret = dpcm_be_dai_trigger(fe, substream->stream, cmd); 165001d7584cSLiam Girdwood if (ret < 0) { 1651103d84a3SLiam Girdwood dev_err(fe->dev,"ASoC: trigger FE failed %d\n", ret); 165201d7584cSLiam Girdwood goto out; 165301d7584cSLiam Girdwood } 165401d7584cSLiam Girdwood 1655103d84a3SLiam Girdwood dev_dbg(fe->dev, "ASoC: post trigger FE %s cmd %d\n", 165601d7584cSLiam Girdwood fe->dai_link->name, cmd); 165701d7584cSLiam Girdwood 165801d7584cSLiam Girdwood ret = soc_pcm_trigger(substream, cmd); 165901d7584cSLiam Girdwood break; 166007bf84aaSLiam Girdwood case SND_SOC_DPCM_TRIGGER_BESPOKE: 166107bf84aaSLiam Girdwood /* bespoke trigger() - handles both FE and BEs */ 166207bf84aaSLiam Girdwood 1663103d84a3SLiam Girdwood dev_dbg(fe->dev, "ASoC: bespoke trigger FE %s cmd %d\n", 166407bf84aaSLiam Girdwood fe->dai_link->name, cmd); 166507bf84aaSLiam Girdwood 166607bf84aaSLiam Girdwood ret = soc_pcm_bespoke_trigger(substream, cmd); 166707bf84aaSLiam Girdwood if (ret < 0) { 1668103d84a3SLiam Girdwood dev_err(fe->dev,"ASoC: trigger FE failed %d\n", ret); 166907bf84aaSLiam Girdwood goto out; 167007bf84aaSLiam Girdwood } 167107bf84aaSLiam Girdwood break; 167201d7584cSLiam Girdwood default: 1673103d84a3SLiam Girdwood dev_err(fe->dev, "ASoC: invalid trigger cmd %d for %s\n", cmd, 167401d7584cSLiam Girdwood fe->dai_link->name); 167501d7584cSLiam Girdwood ret = -EINVAL; 167601d7584cSLiam Girdwood goto out; 167701d7584cSLiam Girdwood } 167801d7584cSLiam Girdwood 167901d7584cSLiam Girdwood switch (cmd) { 168001d7584cSLiam Girdwood case SNDRV_PCM_TRIGGER_START: 168101d7584cSLiam Girdwood case SNDRV_PCM_TRIGGER_RESUME: 168201d7584cSLiam Girdwood case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 168301d7584cSLiam Girdwood fe->dpcm[stream].state = SND_SOC_DPCM_STATE_START; 168401d7584cSLiam Girdwood break; 168501d7584cSLiam Girdwood case SNDRV_PCM_TRIGGER_STOP: 168601d7584cSLiam Girdwood case SNDRV_PCM_TRIGGER_SUSPEND: 168701d7584cSLiam Girdwood case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 168801d7584cSLiam Girdwood fe->dpcm[stream].state = SND_SOC_DPCM_STATE_STOP; 168901d7584cSLiam Girdwood break; 169001d7584cSLiam Girdwood } 169101d7584cSLiam Girdwood 169201d7584cSLiam Girdwood out: 169301d7584cSLiam Girdwood fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO; 169401d7584cSLiam Girdwood return ret; 169501d7584cSLiam Girdwood } 169601d7584cSLiam Girdwood 169701d7584cSLiam Girdwood static int dpcm_be_dai_prepare(struct snd_soc_pcm_runtime *fe, int stream) 169801d7584cSLiam Girdwood { 169901d7584cSLiam Girdwood struct snd_soc_dpcm *dpcm; 170001d7584cSLiam Girdwood int ret = 0; 170101d7584cSLiam Girdwood 170201d7584cSLiam Girdwood list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) { 170301d7584cSLiam Girdwood 170401d7584cSLiam Girdwood struct snd_soc_pcm_runtime *be = dpcm->be; 170501d7584cSLiam Girdwood struct snd_pcm_substream *be_substream = 170601d7584cSLiam Girdwood snd_soc_dpcm_get_substream(be, stream); 170701d7584cSLiam Girdwood 170801d7584cSLiam Girdwood /* is this op for this BE ? */ 170901d7584cSLiam Girdwood if (!snd_soc_dpcm_be_can_update(fe, be, stream)) 171001d7584cSLiam Girdwood continue; 171101d7584cSLiam Girdwood 171201d7584cSLiam Girdwood if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_PARAMS) && 171301d7584cSLiam Girdwood (be->dpcm[stream].state != SND_SOC_DPCM_STATE_STOP)) 171401d7584cSLiam Girdwood continue; 171501d7584cSLiam Girdwood 1716103d84a3SLiam Girdwood dev_dbg(be->dev, "ASoC: prepare BE %s\n", 171701d7584cSLiam Girdwood dpcm->fe->dai_link->name); 171801d7584cSLiam Girdwood 171901d7584cSLiam Girdwood ret = soc_pcm_prepare(be_substream); 172001d7584cSLiam Girdwood if (ret < 0) { 1721103d84a3SLiam Girdwood dev_err(be->dev, "ASoC: backend prepare failed %d\n", 172201d7584cSLiam Girdwood ret); 172301d7584cSLiam Girdwood break; 172401d7584cSLiam Girdwood } 172501d7584cSLiam Girdwood 172601d7584cSLiam Girdwood be->dpcm[stream].state = SND_SOC_DPCM_STATE_PREPARE; 172701d7584cSLiam Girdwood } 172801d7584cSLiam Girdwood return ret; 172901d7584cSLiam Girdwood } 173001d7584cSLiam Girdwood 173145c0a188SMark Brown static int dpcm_fe_dai_prepare(struct snd_pcm_substream *substream) 173201d7584cSLiam Girdwood { 173301d7584cSLiam Girdwood struct snd_soc_pcm_runtime *fe = substream->private_data; 173401d7584cSLiam Girdwood int stream = substream->stream, ret = 0; 173501d7584cSLiam Girdwood 173601d7584cSLiam Girdwood mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME); 173701d7584cSLiam Girdwood 1738103d84a3SLiam Girdwood dev_dbg(fe->dev, "ASoC: prepare FE %s\n", fe->dai_link->name); 173901d7584cSLiam Girdwood 174001d7584cSLiam Girdwood fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE; 174101d7584cSLiam Girdwood 174201d7584cSLiam Girdwood /* there is no point preparing this FE if there are no BEs */ 174301d7584cSLiam Girdwood if (list_empty(&fe->dpcm[stream].be_clients)) { 1744103d84a3SLiam Girdwood dev_err(fe->dev, "ASoC: no backend DAIs enabled for %s\n", 174501d7584cSLiam Girdwood fe->dai_link->name); 174601d7584cSLiam Girdwood ret = -EINVAL; 174701d7584cSLiam Girdwood goto out; 174801d7584cSLiam Girdwood } 174901d7584cSLiam Girdwood 175001d7584cSLiam Girdwood ret = dpcm_be_dai_prepare(fe, substream->stream); 175101d7584cSLiam Girdwood if (ret < 0) 175201d7584cSLiam Girdwood goto out; 175301d7584cSLiam Girdwood 175401d7584cSLiam Girdwood /* call prepare on the frontend */ 175501d7584cSLiam Girdwood ret = soc_pcm_prepare(substream); 175601d7584cSLiam Girdwood if (ret < 0) { 1757103d84a3SLiam Girdwood dev_err(fe->dev,"ASoC: prepare FE %s failed\n", 175801d7584cSLiam Girdwood fe->dai_link->name); 175901d7584cSLiam Girdwood goto out; 176001d7584cSLiam Girdwood } 176101d7584cSLiam Girdwood 176201d7584cSLiam Girdwood /* run the stream event for each BE */ 176301d7584cSLiam Girdwood dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_START); 176401d7584cSLiam Girdwood fe->dpcm[stream].state = SND_SOC_DPCM_STATE_PREPARE; 176501d7584cSLiam Girdwood 176601d7584cSLiam Girdwood out: 176701d7584cSLiam Girdwood fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO; 176801d7584cSLiam Girdwood mutex_unlock(&fe->card->mutex); 176901d7584cSLiam Girdwood 177001d7584cSLiam Girdwood return ret; 177101d7584cSLiam Girdwood } 177201d7584cSLiam Girdwood 1773be3f3f2cSLiam Girdwood static int soc_pcm_ioctl(struct snd_pcm_substream *substream, 1774be3f3f2cSLiam Girdwood unsigned int cmd, void *arg) 1775be3f3f2cSLiam Girdwood { 1776be3f3f2cSLiam Girdwood struct snd_soc_pcm_runtime *rtd = substream->private_data; 1777be3f3f2cSLiam Girdwood struct snd_soc_platform *platform = rtd->platform; 1778be3f3f2cSLiam Girdwood 1779c5914b0aSMark Brown if (platform->driver->ops && platform->driver->ops->ioctl) 1780be3f3f2cSLiam Girdwood return platform->driver->ops->ioctl(substream, cmd, arg); 1781be3f3f2cSLiam Girdwood return snd_pcm_lib_ioctl(substream, cmd, arg); 1782be3f3f2cSLiam Girdwood } 1783be3f3f2cSLiam Girdwood 1784618dae11SLiam Girdwood static int dpcm_run_update_shutdown(struct snd_soc_pcm_runtime *fe, int stream) 1785618dae11SLiam Girdwood { 178607bf84aaSLiam Girdwood struct snd_pcm_substream *substream = 178707bf84aaSLiam Girdwood snd_soc_dpcm_get_substream(fe, stream); 178807bf84aaSLiam Girdwood enum snd_soc_dpcm_trigger trigger = fe->dai_link->trigger[stream]; 1789618dae11SLiam Girdwood int err; 179001d7584cSLiam Girdwood 1791103d84a3SLiam Girdwood dev_dbg(fe->dev, "ASoC: runtime %s close on FE %s\n", 1792618dae11SLiam Girdwood stream ? "capture" : "playback", fe->dai_link->name); 1793618dae11SLiam Girdwood 179407bf84aaSLiam Girdwood if (trigger == SND_SOC_DPCM_TRIGGER_BESPOKE) { 179507bf84aaSLiam Girdwood /* call bespoke trigger - FE takes care of all BE triggers */ 1796103d84a3SLiam Girdwood dev_dbg(fe->dev, "ASoC: bespoke trigger FE %s cmd stop\n", 179707bf84aaSLiam Girdwood fe->dai_link->name); 179807bf84aaSLiam Girdwood 179907bf84aaSLiam Girdwood err = soc_pcm_bespoke_trigger(substream, SNDRV_PCM_TRIGGER_STOP); 180007bf84aaSLiam Girdwood if (err < 0) 1801103d84a3SLiam Girdwood dev_err(fe->dev,"ASoC: trigger FE failed %d\n", err); 180207bf84aaSLiam Girdwood } else { 1803103d84a3SLiam Girdwood dev_dbg(fe->dev, "ASoC: trigger FE %s cmd stop\n", 180407bf84aaSLiam Girdwood fe->dai_link->name); 180507bf84aaSLiam Girdwood 1806618dae11SLiam Girdwood err = dpcm_be_dai_trigger(fe, stream, SNDRV_PCM_TRIGGER_STOP); 1807618dae11SLiam Girdwood if (err < 0) 1808103d84a3SLiam Girdwood dev_err(fe->dev,"ASoC: trigger FE failed %d\n", err); 180907bf84aaSLiam Girdwood } 1810618dae11SLiam Girdwood 1811618dae11SLiam Girdwood err = dpcm_be_dai_hw_free(fe, stream); 1812618dae11SLiam Girdwood if (err < 0) 1813103d84a3SLiam Girdwood dev_err(fe->dev,"ASoC: hw_free FE failed %d\n", err); 1814618dae11SLiam Girdwood 1815618dae11SLiam Girdwood err = dpcm_be_dai_shutdown(fe, stream); 1816618dae11SLiam Girdwood if (err < 0) 1817103d84a3SLiam Girdwood dev_err(fe->dev,"ASoC: shutdown FE failed %d\n", err); 1818618dae11SLiam Girdwood 1819618dae11SLiam Girdwood /* run the stream event for each BE */ 1820618dae11SLiam Girdwood dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_NOP); 1821618dae11SLiam Girdwood 1822618dae11SLiam Girdwood return 0; 1823618dae11SLiam Girdwood } 1824618dae11SLiam Girdwood 1825618dae11SLiam Girdwood static int dpcm_run_update_startup(struct snd_soc_pcm_runtime *fe, int stream) 1826618dae11SLiam Girdwood { 182707bf84aaSLiam Girdwood struct snd_pcm_substream *substream = 182807bf84aaSLiam Girdwood snd_soc_dpcm_get_substream(fe, stream); 1829618dae11SLiam Girdwood struct snd_soc_dpcm *dpcm; 183007bf84aaSLiam Girdwood enum snd_soc_dpcm_trigger trigger = fe->dai_link->trigger[stream]; 1831618dae11SLiam Girdwood int ret; 1832618dae11SLiam Girdwood 1833103d84a3SLiam Girdwood dev_dbg(fe->dev, "ASoC: runtime %s open on FE %s\n", 1834618dae11SLiam Girdwood stream ? "capture" : "playback", fe->dai_link->name); 1835618dae11SLiam Girdwood 1836618dae11SLiam Girdwood /* Only start the BE if the FE is ready */ 1837618dae11SLiam Girdwood if (fe->dpcm[stream].state == SND_SOC_DPCM_STATE_HW_FREE || 1838618dae11SLiam Girdwood fe->dpcm[stream].state == SND_SOC_DPCM_STATE_CLOSE) 1839618dae11SLiam Girdwood return -EINVAL; 1840618dae11SLiam Girdwood 1841618dae11SLiam Girdwood /* startup must always be called for new BEs */ 1842618dae11SLiam Girdwood ret = dpcm_be_dai_startup(fe, stream); 1843fffc0ca2SDan Carpenter if (ret < 0) 1844618dae11SLiam Girdwood goto disconnect; 1845618dae11SLiam Girdwood 1846618dae11SLiam Girdwood /* keep going if FE state is > open */ 1847618dae11SLiam Girdwood if (fe->dpcm[stream].state == SND_SOC_DPCM_STATE_OPEN) 1848618dae11SLiam Girdwood return 0; 1849618dae11SLiam Girdwood 1850618dae11SLiam Girdwood ret = dpcm_be_dai_hw_params(fe, stream); 1851fffc0ca2SDan Carpenter if (ret < 0) 1852618dae11SLiam Girdwood goto close; 1853618dae11SLiam Girdwood 1854618dae11SLiam Girdwood /* keep going if FE state is > hw_params */ 1855618dae11SLiam Girdwood if (fe->dpcm[stream].state == SND_SOC_DPCM_STATE_HW_PARAMS) 1856618dae11SLiam Girdwood return 0; 1857618dae11SLiam Girdwood 1858618dae11SLiam Girdwood 1859618dae11SLiam Girdwood ret = dpcm_be_dai_prepare(fe, stream); 1860fffc0ca2SDan Carpenter if (ret < 0) 1861618dae11SLiam Girdwood goto hw_free; 1862618dae11SLiam Girdwood 1863618dae11SLiam Girdwood /* run the stream event for each BE */ 1864618dae11SLiam Girdwood dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_NOP); 1865618dae11SLiam Girdwood 1866618dae11SLiam Girdwood /* keep going if FE state is > prepare */ 1867618dae11SLiam Girdwood if (fe->dpcm[stream].state == SND_SOC_DPCM_STATE_PREPARE || 1868618dae11SLiam Girdwood fe->dpcm[stream].state == SND_SOC_DPCM_STATE_STOP) 1869618dae11SLiam Girdwood return 0; 1870618dae11SLiam Girdwood 187107bf84aaSLiam Girdwood if (trigger == SND_SOC_DPCM_TRIGGER_BESPOKE) { 187207bf84aaSLiam Girdwood /* call trigger on the frontend - FE takes care of all BE triggers */ 1873103d84a3SLiam Girdwood dev_dbg(fe->dev, "ASoC: bespoke trigger FE %s cmd start\n", 187407bf84aaSLiam Girdwood fe->dai_link->name); 187507bf84aaSLiam Girdwood 187607bf84aaSLiam Girdwood ret = soc_pcm_bespoke_trigger(substream, SNDRV_PCM_TRIGGER_START); 187707bf84aaSLiam Girdwood if (ret < 0) { 1878103d84a3SLiam Girdwood dev_err(fe->dev,"ASoC: bespoke trigger FE failed %d\n", ret); 187907bf84aaSLiam Girdwood goto hw_free; 188007bf84aaSLiam Girdwood } 188107bf84aaSLiam Girdwood } else { 1882103d84a3SLiam Girdwood dev_dbg(fe->dev, "ASoC: trigger FE %s cmd start\n", 1883618dae11SLiam Girdwood fe->dai_link->name); 1884618dae11SLiam Girdwood 1885618dae11SLiam Girdwood ret = dpcm_be_dai_trigger(fe, stream, 1886618dae11SLiam Girdwood SNDRV_PCM_TRIGGER_START); 1887618dae11SLiam Girdwood if (ret < 0) { 1888103d84a3SLiam Girdwood dev_err(fe->dev,"ASoC: trigger FE failed %d\n", ret); 1889618dae11SLiam Girdwood goto hw_free; 1890618dae11SLiam Girdwood } 189107bf84aaSLiam Girdwood } 1892618dae11SLiam Girdwood 1893618dae11SLiam Girdwood return 0; 1894618dae11SLiam Girdwood 1895618dae11SLiam Girdwood hw_free: 1896618dae11SLiam Girdwood dpcm_be_dai_hw_free(fe, stream); 1897618dae11SLiam Girdwood close: 1898618dae11SLiam Girdwood dpcm_be_dai_shutdown(fe, stream); 1899618dae11SLiam Girdwood disconnect: 1900618dae11SLiam Girdwood /* disconnect any non started BEs */ 1901618dae11SLiam Girdwood list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) { 1902618dae11SLiam Girdwood struct snd_soc_pcm_runtime *be = dpcm->be; 1903618dae11SLiam Girdwood if (be->dpcm[stream].state != SND_SOC_DPCM_STATE_START) 1904618dae11SLiam Girdwood dpcm->state = SND_SOC_DPCM_LINK_STATE_FREE; 1905618dae11SLiam Girdwood } 1906618dae11SLiam Girdwood 1907618dae11SLiam Girdwood return ret; 1908618dae11SLiam Girdwood } 1909618dae11SLiam Girdwood 1910618dae11SLiam Girdwood static int dpcm_run_new_update(struct snd_soc_pcm_runtime *fe, int stream) 1911618dae11SLiam Girdwood { 1912618dae11SLiam Girdwood int ret; 1913618dae11SLiam Girdwood 1914618dae11SLiam Girdwood fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_BE; 1915618dae11SLiam Girdwood ret = dpcm_run_update_startup(fe, stream); 1916618dae11SLiam Girdwood if (ret < 0) 1917103d84a3SLiam Girdwood dev_err(fe->dev, "ASoC: failed to startup some BEs\n"); 1918618dae11SLiam Girdwood fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO; 1919618dae11SLiam Girdwood 1920618dae11SLiam Girdwood return ret; 1921618dae11SLiam Girdwood } 1922618dae11SLiam Girdwood 1923618dae11SLiam Girdwood static int dpcm_run_old_update(struct snd_soc_pcm_runtime *fe, int stream) 1924618dae11SLiam Girdwood { 1925618dae11SLiam Girdwood int ret; 1926618dae11SLiam Girdwood 1927618dae11SLiam Girdwood fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_BE; 1928618dae11SLiam Girdwood ret = dpcm_run_update_shutdown(fe, stream); 1929618dae11SLiam Girdwood if (ret < 0) 1930103d84a3SLiam Girdwood dev_err(fe->dev, "ASoC: failed to shutdown some BEs\n"); 1931618dae11SLiam Girdwood fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO; 1932618dae11SLiam Girdwood 1933618dae11SLiam Girdwood return ret; 1934618dae11SLiam Girdwood } 1935618dae11SLiam Girdwood 1936618dae11SLiam Girdwood /* Called by DAPM mixer/mux changes to update audio routing between PCMs and 1937618dae11SLiam Girdwood * any DAI links. 1938618dae11SLiam Girdwood */ 1939c3f48ae6SLars-Peter Clausen int soc_dpcm_runtime_update(struct snd_soc_card *card) 1940618dae11SLiam Girdwood { 1941618dae11SLiam Girdwood int i, old, new, paths; 1942618dae11SLiam Girdwood 1943618dae11SLiam Girdwood mutex_lock_nested(&card->mutex, SND_SOC_CARD_CLASS_RUNTIME); 1944618dae11SLiam Girdwood for (i = 0; i < card->num_rtd; i++) { 1945618dae11SLiam Girdwood struct snd_soc_dapm_widget_list *list; 1946618dae11SLiam Girdwood struct snd_soc_pcm_runtime *fe = &card->rtd[i]; 1947618dae11SLiam Girdwood 1948618dae11SLiam Girdwood /* make sure link is FE */ 1949618dae11SLiam Girdwood if (!fe->dai_link->dynamic) 1950618dae11SLiam Girdwood continue; 1951618dae11SLiam Girdwood 1952618dae11SLiam Girdwood /* only check active links */ 1953618dae11SLiam Girdwood if (!fe->cpu_dai->active) 1954618dae11SLiam Girdwood continue; 1955618dae11SLiam Girdwood 1956618dae11SLiam Girdwood /* DAPM sync will call this to update DSP paths */ 1957103d84a3SLiam Girdwood dev_dbg(fe->dev, "ASoC: DPCM runtime update for FE %s\n", 1958618dae11SLiam Girdwood fe->dai_link->name); 1959618dae11SLiam Girdwood 1960618dae11SLiam Girdwood /* skip if FE doesn't have playback capability */ 1961618dae11SLiam Girdwood if (!fe->cpu_dai->driver->playback.channels_min) 1962618dae11SLiam Girdwood goto capture; 1963618dae11SLiam Girdwood 1964618dae11SLiam Girdwood paths = dpcm_path_get(fe, SNDRV_PCM_STREAM_PLAYBACK, &list); 1965618dae11SLiam Girdwood if (paths < 0) { 1966103d84a3SLiam Girdwood dev_warn(fe->dev, "ASoC: %s no valid %s path\n", 1967618dae11SLiam Girdwood fe->dai_link->name, "playback"); 1968618dae11SLiam Girdwood mutex_unlock(&card->mutex); 1969618dae11SLiam Girdwood return paths; 1970618dae11SLiam Girdwood } 1971618dae11SLiam Girdwood 1972618dae11SLiam Girdwood /* update any new playback paths */ 1973618dae11SLiam Girdwood new = dpcm_process_paths(fe, SNDRV_PCM_STREAM_PLAYBACK, &list, 1); 1974618dae11SLiam Girdwood if (new) { 1975618dae11SLiam Girdwood dpcm_run_new_update(fe, SNDRV_PCM_STREAM_PLAYBACK); 1976618dae11SLiam Girdwood dpcm_clear_pending_state(fe, SNDRV_PCM_STREAM_PLAYBACK); 1977618dae11SLiam Girdwood dpcm_be_disconnect(fe, SNDRV_PCM_STREAM_PLAYBACK); 1978618dae11SLiam Girdwood } 1979618dae11SLiam Girdwood 1980618dae11SLiam Girdwood /* update any old playback paths */ 1981618dae11SLiam Girdwood old = dpcm_process_paths(fe, SNDRV_PCM_STREAM_PLAYBACK, &list, 0); 1982618dae11SLiam Girdwood if (old) { 1983618dae11SLiam Girdwood dpcm_run_old_update(fe, SNDRV_PCM_STREAM_PLAYBACK); 1984618dae11SLiam Girdwood dpcm_clear_pending_state(fe, SNDRV_PCM_STREAM_PLAYBACK); 1985618dae11SLiam Girdwood dpcm_be_disconnect(fe, SNDRV_PCM_STREAM_PLAYBACK); 1986618dae11SLiam Girdwood } 1987618dae11SLiam Girdwood 1988618dae11SLiam Girdwood capture: 1989618dae11SLiam Girdwood /* skip if FE doesn't have capture capability */ 1990618dae11SLiam Girdwood if (!fe->cpu_dai->driver->capture.channels_min) 1991618dae11SLiam Girdwood continue; 1992618dae11SLiam Girdwood 1993618dae11SLiam Girdwood paths = dpcm_path_get(fe, SNDRV_PCM_STREAM_CAPTURE, &list); 1994618dae11SLiam Girdwood if (paths < 0) { 1995103d84a3SLiam Girdwood dev_warn(fe->dev, "ASoC: %s no valid %s path\n", 1996618dae11SLiam Girdwood fe->dai_link->name, "capture"); 1997618dae11SLiam Girdwood mutex_unlock(&card->mutex); 1998618dae11SLiam Girdwood return paths; 1999618dae11SLiam Girdwood } 2000618dae11SLiam Girdwood 2001618dae11SLiam Girdwood /* update any new capture paths */ 2002618dae11SLiam Girdwood new = dpcm_process_paths(fe, SNDRV_PCM_STREAM_CAPTURE, &list, 1); 2003618dae11SLiam Girdwood if (new) { 2004618dae11SLiam Girdwood dpcm_run_new_update(fe, SNDRV_PCM_STREAM_CAPTURE); 2005618dae11SLiam Girdwood dpcm_clear_pending_state(fe, SNDRV_PCM_STREAM_CAPTURE); 2006618dae11SLiam Girdwood dpcm_be_disconnect(fe, SNDRV_PCM_STREAM_CAPTURE); 2007618dae11SLiam Girdwood } 2008618dae11SLiam Girdwood 2009618dae11SLiam Girdwood /* update any old capture paths */ 2010618dae11SLiam Girdwood old = dpcm_process_paths(fe, SNDRV_PCM_STREAM_CAPTURE, &list, 0); 2011618dae11SLiam Girdwood if (old) { 2012618dae11SLiam Girdwood dpcm_run_old_update(fe, SNDRV_PCM_STREAM_CAPTURE); 2013618dae11SLiam Girdwood dpcm_clear_pending_state(fe, SNDRV_PCM_STREAM_CAPTURE); 2014618dae11SLiam Girdwood dpcm_be_disconnect(fe, SNDRV_PCM_STREAM_CAPTURE); 2015618dae11SLiam Girdwood } 2016618dae11SLiam Girdwood 2017618dae11SLiam Girdwood dpcm_path_put(&list); 2018618dae11SLiam Girdwood } 2019618dae11SLiam Girdwood 2020618dae11SLiam Girdwood mutex_unlock(&card->mutex); 2021618dae11SLiam Girdwood return 0; 2022618dae11SLiam Girdwood } 202301d7584cSLiam Girdwood int soc_dpcm_be_digital_mute(struct snd_soc_pcm_runtime *fe, int mute) 202401d7584cSLiam Girdwood { 202501d7584cSLiam Girdwood struct snd_soc_dpcm *dpcm; 202601d7584cSLiam Girdwood struct list_head *clients = 202701d7584cSLiam Girdwood &fe->dpcm[SNDRV_PCM_STREAM_PLAYBACK].be_clients; 202801d7584cSLiam Girdwood 202901d7584cSLiam Girdwood list_for_each_entry(dpcm, clients, list_be) { 203001d7584cSLiam Girdwood 203101d7584cSLiam Girdwood struct snd_soc_pcm_runtime *be = dpcm->be; 203201d7584cSLiam Girdwood struct snd_soc_dai *dai = be->codec_dai; 203301d7584cSLiam Girdwood struct snd_soc_dai_driver *drv = dai->driver; 203401d7584cSLiam Girdwood 203501d7584cSLiam Girdwood if (be->dai_link->ignore_suspend) 203601d7584cSLiam Girdwood continue; 203701d7584cSLiam Girdwood 2038103d84a3SLiam Girdwood dev_dbg(be->dev, "ASoC: BE digital mute %s\n", be->dai_link->name); 203901d7584cSLiam Girdwood 2040c5914b0aSMark Brown if (drv->ops && drv->ops->digital_mute && dai->playback_active) 204101d7584cSLiam Girdwood drv->ops->digital_mute(dai, mute); 204201d7584cSLiam Girdwood } 204301d7584cSLiam Girdwood 204401d7584cSLiam Girdwood return 0; 204501d7584cSLiam Girdwood } 204601d7584cSLiam Girdwood 204745c0a188SMark Brown static int dpcm_fe_dai_open(struct snd_pcm_substream *fe_substream) 204801d7584cSLiam Girdwood { 204901d7584cSLiam Girdwood struct snd_soc_pcm_runtime *fe = fe_substream->private_data; 205001d7584cSLiam Girdwood struct snd_soc_dpcm *dpcm; 205101d7584cSLiam Girdwood struct snd_soc_dapm_widget_list *list; 205201d7584cSLiam Girdwood int ret; 205301d7584cSLiam Girdwood int stream = fe_substream->stream; 205401d7584cSLiam Girdwood 205501d7584cSLiam Girdwood mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME); 205601d7584cSLiam Girdwood fe->dpcm[stream].runtime = fe_substream->runtime; 205701d7584cSLiam Girdwood 205801d7584cSLiam Girdwood if (dpcm_path_get(fe, stream, &list) <= 0) { 2059103d84a3SLiam Girdwood dev_dbg(fe->dev, "ASoC: %s no valid %s route\n", 206001d7584cSLiam Girdwood fe->dai_link->name, stream ? "capture" : "playback"); 206101d7584cSLiam Girdwood } 206201d7584cSLiam Girdwood 206301d7584cSLiam Girdwood /* calculate valid and active FE <-> BE dpcms */ 206401d7584cSLiam Girdwood dpcm_process_paths(fe, stream, &list, 1); 206501d7584cSLiam Girdwood 206601d7584cSLiam Girdwood ret = dpcm_fe_dai_startup(fe_substream); 206701d7584cSLiam Girdwood if (ret < 0) { 206801d7584cSLiam Girdwood /* clean up all links */ 206901d7584cSLiam Girdwood list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) 207001d7584cSLiam Girdwood dpcm->state = SND_SOC_DPCM_LINK_STATE_FREE; 207101d7584cSLiam Girdwood 207201d7584cSLiam Girdwood dpcm_be_disconnect(fe, stream); 207301d7584cSLiam Girdwood fe->dpcm[stream].runtime = NULL; 207401d7584cSLiam Girdwood } 207501d7584cSLiam Girdwood 207601d7584cSLiam Girdwood dpcm_clear_pending_state(fe, stream); 207701d7584cSLiam Girdwood dpcm_path_put(&list); 207801d7584cSLiam Girdwood mutex_unlock(&fe->card->mutex); 207901d7584cSLiam Girdwood return ret; 208001d7584cSLiam Girdwood } 208101d7584cSLiam Girdwood 208245c0a188SMark Brown static int dpcm_fe_dai_close(struct snd_pcm_substream *fe_substream) 208301d7584cSLiam Girdwood { 208401d7584cSLiam Girdwood struct snd_soc_pcm_runtime *fe = fe_substream->private_data; 208501d7584cSLiam Girdwood struct snd_soc_dpcm *dpcm; 208601d7584cSLiam Girdwood int stream = fe_substream->stream, ret; 208701d7584cSLiam Girdwood 208801d7584cSLiam Girdwood mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME); 208901d7584cSLiam Girdwood ret = dpcm_fe_dai_shutdown(fe_substream); 209001d7584cSLiam Girdwood 209101d7584cSLiam Girdwood /* mark FE's links ready to prune */ 209201d7584cSLiam Girdwood list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) 209301d7584cSLiam Girdwood dpcm->state = SND_SOC_DPCM_LINK_STATE_FREE; 209401d7584cSLiam Girdwood 209501d7584cSLiam Girdwood dpcm_be_disconnect(fe, stream); 209601d7584cSLiam Girdwood 209701d7584cSLiam Girdwood fe->dpcm[stream].runtime = NULL; 209801d7584cSLiam Girdwood mutex_unlock(&fe->card->mutex); 209901d7584cSLiam Girdwood return ret; 210001d7584cSLiam Girdwood } 210101d7584cSLiam Girdwood 2102ddee627cSLiam Girdwood /* create a new pcm */ 2103ddee627cSLiam Girdwood int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num) 2104ddee627cSLiam Girdwood { 2105ddee627cSLiam Girdwood struct snd_soc_platform *platform = rtd->platform; 2106ddee627cSLiam Girdwood struct snd_soc_dai *codec_dai = rtd->codec_dai; 2107ddee627cSLiam Girdwood struct snd_soc_dai *cpu_dai = rtd->cpu_dai; 2108ddee627cSLiam Girdwood struct snd_pcm *pcm; 2109ddee627cSLiam Girdwood char new_name[64]; 2110ddee627cSLiam Girdwood int ret = 0, playback = 0, capture = 0; 2111ddee627cSLiam Girdwood 211201d7584cSLiam Girdwood if (rtd->dai_link->dynamic || rtd->dai_link->no_pcm) { 211301d7584cSLiam Girdwood if (cpu_dai->driver->playback.channels_min) 211401d7584cSLiam Girdwood playback = 1; 211501d7584cSLiam Girdwood if (cpu_dai->driver->capture.channels_min) 211601d7584cSLiam Girdwood capture = 1; 211701d7584cSLiam Girdwood } else { 211805679092SMark Brown if (codec_dai->driver->playback.channels_min && 211905679092SMark Brown cpu_dai->driver->playback.channels_min) 2120ddee627cSLiam Girdwood playback = 1; 212105679092SMark Brown if (codec_dai->driver->capture.channels_min && 212205679092SMark Brown cpu_dai->driver->capture.channels_min) 2123ddee627cSLiam Girdwood capture = 1; 212401d7584cSLiam Girdwood } 2125ddee627cSLiam Girdwood 2126d6bead02SFabio Estevam if (rtd->dai_link->playback_only) { 2127d6bead02SFabio Estevam playback = 1; 2128d6bead02SFabio Estevam capture = 0; 2129d6bead02SFabio Estevam } 2130d6bead02SFabio Estevam 2131d6bead02SFabio Estevam if (rtd->dai_link->capture_only) { 2132d6bead02SFabio Estevam playback = 0; 2133d6bead02SFabio Estevam capture = 1; 2134d6bead02SFabio Estevam } 2135d6bead02SFabio Estevam 213601d7584cSLiam Girdwood /* create the PCM */ 213701d7584cSLiam Girdwood if (rtd->dai_link->no_pcm) { 213801d7584cSLiam Girdwood snprintf(new_name, sizeof(new_name), "(%s)", 213901d7584cSLiam Girdwood rtd->dai_link->stream_name); 214001d7584cSLiam Girdwood 214101d7584cSLiam Girdwood ret = snd_pcm_new_internal(rtd->card->snd_card, new_name, num, 214201d7584cSLiam Girdwood playback, capture, &pcm); 214301d7584cSLiam Girdwood } else { 214401d7584cSLiam Girdwood if (rtd->dai_link->dynamic) 214501d7584cSLiam Girdwood snprintf(new_name, sizeof(new_name), "%s (*)", 214601d7584cSLiam Girdwood rtd->dai_link->stream_name); 214701d7584cSLiam Girdwood else 214801d7584cSLiam Girdwood snprintf(new_name, sizeof(new_name), "%s %s-%d", 214901d7584cSLiam Girdwood rtd->dai_link->stream_name, codec_dai->name, num); 215001d7584cSLiam Girdwood 215101d7584cSLiam Girdwood ret = snd_pcm_new(rtd->card->snd_card, new_name, num, playback, 215201d7584cSLiam Girdwood capture, &pcm); 215301d7584cSLiam Girdwood } 2154ddee627cSLiam Girdwood if (ret < 0) { 2155103d84a3SLiam Girdwood dev_err(rtd->card->dev, "ASoC: can't create pcm for %s\n", 21565cb9b748SLiam Girdwood rtd->dai_link->name); 2157ddee627cSLiam Girdwood return ret; 2158ddee627cSLiam Girdwood } 2159103d84a3SLiam Girdwood dev_dbg(rtd->card->dev, "ASoC: registered pcm #%d %s\n",num, new_name); 2160ddee627cSLiam Girdwood 2161ddee627cSLiam Girdwood /* DAPM dai link stream work */ 2162ddee627cSLiam Girdwood INIT_DELAYED_WORK(&rtd->delayed_work, close_delayed_work); 2163ddee627cSLiam Girdwood 2164ddee627cSLiam Girdwood rtd->pcm = pcm; 2165ddee627cSLiam Girdwood pcm->private_data = rtd; 216601d7584cSLiam Girdwood 216701d7584cSLiam Girdwood if (rtd->dai_link->no_pcm) { 216801d7584cSLiam Girdwood if (playback) 216901d7584cSLiam Girdwood pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream->private_data = rtd; 217001d7584cSLiam Girdwood if (capture) 217101d7584cSLiam Girdwood pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream->private_data = rtd; 217201d7584cSLiam Girdwood goto out; 217301d7584cSLiam Girdwood } 217401d7584cSLiam Girdwood 217501d7584cSLiam Girdwood /* ASoC PCM operations */ 217601d7584cSLiam Girdwood if (rtd->dai_link->dynamic) { 217701d7584cSLiam Girdwood rtd->ops.open = dpcm_fe_dai_open; 217801d7584cSLiam Girdwood rtd->ops.hw_params = dpcm_fe_dai_hw_params; 217901d7584cSLiam Girdwood rtd->ops.prepare = dpcm_fe_dai_prepare; 218001d7584cSLiam Girdwood rtd->ops.trigger = dpcm_fe_dai_trigger; 218101d7584cSLiam Girdwood rtd->ops.hw_free = dpcm_fe_dai_hw_free; 218201d7584cSLiam Girdwood rtd->ops.close = dpcm_fe_dai_close; 218301d7584cSLiam Girdwood rtd->ops.pointer = soc_pcm_pointer; 2184be3f3f2cSLiam Girdwood rtd->ops.ioctl = soc_pcm_ioctl; 218501d7584cSLiam Girdwood } else { 218601d7584cSLiam Girdwood rtd->ops.open = soc_pcm_open; 218701d7584cSLiam Girdwood rtd->ops.hw_params = soc_pcm_hw_params; 218801d7584cSLiam Girdwood rtd->ops.prepare = soc_pcm_prepare; 218901d7584cSLiam Girdwood rtd->ops.trigger = soc_pcm_trigger; 219001d7584cSLiam Girdwood rtd->ops.hw_free = soc_pcm_hw_free; 219101d7584cSLiam Girdwood rtd->ops.close = soc_pcm_close; 219201d7584cSLiam Girdwood rtd->ops.pointer = soc_pcm_pointer; 2193be3f3f2cSLiam Girdwood rtd->ops.ioctl = soc_pcm_ioctl; 219401d7584cSLiam Girdwood } 219501d7584cSLiam Girdwood 2196ddee627cSLiam Girdwood if (platform->driver->ops) { 219701d7584cSLiam Girdwood rtd->ops.ack = platform->driver->ops->ack; 219801d7584cSLiam Girdwood rtd->ops.copy = platform->driver->ops->copy; 219901d7584cSLiam Girdwood rtd->ops.silence = platform->driver->ops->silence; 220001d7584cSLiam Girdwood rtd->ops.page = platform->driver->ops->page; 220101d7584cSLiam Girdwood rtd->ops.mmap = platform->driver->ops->mmap; 2202ddee627cSLiam Girdwood } 2203ddee627cSLiam Girdwood 2204ddee627cSLiam Girdwood if (playback) 220501d7584cSLiam Girdwood snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &rtd->ops); 2206ddee627cSLiam Girdwood 2207ddee627cSLiam Girdwood if (capture) 220801d7584cSLiam Girdwood snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &rtd->ops); 2209ddee627cSLiam Girdwood 2210ddee627cSLiam Girdwood if (platform->driver->pcm_new) { 2211ddee627cSLiam Girdwood ret = platform->driver->pcm_new(rtd); 2212ddee627cSLiam Girdwood if (ret < 0) { 2213b1bc7b3cSMark Brown dev_err(platform->dev, 2214b1bc7b3cSMark Brown "ASoC: pcm constructor failed: %d\n", 2215b1bc7b3cSMark Brown ret); 2216ddee627cSLiam Girdwood return ret; 2217ddee627cSLiam Girdwood } 2218ddee627cSLiam Girdwood } 2219ddee627cSLiam Girdwood 2220ddee627cSLiam Girdwood pcm->private_free = platform->driver->pcm_free; 222101d7584cSLiam Girdwood out: 22225cb9b748SLiam Girdwood dev_info(rtd->card->dev, "%s <-> %s mapping ok\n", codec_dai->name, 2223ddee627cSLiam Girdwood cpu_dai->name); 2224ddee627cSLiam Girdwood return ret; 2225ddee627cSLiam Girdwood } 222601d7584cSLiam Girdwood 222701d7584cSLiam Girdwood /* is the current PCM operation for this FE ? */ 222801d7584cSLiam Girdwood int snd_soc_dpcm_fe_can_update(struct snd_soc_pcm_runtime *fe, int stream) 222901d7584cSLiam Girdwood { 223001d7584cSLiam Girdwood if (fe->dpcm[stream].runtime_update == SND_SOC_DPCM_UPDATE_FE) 223101d7584cSLiam Girdwood return 1; 223201d7584cSLiam Girdwood return 0; 223301d7584cSLiam Girdwood } 223401d7584cSLiam Girdwood EXPORT_SYMBOL_GPL(snd_soc_dpcm_fe_can_update); 223501d7584cSLiam Girdwood 223601d7584cSLiam Girdwood /* is the current PCM operation for this BE ? */ 223701d7584cSLiam Girdwood int snd_soc_dpcm_be_can_update(struct snd_soc_pcm_runtime *fe, 223801d7584cSLiam Girdwood struct snd_soc_pcm_runtime *be, int stream) 223901d7584cSLiam Girdwood { 224001d7584cSLiam Girdwood if ((fe->dpcm[stream].runtime_update == SND_SOC_DPCM_UPDATE_FE) || 224101d7584cSLiam Girdwood ((fe->dpcm[stream].runtime_update == SND_SOC_DPCM_UPDATE_BE) && 224201d7584cSLiam Girdwood be->dpcm[stream].runtime_update)) 224301d7584cSLiam Girdwood return 1; 224401d7584cSLiam Girdwood return 0; 224501d7584cSLiam Girdwood } 224601d7584cSLiam Girdwood EXPORT_SYMBOL_GPL(snd_soc_dpcm_be_can_update); 224701d7584cSLiam Girdwood 224801d7584cSLiam Girdwood /* get the substream for this BE */ 224901d7584cSLiam Girdwood struct snd_pcm_substream * 225001d7584cSLiam Girdwood snd_soc_dpcm_get_substream(struct snd_soc_pcm_runtime *be, int stream) 225101d7584cSLiam Girdwood { 225201d7584cSLiam Girdwood return be->pcm->streams[stream].substream; 225301d7584cSLiam Girdwood } 225401d7584cSLiam Girdwood EXPORT_SYMBOL_GPL(snd_soc_dpcm_get_substream); 225501d7584cSLiam Girdwood 225601d7584cSLiam Girdwood /* get the BE runtime state */ 225701d7584cSLiam Girdwood enum snd_soc_dpcm_state 225801d7584cSLiam Girdwood snd_soc_dpcm_be_get_state(struct snd_soc_pcm_runtime *be, int stream) 225901d7584cSLiam Girdwood { 226001d7584cSLiam Girdwood return be->dpcm[stream].state; 226101d7584cSLiam Girdwood } 226201d7584cSLiam Girdwood EXPORT_SYMBOL_GPL(snd_soc_dpcm_be_get_state); 226301d7584cSLiam Girdwood 226401d7584cSLiam Girdwood /* set the BE runtime state */ 226501d7584cSLiam Girdwood void snd_soc_dpcm_be_set_state(struct snd_soc_pcm_runtime *be, 226601d7584cSLiam Girdwood int stream, enum snd_soc_dpcm_state state) 226701d7584cSLiam Girdwood { 226801d7584cSLiam Girdwood be->dpcm[stream].state = state; 226901d7584cSLiam Girdwood } 227001d7584cSLiam Girdwood EXPORT_SYMBOL_GPL(snd_soc_dpcm_be_set_state); 227101d7584cSLiam Girdwood 227201d7584cSLiam Girdwood /* 227301d7584cSLiam Girdwood * We can only hw_free, stop, pause or suspend a BE DAI if any of it's FE 227401d7584cSLiam Girdwood * are not running, paused or suspended for the specified stream direction. 227501d7584cSLiam Girdwood */ 227601d7584cSLiam Girdwood int snd_soc_dpcm_can_be_free_stop(struct snd_soc_pcm_runtime *fe, 227701d7584cSLiam Girdwood struct snd_soc_pcm_runtime *be, int stream) 227801d7584cSLiam Girdwood { 227901d7584cSLiam Girdwood struct snd_soc_dpcm *dpcm; 228001d7584cSLiam Girdwood int state; 228101d7584cSLiam Girdwood 228201d7584cSLiam Girdwood list_for_each_entry(dpcm, &be->dpcm[stream].fe_clients, list_fe) { 228301d7584cSLiam Girdwood 228401d7584cSLiam Girdwood if (dpcm->fe == fe) 228501d7584cSLiam Girdwood continue; 228601d7584cSLiam Girdwood 228701d7584cSLiam Girdwood state = dpcm->fe->dpcm[stream].state; 228801d7584cSLiam Girdwood if (state == SND_SOC_DPCM_STATE_START || 228901d7584cSLiam Girdwood state == SND_SOC_DPCM_STATE_PAUSED || 229001d7584cSLiam Girdwood state == SND_SOC_DPCM_STATE_SUSPEND) 229101d7584cSLiam Girdwood return 0; 229201d7584cSLiam Girdwood } 229301d7584cSLiam Girdwood 229401d7584cSLiam Girdwood /* it's safe to free/stop this BE DAI */ 229501d7584cSLiam Girdwood return 1; 229601d7584cSLiam Girdwood } 229701d7584cSLiam Girdwood EXPORT_SYMBOL_GPL(snd_soc_dpcm_can_be_free_stop); 229801d7584cSLiam Girdwood 229901d7584cSLiam Girdwood /* 230001d7584cSLiam Girdwood * We can only change hw params a BE DAI if any of it's FE are not prepared, 230101d7584cSLiam Girdwood * running, paused or suspended for the specified stream direction. 230201d7584cSLiam Girdwood */ 230301d7584cSLiam Girdwood int snd_soc_dpcm_can_be_params(struct snd_soc_pcm_runtime *fe, 230401d7584cSLiam Girdwood struct snd_soc_pcm_runtime *be, int stream) 230501d7584cSLiam Girdwood { 230601d7584cSLiam Girdwood struct snd_soc_dpcm *dpcm; 230701d7584cSLiam Girdwood int state; 230801d7584cSLiam Girdwood 230901d7584cSLiam Girdwood list_for_each_entry(dpcm, &be->dpcm[stream].fe_clients, list_fe) { 231001d7584cSLiam Girdwood 231101d7584cSLiam Girdwood if (dpcm->fe == fe) 231201d7584cSLiam Girdwood continue; 231301d7584cSLiam Girdwood 231401d7584cSLiam Girdwood state = dpcm->fe->dpcm[stream].state; 231501d7584cSLiam Girdwood if (state == SND_SOC_DPCM_STATE_START || 231601d7584cSLiam Girdwood state == SND_SOC_DPCM_STATE_PAUSED || 231701d7584cSLiam Girdwood state == SND_SOC_DPCM_STATE_SUSPEND || 231801d7584cSLiam Girdwood state == SND_SOC_DPCM_STATE_PREPARE) 231901d7584cSLiam Girdwood return 0; 232001d7584cSLiam Girdwood } 232101d7584cSLiam Girdwood 232201d7584cSLiam Girdwood /* it's safe to change hw_params */ 232301d7584cSLiam Girdwood return 1; 232401d7584cSLiam Girdwood } 232501d7584cSLiam Girdwood EXPORT_SYMBOL_GPL(snd_soc_dpcm_can_be_params); 2326f86dcef8SLiam Girdwood 232707bf84aaSLiam Girdwood int snd_soc_platform_trigger(struct snd_pcm_substream *substream, 232807bf84aaSLiam Girdwood int cmd, struct snd_soc_platform *platform) 232907bf84aaSLiam Girdwood { 2330c5914b0aSMark Brown if (platform->driver->ops && platform->driver->ops->trigger) 233107bf84aaSLiam Girdwood return platform->driver->ops->trigger(substream, cmd); 233207bf84aaSLiam Girdwood return 0; 233307bf84aaSLiam Girdwood } 233407bf84aaSLiam Girdwood EXPORT_SYMBOL_GPL(snd_soc_platform_trigger); 233507bf84aaSLiam Girdwood 2336f86dcef8SLiam Girdwood #ifdef CONFIG_DEBUG_FS 2337f86dcef8SLiam Girdwood static char *dpcm_state_string(enum snd_soc_dpcm_state state) 2338f86dcef8SLiam Girdwood { 2339f86dcef8SLiam Girdwood switch (state) { 2340f86dcef8SLiam Girdwood case SND_SOC_DPCM_STATE_NEW: 2341f86dcef8SLiam Girdwood return "new"; 2342f86dcef8SLiam Girdwood case SND_SOC_DPCM_STATE_OPEN: 2343f86dcef8SLiam Girdwood return "open"; 2344f86dcef8SLiam Girdwood case SND_SOC_DPCM_STATE_HW_PARAMS: 2345f86dcef8SLiam Girdwood return "hw_params"; 2346f86dcef8SLiam Girdwood case SND_SOC_DPCM_STATE_PREPARE: 2347f86dcef8SLiam Girdwood return "prepare"; 2348f86dcef8SLiam Girdwood case SND_SOC_DPCM_STATE_START: 2349f86dcef8SLiam Girdwood return "start"; 2350f86dcef8SLiam Girdwood case SND_SOC_DPCM_STATE_STOP: 2351f86dcef8SLiam Girdwood return "stop"; 2352f86dcef8SLiam Girdwood case SND_SOC_DPCM_STATE_SUSPEND: 2353f86dcef8SLiam Girdwood return "suspend"; 2354f86dcef8SLiam Girdwood case SND_SOC_DPCM_STATE_PAUSED: 2355f86dcef8SLiam Girdwood return "paused"; 2356f86dcef8SLiam Girdwood case SND_SOC_DPCM_STATE_HW_FREE: 2357f86dcef8SLiam Girdwood return "hw_free"; 2358f86dcef8SLiam Girdwood case SND_SOC_DPCM_STATE_CLOSE: 2359f86dcef8SLiam Girdwood return "close"; 2360f86dcef8SLiam Girdwood } 2361f86dcef8SLiam Girdwood 2362f86dcef8SLiam Girdwood return "unknown"; 2363f86dcef8SLiam Girdwood } 2364f86dcef8SLiam Girdwood 2365f86dcef8SLiam Girdwood static ssize_t dpcm_show_state(struct snd_soc_pcm_runtime *fe, 2366f86dcef8SLiam Girdwood int stream, char *buf, size_t size) 2367f86dcef8SLiam Girdwood { 2368f86dcef8SLiam Girdwood struct snd_pcm_hw_params *params = &fe->dpcm[stream].hw_params; 2369f86dcef8SLiam Girdwood struct snd_soc_dpcm *dpcm; 2370f86dcef8SLiam Girdwood ssize_t offset = 0; 2371f86dcef8SLiam Girdwood 2372f86dcef8SLiam Girdwood /* FE state */ 2373f86dcef8SLiam Girdwood offset += snprintf(buf + offset, size - offset, 2374f86dcef8SLiam Girdwood "[%s - %s]\n", fe->dai_link->name, 2375f86dcef8SLiam Girdwood stream ? "Capture" : "Playback"); 2376f86dcef8SLiam Girdwood 2377f86dcef8SLiam Girdwood offset += snprintf(buf + offset, size - offset, "State: %s\n", 2378f86dcef8SLiam Girdwood dpcm_state_string(fe->dpcm[stream].state)); 2379f86dcef8SLiam Girdwood 2380f86dcef8SLiam Girdwood if ((fe->dpcm[stream].state >= SND_SOC_DPCM_STATE_HW_PARAMS) && 2381f86dcef8SLiam Girdwood (fe->dpcm[stream].state <= SND_SOC_DPCM_STATE_STOP)) 2382f86dcef8SLiam Girdwood offset += snprintf(buf + offset, size - offset, 2383f86dcef8SLiam Girdwood "Hardware Params: " 2384f86dcef8SLiam Girdwood "Format = %s, Channels = %d, Rate = %d\n", 2385f86dcef8SLiam Girdwood snd_pcm_format_name(params_format(params)), 2386f86dcef8SLiam Girdwood params_channels(params), 2387f86dcef8SLiam Girdwood params_rate(params)); 2388f86dcef8SLiam Girdwood 2389f86dcef8SLiam Girdwood /* BEs state */ 2390f86dcef8SLiam Girdwood offset += snprintf(buf + offset, size - offset, "Backends:\n"); 2391f86dcef8SLiam Girdwood 2392f86dcef8SLiam Girdwood if (list_empty(&fe->dpcm[stream].be_clients)) { 2393f86dcef8SLiam Girdwood offset += snprintf(buf + offset, size - offset, 2394f86dcef8SLiam Girdwood " No active DSP links\n"); 2395f86dcef8SLiam Girdwood goto out; 2396f86dcef8SLiam Girdwood } 2397f86dcef8SLiam Girdwood 2398f86dcef8SLiam Girdwood list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) { 2399f86dcef8SLiam Girdwood struct snd_soc_pcm_runtime *be = dpcm->be; 2400f86dcef8SLiam Girdwood params = &dpcm->hw_params; 2401f86dcef8SLiam Girdwood 2402f86dcef8SLiam Girdwood offset += snprintf(buf + offset, size - offset, 2403f86dcef8SLiam Girdwood "- %s\n", be->dai_link->name); 2404f86dcef8SLiam Girdwood 2405f86dcef8SLiam Girdwood offset += snprintf(buf + offset, size - offset, 2406f86dcef8SLiam Girdwood " State: %s\n", 2407f86dcef8SLiam Girdwood dpcm_state_string(be->dpcm[stream].state)); 2408f86dcef8SLiam Girdwood 2409f86dcef8SLiam Girdwood if ((be->dpcm[stream].state >= SND_SOC_DPCM_STATE_HW_PARAMS) && 2410f86dcef8SLiam Girdwood (be->dpcm[stream].state <= SND_SOC_DPCM_STATE_STOP)) 2411f86dcef8SLiam Girdwood offset += snprintf(buf + offset, size - offset, 2412f86dcef8SLiam Girdwood " Hardware Params: " 2413f86dcef8SLiam Girdwood "Format = %s, Channels = %d, Rate = %d\n", 2414f86dcef8SLiam Girdwood snd_pcm_format_name(params_format(params)), 2415f86dcef8SLiam Girdwood params_channels(params), 2416f86dcef8SLiam Girdwood params_rate(params)); 2417f86dcef8SLiam Girdwood } 2418f86dcef8SLiam Girdwood 2419f86dcef8SLiam Girdwood out: 2420f86dcef8SLiam Girdwood return offset; 2421f86dcef8SLiam Girdwood } 2422f86dcef8SLiam Girdwood 2423f86dcef8SLiam Girdwood static ssize_t dpcm_state_read_file(struct file *file, char __user *user_buf, 2424f86dcef8SLiam Girdwood size_t count, loff_t *ppos) 2425f86dcef8SLiam Girdwood { 2426f86dcef8SLiam Girdwood struct snd_soc_pcm_runtime *fe = file->private_data; 2427f86dcef8SLiam Girdwood ssize_t out_count = PAGE_SIZE, offset = 0, ret = 0; 2428f86dcef8SLiam Girdwood char *buf; 2429f86dcef8SLiam Girdwood 2430f86dcef8SLiam Girdwood buf = kmalloc(out_count, GFP_KERNEL); 2431f86dcef8SLiam Girdwood if (!buf) 2432f86dcef8SLiam Girdwood return -ENOMEM; 2433f86dcef8SLiam Girdwood 2434f86dcef8SLiam Girdwood if (fe->cpu_dai->driver->playback.channels_min) 2435f86dcef8SLiam Girdwood offset += dpcm_show_state(fe, SNDRV_PCM_STREAM_PLAYBACK, 2436f86dcef8SLiam Girdwood buf + offset, out_count - offset); 2437f86dcef8SLiam Girdwood 2438f86dcef8SLiam Girdwood if (fe->cpu_dai->driver->capture.channels_min) 2439f86dcef8SLiam Girdwood offset += dpcm_show_state(fe, SNDRV_PCM_STREAM_CAPTURE, 2440f86dcef8SLiam Girdwood buf + offset, out_count - offset); 2441f86dcef8SLiam Girdwood 2442f86dcef8SLiam Girdwood ret = simple_read_from_buffer(user_buf, count, ppos, buf, offset); 2443f86dcef8SLiam Girdwood 2444f86dcef8SLiam Girdwood kfree(buf); 2445f86dcef8SLiam Girdwood return ret; 2446f86dcef8SLiam Girdwood } 2447f86dcef8SLiam Girdwood 2448f86dcef8SLiam Girdwood static const struct file_operations dpcm_state_fops = { 2449f57b8488SLiam Girdwood .open = simple_open, 2450f86dcef8SLiam Girdwood .read = dpcm_state_read_file, 2451f86dcef8SLiam Girdwood .llseek = default_llseek, 2452f86dcef8SLiam Girdwood }; 2453f86dcef8SLiam Girdwood 2454f86dcef8SLiam Girdwood int soc_dpcm_debugfs_add(struct snd_soc_pcm_runtime *rtd) 2455f86dcef8SLiam Girdwood { 2456b3bba9a1SMark Brown if (!rtd->dai_link) 2457b3bba9a1SMark Brown return 0; 2458b3bba9a1SMark Brown 2459f86dcef8SLiam Girdwood rtd->debugfs_dpcm_root = debugfs_create_dir(rtd->dai_link->name, 2460f86dcef8SLiam Girdwood rtd->card->debugfs_card_root); 2461f86dcef8SLiam Girdwood if (!rtd->debugfs_dpcm_root) { 2462f86dcef8SLiam Girdwood dev_dbg(rtd->dev, 2463f86dcef8SLiam Girdwood "ASoC: Failed to create dpcm debugfs directory %s\n", 2464f86dcef8SLiam Girdwood rtd->dai_link->name); 2465f86dcef8SLiam Girdwood return -EINVAL; 2466f86dcef8SLiam Girdwood } 2467f86dcef8SLiam Girdwood 2468f57b8488SLiam Girdwood rtd->debugfs_dpcm_state = debugfs_create_file("state", 0444, 2469f86dcef8SLiam Girdwood rtd->debugfs_dpcm_root, 2470f86dcef8SLiam Girdwood rtd, &dpcm_state_fops); 2471f86dcef8SLiam Girdwood 2472f86dcef8SLiam Girdwood return 0; 2473f86dcef8SLiam Girdwood } 2474f86dcef8SLiam Girdwood #endif 2475