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> 22d6652ef8SMark Brown #include <linux/pm_runtime.h> 23ddee627cSLiam Girdwood #include <linux/slab.h> 24ddee627cSLiam Girdwood #include <linux/workqueue.h> 25ddee627cSLiam Girdwood #include <sound/core.h> 26ddee627cSLiam Girdwood #include <sound/pcm.h> 27ddee627cSLiam Girdwood #include <sound/pcm_params.h> 28ddee627cSLiam Girdwood #include <sound/soc.h> 29ddee627cSLiam Girdwood #include <sound/initval.h> 30ddee627cSLiam Girdwood 3117841020SDong Aisheng static int soc_pcm_apply_symmetry(struct snd_pcm_substream *substream, 3217841020SDong Aisheng struct snd_soc_dai *soc_dai) 33ddee627cSLiam Girdwood { 34ddee627cSLiam Girdwood struct snd_soc_pcm_runtime *rtd = substream->private_data; 35ddee627cSLiam Girdwood int ret; 36ddee627cSLiam Girdwood 3717841020SDong Aisheng if (!soc_dai->driver->symmetric_rates && 38ddee627cSLiam Girdwood !rtd->dai_link->symmetric_rates) 39ddee627cSLiam Girdwood return 0; 40ddee627cSLiam Girdwood 41ddee627cSLiam Girdwood /* This can happen if multiple streams are starting simultaneously - 42ddee627cSLiam Girdwood * the second can need to get its constraints before the first has 43ddee627cSLiam Girdwood * picked a rate. Complain and allow the application to carry on. 44ddee627cSLiam Girdwood */ 4517841020SDong Aisheng if (!soc_dai->rate) { 4617841020SDong Aisheng dev_warn(soc_dai->dev, 47ddee627cSLiam Girdwood "Not enforcing symmetric_rates due to race\n"); 48ddee627cSLiam Girdwood return 0; 49ddee627cSLiam Girdwood } 50ddee627cSLiam Girdwood 5117841020SDong Aisheng dev_dbg(soc_dai->dev, "Symmetry forces %dHz rate\n", soc_dai->rate); 52ddee627cSLiam Girdwood 53ddee627cSLiam Girdwood ret = snd_pcm_hw_constraint_minmax(substream->runtime, 54ddee627cSLiam Girdwood SNDRV_PCM_HW_PARAM_RATE, 5517841020SDong Aisheng soc_dai->rate, soc_dai->rate); 56ddee627cSLiam Girdwood if (ret < 0) { 5717841020SDong Aisheng dev_err(soc_dai->dev, 58ddee627cSLiam Girdwood "Unable to apply rate symmetry constraint: %d\n", ret); 59ddee627cSLiam Girdwood return ret; 60ddee627cSLiam Girdwood } 61ddee627cSLiam Girdwood 62ddee627cSLiam Girdwood return 0; 63ddee627cSLiam Girdwood } 64ddee627cSLiam Girdwood 65ddee627cSLiam Girdwood /* 6658ba9b25SMark Brown * List of sample sizes that might go over the bus for parameter 6758ba9b25SMark Brown * application. There ought to be a wildcard sample size for things 6858ba9b25SMark Brown * like the DAC/ADC resolution to use but there isn't right now. 6958ba9b25SMark Brown */ 7058ba9b25SMark Brown static int sample_sizes[] = { 7188e33954SPeter Ujfalusi 24, 32, 7258ba9b25SMark Brown }; 7358ba9b25SMark Brown 7458ba9b25SMark Brown static void soc_pcm_apply_msb(struct snd_pcm_substream *substream, 7558ba9b25SMark Brown struct snd_soc_dai *dai) 7658ba9b25SMark Brown { 7758ba9b25SMark Brown int ret, i, bits; 7858ba9b25SMark Brown 7958ba9b25SMark Brown if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 8058ba9b25SMark Brown bits = dai->driver->playback.sig_bits; 8158ba9b25SMark Brown else 8258ba9b25SMark Brown bits = dai->driver->capture.sig_bits; 8358ba9b25SMark Brown 8458ba9b25SMark Brown if (!bits) 8558ba9b25SMark Brown return; 8658ba9b25SMark Brown 8758ba9b25SMark Brown for (i = 0; i < ARRAY_SIZE(sample_sizes); i++) { 88278047fdSMark Brown if (bits >= sample_sizes[i]) 89278047fdSMark Brown continue; 90278047fdSMark Brown 91278047fdSMark Brown ret = snd_pcm_hw_constraint_msbits(substream->runtime, 0, 92278047fdSMark Brown sample_sizes[i], bits); 9358ba9b25SMark Brown if (ret != 0) 9458ba9b25SMark Brown dev_warn(dai->dev, 9558ba9b25SMark Brown "Failed to set MSB %d/%d: %d\n", 9658ba9b25SMark Brown bits, sample_sizes[i], ret); 9758ba9b25SMark Brown } 9858ba9b25SMark Brown } 9958ba9b25SMark Brown 10058ba9b25SMark Brown /* 101ddee627cSLiam Girdwood * Called by ALSA when a PCM substream is opened, the runtime->hw record is 102ddee627cSLiam Girdwood * then initialized and any private data can be allocated. This also calls 103ddee627cSLiam Girdwood * startup for the cpu DAI, platform, machine and codec DAI. 104ddee627cSLiam Girdwood */ 105ddee627cSLiam Girdwood static int soc_pcm_open(struct snd_pcm_substream *substream) 106ddee627cSLiam Girdwood { 107ddee627cSLiam Girdwood struct snd_soc_pcm_runtime *rtd = substream->private_data; 108ddee627cSLiam Girdwood struct snd_pcm_runtime *runtime = substream->runtime; 109ddee627cSLiam Girdwood struct snd_soc_platform *platform = rtd->platform; 110ddee627cSLiam Girdwood struct snd_soc_dai *cpu_dai = rtd->cpu_dai; 111ddee627cSLiam Girdwood struct snd_soc_dai *codec_dai = rtd->codec_dai; 112ddee627cSLiam Girdwood struct snd_soc_dai_driver *cpu_dai_drv = cpu_dai->driver; 113ddee627cSLiam Girdwood struct snd_soc_dai_driver *codec_dai_drv = codec_dai->driver; 114ddee627cSLiam Girdwood int ret = 0; 115ddee627cSLiam Girdwood 116d6652ef8SMark Brown pm_runtime_get_sync(cpu_dai->dev); 117d6652ef8SMark Brown pm_runtime_get_sync(codec_dai->dev); 118d6652ef8SMark Brown pm_runtime_get_sync(platform->dev); 119d6652ef8SMark Brown 120b8c0dab9SLiam Girdwood mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); 121ddee627cSLiam Girdwood 122ddee627cSLiam Girdwood /* startup the audio subsystem */ 123ddee627cSLiam Girdwood if (cpu_dai->driver->ops->startup) { 124ddee627cSLiam Girdwood ret = cpu_dai->driver->ops->startup(substream, cpu_dai); 125ddee627cSLiam Girdwood if (ret < 0) { 12625bfe662SMark Brown dev_err(cpu_dai->dev, "can't open interface %s: %d\n", 12725bfe662SMark Brown cpu_dai->name, ret); 128ddee627cSLiam Girdwood goto out; 129ddee627cSLiam Girdwood } 130ddee627cSLiam Girdwood } 131ddee627cSLiam Girdwood 132ddee627cSLiam Girdwood if (platform->driver->ops && platform->driver->ops->open) { 133ddee627cSLiam Girdwood ret = platform->driver->ops->open(substream); 134ddee627cSLiam Girdwood if (ret < 0) { 13525bfe662SMark Brown dev_err(platform->dev, "can't open platform %s: %d\n", 13625bfe662SMark Brown platform->name, ret); 137ddee627cSLiam Girdwood goto platform_err; 138ddee627cSLiam Girdwood } 139ddee627cSLiam Girdwood } 140ddee627cSLiam Girdwood 141ddee627cSLiam Girdwood if (codec_dai->driver->ops->startup) { 142ddee627cSLiam Girdwood ret = codec_dai->driver->ops->startup(substream, codec_dai); 143ddee627cSLiam Girdwood if (ret < 0) { 14425bfe662SMark Brown dev_err(codec_dai->dev, "can't open codec %s: %d\n", 14525bfe662SMark Brown codec_dai->name, ret); 146ddee627cSLiam Girdwood goto codec_dai_err; 147ddee627cSLiam Girdwood } 148ddee627cSLiam Girdwood } 149ddee627cSLiam Girdwood 150ddee627cSLiam Girdwood if (rtd->dai_link->ops && rtd->dai_link->ops->startup) { 151ddee627cSLiam Girdwood ret = rtd->dai_link->ops->startup(substream); 152ddee627cSLiam Girdwood if (ret < 0) { 15325bfe662SMark Brown pr_err("asoc: %s startup failed: %d\n", 15425bfe662SMark Brown rtd->dai_link->name, ret); 155ddee627cSLiam Girdwood goto machine_err; 156ddee627cSLiam Girdwood } 157ddee627cSLiam Girdwood } 158ddee627cSLiam Girdwood 159ddee627cSLiam Girdwood /* Check that the codec and cpu DAIs are compatible */ 160ddee627cSLiam Girdwood if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 161ddee627cSLiam Girdwood runtime->hw.rate_min = 162ddee627cSLiam Girdwood max(codec_dai_drv->playback.rate_min, 163ddee627cSLiam Girdwood cpu_dai_drv->playback.rate_min); 164ddee627cSLiam Girdwood runtime->hw.rate_max = 165ddee627cSLiam Girdwood min(codec_dai_drv->playback.rate_max, 166ddee627cSLiam Girdwood cpu_dai_drv->playback.rate_max); 167ddee627cSLiam Girdwood runtime->hw.channels_min = 168ddee627cSLiam Girdwood max(codec_dai_drv->playback.channels_min, 169ddee627cSLiam Girdwood cpu_dai_drv->playback.channels_min); 170ddee627cSLiam Girdwood runtime->hw.channels_max = 171ddee627cSLiam Girdwood min(codec_dai_drv->playback.channels_max, 172ddee627cSLiam Girdwood cpu_dai_drv->playback.channels_max); 173ddee627cSLiam Girdwood runtime->hw.formats = 174ddee627cSLiam Girdwood codec_dai_drv->playback.formats & cpu_dai_drv->playback.formats; 175ddee627cSLiam Girdwood runtime->hw.rates = 176ddee627cSLiam Girdwood codec_dai_drv->playback.rates & cpu_dai_drv->playback.rates; 177ddee627cSLiam Girdwood if (codec_dai_drv->playback.rates 178ddee627cSLiam Girdwood & (SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_CONTINUOUS)) 179ddee627cSLiam Girdwood runtime->hw.rates |= cpu_dai_drv->playback.rates; 180ddee627cSLiam Girdwood if (cpu_dai_drv->playback.rates 181ddee627cSLiam Girdwood & (SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_CONTINUOUS)) 182ddee627cSLiam Girdwood runtime->hw.rates |= codec_dai_drv->playback.rates; 183ddee627cSLiam Girdwood } else { 184ddee627cSLiam Girdwood runtime->hw.rate_min = 185ddee627cSLiam Girdwood max(codec_dai_drv->capture.rate_min, 186ddee627cSLiam Girdwood cpu_dai_drv->capture.rate_min); 187ddee627cSLiam Girdwood runtime->hw.rate_max = 188ddee627cSLiam Girdwood min(codec_dai_drv->capture.rate_max, 189ddee627cSLiam Girdwood cpu_dai_drv->capture.rate_max); 190ddee627cSLiam Girdwood runtime->hw.channels_min = 191ddee627cSLiam Girdwood max(codec_dai_drv->capture.channels_min, 192ddee627cSLiam Girdwood cpu_dai_drv->capture.channels_min); 193ddee627cSLiam Girdwood runtime->hw.channels_max = 194ddee627cSLiam Girdwood min(codec_dai_drv->capture.channels_max, 195ddee627cSLiam Girdwood cpu_dai_drv->capture.channels_max); 196ddee627cSLiam Girdwood runtime->hw.formats = 197ddee627cSLiam Girdwood codec_dai_drv->capture.formats & cpu_dai_drv->capture.formats; 198ddee627cSLiam Girdwood runtime->hw.rates = 199ddee627cSLiam Girdwood codec_dai_drv->capture.rates & cpu_dai_drv->capture.rates; 200ddee627cSLiam Girdwood if (codec_dai_drv->capture.rates 201ddee627cSLiam Girdwood & (SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_CONTINUOUS)) 202ddee627cSLiam Girdwood runtime->hw.rates |= cpu_dai_drv->capture.rates; 203ddee627cSLiam Girdwood if (cpu_dai_drv->capture.rates 204ddee627cSLiam Girdwood & (SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_CONTINUOUS)) 205ddee627cSLiam Girdwood runtime->hw.rates |= codec_dai_drv->capture.rates; 206ddee627cSLiam Girdwood } 207ddee627cSLiam Girdwood 208ddee627cSLiam Girdwood ret = -EINVAL; 209ddee627cSLiam Girdwood snd_pcm_limit_hw_rates(runtime); 210ddee627cSLiam Girdwood if (!runtime->hw.rates) { 211ddee627cSLiam Girdwood printk(KERN_ERR "asoc: %s <-> %s No matching rates\n", 212ddee627cSLiam Girdwood codec_dai->name, cpu_dai->name); 213ddee627cSLiam Girdwood goto config_err; 214ddee627cSLiam Girdwood } 215ddee627cSLiam Girdwood if (!runtime->hw.formats) { 216ddee627cSLiam Girdwood printk(KERN_ERR "asoc: %s <-> %s No matching formats\n", 217ddee627cSLiam Girdwood codec_dai->name, cpu_dai->name); 218ddee627cSLiam Girdwood goto config_err; 219ddee627cSLiam Girdwood } 220ddee627cSLiam Girdwood if (!runtime->hw.channels_min || !runtime->hw.channels_max || 221ddee627cSLiam Girdwood runtime->hw.channels_min > runtime->hw.channels_max) { 222ddee627cSLiam Girdwood printk(KERN_ERR "asoc: %s <-> %s No matching channels\n", 223ddee627cSLiam Girdwood codec_dai->name, cpu_dai->name); 224ddee627cSLiam Girdwood goto config_err; 225ddee627cSLiam Girdwood } 226ddee627cSLiam Girdwood 22758ba9b25SMark Brown soc_pcm_apply_msb(substream, codec_dai); 22858ba9b25SMark Brown soc_pcm_apply_msb(substream, cpu_dai); 22958ba9b25SMark Brown 230ddee627cSLiam Girdwood /* Symmetry only applies if we've already got an active stream. */ 23117841020SDong Aisheng if (cpu_dai->active) { 23217841020SDong Aisheng ret = soc_pcm_apply_symmetry(substream, cpu_dai); 23317841020SDong Aisheng if (ret != 0) 23417841020SDong Aisheng goto config_err; 23517841020SDong Aisheng } 23617841020SDong Aisheng 23717841020SDong Aisheng if (codec_dai->active) { 23817841020SDong Aisheng ret = soc_pcm_apply_symmetry(substream, codec_dai); 239ddee627cSLiam Girdwood if (ret != 0) 240ddee627cSLiam Girdwood goto config_err; 241ddee627cSLiam Girdwood } 242ddee627cSLiam Girdwood 243ddee627cSLiam Girdwood pr_debug("asoc: %s <-> %s info:\n", 244ddee627cSLiam Girdwood codec_dai->name, cpu_dai->name); 245ddee627cSLiam Girdwood pr_debug("asoc: rate mask 0x%x\n", runtime->hw.rates); 246ddee627cSLiam Girdwood pr_debug("asoc: min ch %d max ch %d\n", runtime->hw.channels_min, 247ddee627cSLiam Girdwood runtime->hw.channels_max); 248ddee627cSLiam Girdwood pr_debug("asoc: min rate %d max rate %d\n", runtime->hw.rate_min, 249ddee627cSLiam Girdwood runtime->hw.rate_max); 250ddee627cSLiam Girdwood 251ddee627cSLiam Girdwood if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 252ddee627cSLiam Girdwood cpu_dai->playback_active++; 253ddee627cSLiam Girdwood codec_dai->playback_active++; 254ddee627cSLiam Girdwood } else { 255ddee627cSLiam Girdwood cpu_dai->capture_active++; 256ddee627cSLiam Girdwood codec_dai->capture_active++; 257ddee627cSLiam Girdwood } 258ddee627cSLiam Girdwood cpu_dai->active++; 259ddee627cSLiam Girdwood codec_dai->active++; 260ddee627cSLiam Girdwood rtd->codec->active++; 261b8c0dab9SLiam Girdwood mutex_unlock(&rtd->pcm_mutex); 262ddee627cSLiam Girdwood return 0; 263ddee627cSLiam Girdwood 264ddee627cSLiam Girdwood config_err: 265ddee627cSLiam Girdwood if (rtd->dai_link->ops && rtd->dai_link->ops->shutdown) 266ddee627cSLiam Girdwood rtd->dai_link->ops->shutdown(substream); 267ddee627cSLiam Girdwood 268ddee627cSLiam Girdwood machine_err: 269ddee627cSLiam Girdwood if (codec_dai->driver->ops->shutdown) 270ddee627cSLiam Girdwood codec_dai->driver->ops->shutdown(substream, codec_dai); 271ddee627cSLiam Girdwood 272ddee627cSLiam Girdwood codec_dai_err: 273ddee627cSLiam Girdwood if (platform->driver->ops && platform->driver->ops->close) 274ddee627cSLiam Girdwood platform->driver->ops->close(substream); 275ddee627cSLiam Girdwood 276ddee627cSLiam Girdwood platform_err: 277ddee627cSLiam Girdwood if (cpu_dai->driver->ops->shutdown) 278ddee627cSLiam Girdwood cpu_dai->driver->ops->shutdown(substream, cpu_dai); 279ddee627cSLiam Girdwood out: 280b8c0dab9SLiam Girdwood mutex_unlock(&rtd->pcm_mutex); 281d6652ef8SMark Brown 282d6652ef8SMark Brown pm_runtime_put(platform->dev); 283d6652ef8SMark Brown pm_runtime_put(codec_dai->dev); 284d6652ef8SMark Brown pm_runtime_put(cpu_dai->dev); 285d6652ef8SMark Brown 286ddee627cSLiam Girdwood return ret; 287ddee627cSLiam Girdwood } 288ddee627cSLiam Girdwood 289ddee627cSLiam Girdwood /* 290ddee627cSLiam Girdwood * Power down the audio subsystem pmdown_time msecs after close is called. 291ddee627cSLiam Girdwood * This is to ensure there are no pops or clicks in between any music tracks 292ddee627cSLiam Girdwood * due to DAPM power cycling. 293ddee627cSLiam Girdwood */ 294ddee627cSLiam Girdwood static void close_delayed_work(struct work_struct *work) 295ddee627cSLiam Girdwood { 296ddee627cSLiam Girdwood struct snd_soc_pcm_runtime *rtd = 297ddee627cSLiam Girdwood container_of(work, struct snd_soc_pcm_runtime, delayed_work.work); 298ddee627cSLiam Girdwood struct snd_soc_dai *codec_dai = rtd->codec_dai; 299ddee627cSLiam Girdwood 300b8c0dab9SLiam Girdwood mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); 301ddee627cSLiam Girdwood 302ddee627cSLiam Girdwood pr_debug("pop wq checking: %s status: %s waiting: %s\n", 303ddee627cSLiam Girdwood codec_dai->driver->playback.stream_name, 304ddee627cSLiam Girdwood codec_dai->playback_active ? "active" : "inactive", 305ddee627cSLiam Girdwood codec_dai->pop_wait ? "yes" : "no"); 306ddee627cSLiam Girdwood 307ddee627cSLiam Girdwood /* are we waiting on this codec DAI stream */ 308ddee627cSLiam Girdwood if (codec_dai->pop_wait == 1) { 309ddee627cSLiam Girdwood codec_dai->pop_wait = 0; 3107bd3a6f3SMark Brown snd_soc_dapm_stream_event(rtd, SNDRV_PCM_STREAM_PLAYBACK, 311*d9b0951bSLiam Girdwood SND_SOC_DAPM_STREAM_STOP); 312ddee627cSLiam Girdwood } 313ddee627cSLiam Girdwood 314b8c0dab9SLiam Girdwood mutex_unlock(&rtd->pcm_mutex); 315ddee627cSLiam Girdwood } 316ddee627cSLiam Girdwood 317ddee627cSLiam Girdwood /* 318ddee627cSLiam Girdwood * Called by ALSA when a PCM substream is closed. Private data can be 319ddee627cSLiam Girdwood * freed here. The cpu DAI, codec DAI, machine and platform are also 320ddee627cSLiam Girdwood * shutdown. 321ddee627cSLiam Girdwood */ 32291d5e6b4SLiam Girdwood static int soc_pcm_close(struct snd_pcm_substream *substream) 323ddee627cSLiam Girdwood { 324ddee627cSLiam Girdwood struct snd_soc_pcm_runtime *rtd = substream->private_data; 325ddee627cSLiam Girdwood struct snd_soc_platform *platform = rtd->platform; 326ddee627cSLiam Girdwood struct snd_soc_dai *cpu_dai = rtd->cpu_dai; 327ddee627cSLiam Girdwood struct snd_soc_dai *codec_dai = rtd->codec_dai; 328ddee627cSLiam Girdwood struct snd_soc_codec *codec = rtd->codec; 329ddee627cSLiam Girdwood 330b8c0dab9SLiam Girdwood mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); 331ddee627cSLiam Girdwood 332ddee627cSLiam Girdwood if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 333ddee627cSLiam Girdwood cpu_dai->playback_active--; 334ddee627cSLiam Girdwood codec_dai->playback_active--; 335ddee627cSLiam Girdwood } else { 336ddee627cSLiam Girdwood cpu_dai->capture_active--; 337ddee627cSLiam Girdwood codec_dai->capture_active--; 338ddee627cSLiam Girdwood } 339ddee627cSLiam Girdwood 340ddee627cSLiam Girdwood cpu_dai->active--; 341ddee627cSLiam Girdwood codec_dai->active--; 342ddee627cSLiam Girdwood codec->active--; 343ddee627cSLiam Girdwood 34417841020SDong Aisheng /* clear the corresponding DAIs rate when inactive */ 34517841020SDong Aisheng if (!cpu_dai->active) 34617841020SDong Aisheng cpu_dai->rate = 0; 34717841020SDong Aisheng 34817841020SDong Aisheng if (!codec_dai->active) 34917841020SDong Aisheng codec_dai->rate = 0; 35025b76791SSascha Hauer 351ddee627cSLiam Girdwood /* Muting the DAC suppresses artifacts caused during digital 352ddee627cSLiam Girdwood * shutdown, for example from stopping clocks. 353ddee627cSLiam Girdwood */ 354ddee627cSLiam Girdwood if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 355ddee627cSLiam Girdwood snd_soc_dai_digital_mute(codec_dai, 1); 356ddee627cSLiam Girdwood 357ddee627cSLiam Girdwood if (cpu_dai->driver->ops->shutdown) 358ddee627cSLiam Girdwood cpu_dai->driver->ops->shutdown(substream, cpu_dai); 359ddee627cSLiam Girdwood 360ddee627cSLiam Girdwood if (codec_dai->driver->ops->shutdown) 361ddee627cSLiam Girdwood codec_dai->driver->ops->shutdown(substream, codec_dai); 362ddee627cSLiam Girdwood 363ddee627cSLiam Girdwood if (rtd->dai_link->ops && rtd->dai_link->ops->shutdown) 364ddee627cSLiam Girdwood rtd->dai_link->ops->shutdown(substream); 365ddee627cSLiam Girdwood 366ddee627cSLiam Girdwood if (platform->driver->ops && platform->driver->ops->close) 367ddee627cSLiam Girdwood platform->driver->ops->close(substream); 368ddee627cSLiam Girdwood cpu_dai->runtime = NULL; 369ddee627cSLiam Girdwood 370ddee627cSLiam Girdwood if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 371b5d1d036SMark Brown if (!rtd->pmdown_time || codec->ignore_pmdown_time || 3725b6247abSMark Brown rtd->dai_link->ignore_pmdown_time) { 3731d69c5c5SPeter Ujfalusi /* powered down playback stream now */ 3741d69c5c5SPeter Ujfalusi snd_soc_dapm_stream_event(rtd, 3757bd3a6f3SMark Brown SNDRV_PCM_STREAM_PLAYBACK, 3761d69c5c5SPeter Ujfalusi SND_SOC_DAPM_STREAM_STOP); 3771d69c5c5SPeter Ujfalusi } else { 378ddee627cSLiam Girdwood /* start delayed pop wq here for playback streams */ 379ddee627cSLiam Girdwood codec_dai->pop_wait = 1; 380ddee627cSLiam Girdwood schedule_delayed_work(&rtd->delayed_work, 381ddee627cSLiam Girdwood msecs_to_jiffies(rtd->pmdown_time)); 3821d69c5c5SPeter Ujfalusi } 383ddee627cSLiam Girdwood } else { 384ddee627cSLiam Girdwood /* capture streams can be powered down now */ 3857bd3a6f3SMark Brown snd_soc_dapm_stream_event(rtd, SNDRV_PCM_STREAM_CAPTURE, 386*d9b0951bSLiam Girdwood SND_SOC_DAPM_STREAM_STOP); 387ddee627cSLiam Girdwood } 388ddee627cSLiam Girdwood 389b8c0dab9SLiam Girdwood mutex_unlock(&rtd->pcm_mutex); 390d6652ef8SMark Brown 391d6652ef8SMark Brown pm_runtime_put(platform->dev); 392d6652ef8SMark Brown pm_runtime_put(codec_dai->dev); 393d6652ef8SMark Brown pm_runtime_put(cpu_dai->dev); 394d6652ef8SMark Brown 395ddee627cSLiam Girdwood return 0; 396ddee627cSLiam Girdwood } 397ddee627cSLiam Girdwood 398ddee627cSLiam Girdwood /* 399ddee627cSLiam Girdwood * Called by ALSA when the PCM substream is prepared, can set format, sample 400ddee627cSLiam Girdwood * rate, etc. This function is non atomic and can be called multiple times, 401ddee627cSLiam Girdwood * it can refer to the runtime info. 402ddee627cSLiam Girdwood */ 403ddee627cSLiam Girdwood static int soc_pcm_prepare(struct snd_pcm_substream *substream) 404ddee627cSLiam Girdwood { 405ddee627cSLiam Girdwood struct snd_soc_pcm_runtime *rtd = substream->private_data; 406ddee627cSLiam Girdwood struct snd_soc_platform *platform = rtd->platform; 407ddee627cSLiam Girdwood struct snd_soc_dai *cpu_dai = rtd->cpu_dai; 408ddee627cSLiam Girdwood struct snd_soc_dai *codec_dai = rtd->codec_dai; 409ddee627cSLiam Girdwood int ret = 0; 410ddee627cSLiam Girdwood 411b8c0dab9SLiam Girdwood mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); 412ddee627cSLiam Girdwood 413ddee627cSLiam Girdwood if (rtd->dai_link->ops && rtd->dai_link->ops->prepare) { 414ddee627cSLiam Girdwood ret = rtd->dai_link->ops->prepare(substream); 415ddee627cSLiam Girdwood if (ret < 0) { 41625bfe662SMark Brown pr_err("asoc: machine prepare error: %d\n", ret); 417ddee627cSLiam Girdwood goto out; 418ddee627cSLiam Girdwood } 419ddee627cSLiam Girdwood } 420ddee627cSLiam Girdwood 421ddee627cSLiam Girdwood if (platform->driver->ops && platform->driver->ops->prepare) { 422ddee627cSLiam Girdwood ret = platform->driver->ops->prepare(substream); 423ddee627cSLiam Girdwood if (ret < 0) { 42425bfe662SMark Brown dev_err(platform->dev, "platform prepare error: %d\n", 42525bfe662SMark Brown ret); 426ddee627cSLiam Girdwood goto out; 427ddee627cSLiam Girdwood } 428ddee627cSLiam Girdwood } 429ddee627cSLiam Girdwood 430ddee627cSLiam Girdwood if (codec_dai->driver->ops->prepare) { 431ddee627cSLiam Girdwood ret = codec_dai->driver->ops->prepare(substream, codec_dai); 432ddee627cSLiam Girdwood if (ret < 0) { 43325bfe662SMark Brown dev_err(codec_dai->dev, "DAI prepare error: %d\n", 43425bfe662SMark Brown ret); 435ddee627cSLiam Girdwood goto out; 436ddee627cSLiam Girdwood } 437ddee627cSLiam Girdwood } 438ddee627cSLiam Girdwood 439ddee627cSLiam Girdwood if (cpu_dai->driver->ops->prepare) { 440ddee627cSLiam Girdwood ret = cpu_dai->driver->ops->prepare(substream, cpu_dai); 441ddee627cSLiam Girdwood if (ret < 0) { 44225bfe662SMark Brown dev_err(cpu_dai->dev, "DAI prepare error: %d\n", 44325bfe662SMark Brown ret); 444ddee627cSLiam Girdwood goto out; 445ddee627cSLiam Girdwood } 446ddee627cSLiam Girdwood } 447ddee627cSLiam Girdwood 448ddee627cSLiam Girdwood /* cancel any delayed stream shutdown that is pending */ 449ddee627cSLiam Girdwood if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && 450ddee627cSLiam Girdwood codec_dai->pop_wait) { 451ddee627cSLiam Girdwood codec_dai->pop_wait = 0; 452ddee627cSLiam Girdwood cancel_delayed_work(&rtd->delayed_work); 453ddee627cSLiam Girdwood } 454ddee627cSLiam Girdwood 455*d9b0951bSLiam Girdwood snd_soc_dapm_stream_event(rtd, substream->stream, 456ddee627cSLiam Girdwood SND_SOC_DAPM_STREAM_START); 457ddee627cSLiam Girdwood 458ddee627cSLiam Girdwood snd_soc_dai_digital_mute(codec_dai, 0); 459ddee627cSLiam Girdwood 460ddee627cSLiam Girdwood out: 461b8c0dab9SLiam Girdwood mutex_unlock(&rtd->pcm_mutex); 462ddee627cSLiam Girdwood return ret; 463ddee627cSLiam Girdwood } 464ddee627cSLiam Girdwood 465ddee627cSLiam Girdwood /* 466ddee627cSLiam Girdwood * Called by ALSA when the hardware params are set by application. This 467ddee627cSLiam Girdwood * function can also be called multiple times and can allocate buffers 468ddee627cSLiam Girdwood * (using snd_pcm_lib_* ). It's non-atomic. 469ddee627cSLiam Girdwood */ 470ddee627cSLiam Girdwood static int soc_pcm_hw_params(struct snd_pcm_substream *substream, 471ddee627cSLiam Girdwood struct snd_pcm_hw_params *params) 472ddee627cSLiam Girdwood { 473ddee627cSLiam Girdwood struct snd_soc_pcm_runtime *rtd = substream->private_data; 474ddee627cSLiam Girdwood struct snd_soc_platform *platform = rtd->platform; 475ddee627cSLiam Girdwood struct snd_soc_dai *cpu_dai = rtd->cpu_dai; 476ddee627cSLiam Girdwood struct snd_soc_dai *codec_dai = rtd->codec_dai; 477ddee627cSLiam Girdwood int ret = 0; 478ddee627cSLiam Girdwood 479b8c0dab9SLiam Girdwood mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); 480ddee627cSLiam Girdwood 481ddee627cSLiam Girdwood if (rtd->dai_link->ops && rtd->dai_link->ops->hw_params) { 482ddee627cSLiam Girdwood ret = rtd->dai_link->ops->hw_params(substream, params); 483ddee627cSLiam Girdwood if (ret < 0) { 48425bfe662SMark Brown pr_err("asoc: machine hw_params failed: %d\n", ret); 485ddee627cSLiam Girdwood goto out; 486ddee627cSLiam Girdwood } 487ddee627cSLiam Girdwood } 488ddee627cSLiam Girdwood 489ddee627cSLiam Girdwood if (codec_dai->driver->ops->hw_params) { 490ddee627cSLiam Girdwood ret = codec_dai->driver->ops->hw_params(substream, params, codec_dai); 491ddee627cSLiam Girdwood if (ret < 0) { 49225bfe662SMark Brown dev_err(codec_dai->dev, "can't set %s hw params: %d\n", 49325bfe662SMark Brown codec_dai->name, ret); 494ddee627cSLiam Girdwood goto codec_err; 495ddee627cSLiam Girdwood } 496ddee627cSLiam Girdwood } 497ddee627cSLiam Girdwood 498ddee627cSLiam Girdwood if (cpu_dai->driver->ops->hw_params) { 499ddee627cSLiam Girdwood ret = cpu_dai->driver->ops->hw_params(substream, params, cpu_dai); 500ddee627cSLiam Girdwood if (ret < 0) { 50125bfe662SMark Brown dev_err(cpu_dai->dev, "%s hw params failed: %d\n", 50225bfe662SMark Brown cpu_dai->name, ret); 503ddee627cSLiam Girdwood goto interface_err; 504ddee627cSLiam Girdwood } 505ddee627cSLiam Girdwood } 506ddee627cSLiam Girdwood 507ddee627cSLiam Girdwood if (platform->driver->ops && platform->driver->ops->hw_params) { 508ddee627cSLiam Girdwood ret = platform->driver->ops->hw_params(substream, params); 509ddee627cSLiam Girdwood if (ret < 0) { 51025bfe662SMark Brown dev_err(platform->dev, "%s hw params failed: %d\n", 51125bfe662SMark Brown platform->name, ret); 512ddee627cSLiam Girdwood goto platform_err; 513ddee627cSLiam Girdwood } 514ddee627cSLiam Girdwood } 515ddee627cSLiam Girdwood 51617841020SDong Aisheng /* store the rate for each DAIs */ 51717841020SDong Aisheng cpu_dai->rate = params_rate(params); 51817841020SDong Aisheng codec_dai->rate = params_rate(params); 519ddee627cSLiam Girdwood 520ddee627cSLiam Girdwood out: 521b8c0dab9SLiam Girdwood mutex_unlock(&rtd->pcm_mutex); 522ddee627cSLiam Girdwood return ret; 523ddee627cSLiam Girdwood 524ddee627cSLiam Girdwood platform_err: 525ddee627cSLiam Girdwood if (cpu_dai->driver->ops->hw_free) 526ddee627cSLiam Girdwood cpu_dai->driver->ops->hw_free(substream, cpu_dai); 527ddee627cSLiam Girdwood 528ddee627cSLiam Girdwood interface_err: 529ddee627cSLiam Girdwood if (codec_dai->driver->ops->hw_free) 530ddee627cSLiam Girdwood codec_dai->driver->ops->hw_free(substream, codec_dai); 531ddee627cSLiam Girdwood 532ddee627cSLiam Girdwood codec_err: 533ddee627cSLiam Girdwood if (rtd->dai_link->ops && rtd->dai_link->ops->hw_free) 534ddee627cSLiam Girdwood rtd->dai_link->ops->hw_free(substream); 535ddee627cSLiam Girdwood 536b8c0dab9SLiam Girdwood mutex_unlock(&rtd->pcm_mutex); 537ddee627cSLiam Girdwood return ret; 538ddee627cSLiam Girdwood } 539ddee627cSLiam Girdwood 540ddee627cSLiam Girdwood /* 541ddee627cSLiam Girdwood * Frees resources allocated by hw_params, can be called multiple times 542ddee627cSLiam Girdwood */ 543ddee627cSLiam Girdwood static int soc_pcm_hw_free(struct snd_pcm_substream *substream) 544ddee627cSLiam Girdwood { 545ddee627cSLiam Girdwood struct snd_soc_pcm_runtime *rtd = substream->private_data; 546ddee627cSLiam Girdwood struct snd_soc_platform *platform = rtd->platform; 547ddee627cSLiam Girdwood struct snd_soc_dai *cpu_dai = rtd->cpu_dai; 548ddee627cSLiam Girdwood struct snd_soc_dai *codec_dai = rtd->codec_dai; 549ddee627cSLiam Girdwood struct snd_soc_codec *codec = rtd->codec; 550ddee627cSLiam Girdwood 551b8c0dab9SLiam Girdwood mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); 552ddee627cSLiam Girdwood 553ddee627cSLiam Girdwood /* apply codec digital mute */ 554ddee627cSLiam Girdwood if (!codec->active) 555ddee627cSLiam Girdwood snd_soc_dai_digital_mute(codec_dai, 1); 556ddee627cSLiam Girdwood 557ddee627cSLiam Girdwood /* free any machine hw params */ 558ddee627cSLiam Girdwood if (rtd->dai_link->ops && rtd->dai_link->ops->hw_free) 559ddee627cSLiam Girdwood rtd->dai_link->ops->hw_free(substream); 560ddee627cSLiam Girdwood 561ddee627cSLiam Girdwood /* free any DMA resources */ 562ddee627cSLiam Girdwood if (platform->driver->ops && platform->driver->ops->hw_free) 563ddee627cSLiam Girdwood platform->driver->ops->hw_free(substream); 564ddee627cSLiam Girdwood 565ddee627cSLiam Girdwood /* now free hw params for the DAIs */ 566ddee627cSLiam Girdwood if (codec_dai->driver->ops->hw_free) 567ddee627cSLiam Girdwood codec_dai->driver->ops->hw_free(substream, codec_dai); 568ddee627cSLiam Girdwood 569ddee627cSLiam Girdwood if (cpu_dai->driver->ops->hw_free) 570ddee627cSLiam Girdwood cpu_dai->driver->ops->hw_free(substream, cpu_dai); 571ddee627cSLiam Girdwood 572b8c0dab9SLiam Girdwood mutex_unlock(&rtd->pcm_mutex); 573ddee627cSLiam Girdwood return 0; 574ddee627cSLiam Girdwood } 575ddee627cSLiam Girdwood 576ddee627cSLiam Girdwood static int soc_pcm_trigger(struct snd_pcm_substream *substream, int cmd) 577ddee627cSLiam Girdwood { 578ddee627cSLiam Girdwood struct snd_soc_pcm_runtime *rtd = substream->private_data; 579ddee627cSLiam Girdwood struct snd_soc_platform *platform = rtd->platform; 580ddee627cSLiam Girdwood struct snd_soc_dai *cpu_dai = rtd->cpu_dai; 581ddee627cSLiam Girdwood struct snd_soc_dai *codec_dai = rtd->codec_dai; 582ddee627cSLiam Girdwood int ret; 583ddee627cSLiam Girdwood 584ddee627cSLiam Girdwood if (codec_dai->driver->ops->trigger) { 585ddee627cSLiam Girdwood ret = codec_dai->driver->ops->trigger(substream, cmd, codec_dai); 586ddee627cSLiam Girdwood if (ret < 0) 587ddee627cSLiam Girdwood return ret; 588ddee627cSLiam Girdwood } 589ddee627cSLiam Girdwood 590ddee627cSLiam Girdwood if (platform->driver->ops && platform->driver->ops->trigger) { 591ddee627cSLiam Girdwood ret = platform->driver->ops->trigger(substream, cmd); 592ddee627cSLiam Girdwood if (ret < 0) 593ddee627cSLiam Girdwood return ret; 594ddee627cSLiam Girdwood } 595ddee627cSLiam Girdwood 596ddee627cSLiam Girdwood if (cpu_dai->driver->ops->trigger) { 597ddee627cSLiam Girdwood ret = cpu_dai->driver->ops->trigger(substream, cmd, cpu_dai); 598ddee627cSLiam Girdwood if (ret < 0) 599ddee627cSLiam Girdwood return ret; 600ddee627cSLiam Girdwood } 601ddee627cSLiam Girdwood return 0; 602ddee627cSLiam Girdwood } 603ddee627cSLiam Girdwood 604ddee627cSLiam Girdwood /* 605ddee627cSLiam Girdwood * soc level wrapper for pointer callback 606ddee627cSLiam Girdwood * If cpu_dai, codec_dai, platform driver has the delay callback, than 607ddee627cSLiam Girdwood * the runtime->delay will be updated accordingly. 608ddee627cSLiam Girdwood */ 609ddee627cSLiam Girdwood static snd_pcm_uframes_t soc_pcm_pointer(struct snd_pcm_substream *substream) 610ddee627cSLiam Girdwood { 611ddee627cSLiam Girdwood struct snd_soc_pcm_runtime *rtd = substream->private_data; 612ddee627cSLiam Girdwood struct snd_soc_platform *platform = rtd->platform; 613ddee627cSLiam Girdwood struct snd_soc_dai *cpu_dai = rtd->cpu_dai; 614ddee627cSLiam Girdwood struct snd_soc_dai *codec_dai = rtd->codec_dai; 615ddee627cSLiam Girdwood struct snd_pcm_runtime *runtime = substream->runtime; 616ddee627cSLiam Girdwood snd_pcm_uframes_t offset = 0; 617ddee627cSLiam Girdwood snd_pcm_sframes_t delay = 0; 618ddee627cSLiam Girdwood 619ddee627cSLiam Girdwood if (platform->driver->ops && platform->driver->ops->pointer) 620ddee627cSLiam Girdwood offset = platform->driver->ops->pointer(substream); 621ddee627cSLiam Girdwood 622ddee627cSLiam Girdwood if (cpu_dai->driver->ops->delay) 623ddee627cSLiam Girdwood delay += cpu_dai->driver->ops->delay(substream, cpu_dai); 624ddee627cSLiam Girdwood 625ddee627cSLiam Girdwood if (codec_dai->driver->ops->delay) 626ddee627cSLiam Girdwood delay += codec_dai->driver->ops->delay(substream, codec_dai); 627ddee627cSLiam Girdwood 628ddee627cSLiam Girdwood if (platform->driver->delay) 629ddee627cSLiam Girdwood delay += platform->driver->delay(substream, codec_dai); 630ddee627cSLiam Girdwood 631ddee627cSLiam Girdwood runtime->delay = delay; 632ddee627cSLiam Girdwood 633ddee627cSLiam Girdwood return offset; 634ddee627cSLiam Girdwood } 635ddee627cSLiam Girdwood 636ddee627cSLiam Girdwood /* create a new pcm */ 637ddee627cSLiam Girdwood int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num) 638ddee627cSLiam Girdwood { 639ddee627cSLiam Girdwood struct snd_soc_codec *codec = rtd->codec; 640ddee627cSLiam Girdwood struct snd_soc_platform *platform = rtd->platform; 641ddee627cSLiam Girdwood struct snd_soc_dai *codec_dai = rtd->codec_dai; 642ddee627cSLiam Girdwood struct snd_soc_dai *cpu_dai = rtd->cpu_dai; 643a500231dSSangsu Park struct snd_pcm_ops *soc_pcm_ops = &rtd->ops; 644ddee627cSLiam Girdwood struct snd_pcm *pcm; 645ddee627cSLiam Girdwood char new_name[64]; 646ddee627cSLiam Girdwood int ret = 0, playback = 0, capture = 0; 647ddee627cSLiam Girdwood 648a500231dSSangsu Park soc_pcm_ops->open = soc_pcm_open; 649a500231dSSangsu Park soc_pcm_ops->close = soc_pcm_close; 650a500231dSSangsu Park soc_pcm_ops->hw_params = soc_pcm_hw_params; 651a500231dSSangsu Park soc_pcm_ops->hw_free = soc_pcm_hw_free; 652a500231dSSangsu Park soc_pcm_ops->prepare = soc_pcm_prepare; 653a500231dSSangsu Park soc_pcm_ops->trigger = soc_pcm_trigger; 654a500231dSSangsu Park soc_pcm_ops->pointer = soc_pcm_pointer; 655a500231dSSangsu Park 656ddee627cSLiam Girdwood /* check client and interface hw capabilities */ 657ddee627cSLiam Girdwood snprintf(new_name, sizeof(new_name), "%s %s-%d", 658ddee627cSLiam Girdwood rtd->dai_link->stream_name, codec_dai->name, num); 659ddee627cSLiam Girdwood 660ddee627cSLiam Girdwood if (codec_dai->driver->playback.channels_min) 661ddee627cSLiam Girdwood playback = 1; 662ddee627cSLiam Girdwood if (codec_dai->driver->capture.channels_min) 663ddee627cSLiam Girdwood capture = 1; 664ddee627cSLiam Girdwood 665ddee627cSLiam Girdwood dev_dbg(rtd->card->dev, "registered pcm #%d %s\n",num,new_name); 666ddee627cSLiam Girdwood ret = snd_pcm_new(rtd->card->snd_card, new_name, 667ddee627cSLiam Girdwood num, playback, capture, &pcm); 668ddee627cSLiam Girdwood if (ret < 0) { 669ddee627cSLiam Girdwood printk(KERN_ERR "asoc: can't create pcm for codec %s\n", codec->name); 670ddee627cSLiam Girdwood return ret; 671ddee627cSLiam Girdwood } 672ddee627cSLiam Girdwood 673ddee627cSLiam Girdwood /* DAPM dai link stream work */ 674ddee627cSLiam Girdwood INIT_DELAYED_WORK(&rtd->delayed_work, close_delayed_work); 675ddee627cSLiam Girdwood 676ddee627cSLiam Girdwood rtd->pcm = pcm; 677ddee627cSLiam Girdwood pcm->private_data = rtd; 678ddee627cSLiam Girdwood if (platform->driver->ops) { 679a500231dSSangsu Park soc_pcm_ops->mmap = platform->driver->ops->mmap; 680a500231dSSangsu Park soc_pcm_ops->pointer = platform->driver->ops->pointer; 681a500231dSSangsu Park soc_pcm_ops->ioctl = platform->driver->ops->ioctl; 682a500231dSSangsu Park soc_pcm_ops->copy = platform->driver->ops->copy; 683a500231dSSangsu Park soc_pcm_ops->silence = platform->driver->ops->silence; 684a500231dSSangsu Park soc_pcm_ops->ack = platform->driver->ops->ack; 685a500231dSSangsu Park soc_pcm_ops->page = platform->driver->ops->page; 686ddee627cSLiam Girdwood } 687ddee627cSLiam Girdwood 688ddee627cSLiam Girdwood if (playback) 689a500231dSSangsu Park snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, soc_pcm_ops); 690ddee627cSLiam Girdwood 691ddee627cSLiam Girdwood if (capture) 692a500231dSSangsu Park snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, soc_pcm_ops); 693ddee627cSLiam Girdwood 694ddee627cSLiam Girdwood if (platform->driver->pcm_new) { 695ddee627cSLiam Girdwood ret = platform->driver->pcm_new(rtd); 696ddee627cSLiam Girdwood if (ret < 0) { 697ddee627cSLiam Girdwood pr_err("asoc: platform pcm constructor failed\n"); 698ddee627cSLiam Girdwood return ret; 699ddee627cSLiam Girdwood } 700ddee627cSLiam Girdwood } 701ddee627cSLiam Girdwood 702ddee627cSLiam Girdwood pcm->private_free = platform->driver->pcm_free; 703ddee627cSLiam Girdwood printk(KERN_INFO "asoc: %s <-> %s mapping ok\n", codec_dai->name, 704ddee627cSLiam Girdwood cpu_dai->name); 705ddee627cSLiam Girdwood return ret; 706ddee627cSLiam Girdwood } 707