1 /* 2 * soc-compress.c -- ALSA SoC Compress 3 * 4 * Copyright (C) 2012 Intel Corp. 5 * 6 * Authors: Namarta Kohli <namartax.kohli@intel.com> 7 * Ramesh Babu K V <ramesh.babu@linux.intel.com> 8 * Vinod Koul <vinod.koul@linux.intel.com> 9 * 10 * This program is free software; you can redistribute it and/or modify it 11 * under the terms of the GNU General Public License as published by the 12 * Free Software Foundation; either version 2 of the License, or (at your 13 * option) any later version. 14 * 15 */ 16 17 #include <linux/kernel.h> 18 #include <linux/init.h> 19 #include <linux/delay.h> 20 #include <linux/slab.h> 21 #include <linux/workqueue.h> 22 #include <sound/core.h> 23 #include <sound/compress_params.h> 24 #include <sound/compress_driver.h> 25 #include <sound/soc.h> 26 #include <sound/initval.h> 27 28 static int soc_compr_open(struct snd_compr_stream *cstream) 29 { 30 struct snd_soc_pcm_runtime *rtd = cstream->private_data; 31 struct snd_soc_platform *platform = rtd->platform; 32 struct snd_soc_dai *cpu_dai = rtd->cpu_dai; 33 struct snd_soc_dai *codec_dai = rtd->codec_dai; 34 int ret = 0; 35 36 if (platform->driver->compr_ops && platform->driver->compr_ops->open) { 37 ret = platform->driver->compr_ops->open(cstream); 38 if (ret < 0) { 39 pr_err("compress asoc: can't open platform %s\n", platform->name); 40 goto out; 41 } 42 } 43 44 if (rtd->dai_link->compr_ops && rtd->dai_link->compr_ops->startup) { 45 ret = rtd->dai_link->compr_ops->startup(cstream); 46 if (ret < 0) { 47 pr_err("compress asoc: %s startup failed\n", rtd->dai_link->name); 48 goto machine_err; 49 } 50 } 51 52 if (cstream->direction == SND_COMPRESS_PLAYBACK) { 53 cpu_dai->playback_active++; 54 codec_dai->playback_active++; 55 } else { 56 cpu_dai->capture_active++; 57 codec_dai->capture_active++; 58 } 59 60 cpu_dai->active++; 61 codec_dai->active++; 62 rtd->codec->active++; 63 64 return 0; 65 66 machine_err: 67 if (platform->driver->compr_ops && platform->driver->compr_ops->free) 68 platform->driver->compr_ops->free(cstream); 69 out: 70 return ret; 71 } 72 73 static int soc_compr_free(struct snd_compr_stream *cstream) 74 { 75 struct snd_soc_pcm_runtime *rtd = cstream->private_data; 76 struct snd_soc_platform *platform = rtd->platform; 77 struct snd_soc_dai *cpu_dai = rtd->cpu_dai; 78 struct snd_soc_dai *codec_dai = rtd->codec_dai; 79 struct snd_soc_codec *codec = rtd->codec; 80 81 if (cstream->direction == SND_COMPRESS_PLAYBACK) { 82 cpu_dai->playback_active--; 83 codec_dai->playback_active--; 84 } else { 85 cpu_dai->capture_active--; 86 codec_dai->capture_active--; 87 } 88 89 snd_soc_dai_digital_mute(codec_dai, 1); 90 91 cpu_dai->active--; 92 codec_dai->active--; 93 codec->active--; 94 95 if (!cpu_dai->active) 96 cpu_dai->rate = 0; 97 98 if (!codec_dai->active) 99 codec_dai->rate = 0; 100 101 102 if (rtd->dai_link->compr_ops && rtd->dai_link->compr_ops->shutdown) 103 rtd->dai_link->compr_ops->shutdown(cstream); 104 105 if (platform->driver->compr_ops && platform->driver->compr_ops->free) 106 platform->driver->compr_ops->free(cstream); 107 cpu_dai->runtime = NULL; 108 109 if (cstream->direction == SND_COMPRESS_PLAYBACK) { 110 if (!rtd->pmdown_time || codec->ignore_pmdown_time || 111 rtd->dai_link->ignore_pmdown_time) { 112 snd_soc_dapm_stream_event(rtd, 113 SNDRV_PCM_STREAM_PLAYBACK, 114 SND_SOC_DAPM_STREAM_STOP); 115 } else 116 rtd->pop_wait = 1; 117 schedule_delayed_work(&rtd->delayed_work, 118 msecs_to_jiffies(rtd->pmdown_time)); 119 } else { 120 /* capture streams can be powered down now */ 121 snd_soc_dapm_stream_event(rtd, 122 SNDRV_PCM_STREAM_CAPTURE, 123 SND_SOC_DAPM_STREAM_STOP); 124 } 125 126 return 0; 127 } 128 129 static int soc_compr_trigger(struct snd_compr_stream *cstream, int cmd) 130 { 131 132 struct snd_soc_pcm_runtime *rtd = cstream->private_data; 133 struct snd_soc_platform *platform = rtd->platform; 134 struct snd_soc_dai *codec_dai = rtd->codec_dai; 135 int ret = 0; 136 137 if (platform->driver->compr_ops && platform->driver->compr_ops->trigger) { 138 ret = platform->driver->compr_ops->trigger(cstream, cmd); 139 if (ret < 0) 140 return ret; 141 } 142 143 if (cmd == SNDRV_PCM_TRIGGER_START) 144 snd_soc_dai_digital_mute(codec_dai, 0); 145 else if (cmd == SNDRV_PCM_TRIGGER_STOP) 146 snd_soc_dai_digital_mute(codec_dai, 1); 147 148 return ret; 149 } 150 151 static int soc_compr_set_params(struct snd_compr_stream *cstream, 152 struct snd_compr_params *params) 153 { 154 struct snd_soc_pcm_runtime *rtd = cstream->private_data; 155 struct snd_soc_platform *platform = rtd->platform; 156 int ret = 0; 157 158 /* first we call set_params for the platform driver 159 * this should configure the soc side 160 * if the machine has compressed ops then we call that as well 161 * expectation is that platform and machine will configure everything 162 * for this compress path, like configuring pcm port for codec 163 */ 164 if (platform->driver->compr_ops && platform->driver->compr_ops->set_params) { 165 ret = platform->driver->compr_ops->set_params(cstream, params); 166 if (ret < 0) 167 return ret; 168 } 169 170 if (rtd->dai_link->compr_ops && rtd->dai_link->compr_ops->set_params) { 171 ret = rtd->dai_link->compr_ops->set_params(cstream); 172 if (ret < 0) 173 return ret; 174 } 175 176 snd_soc_dapm_stream_event(rtd, SNDRV_PCM_STREAM_PLAYBACK, 177 SND_SOC_DAPM_STREAM_START); 178 179 return ret; 180 } 181 182 static int soc_compr_get_params(struct snd_compr_stream *cstream, 183 struct snd_codec *params) 184 { 185 struct snd_soc_pcm_runtime *rtd = cstream->private_data; 186 struct snd_soc_platform *platform = rtd->platform; 187 int ret = 0; 188 189 if (platform->driver->compr_ops && platform->driver->compr_ops->get_params) 190 ret = platform->driver->compr_ops->get_params(cstream, params); 191 192 return ret; 193 } 194 195 static int soc_compr_get_caps(struct snd_compr_stream *cstream, 196 struct snd_compr_caps *caps) 197 { 198 struct snd_soc_pcm_runtime *rtd = cstream->private_data; 199 struct snd_soc_platform *platform = rtd->platform; 200 int ret = 0; 201 202 if (platform->driver->compr_ops && platform->driver->compr_ops->get_caps) 203 ret = platform->driver->compr_ops->get_caps(cstream, caps); 204 205 return ret; 206 } 207 208 static int soc_compr_get_codec_caps(struct snd_compr_stream *cstream, 209 struct snd_compr_codec_caps *codec) 210 { 211 struct snd_soc_pcm_runtime *rtd = cstream->private_data; 212 struct snd_soc_platform *platform = rtd->platform; 213 int ret = 0; 214 215 if (platform->driver->compr_ops && platform->driver->compr_ops->get_codec_caps) 216 ret = platform->driver->compr_ops->get_codec_caps(cstream, codec); 217 218 return ret; 219 } 220 221 static int soc_compr_ack(struct snd_compr_stream *cstream, size_t bytes) 222 { 223 struct snd_soc_pcm_runtime *rtd = cstream->private_data; 224 struct snd_soc_platform *platform = rtd->platform; 225 int ret = 0; 226 227 if (platform->driver->compr_ops && platform->driver->compr_ops->ack) 228 ret = platform->driver->compr_ops->ack(cstream, bytes); 229 230 return ret; 231 } 232 233 static int soc_compr_pointer(struct snd_compr_stream *cstream, 234 struct snd_compr_tstamp *tstamp) 235 { 236 struct snd_soc_pcm_runtime *rtd = cstream->private_data; 237 struct snd_soc_platform *platform = rtd->platform; 238 239 if (platform->driver->compr_ops && platform->driver->compr_ops->pointer) 240 platform->driver->compr_ops->pointer(cstream, tstamp); 241 242 return 0; 243 } 244 245 /* ASoC Compress operations */ 246 static struct snd_compr_ops soc_compr_ops = { 247 .open = soc_compr_open, 248 .free = soc_compr_free, 249 .set_params = soc_compr_set_params, 250 .get_params = soc_compr_get_params, 251 .trigger = soc_compr_trigger, 252 .pointer = soc_compr_pointer, 253 .ack = soc_compr_ack, 254 .get_caps = soc_compr_get_caps, 255 .get_codec_caps = soc_compr_get_codec_caps 256 }; 257 258 /* create a new compress */ 259 int soc_new_compress(struct snd_soc_pcm_runtime *rtd, int num) 260 { 261 struct snd_soc_codec *codec = rtd->codec; 262 struct snd_soc_dai *codec_dai = rtd->codec_dai; 263 struct snd_soc_dai *cpu_dai = rtd->cpu_dai; 264 struct snd_compr *compr; 265 char new_name[64]; 266 int ret = 0, direction = 0; 267 268 /* check client and interface hw capabilities */ 269 snprintf(new_name, sizeof(new_name), "%s %s-%d", 270 rtd->dai_link->stream_name, codec_dai->name, num); 271 direction = SND_COMPRESS_PLAYBACK; 272 compr = kzalloc(sizeof(*compr), GFP_KERNEL); 273 if (compr == NULL) { 274 snd_printk(KERN_ERR "Cannot allocate compr\n"); 275 return -ENOMEM; 276 } 277 278 compr->ops = &soc_compr_ops; 279 mutex_init(&compr->lock); 280 ret = snd_compress_new(rtd->card->snd_card, num, direction, compr); 281 if (ret < 0) { 282 pr_err("compress asoc: can't create compress for codec %s\n", 283 codec->name); 284 kfree(compr); 285 return ret; 286 } 287 288 rtd->compr = compr; 289 compr->private_data = rtd; 290 291 printk(KERN_INFO "compress asoc: %s <-> %s mapping ok\n", codec_dai->name, 292 cpu_dai->name); 293 return ret; 294 } 295