xref: /linux/sound/soc/soc-compress.c (revision b889fcf63cb62e7fdb7816565e28f44dbe4a76a5)
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