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 /* 66*58ba9b25SMark Brown * List of sample sizes that might go over the bus for parameter 67*58ba9b25SMark Brown * application. There ought to be a wildcard sample size for things 68*58ba9b25SMark Brown * like the DAC/ADC resolution to use but there isn't right now. 69*58ba9b25SMark Brown */ 70*58ba9b25SMark Brown static int sample_sizes[] = { 71*58ba9b25SMark Brown 8, 16, 24, 32, 72*58ba9b25SMark Brown }; 73*58ba9b25SMark Brown 74*58ba9b25SMark Brown static void soc_pcm_apply_msb(struct snd_pcm_substream *substream, 75*58ba9b25SMark Brown struct snd_soc_dai *dai) 76*58ba9b25SMark Brown { 77*58ba9b25SMark Brown int ret, i, bits; 78*58ba9b25SMark Brown 79*58ba9b25SMark Brown if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 80*58ba9b25SMark Brown bits = dai->driver->playback.sig_bits; 81*58ba9b25SMark Brown else 82*58ba9b25SMark Brown bits = dai->driver->capture.sig_bits; 83*58ba9b25SMark Brown 84*58ba9b25SMark Brown if (!bits) 85*58ba9b25SMark Brown return; 86*58ba9b25SMark Brown 87*58ba9b25SMark Brown for (i = 0; i < ARRAY_SIZE(sample_sizes); i++) { 88*58ba9b25SMark Brown ret = snd_pcm_hw_constraint_msbits(substream->runtime, 89*58ba9b25SMark Brown 0, sample_sizes[i], 90*58ba9b25SMark Brown bits); 91*58ba9b25SMark Brown if (ret != 0) 92*58ba9b25SMark Brown dev_warn(dai->dev, 93*58ba9b25SMark Brown "Failed to set MSB %d/%d: %d\n", 94*58ba9b25SMark Brown bits, sample_sizes[i], ret); 95*58ba9b25SMark Brown } 96*58ba9b25SMark Brown } 97*58ba9b25SMark Brown 98*58ba9b25SMark Brown /* 99ddee627cSLiam Girdwood * Called by ALSA when a PCM substream is opened, the runtime->hw record is 100ddee627cSLiam Girdwood * then initialized and any private data can be allocated. This also calls 101ddee627cSLiam Girdwood * startup for the cpu DAI, platform, machine and codec DAI. 102ddee627cSLiam Girdwood */ 103ddee627cSLiam Girdwood static int soc_pcm_open(struct snd_pcm_substream *substream) 104ddee627cSLiam Girdwood { 105ddee627cSLiam Girdwood struct snd_soc_pcm_runtime *rtd = substream->private_data; 106ddee627cSLiam Girdwood struct snd_pcm_runtime *runtime = substream->runtime; 107ddee627cSLiam Girdwood struct snd_soc_platform *platform = rtd->platform; 108ddee627cSLiam Girdwood struct snd_soc_dai *cpu_dai = rtd->cpu_dai; 109ddee627cSLiam Girdwood struct snd_soc_dai *codec_dai = rtd->codec_dai; 110ddee627cSLiam Girdwood struct snd_soc_dai_driver *cpu_dai_drv = cpu_dai->driver; 111ddee627cSLiam Girdwood struct snd_soc_dai_driver *codec_dai_drv = codec_dai->driver; 112ddee627cSLiam Girdwood int ret = 0; 113ddee627cSLiam Girdwood 114d6652ef8SMark Brown pm_runtime_get_sync(cpu_dai->dev); 115d6652ef8SMark Brown pm_runtime_get_sync(codec_dai->dev); 116d6652ef8SMark Brown pm_runtime_get_sync(platform->dev); 117d6652ef8SMark Brown 118b8c0dab9SLiam Girdwood mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); 119ddee627cSLiam Girdwood 120ddee627cSLiam Girdwood /* startup the audio subsystem */ 121ddee627cSLiam Girdwood if (cpu_dai->driver->ops->startup) { 122ddee627cSLiam Girdwood ret = cpu_dai->driver->ops->startup(substream, cpu_dai); 123ddee627cSLiam Girdwood if (ret < 0) { 124ddee627cSLiam Girdwood printk(KERN_ERR "asoc: can't open interface %s\n", 125ddee627cSLiam Girdwood cpu_dai->name); 126ddee627cSLiam Girdwood goto out; 127ddee627cSLiam Girdwood } 128ddee627cSLiam Girdwood } 129ddee627cSLiam Girdwood 130ddee627cSLiam Girdwood if (platform->driver->ops && platform->driver->ops->open) { 131ddee627cSLiam Girdwood ret = platform->driver->ops->open(substream); 132ddee627cSLiam Girdwood if (ret < 0) { 133ddee627cSLiam Girdwood printk(KERN_ERR "asoc: can't open platform %s\n", platform->name); 134ddee627cSLiam Girdwood goto platform_err; 135ddee627cSLiam Girdwood } 136ddee627cSLiam Girdwood } 137ddee627cSLiam Girdwood 138ddee627cSLiam Girdwood if (codec_dai->driver->ops->startup) { 139ddee627cSLiam Girdwood ret = codec_dai->driver->ops->startup(substream, codec_dai); 140ddee627cSLiam Girdwood if (ret < 0) { 141ddee627cSLiam Girdwood printk(KERN_ERR "asoc: can't open codec %s\n", 142ddee627cSLiam Girdwood codec_dai->name); 143ddee627cSLiam Girdwood goto codec_dai_err; 144ddee627cSLiam Girdwood } 145ddee627cSLiam Girdwood } 146ddee627cSLiam Girdwood 147ddee627cSLiam Girdwood if (rtd->dai_link->ops && rtd->dai_link->ops->startup) { 148ddee627cSLiam Girdwood ret = rtd->dai_link->ops->startup(substream); 149ddee627cSLiam Girdwood if (ret < 0) { 150ddee627cSLiam Girdwood printk(KERN_ERR "asoc: %s startup failed\n", rtd->dai_link->name); 151ddee627cSLiam Girdwood goto machine_err; 152ddee627cSLiam Girdwood } 153ddee627cSLiam Girdwood } 154ddee627cSLiam Girdwood 155ddee627cSLiam Girdwood /* Check that the codec and cpu DAIs are compatible */ 156ddee627cSLiam Girdwood if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 157ddee627cSLiam Girdwood runtime->hw.rate_min = 158ddee627cSLiam Girdwood max(codec_dai_drv->playback.rate_min, 159ddee627cSLiam Girdwood cpu_dai_drv->playback.rate_min); 160ddee627cSLiam Girdwood runtime->hw.rate_max = 161ddee627cSLiam Girdwood min(codec_dai_drv->playback.rate_max, 162ddee627cSLiam Girdwood cpu_dai_drv->playback.rate_max); 163ddee627cSLiam Girdwood runtime->hw.channels_min = 164ddee627cSLiam Girdwood max(codec_dai_drv->playback.channels_min, 165ddee627cSLiam Girdwood cpu_dai_drv->playback.channels_min); 166ddee627cSLiam Girdwood runtime->hw.channels_max = 167ddee627cSLiam Girdwood min(codec_dai_drv->playback.channels_max, 168ddee627cSLiam Girdwood cpu_dai_drv->playback.channels_max); 169ddee627cSLiam Girdwood runtime->hw.formats = 170ddee627cSLiam Girdwood codec_dai_drv->playback.formats & cpu_dai_drv->playback.formats; 171ddee627cSLiam Girdwood runtime->hw.rates = 172ddee627cSLiam Girdwood codec_dai_drv->playback.rates & cpu_dai_drv->playback.rates; 173ddee627cSLiam Girdwood if (codec_dai_drv->playback.rates 174ddee627cSLiam Girdwood & (SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_CONTINUOUS)) 175ddee627cSLiam Girdwood runtime->hw.rates |= cpu_dai_drv->playback.rates; 176ddee627cSLiam Girdwood if (cpu_dai_drv->playback.rates 177ddee627cSLiam Girdwood & (SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_CONTINUOUS)) 178ddee627cSLiam Girdwood runtime->hw.rates |= codec_dai_drv->playback.rates; 179ddee627cSLiam Girdwood } else { 180ddee627cSLiam Girdwood runtime->hw.rate_min = 181ddee627cSLiam Girdwood max(codec_dai_drv->capture.rate_min, 182ddee627cSLiam Girdwood cpu_dai_drv->capture.rate_min); 183ddee627cSLiam Girdwood runtime->hw.rate_max = 184ddee627cSLiam Girdwood min(codec_dai_drv->capture.rate_max, 185ddee627cSLiam Girdwood cpu_dai_drv->capture.rate_max); 186ddee627cSLiam Girdwood runtime->hw.channels_min = 187ddee627cSLiam Girdwood max(codec_dai_drv->capture.channels_min, 188ddee627cSLiam Girdwood cpu_dai_drv->capture.channels_min); 189ddee627cSLiam Girdwood runtime->hw.channels_max = 190ddee627cSLiam Girdwood min(codec_dai_drv->capture.channels_max, 191ddee627cSLiam Girdwood cpu_dai_drv->capture.channels_max); 192ddee627cSLiam Girdwood runtime->hw.formats = 193ddee627cSLiam Girdwood codec_dai_drv->capture.formats & cpu_dai_drv->capture.formats; 194ddee627cSLiam Girdwood runtime->hw.rates = 195ddee627cSLiam Girdwood codec_dai_drv->capture.rates & cpu_dai_drv->capture.rates; 196ddee627cSLiam Girdwood if (codec_dai_drv->capture.rates 197ddee627cSLiam Girdwood & (SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_CONTINUOUS)) 198ddee627cSLiam Girdwood runtime->hw.rates |= cpu_dai_drv->capture.rates; 199ddee627cSLiam Girdwood if (cpu_dai_drv->capture.rates 200ddee627cSLiam Girdwood & (SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_CONTINUOUS)) 201ddee627cSLiam Girdwood runtime->hw.rates |= codec_dai_drv->capture.rates; 202ddee627cSLiam Girdwood } 203ddee627cSLiam Girdwood 204ddee627cSLiam Girdwood ret = -EINVAL; 205ddee627cSLiam Girdwood snd_pcm_limit_hw_rates(runtime); 206ddee627cSLiam Girdwood if (!runtime->hw.rates) { 207ddee627cSLiam Girdwood printk(KERN_ERR "asoc: %s <-> %s No matching rates\n", 208ddee627cSLiam Girdwood codec_dai->name, cpu_dai->name); 209ddee627cSLiam Girdwood goto config_err; 210ddee627cSLiam Girdwood } 211ddee627cSLiam Girdwood if (!runtime->hw.formats) { 212ddee627cSLiam Girdwood printk(KERN_ERR "asoc: %s <-> %s No matching formats\n", 213ddee627cSLiam Girdwood codec_dai->name, cpu_dai->name); 214ddee627cSLiam Girdwood goto config_err; 215ddee627cSLiam Girdwood } 216ddee627cSLiam Girdwood if (!runtime->hw.channels_min || !runtime->hw.channels_max || 217ddee627cSLiam Girdwood runtime->hw.channels_min > runtime->hw.channels_max) { 218ddee627cSLiam Girdwood printk(KERN_ERR "asoc: %s <-> %s No matching channels\n", 219ddee627cSLiam Girdwood codec_dai->name, cpu_dai->name); 220ddee627cSLiam Girdwood goto config_err; 221ddee627cSLiam Girdwood } 222ddee627cSLiam Girdwood 223*58ba9b25SMark Brown soc_pcm_apply_msb(substream, codec_dai); 224*58ba9b25SMark Brown soc_pcm_apply_msb(substream, cpu_dai); 225*58ba9b25SMark Brown 226ddee627cSLiam Girdwood /* Symmetry only applies if we've already got an active stream. */ 22717841020SDong Aisheng if (cpu_dai->active) { 22817841020SDong Aisheng ret = soc_pcm_apply_symmetry(substream, cpu_dai); 22917841020SDong Aisheng if (ret != 0) 23017841020SDong Aisheng goto config_err; 23117841020SDong Aisheng } 23217841020SDong Aisheng 23317841020SDong Aisheng if (codec_dai->active) { 23417841020SDong Aisheng ret = soc_pcm_apply_symmetry(substream, codec_dai); 235ddee627cSLiam Girdwood if (ret != 0) 236ddee627cSLiam Girdwood goto config_err; 237ddee627cSLiam Girdwood } 238ddee627cSLiam Girdwood 239ddee627cSLiam Girdwood pr_debug("asoc: %s <-> %s info:\n", 240ddee627cSLiam Girdwood codec_dai->name, cpu_dai->name); 241ddee627cSLiam Girdwood pr_debug("asoc: rate mask 0x%x\n", runtime->hw.rates); 242ddee627cSLiam Girdwood pr_debug("asoc: min ch %d max ch %d\n", runtime->hw.channels_min, 243ddee627cSLiam Girdwood runtime->hw.channels_max); 244ddee627cSLiam Girdwood pr_debug("asoc: min rate %d max rate %d\n", runtime->hw.rate_min, 245ddee627cSLiam Girdwood runtime->hw.rate_max); 246ddee627cSLiam Girdwood 247ddee627cSLiam Girdwood if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 248ddee627cSLiam Girdwood cpu_dai->playback_active++; 249ddee627cSLiam Girdwood codec_dai->playback_active++; 250ddee627cSLiam Girdwood } else { 251ddee627cSLiam Girdwood cpu_dai->capture_active++; 252ddee627cSLiam Girdwood codec_dai->capture_active++; 253ddee627cSLiam Girdwood } 254ddee627cSLiam Girdwood cpu_dai->active++; 255ddee627cSLiam Girdwood codec_dai->active++; 256ddee627cSLiam Girdwood rtd->codec->active++; 257b8c0dab9SLiam Girdwood mutex_unlock(&rtd->pcm_mutex); 258ddee627cSLiam Girdwood return 0; 259ddee627cSLiam Girdwood 260ddee627cSLiam Girdwood config_err: 261ddee627cSLiam Girdwood if (rtd->dai_link->ops && rtd->dai_link->ops->shutdown) 262ddee627cSLiam Girdwood rtd->dai_link->ops->shutdown(substream); 263ddee627cSLiam Girdwood 264ddee627cSLiam Girdwood machine_err: 265ddee627cSLiam Girdwood if (codec_dai->driver->ops->shutdown) 266ddee627cSLiam Girdwood codec_dai->driver->ops->shutdown(substream, codec_dai); 267ddee627cSLiam Girdwood 268ddee627cSLiam Girdwood codec_dai_err: 269ddee627cSLiam Girdwood if (platform->driver->ops && platform->driver->ops->close) 270ddee627cSLiam Girdwood platform->driver->ops->close(substream); 271ddee627cSLiam Girdwood 272ddee627cSLiam Girdwood platform_err: 273ddee627cSLiam Girdwood if (cpu_dai->driver->ops->shutdown) 274ddee627cSLiam Girdwood cpu_dai->driver->ops->shutdown(substream, cpu_dai); 275ddee627cSLiam Girdwood out: 276b8c0dab9SLiam Girdwood mutex_unlock(&rtd->pcm_mutex); 277d6652ef8SMark Brown 278d6652ef8SMark Brown pm_runtime_put(platform->dev); 279d6652ef8SMark Brown pm_runtime_put(codec_dai->dev); 280d6652ef8SMark Brown pm_runtime_put(cpu_dai->dev); 281d6652ef8SMark Brown 282ddee627cSLiam Girdwood return ret; 283ddee627cSLiam Girdwood } 284ddee627cSLiam Girdwood 285ddee627cSLiam Girdwood /* 286ddee627cSLiam Girdwood * Power down the audio subsystem pmdown_time msecs after close is called. 287ddee627cSLiam Girdwood * This is to ensure there are no pops or clicks in between any music tracks 288ddee627cSLiam Girdwood * due to DAPM power cycling. 289ddee627cSLiam Girdwood */ 290ddee627cSLiam Girdwood static void close_delayed_work(struct work_struct *work) 291ddee627cSLiam Girdwood { 292ddee627cSLiam Girdwood struct snd_soc_pcm_runtime *rtd = 293ddee627cSLiam Girdwood container_of(work, struct snd_soc_pcm_runtime, delayed_work.work); 294ddee627cSLiam Girdwood struct snd_soc_dai *codec_dai = rtd->codec_dai; 295ddee627cSLiam Girdwood 296b8c0dab9SLiam Girdwood mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); 297ddee627cSLiam Girdwood 298ddee627cSLiam Girdwood pr_debug("pop wq checking: %s status: %s waiting: %s\n", 299ddee627cSLiam Girdwood codec_dai->driver->playback.stream_name, 300ddee627cSLiam Girdwood codec_dai->playback_active ? "active" : "inactive", 301ddee627cSLiam Girdwood codec_dai->pop_wait ? "yes" : "no"); 302ddee627cSLiam Girdwood 303ddee627cSLiam Girdwood /* are we waiting on this codec DAI stream */ 304ddee627cSLiam Girdwood if (codec_dai->pop_wait == 1) { 305ddee627cSLiam Girdwood codec_dai->pop_wait = 0; 306ddee627cSLiam Girdwood snd_soc_dapm_stream_event(rtd, 307ddee627cSLiam Girdwood codec_dai->driver->playback.stream_name, 308ddee627cSLiam Girdwood SND_SOC_DAPM_STREAM_STOP); 309ddee627cSLiam Girdwood } 310ddee627cSLiam Girdwood 311b8c0dab9SLiam Girdwood mutex_unlock(&rtd->pcm_mutex); 312ddee627cSLiam Girdwood } 313ddee627cSLiam Girdwood 314ddee627cSLiam Girdwood /* 315ddee627cSLiam Girdwood * Called by ALSA when a PCM substream is closed. Private data can be 316ddee627cSLiam Girdwood * freed here. The cpu DAI, codec DAI, machine and platform are also 317ddee627cSLiam Girdwood * shutdown. 318ddee627cSLiam Girdwood */ 31991d5e6b4SLiam Girdwood static int soc_pcm_close(struct snd_pcm_substream *substream) 320ddee627cSLiam Girdwood { 321ddee627cSLiam Girdwood struct snd_soc_pcm_runtime *rtd = substream->private_data; 322ddee627cSLiam Girdwood struct snd_soc_platform *platform = rtd->platform; 323ddee627cSLiam Girdwood struct snd_soc_dai *cpu_dai = rtd->cpu_dai; 324ddee627cSLiam Girdwood struct snd_soc_dai *codec_dai = rtd->codec_dai; 325ddee627cSLiam Girdwood struct snd_soc_codec *codec = rtd->codec; 326ddee627cSLiam Girdwood 327b8c0dab9SLiam Girdwood mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); 328ddee627cSLiam Girdwood 329ddee627cSLiam Girdwood if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 330ddee627cSLiam Girdwood cpu_dai->playback_active--; 331ddee627cSLiam Girdwood codec_dai->playback_active--; 332ddee627cSLiam Girdwood } else { 333ddee627cSLiam Girdwood cpu_dai->capture_active--; 334ddee627cSLiam Girdwood codec_dai->capture_active--; 335ddee627cSLiam Girdwood } 336ddee627cSLiam Girdwood 337ddee627cSLiam Girdwood cpu_dai->active--; 338ddee627cSLiam Girdwood codec_dai->active--; 339ddee627cSLiam Girdwood codec->active--; 340ddee627cSLiam Girdwood 34117841020SDong Aisheng /* clear the corresponding DAIs rate when inactive */ 34217841020SDong Aisheng if (!cpu_dai->active) 34317841020SDong Aisheng cpu_dai->rate = 0; 34417841020SDong Aisheng 34517841020SDong Aisheng if (!codec_dai->active) 34617841020SDong Aisheng codec_dai->rate = 0; 34725b76791SSascha Hauer 348ddee627cSLiam Girdwood /* Muting the DAC suppresses artifacts caused during digital 349ddee627cSLiam Girdwood * shutdown, for example from stopping clocks. 350ddee627cSLiam Girdwood */ 351ddee627cSLiam Girdwood if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 352ddee627cSLiam Girdwood snd_soc_dai_digital_mute(codec_dai, 1); 353ddee627cSLiam Girdwood 354ddee627cSLiam Girdwood if (cpu_dai->driver->ops->shutdown) 355ddee627cSLiam Girdwood cpu_dai->driver->ops->shutdown(substream, cpu_dai); 356ddee627cSLiam Girdwood 357ddee627cSLiam Girdwood if (codec_dai->driver->ops->shutdown) 358ddee627cSLiam Girdwood codec_dai->driver->ops->shutdown(substream, codec_dai); 359ddee627cSLiam Girdwood 360ddee627cSLiam Girdwood if (rtd->dai_link->ops && rtd->dai_link->ops->shutdown) 361ddee627cSLiam Girdwood rtd->dai_link->ops->shutdown(substream); 362ddee627cSLiam Girdwood 363ddee627cSLiam Girdwood if (platform->driver->ops && platform->driver->ops->close) 364ddee627cSLiam Girdwood platform->driver->ops->close(substream); 365ddee627cSLiam Girdwood cpu_dai->runtime = NULL; 366ddee627cSLiam Girdwood 367ddee627cSLiam Girdwood if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 3685b6247abSMark Brown if (codec->ignore_pmdown_time || 3695b6247abSMark Brown rtd->dai_link->ignore_pmdown_time) { 3701d69c5c5SPeter Ujfalusi /* powered down playback stream now */ 3711d69c5c5SPeter Ujfalusi snd_soc_dapm_stream_event(rtd, 3721d69c5c5SPeter Ujfalusi codec_dai->driver->playback.stream_name, 3731d69c5c5SPeter Ujfalusi SND_SOC_DAPM_STREAM_STOP); 3741d69c5c5SPeter Ujfalusi } else { 375ddee627cSLiam Girdwood /* start delayed pop wq here for playback streams */ 376ddee627cSLiam Girdwood codec_dai->pop_wait = 1; 377ddee627cSLiam Girdwood schedule_delayed_work(&rtd->delayed_work, 378ddee627cSLiam Girdwood msecs_to_jiffies(rtd->pmdown_time)); 3791d69c5c5SPeter Ujfalusi } 380ddee627cSLiam Girdwood } else { 381ddee627cSLiam Girdwood /* capture streams can be powered down now */ 382ddee627cSLiam Girdwood snd_soc_dapm_stream_event(rtd, 383ddee627cSLiam Girdwood codec_dai->driver->capture.stream_name, 384ddee627cSLiam Girdwood SND_SOC_DAPM_STREAM_STOP); 385ddee627cSLiam Girdwood } 386ddee627cSLiam Girdwood 387b8c0dab9SLiam Girdwood mutex_unlock(&rtd->pcm_mutex); 388d6652ef8SMark Brown 389d6652ef8SMark Brown pm_runtime_put(platform->dev); 390d6652ef8SMark Brown pm_runtime_put(codec_dai->dev); 391d6652ef8SMark Brown pm_runtime_put(cpu_dai->dev); 392d6652ef8SMark Brown 393ddee627cSLiam Girdwood return 0; 394ddee627cSLiam Girdwood } 395ddee627cSLiam Girdwood 396ddee627cSLiam Girdwood /* 397ddee627cSLiam Girdwood * Called by ALSA when the PCM substream is prepared, can set format, sample 398ddee627cSLiam Girdwood * rate, etc. This function is non atomic and can be called multiple times, 399ddee627cSLiam Girdwood * it can refer to the runtime info. 400ddee627cSLiam Girdwood */ 401ddee627cSLiam Girdwood static int soc_pcm_prepare(struct snd_pcm_substream *substream) 402ddee627cSLiam Girdwood { 403ddee627cSLiam Girdwood struct snd_soc_pcm_runtime *rtd = substream->private_data; 404ddee627cSLiam Girdwood struct snd_soc_platform *platform = rtd->platform; 405ddee627cSLiam Girdwood struct snd_soc_dai *cpu_dai = rtd->cpu_dai; 406ddee627cSLiam Girdwood struct snd_soc_dai *codec_dai = rtd->codec_dai; 407ddee627cSLiam Girdwood int ret = 0; 408ddee627cSLiam Girdwood 409b8c0dab9SLiam Girdwood mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); 410ddee627cSLiam Girdwood 411ddee627cSLiam Girdwood if (rtd->dai_link->ops && rtd->dai_link->ops->prepare) { 412ddee627cSLiam Girdwood ret = rtd->dai_link->ops->prepare(substream); 413ddee627cSLiam Girdwood if (ret < 0) { 414ddee627cSLiam Girdwood printk(KERN_ERR "asoc: machine prepare error\n"); 415ddee627cSLiam Girdwood goto out; 416ddee627cSLiam Girdwood } 417ddee627cSLiam Girdwood } 418ddee627cSLiam Girdwood 419ddee627cSLiam Girdwood if (platform->driver->ops && platform->driver->ops->prepare) { 420ddee627cSLiam Girdwood ret = platform->driver->ops->prepare(substream); 421ddee627cSLiam Girdwood if (ret < 0) { 422ddee627cSLiam Girdwood printk(KERN_ERR "asoc: platform prepare error\n"); 423ddee627cSLiam Girdwood goto out; 424ddee627cSLiam Girdwood } 425ddee627cSLiam Girdwood } 426ddee627cSLiam Girdwood 427ddee627cSLiam Girdwood if (codec_dai->driver->ops->prepare) { 428ddee627cSLiam Girdwood ret = codec_dai->driver->ops->prepare(substream, codec_dai); 429ddee627cSLiam Girdwood if (ret < 0) { 430ddee627cSLiam Girdwood printk(KERN_ERR "asoc: codec DAI prepare error\n"); 431ddee627cSLiam Girdwood goto out; 432ddee627cSLiam Girdwood } 433ddee627cSLiam Girdwood } 434ddee627cSLiam Girdwood 435ddee627cSLiam Girdwood if (cpu_dai->driver->ops->prepare) { 436ddee627cSLiam Girdwood ret = cpu_dai->driver->ops->prepare(substream, cpu_dai); 437ddee627cSLiam Girdwood if (ret < 0) { 438ddee627cSLiam Girdwood printk(KERN_ERR "asoc: cpu DAI prepare error\n"); 439ddee627cSLiam Girdwood goto out; 440ddee627cSLiam Girdwood } 441ddee627cSLiam Girdwood } 442ddee627cSLiam Girdwood 443ddee627cSLiam Girdwood /* cancel any delayed stream shutdown that is pending */ 444ddee627cSLiam Girdwood if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && 445ddee627cSLiam Girdwood codec_dai->pop_wait) { 446ddee627cSLiam Girdwood codec_dai->pop_wait = 0; 447ddee627cSLiam Girdwood cancel_delayed_work(&rtd->delayed_work); 448ddee627cSLiam Girdwood } 449ddee627cSLiam Girdwood 450ddee627cSLiam Girdwood if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 451ddee627cSLiam Girdwood snd_soc_dapm_stream_event(rtd, 452ddee627cSLiam Girdwood codec_dai->driver->playback.stream_name, 453ddee627cSLiam Girdwood SND_SOC_DAPM_STREAM_START); 454ddee627cSLiam Girdwood else 455ddee627cSLiam Girdwood snd_soc_dapm_stream_event(rtd, 456ddee627cSLiam Girdwood codec_dai->driver->capture.stream_name, 457ddee627cSLiam Girdwood SND_SOC_DAPM_STREAM_START); 458ddee627cSLiam Girdwood 459ddee627cSLiam Girdwood snd_soc_dai_digital_mute(codec_dai, 0); 460ddee627cSLiam Girdwood 461ddee627cSLiam Girdwood out: 462b8c0dab9SLiam Girdwood mutex_unlock(&rtd->pcm_mutex); 463ddee627cSLiam Girdwood return ret; 464ddee627cSLiam Girdwood } 465ddee627cSLiam Girdwood 466ddee627cSLiam Girdwood /* 467ddee627cSLiam Girdwood * Called by ALSA when the hardware params are set by application. This 468ddee627cSLiam Girdwood * function can also be called multiple times and can allocate buffers 469ddee627cSLiam Girdwood * (using snd_pcm_lib_* ). It's non-atomic. 470ddee627cSLiam Girdwood */ 471ddee627cSLiam Girdwood static int soc_pcm_hw_params(struct snd_pcm_substream *substream, 472ddee627cSLiam Girdwood struct snd_pcm_hw_params *params) 473ddee627cSLiam Girdwood { 474ddee627cSLiam Girdwood struct snd_soc_pcm_runtime *rtd = substream->private_data; 475ddee627cSLiam Girdwood struct snd_soc_platform *platform = rtd->platform; 476ddee627cSLiam Girdwood struct snd_soc_dai *cpu_dai = rtd->cpu_dai; 477ddee627cSLiam Girdwood struct snd_soc_dai *codec_dai = rtd->codec_dai; 478ddee627cSLiam Girdwood int ret = 0; 479ddee627cSLiam Girdwood 480b8c0dab9SLiam Girdwood mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); 481ddee627cSLiam Girdwood 482ddee627cSLiam Girdwood if (rtd->dai_link->ops && rtd->dai_link->ops->hw_params) { 483ddee627cSLiam Girdwood ret = rtd->dai_link->ops->hw_params(substream, params); 484ddee627cSLiam Girdwood if (ret < 0) { 485ddee627cSLiam Girdwood printk(KERN_ERR "asoc: machine hw_params failed\n"); 486ddee627cSLiam Girdwood goto out; 487ddee627cSLiam Girdwood } 488ddee627cSLiam Girdwood } 489ddee627cSLiam Girdwood 490ddee627cSLiam Girdwood if (codec_dai->driver->ops->hw_params) { 491ddee627cSLiam Girdwood ret = codec_dai->driver->ops->hw_params(substream, params, codec_dai); 492ddee627cSLiam Girdwood if (ret < 0) { 493ddee627cSLiam Girdwood printk(KERN_ERR "asoc: can't set codec %s hw params\n", 494ddee627cSLiam Girdwood codec_dai->name); 495ddee627cSLiam Girdwood goto codec_err; 496ddee627cSLiam Girdwood } 497ddee627cSLiam Girdwood } 498ddee627cSLiam Girdwood 499ddee627cSLiam Girdwood if (cpu_dai->driver->ops->hw_params) { 500ddee627cSLiam Girdwood ret = cpu_dai->driver->ops->hw_params(substream, params, cpu_dai); 501ddee627cSLiam Girdwood if (ret < 0) { 502ddee627cSLiam Girdwood printk(KERN_ERR "asoc: interface %s hw params failed\n", 503ddee627cSLiam Girdwood cpu_dai->name); 504ddee627cSLiam Girdwood goto interface_err; 505ddee627cSLiam Girdwood } 506ddee627cSLiam Girdwood } 507ddee627cSLiam Girdwood 508ddee627cSLiam Girdwood if (platform->driver->ops && platform->driver->ops->hw_params) { 509ddee627cSLiam Girdwood ret = platform->driver->ops->hw_params(substream, params); 510ddee627cSLiam Girdwood if (ret < 0) { 511ddee627cSLiam Girdwood printk(KERN_ERR "asoc: platform %s hw params failed\n", 512ddee627cSLiam Girdwood platform->name); 513ddee627cSLiam Girdwood goto platform_err; 514ddee627cSLiam Girdwood } 515ddee627cSLiam Girdwood } 516ddee627cSLiam Girdwood 51717841020SDong Aisheng /* store the rate for each DAIs */ 51817841020SDong Aisheng cpu_dai->rate = params_rate(params); 51917841020SDong Aisheng codec_dai->rate = params_rate(params); 520ddee627cSLiam Girdwood 521ddee627cSLiam Girdwood out: 522b8c0dab9SLiam Girdwood mutex_unlock(&rtd->pcm_mutex); 523ddee627cSLiam Girdwood return ret; 524ddee627cSLiam Girdwood 525ddee627cSLiam Girdwood platform_err: 526ddee627cSLiam Girdwood if (cpu_dai->driver->ops->hw_free) 527ddee627cSLiam Girdwood cpu_dai->driver->ops->hw_free(substream, cpu_dai); 528ddee627cSLiam Girdwood 529ddee627cSLiam Girdwood interface_err: 530ddee627cSLiam Girdwood if (codec_dai->driver->ops->hw_free) 531ddee627cSLiam Girdwood codec_dai->driver->ops->hw_free(substream, codec_dai); 532ddee627cSLiam Girdwood 533ddee627cSLiam Girdwood codec_err: 534ddee627cSLiam Girdwood if (rtd->dai_link->ops && rtd->dai_link->ops->hw_free) 535ddee627cSLiam Girdwood rtd->dai_link->ops->hw_free(substream); 536ddee627cSLiam Girdwood 537b8c0dab9SLiam Girdwood mutex_unlock(&rtd->pcm_mutex); 538ddee627cSLiam Girdwood return ret; 539ddee627cSLiam Girdwood } 540ddee627cSLiam Girdwood 541ddee627cSLiam Girdwood /* 542ddee627cSLiam Girdwood * Frees resources allocated by hw_params, can be called multiple times 543ddee627cSLiam Girdwood */ 544ddee627cSLiam Girdwood static int soc_pcm_hw_free(struct snd_pcm_substream *substream) 545ddee627cSLiam Girdwood { 546ddee627cSLiam Girdwood struct snd_soc_pcm_runtime *rtd = substream->private_data; 547ddee627cSLiam Girdwood struct snd_soc_platform *platform = rtd->platform; 548ddee627cSLiam Girdwood struct snd_soc_dai *cpu_dai = rtd->cpu_dai; 549ddee627cSLiam Girdwood struct snd_soc_dai *codec_dai = rtd->codec_dai; 550ddee627cSLiam Girdwood struct snd_soc_codec *codec = rtd->codec; 551ddee627cSLiam Girdwood 552b8c0dab9SLiam Girdwood mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); 553ddee627cSLiam Girdwood 554ddee627cSLiam Girdwood /* apply codec digital mute */ 555ddee627cSLiam Girdwood if (!codec->active) 556ddee627cSLiam Girdwood snd_soc_dai_digital_mute(codec_dai, 1); 557ddee627cSLiam Girdwood 558ddee627cSLiam Girdwood /* free any machine hw params */ 559ddee627cSLiam Girdwood if (rtd->dai_link->ops && rtd->dai_link->ops->hw_free) 560ddee627cSLiam Girdwood rtd->dai_link->ops->hw_free(substream); 561ddee627cSLiam Girdwood 562ddee627cSLiam Girdwood /* free any DMA resources */ 563ddee627cSLiam Girdwood if (platform->driver->ops && platform->driver->ops->hw_free) 564ddee627cSLiam Girdwood platform->driver->ops->hw_free(substream); 565ddee627cSLiam Girdwood 566ddee627cSLiam Girdwood /* now free hw params for the DAIs */ 567ddee627cSLiam Girdwood if (codec_dai->driver->ops->hw_free) 568ddee627cSLiam Girdwood codec_dai->driver->ops->hw_free(substream, codec_dai); 569ddee627cSLiam Girdwood 570ddee627cSLiam Girdwood if (cpu_dai->driver->ops->hw_free) 571ddee627cSLiam Girdwood cpu_dai->driver->ops->hw_free(substream, cpu_dai); 572ddee627cSLiam Girdwood 573b8c0dab9SLiam Girdwood mutex_unlock(&rtd->pcm_mutex); 574ddee627cSLiam Girdwood return 0; 575ddee627cSLiam Girdwood } 576ddee627cSLiam Girdwood 577ddee627cSLiam Girdwood static int soc_pcm_trigger(struct snd_pcm_substream *substream, int cmd) 578ddee627cSLiam Girdwood { 579ddee627cSLiam Girdwood struct snd_soc_pcm_runtime *rtd = substream->private_data; 580ddee627cSLiam Girdwood struct snd_soc_platform *platform = rtd->platform; 581ddee627cSLiam Girdwood struct snd_soc_dai *cpu_dai = rtd->cpu_dai; 582ddee627cSLiam Girdwood struct snd_soc_dai *codec_dai = rtd->codec_dai; 583ddee627cSLiam Girdwood int ret; 584ddee627cSLiam Girdwood 585ddee627cSLiam Girdwood if (codec_dai->driver->ops->trigger) { 586ddee627cSLiam Girdwood ret = codec_dai->driver->ops->trigger(substream, cmd, codec_dai); 587ddee627cSLiam Girdwood if (ret < 0) 588ddee627cSLiam Girdwood return ret; 589ddee627cSLiam Girdwood } 590ddee627cSLiam Girdwood 591ddee627cSLiam Girdwood if (platform->driver->ops && platform->driver->ops->trigger) { 592ddee627cSLiam Girdwood ret = platform->driver->ops->trigger(substream, cmd); 593ddee627cSLiam Girdwood if (ret < 0) 594ddee627cSLiam Girdwood return ret; 595ddee627cSLiam Girdwood } 596ddee627cSLiam Girdwood 597ddee627cSLiam Girdwood if (cpu_dai->driver->ops->trigger) { 598ddee627cSLiam Girdwood ret = cpu_dai->driver->ops->trigger(substream, cmd, cpu_dai); 599ddee627cSLiam Girdwood if (ret < 0) 600ddee627cSLiam Girdwood return ret; 601ddee627cSLiam Girdwood } 602ddee627cSLiam Girdwood return 0; 603ddee627cSLiam Girdwood } 604ddee627cSLiam Girdwood 605ddee627cSLiam Girdwood /* 606ddee627cSLiam Girdwood * soc level wrapper for pointer callback 607ddee627cSLiam Girdwood * If cpu_dai, codec_dai, platform driver has the delay callback, than 608ddee627cSLiam Girdwood * the runtime->delay will be updated accordingly. 609ddee627cSLiam Girdwood */ 610ddee627cSLiam Girdwood static snd_pcm_uframes_t soc_pcm_pointer(struct snd_pcm_substream *substream) 611ddee627cSLiam Girdwood { 612ddee627cSLiam Girdwood struct snd_soc_pcm_runtime *rtd = substream->private_data; 613ddee627cSLiam Girdwood struct snd_soc_platform *platform = rtd->platform; 614ddee627cSLiam Girdwood struct snd_soc_dai *cpu_dai = rtd->cpu_dai; 615ddee627cSLiam Girdwood struct snd_soc_dai *codec_dai = rtd->codec_dai; 616ddee627cSLiam Girdwood struct snd_pcm_runtime *runtime = substream->runtime; 617ddee627cSLiam Girdwood snd_pcm_uframes_t offset = 0; 618ddee627cSLiam Girdwood snd_pcm_sframes_t delay = 0; 619ddee627cSLiam Girdwood 620ddee627cSLiam Girdwood if (platform->driver->ops && platform->driver->ops->pointer) 621ddee627cSLiam Girdwood offset = platform->driver->ops->pointer(substream); 622ddee627cSLiam Girdwood 623ddee627cSLiam Girdwood if (cpu_dai->driver->ops->delay) 624ddee627cSLiam Girdwood delay += cpu_dai->driver->ops->delay(substream, cpu_dai); 625ddee627cSLiam Girdwood 626ddee627cSLiam Girdwood if (codec_dai->driver->ops->delay) 627ddee627cSLiam Girdwood delay += codec_dai->driver->ops->delay(substream, codec_dai); 628ddee627cSLiam Girdwood 629ddee627cSLiam Girdwood if (platform->driver->delay) 630ddee627cSLiam Girdwood delay += platform->driver->delay(substream, codec_dai); 631ddee627cSLiam Girdwood 632ddee627cSLiam Girdwood runtime->delay = delay; 633ddee627cSLiam Girdwood 634ddee627cSLiam Girdwood return offset; 635ddee627cSLiam Girdwood } 636ddee627cSLiam Girdwood 637ddee627cSLiam Girdwood /* create a new pcm */ 638ddee627cSLiam Girdwood int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num) 639ddee627cSLiam Girdwood { 640ddee627cSLiam Girdwood struct snd_soc_codec *codec = rtd->codec; 641ddee627cSLiam Girdwood struct snd_soc_platform *platform = rtd->platform; 642ddee627cSLiam Girdwood struct snd_soc_dai *codec_dai = rtd->codec_dai; 643ddee627cSLiam Girdwood struct snd_soc_dai *cpu_dai = rtd->cpu_dai; 644a500231dSSangsu Park struct snd_pcm_ops *soc_pcm_ops = &rtd->ops; 645ddee627cSLiam Girdwood struct snd_pcm *pcm; 646ddee627cSLiam Girdwood char new_name[64]; 647ddee627cSLiam Girdwood int ret = 0, playback = 0, capture = 0; 648ddee627cSLiam Girdwood 649a500231dSSangsu Park soc_pcm_ops->open = soc_pcm_open; 650a500231dSSangsu Park soc_pcm_ops->close = soc_pcm_close; 651a500231dSSangsu Park soc_pcm_ops->hw_params = soc_pcm_hw_params; 652a500231dSSangsu Park soc_pcm_ops->hw_free = soc_pcm_hw_free; 653a500231dSSangsu Park soc_pcm_ops->prepare = soc_pcm_prepare; 654a500231dSSangsu Park soc_pcm_ops->trigger = soc_pcm_trigger; 655a500231dSSangsu Park soc_pcm_ops->pointer = soc_pcm_pointer; 656a500231dSSangsu Park 657ddee627cSLiam Girdwood /* check client and interface hw capabilities */ 658ddee627cSLiam Girdwood snprintf(new_name, sizeof(new_name), "%s %s-%d", 659ddee627cSLiam Girdwood rtd->dai_link->stream_name, codec_dai->name, num); 660ddee627cSLiam Girdwood 661ddee627cSLiam Girdwood if (codec_dai->driver->playback.channels_min) 662ddee627cSLiam Girdwood playback = 1; 663ddee627cSLiam Girdwood if (codec_dai->driver->capture.channels_min) 664ddee627cSLiam Girdwood capture = 1; 665ddee627cSLiam Girdwood 666ddee627cSLiam Girdwood dev_dbg(rtd->card->dev, "registered pcm #%d %s\n",num,new_name); 667ddee627cSLiam Girdwood ret = snd_pcm_new(rtd->card->snd_card, new_name, 668ddee627cSLiam Girdwood num, playback, capture, &pcm); 669ddee627cSLiam Girdwood if (ret < 0) { 670ddee627cSLiam Girdwood printk(KERN_ERR "asoc: can't create pcm for codec %s\n", codec->name); 671ddee627cSLiam Girdwood return ret; 672ddee627cSLiam Girdwood } 673ddee627cSLiam Girdwood 674ddee627cSLiam Girdwood /* DAPM dai link stream work */ 675ddee627cSLiam Girdwood INIT_DELAYED_WORK(&rtd->delayed_work, close_delayed_work); 676ddee627cSLiam Girdwood 677ddee627cSLiam Girdwood rtd->pcm = pcm; 678ddee627cSLiam Girdwood pcm->private_data = rtd; 679ddee627cSLiam Girdwood if (platform->driver->ops) { 680a500231dSSangsu Park soc_pcm_ops->mmap = platform->driver->ops->mmap; 681a500231dSSangsu Park soc_pcm_ops->pointer = platform->driver->ops->pointer; 682a500231dSSangsu Park soc_pcm_ops->ioctl = platform->driver->ops->ioctl; 683a500231dSSangsu Park soc_pcm_ops->copy = platform->driver->ops->copy; 684a500231dSSangsu Park soc_pcm_ops->silence = platform->driver->ops->silence; 685a500231dSSangsu Park soc_pcm_ops->ack = platform->driver->ops->ack; 686a500231dSSangsu Park soc_pcm_ops->page = platform->driver->ops->page; 687ddee627cSLiam Girdwood } 688ddee627cSLiam Girdwood 689ddee627cSLiam Girdwood if (playback) 690a500231dSSangsu Park snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, soc_pcm_ops); 691ddee627cSLiam Girdwood 692ddee627cSLiam Girdwood if (capture) 693a500231dSSangsu Park snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, soc_pcm_ops); 694ddee627cSLiam Girdwood 695ddee627cSLiam Girdwood if (platform->driver->pcm_new) { 696ddee627cSLiam Girdwood ret = platform->driver->pcm_new(rtd); 697ddee627cSLiam Girdwood if (ret < 0) { 698ddee627cSLiam Girdwood pr_err("asoc: platform pcm constructor failed\n"); 699ddee627cSLiam Girdwood return ret; 700ddee627cSLiam Girdwood } 701ddee627cSLiam Girdwood } 702ddee627cSLiam Girdwood 703ddee627cSLiam Girdwood pcm->private_free = platform->driver->pcm_free; 704ddee627cSLiam Girdwood printk(KERN_INFO "asoc: %s <-> %s mapping ok\n", codec_dai->name, 705ddee627cSLiam Girdwood cpu_dai->name); 706ddee627cSLiam Girdwood return ret; 707ddee627cSLiam Girdwood } 708