1*ddee627cSLiam Girdwood /* 2*ddee627cSLiam Girdwood * soc-pcm.c -- ALSA SoC PCM 3*ddee627cSLiam Girdwood * 4*ddee627cSLiam Girdwood * Copyright 2005 Wolfson Microelectronics PLC. 5*ddee627cSLiam Girdwood * Copyright 2005 Openedhand Ltd. 6*ddee627cSLiam Girdwood * Copyright (C) 2010 Slimlogic Ltd. 7*ddee627cSLiam Girdwood * Copyright (C) 2010 Texas Instruments Inc. 8*ddee627cSLiam Girdwood * 9*ddee627cSLiam Girdwood * Authors: Liam Girdwood <lrg@ti.com> 10*ddee627cSLiam Girdwood * Mark Brown <broonie@opensource.wolfsonmicro.com> 11*ddee627cSLiam Girdwood * 12*ddee627cSLiam Girdwood * This program is free software; you can redistribute it and/or modify it 13*ddee627cSLiam Girdwood * under the terms of the GNU General Public License as published by the 14*ddee627cSLiam Girdwood * Free Software Foundation; either version 2 of the License, or (at your 15*ddee627cSLiam Girdwood * option) any later version. 16*ddee627cSLiam Girdwood * 17*ddee627cSLiam Girdwood */ 18*ddee627cSLiam Girdwood 19*ddee627cSLiam Girdwood #include <linux/kernel.h> 20*ddee627cSLiam Girdwood #include <linux/init.h> 21*ddee627cSLiam Girdwood #include <linux/delay.h> 22*ddee627cSLiam Girdwood #include <linux/slab.h> 23*ddee627cSLiam Girdwood #include <linux/workqueue.h> 24*ddee627cSLiam Girdwood #include <sound/core.h> 25*ddee627cSLiam Girdwood #include <sound/pcm.h> 26*ddee627cSLiam Girdwood #include <sound/pcm_params.h> 27*ddee627cSLiam Girdwood #include <sound/soc.h> 28*ddee627cSLiam Girdwood #include <sound/initval.h> 29*ddee627cSLiam Girdwood 30*ddee627cSLiam Girdwood static DEFINE_MUTEX(pcm_mutex); 31*ddee627cSLiam Girdwood 32*ddee627cSLiam Girdwood static int soc_pcm_apply_symmetry(struct snd_pcm_substream *substream) 33*ddee627cSLiam Girdwood { 34*ddee627cSLiam Girdwood struct snd_soc_pcm_runtime *rtd = substream->private_data; 35*ddee627cSLiam Girdwood struct snd_soc_dai *cpu_dai = rtd->cpu_dai; 36*ddee627cSLiam Girdwood struct snd_soc_dai *codec_dai = rtd->codec_dai; 37*ddee627cSLiam Girdwood int ret; 38*ddee627cSLiam Girdwood 39*ddee627cSLiam Girdwood if (!codec_dai->driver->symmetric_rates && 40*ddee627cSLiam Girdwood !cpu_dai->driver->symmetric_rates && 41*ddee627cSLiam Girdwood !rtd->dai_link->symmetric_rates) 42*ddee627cSLiam Girdwood return 0; 43*ddee627cSLiam Girdwood 44*ddee627cSLiam Girdwood /* This can happen if multiple streams are starting simultaneously - 45*ddee627cSLiam Girdwood * the second can need to get its constraints before the first has 46*ddee627cSLiam Girdwood * picked a rate. Complain and allow the application to carry on. 47*ddee627cSLiam Girdwood */ 48*ddee627cSLiam Girdwood if (!rtd->rate) { 49*ddee627cSLiam Girdwood dev_warn(&rtd->dev, 50*ddee627cSLiam Girdwood "Not enforcing symmetric_rates due to race\n"); 51*ddee627cSLiam Girdwood return 0; 52*ddee627cSLiam Girdwood } 53*ddee627cSLiam Girdwood 54*ddee627cSLiam Girdwood dev_dbg(&rtd->dev, "Symmetry forces %dHz rate\n", rtd->rate); 55*ddee627cSLiam Girdwood 56*ddee627cSLiam Girdwood ret = snd_pcm_hw_constraint_minmax(substream->runtime, 57*ddee627cSLiam Girdwood SNDRV_PCM_HW_PARAM_RATE, 58*ddee627cSLiam Girdwood rtd->rate, rtd->rate); 59*ddee627cSLiam Girdwood if (ret < 0) { 60*ddee627cSLiam Girdwood dev_err(&rtd->dev, 61*ddee627cSLiam Girdwood "Unable to apply rate symmetry constraint: %d\n", ret); 62*ddee627cSLiam Girdwood return ret; 63*ddee627cSLiam Girdwood } 64*ddee627cSLiam Girdwood 65*ddee627cSLiam Girdwood return 0; 66*ddee627cSLiam Girdwood } 67*ddee627cSLiam Girdwood 68*ddee627cSLiam Girdwood /* 69*ddee627cSLiam Girdwood * Called by ALSA when a PCM substream is opened, the runtime->hw record is 70*ddee627cSLiam Girdwood * then initialized and any private data can be allocated. This also calls 71*ddee627cSLiam Girdwood * startup for the cpu DAI, platform, machine and codec DAI. 72*ddee627cSLiam Girdwood */ 73*ddee627cSLiam Girdwood static int soc_pcm_open(struct snd_pcm_substream *substream) 74*ddee627cSLiam Girdwood { 75*ddee627cSLiam Girdwood struct snd_soc_pcm_runtime *rtd = substream->private_data; 76*ddee627cSLiam Girdwood struct snd_pcm_runtime *runtime = substream->runtime; 77*ddee627cSLiam Girdwood struct snd_soc_platform *platform = rtd->platform; 78*ddee627cSLiam Girdwood struct snd_soc_dai *cpu_dai = rtd->cpu_dai; 79*ddee627cSLiam Girdwood struct snd_soc_dai *codec_dai = rtd->codec_dai; 80*ddee627cSLiam Girdwood struct snd_soc_dai_driver *cpu_dai_drv = cpu_dai->driver; 81*ddee627cSLiam Girdwood struct snd_soc_dai_driver *codec_dai_drv = codec_dai->driver; 82*ddee627cSLiam Girdwood int ret = 0; 83*ddee627cSLiam Girdwood 84*ddee627cSLiam Girdwood mutex_lock(&pcm_mutex); 85*ddee627cSLiam Girdwood 86*ddee627cSLiam Girdwood /* startup the audio subsystem */ 87*ddee627cSLiam Girdwood if (cpu_dai->driver->ops->startup) { 88*ddee627cSLiam Girdwood ret = cpu_dai->driver->ops->startup(substream, cpu_dai); 89*ddee627cSLiam Girdwood if (ret < 0) { 90*ddee627cSLiam Girdwood printk(KERN_ERR "asoc: can't open interface %s\n", 91*ddee627cSLiam Girdwood cpu_dai->name); 92*ddee627cSLiam Girdwood goto out; 93*ddee627cSLiam Girdwood } 94*ddee627cSLiam Girdwood } 95*ddee627cSLiam Girdwood 96*ddee627cSLiam Girdwood if (platform->driver->ops && platform->driver->ops->open) { 97*ddee627cSLiam Girdwood ret = platform->driver->ops->open(substream); 98*ddee627cSLiam Girdwood if (ret < 0) { 99*ddee627cSLiam Girdwood printk(KERN_ERR "asoc: can't open platform %s\n", platform->name); 100*ddee627cSLiam Girdwood goto platform_err; 101*ddee627cSLiam Girdwood } 102*ddee627cSLiam Girdwood } 103*ddee627cSLiam Girdwood 104*ddee627cSLiam Girdwood if (codec_dai->driver->ops->startup) { 105*ddee627cSLiam Girdwood ret = codec_dai->driver->ops->startup(substream, codec_dai); 106*ddee627cSLiam Girdwood if (ret < 0) { 107*ddee627cSLiam Girdwood printk(KERN_ERR "asoc: can't open codec %s\n", 108*ddee627cSLiam Girdwood codec_dai->name); 109*ddee627cSLiam Girdwood goto codec_dai_err; 110*ddee627cSLiam Girdwood } 111*ddee627cSLiam Girdwood } 112*ddee627cSLiam Girdwood 113*ddee627cSLiam Girdwood if (rtd->dai_link->ops && rtd->dai_link->ops->startup) { 114*ddee627cSLiam Girdwood ret = rtd->dai_link->ops->startup(substream); 115*ddee627cSLiam Girdwood if (ret < 0) { 116*ddee627cSLiam Girdwood printk(KERN_ERR "asoc: %s startup failed\n", rtd->dai_link->name); 117*ddee627cSLiam Girdwood goto machine_err; 118*ddee627cSLiam Girdwood } 119*ddee627cSLiam Girdwood } 120*ddee627cSLiam Girdwood 121*ddee627cSLiam Girdwood /* Check that the codec and cpu DAIs are compatible */ 122*ddee627cSLiam Girdwood if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 123*ddee627cSLiam Girdwood runtime->hw.rate_min = 124*ddee627cSLiam Girdwood max(codec_dai_drv->playback.rate_min, 125*ddee627cSLiam Girdwood cpu_dai_drv->playback.rate_min); 126*ddee627cSLiam Girdwood runtime->hw.rate_max = 127*ddee627cSLiam Girdwood min(codec_dai_drv->playback.rate_max, 128*ddee627cSLiam Girdwood cpu_dai_drv->playback.rate_max); 129*ddee627cSLiam Girdwood runtime->hw.channels_min = 130*ddee627cSLiam Girdwood max(codec_dai_drv->playback.channels_min, 131*ddee627cSLiam Girdwood cpu_dai_drv->playback.channels_min); 132*ddee627cSLiam Girdwood runtime->hw.channels_max = 133*ddee627cSLiam Girdwood min(codec_dai_drv->playback.channels_max, 134*ddee627cSLiam Girdwood cpu_dai_drv->playback.channels_max); 135*ddee627cSLiam Girdwood runtime->hw.formats = 136*ddee627cSLiam Girdwood codec_dai_drv->playback.formats & cpu_dai_drv->playback.formats; 137*ddee627cSLiam Girdwood runtime->hw.rates = 138*ddee627cSLiam Girdwood codec_dai_drv->playback.rates & cpu_dai_drv->playback.rates; 139*ddee627cSLiam Girdwood if (codec_dai_drv->playback.rates 140*ddee627cSLiam Girdwood & (SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_CONTINUOUS)) 141*ddee627cSLiam Girdwood runtime->hw.rates |= cpu_dai_drv->playback.rates; 142*ddee627cSLiam Girdwood if (cpu_dai_drv->playback.rates 143*ddee627cSLiam Girdwood & (SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_CONTINUOUS)) 144*ddee627cSLiam Girdwood runtime->hw.rates |= codec_dai_drv->playback.rates; 145*ddee627cSLiam Girdwood } else { 146*ddee627cSLiam Girdwood runtime->hw.rate_min = 147*ddee627cSLiam Girdwood max(codec_dai_drv->capture.rate_min, 148*ddee627cSLiam Girdwood cpu_dai_drv->capture.rate_min); 149*ddee627cSLiam Girdwood runtime->hw.rate_max = 150*ddee627cSLiam Girdwood min(codec_dai_drv->capture.rate_max, 151*ddee627cSLiam Girdwood cpu_dai_drv->capture.rate_max); 152*ddee627cSLiam Girdwood runtime->hw.channels_min = 153*ddee627cSLiam Girdwood max(codec_dai_drv->capture.channels_min, 154*ddee627cSLiam Girdwood cpu_dai_drv->capture.channels_min); 155*ddee627cSLiam Girdwood runtime->hw.channels_max = 156*ddee627cSLiam Girdwood min(codec_dai_drv->capture.channels_max, 157*ddee627cSLiam Girdwood cpu_dai_drv->capture.channels_max); 158*ddee627cSLiam Girdwood runtime->hw.formats = 159*ddee627cSLiam Girdwood codec_dai_drv->capture.formats & cpu_dai_drv->capture.formats; 160*ddee627cSLiam Girdwood runtime->hw.rates = 161*ddee627cSLiam Girdwood codec_dai_drv->capture.rates & cpu_dai_drv->capture.rates; 162*ddee627cSLiam Girdwood if (codec_dai_drv->capture.rates 163*ddee627cSLiam Girdwood & (SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_CONTINUOUS)) 164*ddee627cSLiam Girdwood runtime->hw.rates |= cpu_dai_drv->capture.rates; 165*ddee627cSLiam Girdwood if (cpu_dai_drv->capture.rates 166*ddee627cSLiam Girdwood & (SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_CONTINUOUS)) 167*ddee627cSLiam Girdwood runtime->hw.rates |= codec_dai_drv->capture.rates; 168*ddee627cSLiam Girdwood } 169*ddee627cSLiam Girdwood 170*ddee627cSLiam Girdwood ret = -EINVAL; 171*ddee627cSLiam Girdwood snd_pcm_limit_hw_rates(runtime); 172*ddee627cSLiam Girdwood if (!runtime->hw.rates) { 173*ddee627cSLiam Girdwood printk(KERN_ERR "asoc: %s <-> %s No matching rates\n", 174*ddee627cSLiam Girdwood codec_dai->name, cpu_dai->name); 175*ddee627cSLiam Girdwood goto config_err; 176*ddee627cSLiam Girdwood } 177*ddee627cSLiam Girdwood if (!runtime->hw.formats) { 178*ddee627cSLiam Girdwood printk(KERN_ERR "asoc: %s <-> %s No matching formats\n", 179*ddee627cSLiam Girdwood codec_dai->name, cpu_dai->name); 180*ddee627cSLiam Girdwood goto config_err; 181*ddee627cSLiam Girdwood } 182*ddee627cSLiam Girdwood if (!runtime->hw.channels_min || !runtime->hw.channels_max || 183*ddee627cSLiam Girdwood runtime->hw.channels_min > runtime->hw.channels_max) { 184*ddee627cSLiam Girdwood printk(KERN_ERR "asoc: %s <-> %s No matching channels\n", 185*ddee627cSLiam Girdwood codec_dai->name, cpu_dai->name); 186*ddee627cSLiam Girdwood goto config_err; 187*ddee627cSLiam Girdwood } 188*ddee627cSLiam Girdwood 189*ddee627cSLiam Girdwood /* Symmetry only applies if we've already got an active stream. */ 190*ddee627cSLiam Girdwood if (cpu_dai->active || codec_dai->active) { 191*ddee627cSLiam Girdwood ret = soc_pcm_apply_symmetry(substream); 192*ddee627cSLiam Girdwood if (ret != 0) 193*ddee627cSLiam Girdwood goto config_err; 194*ddee627cSLiam Girdwood } 195*ddee627cSLiam Girdwood 196*ddee627cSLiam Girdwood pr_debug("asoc: %s <-> %s info:\n", 197*ddee627cSLiam Girdwood codec_dai->name, cpu_dai->name); 198*ddee627cSLiam Girdwood pr_debug("asoc: rate mask 0x%x\n", runtime->hw.rates); 199*ddee627cSLiam Girdwood pr_debug("asoc: min ch %d max ch %d\n", runtime->hw.channels_min, 200*ddee627cSLiam Girdwood runtime->hw.channels_max); 201*ddee627cSLiam Girdwood pr_debug("asoc: min rate %d max rate %d\n", runtime->hw.rate_min, 202*ddee627cSLiam Girdwood runtime->hw.rate_max); 203*ddee627cSLiam Girdwood 204*ddee627cSLiam Girdwood if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 205*ddee627cSLiam Girdwood cpu_dai->playback_active++; 206*ddee627cSLiam Girdwood codec_dai->playback_active++; 207*ddee627cSLiam Girdwood } else { 208*ddee627cSLiam Girdwood cpu_dai->capture_active++; 209*ddee627cSLiam Girdwood codec_dai->capture_active++; 210*ddee627cSLiam Girdwood } 211*ddee627cSLiam Girdwood cpu_dai->active++; 212*ddee627cSLiam Girdwood codec_dai->active++; 213*ddee627cSLiam Girdwood rtd->codec->active++; 214*ddee627cSLiam Girdwood mutex_unlock(&pcm_mutex); 215*ddee627cSLiam Girdwood return 0; 216*ddee627cSLiam Girdwood 217*ddee627cSLiam Girdwood config_err: 218*ddee627cSLiam Girdwood if (rtd->dai_link->ops && rtd->dai_link->ops->shutdown) 219*ddee627cSLiam Girdwood rtd->dai_link->ops->shutdown(substream); 220*ddee627cSLiam Girdwood 221*ddee627cSLiam Girdwood machine_err: 222*ddee627cSLiam Girdwood if (codec_dai->driver->ops->shutdown) 223*ddee627cSLiam Girdwood codec_dai->driver->ops->shutdown(substream, codec_dai); 224*ddee627cSLiam Girdwood 225*ddee627cSLiam Girdwood codec_dai_err: 226*ddee627cSLiam Girdwood if (platform->driver->ops && platform->driver->ops->close) 227*ddee627cSLiam Girdwood platform->driver->ops->close(substream); 228*ddee627cSLiam Girdwood 229*ddee627cSLiam Girdwood platform_err: 230*ddee627cSLiam Girdwood if (cpu_dai->driver->ops->shutdown) 231*ddee627cSLiam Girdwood cpu_dai->driver->ops->shutdown(substream, cpu_dai); 232*ddee627cSLiam Girdwood out: 233*ddee627cSLiam Girdwood mutex_unlock(&pcm_mutex); 234*ddee627cSLiam Girdwood return ret; 235*ddee627cSLiam Girdwood } 236*ddee627cSLiam Girdwood 237*ddee627cSLiam Girdwood /* 238*ddee627cSLiam Girdwood * Power down the audio subsystem pmdown_time msecs after close is called. 239*ddee627cSLiam Girdwood * This is to ensure there are no pops or clicks in between any music tracks 240*ddee627cSLiam Girdwood * due to DAPM power cycling. 241*ddee627cSLiam Girdwood */ 242*ddee627cSLiam Girdwood static void close_delayed_work(struct work_struct *work) 243*ddee627cSLiam Girdwood { 244*ddee627cSLiam Girdwood struct snd_soc_pcm_runtime *rtd = 245*ddee627cSLiam Girdwood container_of(work, struct snd_soc_pcm_runtime, delayed_work.work); 246*ddee627cSLiam Girdwood struct snd_soc_dai *codec_dai = rtd->codec_dai; 247*ddee627cSLiam Girdwood 248*ddee627cSLiam Girdwood mutex_lock(&pcm_mutex); 249*ddee627cSLiam Girdwood 250*ddee627cSLiam Girdwood pr_debug("pop wq checking: %s status: %s waiting: %s\n", 251*ddee627cSLiam Girdwood codec_dai->driver->playback.stream_name, 252*ddee627cSLiam Girdwood codec_dai->playback_active ? "active" : "inactive", 253*ddee627cSLiam Girdwood codec_dai->pop_wait ? "yes" : "no"); 254*ddee627cSLiam Girdwood 255*ddee627cSLiam Girdwood /* are we waiting on this codec DAI stream */ 256*ddee627cSLiam Girdwood if (codec_dai->pop_wait == 1) { 257*ddee627cSLiam Girdwood codec_dai->pop_wait = 0; 258*ddee627cSLiam Girdwood snd_soc_dapm_stream_event(rtd, 259*ddee627cSLiam Girdwood codec_dai->driver->playback.stream_name, 260*ddee627cSLiam Girdwood SND_SOC_DAPM_STREAM_STOP); 261*ddee627cSLiam Girdwood } 262*ddee627cSLiam Girdwood 263*ddee627cSLiam Girdwood mutex_unlock(&pcm_mutex); 264*ddee627cSLiam Girdwood } 265*ddee627cSLiam Girdwood 266*ddee627cSLiam Girdwood /* 267*ddee627cSLiam Girdwood * Called by ALSA when a PCM substream is closed. Private data can be 268*ddee627cSLiam Girdwood * freed here. The cpu DAI, codec DAI, machine and platform are also 269*ddee627cSLiam Girdwood * shutdown. 270*ddee627cSLiam Girdwood */ 271*ddee627cSLiam Girdwood static int soc_codec_close(struct snd_pcm_substream *substream) 272*ddee627cSLiam Girdwood { 273*ddee627cSLiam Girdwood struct snd_soc_pcm_runtime *rtd = substream->private_data; 274*ddee627cSLiam Girdwood struct snd_soc_platform *platform = rtd->platform; 275*ddee627cSLiam Girdwood struct snd_soc_dai *cpu_dai = rtd->cpu_dai; 276*ddee627cSLiam Girdwood struct snd_soc_dai *codec_dai = rtd->codec_dai; 277*ddee627cSLiam Girdwood struct snd_soc_codec *codec = rtd->codec; 278*ddee627cSLiam Girdwood 279*ddee627cSLiam Girdwood mutex_lock(&pcm_mutex); 280*ddee627cSLiam Girdwood 281*ddee627cSLiam Girdwood if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 282*ddee627cSLiam Girdwood cpu_dai->playback_active--; 283*ddee627cSLiam Girdwood codec_dai->playback_active--; 284*ddee627cSLiam Girdwood } else { 285*ddee627cSLiam Girdwood cpu_dai->capture_active--; 286*ddee627cSLiam Girdwood codec_dai->capture_active--; 287*ddee627cSLiam Girdwood } 288*ddee627cSLiam Girdwood 289*ddee627cSLiam Girdwood cpu_dai->active--; 290*ddee627cSLiam Girdwood codec_dai->active--; 291*ddee627cSLiam Girdwood codec->active--; 292*ddee627cSLiam Girdwood 293*ddee627cSLiam Girdwood /* Muting the DAC suppresses artifacts caused during digital 294*ddee627cSLiam Girdwood * shutdown, for example from stopping clocks. 295*ddee627cSLiam Girdwood */ 296*ddee627cSLiam Girdwood if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 297*ddee627cSLiam Girdwood snd_soc_dai_digital_mute(codec_dai, 1); 298*ddee627cSLiam Girdwood 299*ddee627cSLiam Girdwood if (cpu_dai->driver->ops->shutdown) 300*ddee627cSLiam Girdwood cpu_dai->driver->ops->shutdown(substream, cpu_dai); 301*ddee627cSLiam Girdwood 302*ddee627cSLiam Girdwood if (codec_dai->driver->ops->shutdown) 303*ddee627cSLiam Girdwood codec_dai->driver->ops->shutdown(substream, codec_dai); 304*ddee627cSLiam Girdwood 305*ddee627cSLiam Girdwood if (rtd->dai_link->ops && rtd->dai_link->ops->shutdown) 306*ddee627cSLiam Girdwood rtd->dai_link->ops->shutdown(substream); 307*ddee627cSLiam Girdwood 308*ddee627cSLiam Girdwood if (platform->driver->ops && platform->driver->ops->close) 309*ddee627cSLiam Girdwood platform->driver->ops->close(substream); 310*ddee627cSLiam Girdwood cpu_dai->runtime = NULL; 311*ddee627cSLiam Girdwood 312*ddee627cSLiam Girdwood if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 313*ddee627cSLiam Girdwood /* start delayed pop wq here for playback streams */ 314*ddee627cSLiam Girdwood codec_dai->pop_wait = 1; 315*ddee627cSLiam Girdwood schedule_delayed_work(&rtd->delayed_work, 316*ddee627cSLiam Girdwood msecs_to_jiffies(rtd->pmdown_time)); 317*ddee627cSLiam Girdwood } else { 318*ddee627cSLiam Girdwood /* capture streams can be powered down now */ 319*ddee627cSLiam Girdwood snd_soc_dapm_stream_event(rtd, 320*ddee627cSLiam Girdwood codec_dai->driver->capture.stream_name, 321*ddee627cSLiam Girdwood SND_SOC_DAPM_STREAM_STOP); 322*ddee627cSLiam Girdwood } 323*ddee627cSLiam Girdwood 324*ddee627cSLiam Girdwood mutex_unlock(&pcm_mutex); 325*ddee627cSLiam Girdwood return 0; 326*ddee627cSLiam Girdwood } 327*ddee627cSLiam Girdwood 328*ddee627cSLiam Girdwood /* 329*ddee627cSLiam Girdwood * Called by ALSA when the PCM substream is prepared, can set format, sample 330*ddee627cSLiam Girdwood * rate, etc. This function is non atomic and can be called multiple times, 331*ddee627cSLiam Girdwood * it can refer to the runtime info. 332*ddee627cSLiam Girdwood */ 333*ddee627cSLiam Girdwood static int soc_pcm_prepare(struct snd_pcm_substream *substream) 334*ddee627cSLiam Girdwood { 335*ddee627cSLiam Girdwood struct snd_soc_pcm_runtime *rtd = substream->private_data; 336*ddee627cSLiam Girdwood struct snd_soc_platform *platform = rtd->platform; 337*ddee627cSLiam Girdwood struct snd_soc_dai *cpu_dai = rtd->cpu_dai; 338*ddee627cSLiam Girdwood struct snd_soc_dai *codec_dai = rtd->codec_dai; 339*ddee627cSLiam Girdwood int ret = 0; 340*ddee627cSLiam Girdwood 341*ddee627cSLiam Girdwood mutex_lock(&pcm_mutex); 342*ddee627cSLiam Girdwood 343*ddee627cSLiam Girdwood if (rtd->dai_link->ops && rtd->dai_link->ops->prepare) { 344*ddee627cSLiam Girdwood ret = rtd->dai_link->ops->prepare(substream); 345*ddee627cSLiam Girdwood if (ret < 0) { 346*ddee627cSLiam Girdwood printk(KERN_ERR "asoc: machine prepare error\n"); 347*ddee627cSLiam Girdwood goto out; 348*ddee627cSLiam Girdwood } 349*ddee627cSLiam Girdwood } 350*ddee627cSLiam Girdwood 351*ddee627cSLiam Girdwood if (platform->driver->ops && platform->driver->ops->prepare) { 352*ddee627cSLiam Girdwood ret = platform->driver->ops->prepare(substream); 353*ddee627cSLiam Girdwood if (ret < 0) { 354*ddee627cSLiam Girdwood printk(KERN_ERR "asoc: platform prepare error\n"); 355*ddee627cSLiam Girdwood goto out; 356*ddee627cSLiam Girdwood } 357*ddee627cSLiam Girdwood } 358*ddee627cSLiam Girdwood 359*ddee627cSLiam Girdwood if (codec_dai->driver->ops->prepare) { 360*ddee627cSLiam Girdwood ret = codec_dai->driver->ops->prepare(substream, codec_dai); 361*ddee627cSLiam Girdwood if (ret < 0) { 362*ddee627cSLiam Girdwood printk(KERN_ERR "asoc: codec DAI prepare error\n"); 363*ddee627cSLiam Girdwood goto out; 364*ddee627cSLiam Girdwood } 365*ddee627cSLiam Girdwood } 366*ddee627cSLiam Girdwood 367*ddee627cSLiam Girdwood if (cpu_dai->driver->ops->prepare) { 368*ddee627cSLiam Girdwood ret = cpu_dai->driver->ops->prepare(substream, cpu_dai); 369*ddee627cSLiam Girdwood if (ret < 0) { 370*ddee627cSLiam Girdwood printk(KERN_ERR "asoc: cpu DAI prepare error\n"); 371*ddee627cSLiam Girdwood goto out; 372*ddee627cSLiam Girdwood } 373*ddee627cSLiam Girdwood } 374*ddee627cSLiam Girdwood 375*ddee627cSLiam Girdwood /* cancel any delayed stream shutdown that is pending */ 376*ddee627cSLiam Girdwood if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && 377*ddee627cSLiam Girdwood codec_dai->pop_wait) { 378*ddee627cSLiam Girdwood codec_dai->pop_wait = 0; 379*ddee627cSLiam Girdwood cancel_delayed_work(&rtd->delayed_work); 380*ddee627cSLiam Girdwood } 381*ddee627cSLiam Girdwood 382*ddee627cSLiam Girdwood if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 383*ddee627cSLiam Girdwood snd_soc_dapm_stream_event(rtd, 384*ddee627cSLiam Girdwood codec_dai->driver->playback.stream_name, 385*ddee627cSLiam Girdwood SND_SOC_DAPM_STREAM_START); 386*ddee627cSLiam Girdwood else 387*ddee627cSLiam Girdwood snd_soc_dapm_stream_event(rtd, 388*ddee627cSLiam Girdwood codec_dai->driver->capture.stream_name, 389*ddee627cSLiam Girdwood SND_SOC_DAPM_STREAM_START); 390*ddee627cSLiam Girdwood 391*ddee627cSLiam Girdwood snd_soc_dai_digital_mute(codec_dai, 0); 392*ddee627cSLiam Girdwood 393*ddee627cSLiam Girdwood out: 394*ddee627cSLiam Girdwood mutex_unlock(&pcm_mutex); 395*ddee627cSLiam Girdwood return ret; 396*ddee627cSLiam Girdwood } 397*ddee627cSLiam Girdwood 398*ddee627cSLiam Girdwood /* 399*ddee627cSLiam Girdwood * Called by ALSA when the hardware params are set by application. This 400*ddee627cSLiam Girdwood * function can also be called multiple times and can allocate buffers 401*ddee627cSLiam Girdwood * (using snd_pcm_lib_* ). It's non-atomic. 402*ddee627cSLiam Girdwood */ 403*ddee627cSLiam Girdwood static int soc_pcm_hw_params(struct snd_pcm_substream *substream, 404*ddee627cSLiam Girdwood struct snd_pcm_hw_params *params) 405*ddee627cSLiam Girdwood { 406*ddee627cSLiam Girdwood struct snd_soc_pcm_runtime *rtd = substream->private_data; 407*ddee627cSLiam Girdwood struct snd_soc_platform *platform = rtd->platform; 408*ddee627cSLiam Girdwood struct snd_soc_dai *cpu_dai = rtd->cpu_dai; 409*ddee627cSLiam Girdwood struct snd_soc_dai *codec_dai = rtd->codec_dai; 410*ddee627cSLiam Girdwood int ret = 0; 411*ddee627cSLiam Girdwood 412*ddee627cSLiam Girdwood mutex_lock(&pcm_mutex); 413*ddee627cSLiam Girdwood 414*ddee627cSLiam Girdwood if (rtd->dai_link->ops && rtd->dai_link->ops->hw_params) { 415*ddee627cSLiam Girdwood ret = rtd->dai_link->ops->hw_params(substream, params); 416*ddee627cSLiam Girdwood if (ret < 0) { 417*ddee627cSLiam Girdwood printk(KERN_ERR "asoc: machine hw_params failed\n"); 418*ddee627cSLiam Girdwood goto out; 419*ddee627cSLiam Girdwood } 420*ddee627cSLiam Girdwood } 421*ddee627cSLiam Girdwood 422*ddee627cSLiam Girdwood if (codec_dai->driver->ops->hw_params) { 423*ddee627cSLiam Girdwood ret = codec_dai->driver->ops->hw_params(substream, params, codec_dai); 424*ddee627cSLiam Girdwood if (ret < 0) { 425*ddee627cSLiam Girdwood printk(KERN_ERR "asoc: can't set codec %s hw params\n", 426*ddee627cSLiam Girdwood codec_dai->name); 427*ddee627cSLiam Girdwood goto codec_err; 428*ddee627cSLiam Girdwood } 429*ddee627cSLiam Girdwood } 430*ddee627cSLiam Girdwood 431*ddee627cSLiam Girdwood if (cpu_dai->driver->ops->hw_params) { 432*ddee627cSLiam Girdwood ret = cpu_dai->driver->ops->hw_params(substream, params, cpu_dai); 433*ddee627cSLiam Girdwood if (ret < 0) { 434*ddee627cSLiam Girdwood printk(KERN_ERR "asoc: interface %s hw params failed\n", 435*ddee627cSLiam Girdwood cpu_dai->name); 436*ddee627cSLiam Girdwood goto interface_err; 437*ddee627cSLiam Girdwood } 438*ddee627cSLiam Girdwood } 439*ddee627cSLiam Girdwood 440*ddee627cSLiam Girdwood if (platform->driver->ops && platform->driver->ops->hw_params) { 441*ddee627cSLiam Girdwood ret = platform->driver->ops->hw_params(substream, params); 442*ddee627cSLiam Girdwood if (ret < 0) { 443*ddee627cSLiam Girdwood printk(KERN_ERR "asoc: platform %s hw params failed\n", 444*ddee627cSLiam Girdwood platform->name); 445*ddee627cSLiam Girdwood goto platform_err; 446*ddee627cSLiam Girdwood } 447*ddee627cSLiam Girdwood } 448*ddee627cSLiam Girdwood 449*ddee627cSLiam Girdwood rtd->rate = params_rate(params); 450*ddee627cSLiam Girdwood 451*ddee627cSLiam Girdwood out: 452*ddee627cSLiam Girdwood mutex_unlock(&pcm_mutex); 453*ddee627cSLiam Girdwood return ret; 454*ddee627cSLiam Girdwood 455*ddee627cSLiam Girdwood platform_err: 456*ddee627cSLiam Girdwood if (cpu_dai->driver->ops->hw_free) 457*ddee627cSLiam Girdwood cpu_dai->driver->ops->hw_free(substream, cpu_dai); 458*ddee627cSLiam Girdwood 459*ddee627cSLiam Girdwood interface_err: 460*ddee627cSLiam Girdwood if (codec_dai->driver->ops->hw_free) 461*ddee627cSLiam Girdwood codec_dai->driver->ops->hw_free(substream, codec_dai); 462*ddee627cSLiam Girdwood 463*ddee627cSLiam Girdwood codec_err: 464*ddee627cSLiam Girdwood if (rtd->dai_link->ops && rtd->dai_link->ops->hw_free) 465*ddee627cSLiam Girdwood rtd->dai_link->ops->hw_free(substream); 466*ddee627cSLiam Girdwood 467*ddee627cSLiam Girdwood mutex_unlock(&pcm_mutex); 468*ddee627cSLiam Girdwood return ret; 469*ddee627cSLiam Girdwood } 470*ddee627cSLiam Girdwood 471*ddee627cSLiam Girdwood /* 472*ddee627cSLiam Girdwood * Frees resources allocated by hw_params, can be called multiple times 473*ddee627cSLiam Girdwood */ 474*ddee627cSLiam Girdwood static int soc_pcm_hw_free(struct snd_pcm_substream *substream) 475*ddee627cSLiam Girdwood { 476*ddee627cSLiam Girdwood struct snd_soc_pcm_runtime *rtd = substream->private_data; 477*ddee627cSLiam Girdwood struct snd_soc_platform *platform = rtd->platform; 478*ddee627cSLiam Girdwood struct snd_soc_dai *cpu_dai = rtd->cpu_dai; 479*ddee627cSLiam Girdwood struct snd_soc_dai *codec_dai = rtd->codec_dai; 480*ddee627cSLiam Girdwood struct snd_soc_codec *codec = rtd->codec; 481*ddee627cSLiam Girdwood 482*ddee627cSLiam Girdwood mutex_lock(&pcm_mutex); 483*ddee627cSLiam Girdwood 484*ddee627cSLiam Girdwood /* apply codec digital mute */ 485*ddee627cSLiam Girdwood if (!codec->active) 486*ddee627cSLiam Girdwood snd_soc_dai_digital_mute(codec_dai, 1); 487*ddee627cSLiam Girdwood 488*ddee627cSLiam Girdwood /* free any machine hw params */ 489*ddee627cSLiam Girdwood if (rtd->dai_link->ops && rtd->dai_link->ops->hw_free) 490*ddee627cSLiam Girdwood rtd->dai_link->ops->hw_free(substream); 491*ddee627cSLiam Girdwood 492*ddee627cSLiam Girdwood /* free any DMA resources */ 493*ddee627cSLiam Girdwood if (platform->driver->ops && platform->driver->ops->hw_free) 494*ddee627cSLiam Girdwood platform->driver->ops->hw_free(substream); 495*ddee627cSLiam Girdwood 496*ddee627cSLiam Girdwood /* now free hw params for the DAIs */ 497*ddee627cSLiam Girdwood if (codec_dai->driver->ops->hw_free) 498*ddee627cSLiam Girdwood codec_dai->driver->ops->hw_free(substream, codec_dai); 499*ddee627cSLiam Girdwood 500*ddee627cSLiam Girdwood if (cpu_dai->driver->ops->hw_free) 501*ddee627cSLiam Girdwood cpu_dai->driver->ops->hw_free(substream, cpu_dai); 502*ddee627cSLiam Girdwood 503*ddee627cSLiam Girdwood mutex_unlock(&pcm_mutex); 504*ddee627cSLiam Girdwood return 0; 505*ddee627cSLiam Girdwood } 506*ddee627cSLiam Girdwood 507*ddee627cSLiam Girdwood static int soc_pcm_trigger(struct snd_pcm_substream *substream, int cmd) 508*ddee627cSLiam Girdwood { 509*ddee627cSLiam Girdwood struct snd_soc_pcm_runtime *rtd = substream->private_data; 510*ddee627cSLiam Girdwood struct snd_soc_platform *platform = rtd->platform; 511*ddee627cSLiam Girdwood struct snd_soc_dai *cpu_dai = rtd->cpu_dai; 512*ddee627cSLiam Girdwood struct snd_soc_dai *codec_dai = rtd->codec_dai; 513*ddee627cSLiam Girdwood int ret; 514*ddee627cSLiam Girdwood 515*ddee627cSLiam Girdwood if (codec_dai->driver->ops->trigger) { 516*ddee627cSLiam Girdwood ret = codec_dai->driver->ops->trigger(substream, cmd, codec_dai); 517*ddee627cSLiam Girdwood if (ret < 0) 518*ddee627cSLiam Girdwood return ret; 519*ddee627cSLiam Girdwood } 520*ddee627cSLiam Girdwood 521*ddee627cSLiam Girdwood if (platform->driver->ops && platform->driver->ops->trigger) { 522*ddee627cSLiam Girdwood ret = platform->driver->ops->trigger(substream, cmd); 523*ddee627cSLiam Girdwood if (ret < 0) 524*ddee627cSLiam Girdwood return ret; 525*ddee627cSLiam Girdwood } 526*ddee627cSLiam Girdwood 527*ddee627cSLiam Girdwood if (cpu_dai->driver->ops->trigger) { 528*ddee627cSLiam Girdwood ret = cpu_dai->driver->ops->trigger(substream, cmd, cpu_dai); 529*ddee627cSLiam Girdwood if (ret < 0) 530*ddee627cSLiam Girdwood return ret; 531*ddee627cSLiam Girdwood } 532*ddee627cSLiam Girdwood return 0; 533*ddee627cSLiam Girdwood } 534*ddee627cSLiam Girdwood 535*ddee627cSLiam Girdwood /* 536*ddee627cSLiam Girdwood * soc level wrapper for pointer callback 537*ddee627cSLiam Girdwood * If cpu_dai, codec_dai, platform driver has the delay callback, than 538*ddee627cSLiam Girdwood * the runtime->delay will be updated accordingly. 539*ddee627cSLiam Girdwood */ 540*ddee627cSLiam Girdwood static snd_pcm_uframes_t soc_pcm_pointer(struct snd_pcm_substream *substream) 541*ddee627cSLiam Girdwood { 542*ddee627cSLiam Girdwood struct snd_soc_pcm_runtime *rtd = substream->private_data; 543*ddee627cSLiam Girdwood struct snd_soc_platform *platform = rtd->platform; 544*ddee627cSLiam Girdwood struct snd_soc_dai *cpu_dai = rtd->cpu_dai; 545*ddee627cSLiam Girdwood struct snd_soc_dai *codec_dai = rtd->codec_dai; 546*ddee627cSLiam Girdwood struct snd_pcm_runtime *runtime = substream->runtime; 547*ddee627cSLiam Girdwood snd_pcm_uframes_t offset = 0; 548*ddee627cSLiam Girdwood snd_pcm_sframes_t delay = 0; 549*ddee627cSLiam Girdwood 550*ddee627cSLiam Girdwood if (platform->driver->ops && platform->driver->ops->pointer) 551*ddee627cSLiam Girdwood offset = platform->driver->ops->pointer(substream); 552*ddee627cSLiam Girdwood 553*ddee627cSLiam Girdwood if (cpu_dai->driver->ops->delay) 554*ddee627cSLiam Girdwood delay += cpu_dai->driver->ops->delay(substream, cpu_dai); 555*ddee627cSLiam Girdwood 556*ddee627cSLiam Girdwood if (codec_dai->driver->ops->delay) 557*ddee627cSLiam Girdwood delay += codec_dai->driver->ops->delay(substream, codec_dai); 558*ddee627cSLiam Girdwood 559*ddee627cSLiam Girdwood if (platform->driver->delay) 560*ddee627cSLiam Girdwood delay += platform->driver->delay(substream, codec_dai); 561*ddee627cSLiam Girdwood 562*ddee627cSLiam Girdwood runtime->delay = delay; 563*ddee627cSLiam Girdwood 564*ddee627cSLiam Girdwood return offset; 565*ddee627cSLiam Girdwood } 566*ddee627cSLiam Girdwood 567*ddee627cSLiam Girdwood /* ASoC PCM operations */ 568*ddee627cSLiam Girdwood static struct snd_pcm_ops soc_pcm_ops = { 569*ddee627cSLiam Girdwood .open = soc_pcm_open, 570*ddee627cSLiam Girdwood .close = soc_codec_close, 571*ddee627cSLiam Girdwood .hw_params = soc_pcm_hw_params, 572*ddee627cSLiam Girdwood .hw_free = soc_pcm_hw_free, 573*ddee627cSLiam Girdwood .prepare = soc_pcm_prepare, 574*ddee627cSLiam Girdwood .trigger = soc_pcm_trigger, 575*ddee627cSLiam Girdwood .pointer = soc_pcm_pointer, 576*ddee627cSLiam Girdwood }; 577*ddee627cSLiam Girdwood 578*ddee627cSLiam Girdwood /* create a new pcm */ 579*ddee627cSLiam Girdwood int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num) 580*ddee627cSLiam Girdwood { 581*ddee627cSLiam Girdwood struct snd_soc_codec *codec = rtd->codec; 582*ddee627cSLiam Girdwood struct snd_soc_platform *platform = rtd->platform; 583*ddee627cSLiam Girdwood struct snd_soc_dai *codec_dai = rtd->codec_dai; 584*ddee627cSLiam Girdwood struct snd_soc_dai *cpu_dai = rtd->cpu_dai; 585*ddee627cSLiam Girdwood struct snd_pcm *pcm; 586*ddee627cSLiam Girdwood char new_name[64]; 587*ddee627cSLiam Girdwood int ret = 0, playback = 0, capture = 0; 588*ddee627cSLiam Girdwood 589*ddee627cSLiam Girdwood /* check client and interface hw capabilities */ 590*ddee627cSLiam Girdwood snprintf(new_name, sizeof(new_name), "%s %s-%d", 591*ddee627cSLiam Girdwood rtd->dai_link->stream_name, codec_dai->name, num); 592*ddee627cSLiam Girdwood 593*ddee627cSLiam Girdwood if (codec_dai->driver->playback.channels_min) 594*ddee627cSLiam Girdwood playback = 1; 595*ddee627cSLiam Girdwood if (codec_dai->driver->capture.channels_min) 596*ddee627cSLiam Girdwood capture = 1; 597*ddee627cSLiam Girdwood 598*ddee627cSLiam Girdwood dev_dbg(rtd->card->dev, "registered pcm #%d %s\n",num,new_name); 599*ddee627cSLiam Girdwood ret = snd_pcm_new(rtd->card->snd_card, new_name, 600*ddee627cSLiam Girdwood num, playback, capture, &pcm); 601*ddee627cSLiam Girdwood if (ret < 0) { 602*ddee627cSLiam Girdwood printk(KERN_ERR "asoc: can't create pcm for codec %s\n", codec->name); 603*ddee627cSLiam Girdwood return ret; 604*ddee627cSLiam Girdwood } 605*ddee627cSLiam Girdwood 606*ddee627cSLiam Girdwood /* DAPM dai link stream work */ 607*ddee627cSLiam Girdwood INIT_DELAYED_WORK(&rtd->delayed_work, close_delayed_work); 608*ddee627cSLiam Girdwood 609*ddee627cSLiam Girdwood rtd->pcm = pcm; 610*ddee627cSLiam Girdwood pcm->private_data = rtd; 611*ddee627cSLiam Girdwood if (platform->driver->ops) { 612*ddee627cSLiam Girdwood soc_pcm_ops.mmap = platform->driver->ops->mmap; 613*ddee627cSLiam Girdwood soc_pcm_ops.pointer = platform->driver->ops->pointer; 614*ddee627cSLiam Girdwood soc_pcm_ops.ioctl = platform->driver->ops->ioctl; 615*ddee627cSLiam Girdwood soc_pcm_ops.copy = platform->driver->ops->copy; 616*ddee627cSLiam Girdwood soc_pcm_ops.silence = platform->driver->ops->silence; 617*ddee627cSLiam Girdwood soc_pcm_ops.ack = platform->driver->ops->ack; 618*ddee627cSLiam Girdwood soc_pcm_ops.page = platform->driver->ops->page; 619*ddee627cSLiam Girdwood } 620*ddee627cSLiam Girdwood 621*ddee627cSLiam Girdwood if (playback) 622*ddee627cSLiam Girdwood snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &soc_pcm_ops); 623*ddee627cSLiam Girdwood 624*ddee627cSLiam Girdwood if (capture) 625*ddee627cSLiam Girdwood snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &soc_pcm_ops); 626*ddee627cSLiam Girdwood 627*ddee627cSLiam Girdwood if (platform->driver->pcm_new) { 628*ddee627cSLiam Girdwood ret = platform->driver->pcm_new(rtd); 629*ddee627cSLiam Girdwood if (ret < 0) { 630*ddee627cSLiam Girdwood pr_err("asoc: platform pcm constructor failed\n"); 631*ddee627cSLiam Girdwood return ret; 632*ddee627cSLiam Girdwood } 633*ddee627cSLiam Girdwood } 634*ddee627cSLiam Girdwood 635*ddee627cSLiam Girdwood pcm->private_free = platform->driver->pcm_free; 636*ddee627cSLiam Girdwood printk(KERN_INFO "asoc: %s <-> %s mapping ok\n", codec_dai->name, 637*ddee627cSLiam Girdwood cpu_dai->name); 638*ddee627cSLiam Girdwood return ret; 639*ddee627cSLiam Girdwood } 640