blob: 5215df2940b335eeeb8ed3c273a3c54714883c48 [file] [log] [blame]
Vinod Koulb21c60a2011-12-23 10:36:39 +05301/*
2 * compress_core.c - compress offload core
3 *
4 * Copyright (C) 2011 Intel Corporation
5 * Authors: Vinod Koul <vinod.koul@linux.intel.com>
6 * Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
7 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; version 2 of the License.
12 *
13 * This program is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License along
19 * with this program; if not, write to the Free Software Foundation, Inc.,
20 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
21 *
22 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
23 *
24 */
25#define FORMAT(fmt) "%s: %d: " fmt, __func__, __LINE__
26#define pr_fmt(fmt) KBUILD_MODNAME ": " FORMAT(fmt)
27
28#include <linux/file.h>
29#include <linux/fs.h>
30#include <linux/list.h>
Charles Keepaxf0283b52013-04-18 11:03:46 +010031#include <linux/math64.h>
Vinod Koulb21c60a2011-12-23 10:36:39 +053032#include <linux/mm.h>
33#include <linux/mutex.h>
34#include <linux/poll.h>
35#include <linux/slab.h>
36#include <linux/sched.h>
Charles Keepaxf0283b52013-04-18 11:03:46 +010037#include <linux/types.h>
Vinod Koulb21c60a2011-12-23 10:36:39 +053038#include <linux/uio.h>
39#include <linux/uaccess.h>
40#include <linux/module.h>
Ravindra Lokhandec1036882015-12-07 12:08:31 +053041#include <linux/compat.h>
Vinod Koulb21c60a2011-12-23 10:36:39 +053042#include <sound/core.h>
43#include <sound/initval.h>
Richard Fitzgerald31742722015-11-25 13:00:23 +000044#include <sound/info.h>
Vinod Koulb21c60a2011-12-23 10:36:39 +053045#include <sound/compress_params.h>
46#include <sound/compress_offload.h>
47#include <sound/compress_driver.h>
48
Takashi Iwai462b3f12016-01-25 13:59:21 +010049/* struct snd_compr_codec_caps overflows the ioctl bit size for some
50 * architectures, so we need to disable the relevant ioctls.
51 */
52#if _IOC_SIZEBITS < 14
53#define COMPR_CODEC_CAPS_OVERFLOW
54#endif
55
Vinod Koulb21c60a2011-12-23 10:36:39 +053056/* TODO:
57 * - add substream support for multiple devices in case of
58 * SND_DYNAMIC_MINORS is not used
59 * - Multiple node representation
60 * driver should be able to register multiple nodes
61 */
62
63static DEFINE_MUTEX(device_mutex);
64
65struct snd_compr_file {
66 unsigned long caps;
67 struct snd_compr_stream stream;
68};
69
70/*
71 * a note on stream states used:
Vinod Koul41eb94f2016-03-04 20:25:30 +053072 * we use following states in the compressed core
Vinod Koulb21c60a2011-12-23 10:36:39 +053073 * SNDRV_PCM_STATE_OPEN: When stream has been opened.
74 * SNDRV_PCM_STATE_SETUP: When stream has been initialized. This is done by
Vinod Koul41eb94f2016-03-04 20:25:30 +053075 * calling SNDRV_COMPRESS_SET_PARAMS. Running streams will come to this
Vinod Koulb21c60a2011-12-23 10:36:39 +053076 * state at stop by calling SNDRV_COMPRESS_STOP, or at end of drain.
Vinod Koul862bca52016-03-04 20:25:29 +053077 * SNDRV_PCM_STATE_PREPARED: When a stream has been written to (for
78 * playback only). User after setting up stream writes the data buffer
79 * before starting the stream.
Vinod Koulb21c60a2011-12-23 10:36:39 +053080 * SNDRV_PCM_STATE_RUNNING: When stream has been started and is
81 * decoding/encoding and rendering/capturing data.
82 * SNDRV_PCM_STATE_DRAINING: When stream is draining current data. This is done
83 * by calling SNDRV_COMPRESS_DRAIN.
84 * SNDRV_PCM_STATE_PAUSED: When stream is paused. This is done by calling
85 * SNDRV_COMPRESS_PAUSE. It can be stopped or resumed by calling
86 * SNDRV_COMPRESS_STOP or SNDRV_COMPRESS_RESUME respectively.
87 */
88static int snd_compr_open(struct inode *inode, struct file *f)
89{
90 struct snd_compr *compr;
91 struct snd_compr_file *data;
92 struct snd_compr_runtime *runtime;
93 enum snd_compr_direction dirn;
94 int maj = imajor(inode);
95 int ret;
96
Dan Carpenter81cb3242012-09-11 14:12:43 +030097 if ((f->f_flags & O_ACCMODE) == O_WRONLY)
Vinod Koulb21c60a2011-12-23 10:36:39 +053098 dirn = SND_COMPRESS_PLAYBACK;
Dan Carpenter81cb3242012-09-11 14:12:43 +030099 else if ((f->f_flags & O_ACCMODE) == O_RDONLY)
Vinod Koulb21c60a2011-12-23 10:36:39 +0530100 dirn = SND_COMPRESS_CAPTURE;
Dan Carpenter81cb3242012-09-11 14:12:43 +0300101 else
Vinod Koulb21c60a2011-12-23 10:36:39 +0530102 return -EINVAL;
Vinod Koulb21c60a2011-12-23 10:36:39 +0530103
104 if (maj == snd_major)
105 compr = snd_lookup_minor_data(iminor(inode),
106 SNDRV_DEVICE_TYPE_COMPRESS);
107 else
108 return -EBADFD;
109
110 if (compr == NULL) {
111 pr_err("no device data!!!\n");
112 return -ENODEV;
113 }
114
115 if (dirn != compr->direction) {
116 pr_err("this device doesn't support this direction\n");
Takashi Iwaia0830db2012-10-16 13:05:59 +0200117 snd_card_unref(compr->card);
Vinod Koulb21c60a2011-12-23 10:36:39 +0530118 return -EINVAL;
119 }
120
121 data = kzalloc(sizeof(*data), GFP_KERNEL);
Takashi Iwaia0830db2012-10-16 13:05:59 +0200122 if (!data) {
123 snd_card_unref(compr->card);
Vinod Koulb21c60a2011-12-23 10:36:39 +0530124 return -ENOMEM;
Takashi Iwaia0830db2012-10-16 13:05:59 +0200125 }
Vinod Koulb21c60a2011-12-23 10:36:39 +0530126 data->stream.ops = compr->ops;
127 data->stream.direction = dirn;
128 data->stream.private_data = compr->private_data;
129 data->stream.device = compr;
130 runtime = kzalloc(sizeof(*runtime), GFP_KERNEL);
131 if (!runtime) {
132 kfree(data);
Takashi Iwaia0830db2012-10-16 13:05:59 +0200133 snd_card_unref(compr->card);
Vinod Koulb21c60a2011-12-23 10:36:39 +0530134 return -ENOMEM;
135 }
136 runtime->state = SNDRV_PCM_STATE_OPEN;
137 init_waitqueue_head(&runtime->sleep);
138 data->stream.runtime = runtime;
139 f->private_data = (void *)data;
140 mutex_lock(&compr->lock);
141 ret = compr->ops->open(&data->stream);
142 mutex_unlock(&compr->lock);
143 if (ret) {
144 kfree(runtime);
145 kfree(data);
146 }
Takashi Iwaia0830db2012-10-16 13:05:59 +0200147 snd_card_unref(compr->card);
Charles Keepax749d3222014-03-19 12:59:39 +0000148 return ret;
Vinod Koulb21c60a2011-12-23 10:36:39 +0530149}
150
151static int snd_compr_free(struct inode *inode, struct file *f)
152{
153 struct snd_compr_file *data = f->private_data;
Liam Girdwoodb26d19e2013-09-13 17:43:16 +0100154 struct snd_compr_runtime *runtime = data->stream.runtime;
155
156 switch (runtime->state) {
157 case SNDRV_PCM_STATE_RUNNING:
158 case SNDRV_PCM_STATE_DRAINING:
159 case SNDRV_PCM_STATE_PAUSED:
160 data->stream.ops->trigger(&data->stream, SNDRV_PCM_TRIGGER_STOP);
161 break;
162 default:
163 break;
164 }
165
Vinod Koulb21c60a2011-12-23 10:36:39 +0530166 data->stream.ops->free(&data->stream);
167 kfree(data->stream.runtime->buffer);
168 kfree(data->stream.runtime);
169 kfree(data);
170 return 0;
171}
172
Richard Fitzgerald17ac8e52013-02-11 13:44:53 +0000173static int snd_compr_update_tstamp(struct snd_compr_stream *stream,
Vinod Koulb21c60a2011-12-23 10:36:39 +0530174 struct snd_compr_tstamp *tstamp)
175{
176 if (!stream->ops->pointer)
Richard Fitzgerald17ac8e52013-02-11 13:44:53 +0000177 return -ENOTSUPP;
Vinod Koulb21c60a2011-12-23 10:36:39 +0530178 stream->ops->pointer(stream, tstamp);
179 pr_debug("dsp consumed till %d total %d bytes\n",
180 tstamp->byte_offset, tstamp->copied_total);
Charles Keepax5b1f79f2013-04-18 11:01:03 +0100181 if (stream->direction == SND_COMPRESS_PLAYBACK)
182 stream->runtime->total_bytes_transferred = tstamp->copied_total;
183 else
184 stream->runtime->total_bytes_available = tstamp->copied_total;
Richard Fitzgerald17ac8e52013-02-11 13:44:53 +0000185 return 0;
Vinod Koulb21c60a2011-12-23 10:36:39 +0530186}
187
188static size_t snd_compr_calc_avail(struct snd_compr_stream *stream,
189 struct snd_compr_avail *avail)
190{
Richard Fitzgerald17ac8e52013-02-11 13:44:53 +0000191 memset(avail, 0, sizeof(*avail));
Vinod Koulb21c60a2011-12-23 10:36:39 +0530192 snd_compr_update_tstamp(stream, &avail->tstamp);
Richard Fitzgerald17ac8e52013-02-11 13:44:53 +0000193 /* Still need to return avail even if tstamp can't be filled in */
Vinod Koulb21c60a2011-12-23 10:36:39 +0530194
Vinod Koulb21c60a2011-12-23 10:36:39 +0530195 if (stream->runtime->total_bytes_available == 0 &&
Charles Keepax5b1f79f2013-04-18 11:01:03 +0100196 stream->runtime->state == SNDRV_PCM_STATE_SETUP &&
197 stream->direction == SND_COMPRESS_PLAYBACK) {
Vinod Koulb21c60a2011-12-23 10:36:39 +0530198 pr_debug("detected init and someone forgot to do a write\n");
199 return stream->runtime->buffer_size;
200 }
201 pr_debug("app wrote %lld, DSP consumed %lld\n",
202 stream->runtime->total_bytes_available,
203 stream->runtime->total_bytes_transferred);
204 if (stream->runtime->total_bytes_available ==
205 stream->runtime->total_bytes_transferred) {
Charles Keepax5b1f79f2013-04-18 11:01:03 +0100206 if (stream->direction == SND_COMPRESS_PLAYBACK) {
207 pr_debug("both pointers are same, returning full avail\n");
208 return stream->runtime->buffer_size;
209 } else {
210 pr_debug("both pointers are same, returning no avail\n");
211 return 0;
212 }
Vinod Koulb21c60a2011-12-23 10:36:39 +0530213 }
214
Charles Keepax5b1f79f2013-04-18 11:01:03 +0100215 avail->avail = stream->runtime->total_bytes_available -
216 stream->runtime->total_bytes_transferred;
217 if (stream->direction == SND_COMPRESS_PLAYBACK)
218 avail->avail = stream->runtime->buffer_size - avail->avail;
219
Charles Keepax4c28e322013-04-18 10:59:23 +0100220 pr_debug("ret avail as %lld\n", avail->avail);
221 return avail->avail;
Vinod Koulb21c60a2011-12-23 10:36:39 +0530222}
223
224static inline size_t snd_compr_get_avail(struct snd_compr_stream *stream)
225{
226 struct snd_compr_avail avail;
227
228 return snd_compr_calc_avail(stream, &avail);
229}
230
231static int
232snd_compr_ioctl_avail(struct snd_compr_stream *stream, unsigned long arg)
233{
234 struct snd_compr_avail ioctl_avail;
235 size_t avail;
236
237 avail = snd_compr_calc_avail(stream, &ioctl_avail);
238 ioctl_avail.avail = avail;
239
240 if (copy_to_user((__u64 __user *)arg,
241 &ioctl_avail, sizeof(ioctl_avail)))
242 return -EFAULT;
243 return 0;
244}
245
246static int snd_compr_write_data(struct snd_compr_stream *stream,
247 const char __user *buf, size_t count)
248{
249 void *dstn;
250 size_t copy;
251 struct snd_compr_runtime *runtime = stream->runtime;
Charles Keepaxf0283b52013-04-18 11:03:46 +0100252 /* 64-bit Modulus */
253 u64 app_pointer = div64_u64(runtime->total_bytes_available,
254 runtime->buffer_size);
255 app_pointer = runtime->total_bytes_available -
256 (app_pointer * runtime->buffer_size);
Vinod Koulb21c60a2011-12-23 10:36:39 +0530257
Charles Keepaxf0283b52013-04-18 11:03:46 +0100258 dstn = runtime->buffer + app_pointer;
Vinod Koulb21c60a2011-12-23 10:36:39 +0530259 pr_debug("copying %ld at %lld\n",
Charles Keepaxf0283b52013-04-18 11:03:46 +0100260 (unsigned long)count, app_pointer);
261 if (count < runtime->buffer_size - app_pointer) {
Vinod Koulb21c60a2011-12-23 10:36:39 +0530262 if (copy_from_user(dstn, buf, count))
263 return -EFAULT;
Vinod Koulb21c60a2011-12-23 10:36:39 +0530264 } else {
Charles Keepaxf0283b52013-04-18 11:03:46 +0100265 copy = runtime->buffer_size - app_pointer;
Vinod Koulb21c60a2011-12-23 10:36:39 +0530266 if (copy_from_user(dstn, buf, copy))
267 return -EFAULT;
268 if (copy_from_user(runtime->buffer, buf + copy, count - copy))
269 return -EFAULT;
Vinod Koulb21c60a2011-12-23 10:36:39 +0530270 }
271 /* if DSP cares, let it know data has been written */
272 if (stream->ops->ack)
273 stream->ops->ack(stream, count);
274 return count;
275}
276
277static ssize_t snd_compr_write(struct file *f, const char __user *buf,
278 size_t count, loff_t *offset)
279{
280 struct snd_compr_file *data = f->private_data;
281 struct snd_compr_stream *stream;
282 size_t avail;
283 int retval;
284
285 if (snd_BUG_ON(!data))
286 return -EFAULT;
287
288 stream = &data->stream;
289 mutex_lock(&stream->device->lock);
290 /* write is allowed when stream is running or has been steup */
291 if (stream->runtime->state != SNDRV_PCM_STATE_SETUP &&
Eric Laurent35383a22016-03-02 09:54:57 -0800292 stream->runtime->state != SNDRV_PCM_STATE_PREPARED &&
Vinod Koulb21c60a2011-12-23 10:36:39 +0530293 stream->runtime->state != SNDRV_PCM_STATE_RUNNING) {
294 mutex_unlock(&stream->device->lock);
295 return -EBADFD;
296 }
297
298 avail = snd_compr_get_avail(stream);
299 pr_debug("avail returned %ld\n", (unsigned long)avail);
300 /* calculate how much we can write to buffer */
301 if (avail > count)
302 avail = count;
303
Charles Keepax4daf8912013-04-18 11:01:38 +0100304 if (stream->ops->copy) {
305 char __user* cbuf = (char __user*)buf;
306 retval = stream->ops->copy(stream, cbuf, avail);
307 } else {
Vinod Koulb21c60a2011-12-23 10:36:39 +0530308 retval = snd_compr_write_data(stream, buf, avail);
Charles Keepax4daf8912013-04-18 11:01:38 +0100309 }
Vinod Koulb21c60a2011-12-23 10:36:39 +0530310 if (retval > 0)
311 stream->runtime->total_bytes_available += retval;
312
313 /* while initiating the stream, write should be called before START
314 * call, so in setup move state */
315 if (stream->runtime->state == SNDRV_PCM_STATE_SETUP) {
316 stream->runtime->state = SNDRV_PCM_STATE_PREPARED;
317 pr_debug("stream prepared, Houston we are good to go\n");
318 }
319
320 mutex_unlock(&stream->device->lock);
321 return retval;
322}
323
324
325static ssize_t snd_compr_read(struct file *f, char __user *buf,
326 size_t count, loff_t *offset)
327{
Charles Keepax49bb6402013-04-18 11:02:08 +0100328 struct snd_compr_file *data = f->private_data;
329 struct snd_compr_stream *stream;
330 size_t avail;
331 int retval;
332
333 if (snd_BUG_ON(!data))
334 return -EFAULT;
335
336 stream = &data->stream;
337 mutex_lock(&stream->device->lock);
338
Vinod Koul75481342013-04-29 14:25:23 +0530339 /* read is allowed when stream is running, paused, draining and setup
340 * (yes setup is state which we transition to after stop, so if user
341 * wants to read data after stop we allow that)
342 */
343 switch (stream->runtime->state) {
344 case SNDRV_PCM_STATE_OPEN:
345 case SNDRV_PCM_STATE_PREPARED:
346 case SNDRV_PCM_STATE_XRUN:
347 case SNDRV_PCM_STATE_SUSPENDED:
348 case SNDRV_PCM_STATE_DISCONNECTED:
Charles Keepax49bb6402013-04-18 11:02:08 +0100349 retval = -EBADFD;
350 goto out;
351 }
352
353 avail = snd_compr_get_avail(stream);
354 pr_debug("avail returned %ld\n", (unsigned long)avail);
355 /* calculate how much we can read from buffer */
356 if (avail > count)
357 avail = count;
358
359 if (stream->ops->copy) {
360 retval = stream->ops->copy(stream, buf, avail);
361 } else {
362 retval = -ENXIO;
363 goto out;
364 }
365 if (retval > 0)
366 stream->runtime->total_bytes_transferred += retval;
367
368out:
369 mutex_unlock(&stream->device->lock);
370 return retval;
Vinod Koulb21c60a2011-12-23 10:36:39 +0530371}
372
373static int snd_compr_mmap(struct file *f, struct vm_area_struct *vma)
374{
375 return -ENXIO;
376}
377
378static inline int snd_compr_get_poll(struct snd_compr_stream *stream)
379{
380 if (stream->direction == SND_COMPRESS_PLAYBACK)
381 return POLLOUT | POLLWRNORM;
382 else
383 return POLLIN | POLLRDNORM;
384}
385
386static unsigned int snd_compr_poll(struct file *f, poll_table *wait)
387{
388 struct snd_compr_file *data = f->private_data;
389 struct snd_compr_stream *stream;
390 size_t avail;
391 int retval = 0;
392
393 if (snd_BUG_ON(!data))
394 return -EFAULT;
Charles Keepax5bd05392016-05-04 14:59:09 +0100395
Vinod Koulb21c60a2011-12-23 10:36:39 +0530396 stream = &data->stream;
Vinod Koulb21c60a2011-12-23 10:36:39 +0530397
398 mutex_lock(&stream->device->lock);
Richard Fitzgeraldc15b1492013-10-22 11:26:48 +0100399 if (stream->runtime->state == SNDRV_PCM_STATE_OPEN) {
Vinod Koulb21c60a2011-12-23 10:36:39 +0530400 retval = -EBADFD;
401 goto out;
402 }
403 poll_wait(f, &stream->runtime->sleep, wait);
404
405 avail = snd_compr_get_avail(stream);
406 pr_debug("avail is %ld\n", (unsigned long)avail);
407 /* check if we have at least one fragment to fill */
408 switch (stream->runtime->state) {
409 case SNDRV_PCM_STATE_DRAINING:
410 /* stream has been woken up after drain is complete
411 * draining done so set stream state to stopped
412 */
413 retval = snd_compr_get_poll(stream);
414 stream->runtime->state = SNDRV_PCM_STATE_SETUP;
415 break;
416 case SNDRV_PCM_STATE_RUNNING:
417 case SNDRV_PCM_STATE_PREPARED:
418 case SNDRV_PCM_STATE_PAUSED:
419 if (avail >= stream->runtime->fragment_size)
420 retval = snd_compr_get_poll(stream);
421 break;
422 default:
Charles Keepax0b92b0c2016-05-04 14:59:08 +0100423 retval = snd_compr_get_poll(stream) | POLLERR;
Vinod Koulb21c60a2011-12-23 10:36:39 +0530424 break;
425 }
426out:
427 mutex_unlock(&stream->device->lock);
428 return retval;
429}
430
431static int
432snd_compr_get_caps(struct snd_compr_stream *stream, unsigned long arg)
433{
434 int retval;
435 struct snd_compr_caps caps;
436
437 if (!stream->ops->get_caps)
438 return -ENXIO;
439
Dan Carpenter1c62e9f2013-04-21 14:07:29 +0300440 memset(&caps, 0, sizeof(caps));
Vinod Koulb21c60a2011-12-23 10:36:39 +0530441 retval = stream->ops->get_caps(stream, &caps);
442 if (retval)
443 goto out;
444 if (copy_to_user((void __user *)arg, &caps, sizeof(caps)))
445 retval = -EFAULT;
446out:
447 return retval;
448}
449
Takashi Iwai462b3f12016-01-25 13:59:21 +0100450#ifndef COMPR_CODEC_CAPS_OVERFLOW
Vinod Koulb21c60a2011-12-23 10:36:39 +0530451static int
452snd_compr_get_codec_caps(struct snd_compr_stream *stream, unsigned long arg)
453{
454 int retval;
455 struct snd_compr_codec_caps *caps;
456
457 if (!stream->ops->get_codec_caps)
458 return -ENXIO;
459
Takashi Iwai47966e92013-04-22 10:38:26 +0200460 caps = kzalloc(sizeof(*caps), GFP_KERNEL);
Vinod Koulb21c60a2011-12-23 10:36:39 +0530461 if (!caps)
462 return -ENOMEM;
463
464 retval = stream->ops->get_codec_caps(stream, caps);
465 if (retval)
466 goto out;
467 if (copy_to_user((void __user *)arg, caps, sizeof(*caps)))
468 retval = -EFAULT;
469
470out:
471 kfree(caps);
472 return retval;
473}
Takashi Iwai462b3f12016-01-25 13:59:21 +0100474#endif /* !COMPR_CODEC_CAPS_OVERFLOW */
Vinod Koulb21c60a2011-12-23 10:36:39 +0530475
476/* revisit this with snd_pcm_preallocate_xxx */
477static int snd_compr_allocate_buffer(struct snd_compr_stream *stream,
478 struct snd_compr_params *params)
479{
480 unsigned int buffer_size;
481 void *buffer;
482
483 buffer_size = params->buffer.fragment_size * params->buffer.fragments;
484 if (stream->ops->copy) {
485 buffer = NULL;
486 /* if copy is defined the driver will be required to copy
487 * the data from core
488 */
489 } else {
490 buffer = kmalloc(buffer_size, GFP_KERNEL);
491 if (!buffer)
492 return -ENOMEM;
493 }
494 stream->runtime->fragment_size = params->buffer.fragment_size;
495 stream->runtime->fragments = params->buffer.fragments;
496 stream->runtime->buffer = buffer;
497 stream->runtime->buffer_size = buffer_size;
498 return 0;
499}
500
Vinod Koul4dc040a2012-09-17 11:51:25 +0530501static int snd_compress_check_input(struct snd_compr_params *params)
502{
503 /* first let's check the buffer parameter's */
504 if (params->buffer.fragment_size == 0 ||
Dan Carpenter6217e5e2014-07-16 09:37:04 +0300505 params->buffer.fragments > INT_MAX / params->buffer.fragment_size)
Vinod Koul4dc040a2012-09-17 11:51:25 +0530506 return -EINVAL;
507
Vinod Koulfb4a9772012-09-17 11:51:26 +0530508 /* now codec parameters */
509 if (params->codec.id == 0 || params->codec.id > SND_AUDIOCODEC_MAX)
510 return -EINVAL;
511
512 if (params->codec.ch_in == 0 || params->codec.ch_out == 0)
513 return -EINVAL;
514
Vinod Koul4dc040a2012-09-17 11:51:25 +0530515 return 0;
516}
517
Vinod Koulb21c60a2011-12-23 10:36:39 +0530518static int
519snd_compr_set_params(struct snd_compr_stream *stream, unsigned long arg)
520{
521 struct snd_compr_params *params;
522 int retval;
523
524 if (stream->runtime->state == SNDRV_PCM_STATE_OPEN) {
525 /*
526 * we should allow parameter change only when stream has been
527 * opened not in other cases
528 */
529 params = kmalloc(sizeof(*params), GFP_KERNEL);
530 if (!params)
531 return -ENOMEM;
Jesper Juhl769fab22012-01-23 21:02:57 +0100532 if (copy_from_user(params, (void __user *)arg, sizeof(*params))) {
533 retval = -EFAULT;
534 goto out;
535 }
Vinod Koul4dc040a2012-09-17 11:51:25 +0530536
537 retval = snd_compress_check_input(params);
538 if (retval)
539 goto out;
540
Vinod Koulb21c60a2011-12-23 10:36:39 +0530541 retval = snd_compr_allocate_buffer(stream, params);
542 if (retval) {
Jesper Juhl769fab22012-01-23 21:02:57 +0100543 retval = -ENOMEM;
544 goto out;
Vinod Koulb21c60a2011-12-23 10:36:39 +0530545 }
Vinod Koul4dc040a2012-09-17 11:51:25 +0530546
Vinod Koulb21c60a2011-12-23 10:36:39 +0530547 retval = stream->ops->set_params(stream, params);
548 if (retval)
549 goto out;
Charles Keepax49bb6402013-04-18 11:02:08 +0100550
Jeeja KP9727b492013-02-14 16:52:51 +0530551 stream->metadata_set = false;
552 stream->next_track = false;
Charles Keepax49bb6402013-04-18 11:02:08 +0100553
554 if (stream->direction == SND_COMPRESS_PLAYBACK)
555 stream->runtime->state = SNDRV_PCM_STATE_SETUP;
556 else
557 stream->runtime->state = SNDRV_PCM_STATE_PREPARED;
Jesper Juhl769fab22012-01-23 21:02:57 +0100558 } else {
Vinod Koulb21c60a2011-12-23 10:36:39 +0530559 return -EPERM;
Jesper Juhl769fab22012-01-23 21:02:57 +0100560 }
Vinod Koulb21c60a2011-12-23 10:36:39 +0530561out:
562 kfree(params);
563 return retval;
564}
565
566static int
567snd_compr_get_params(struct snd_compr_stream *stream, unsigned long arg)
568{
569 struct snd_codec *params;
570 int retval;
571
572 if (!stream->ops->get_params)
573 return -EBADFD;
574
Takashi Iwai47966e92013-04-22 10:38:26 +0200575 params = kzalloc(sizeof(*params), GFP_KERNEL);
Vinod Koulb21c60a2011-12-23 10:36:39 +0530576 if (!params)
577 return -ENOMEM;
578 retval = stream->ops->get_params(stream, params);
579 if (retval)
580 goto out;
581 if (copy_to_user((char __user *)arg, params, sizeof(*params)))
582 retval = -EFAULT;
583
584out:
585 kfree(params);
586 return retval;
587}
588
Jeeja KP9727b492013-02-14 16:52:51 +0530589static int
590snd_compr_get_metadata(struct snd_compr_stream *stream, unsigned long arg)
591{
592 struct snd_compr_metadata metadata;
593 int retval;
594
595 if (!stream->ops->get_metadata)
596 return -ENXIO;
597
598 if (copy_from_user(&metadata, (void __user *)arg, sizeof(metadata)))
599 return -EFAULT;
600
601 retval = stream->ops->get_metadata(stream, &metadata);
602 if (retval != 0)
603 return retval;
604
605 if (copy_to_user((void __user *)arg, &metadata, sizeof(metadata)))
606 return -EFAULT;
607
608 return 0;
609}
610
611static int
612snd_compr_set_metadata(struct snd_compr_stream *stream, unsigned long arg)
613{
614 struct snd_compr_metadata metadata;
615 int retval;
616
617 if (!stream->ops->set_metadata)
618 return -ENXIO;
619 /*
620 * we should allow parameter change only when stream has been
621 * opened not in other cases
622 */
623 if (copy_from_user(&metadata, (void __user *)arg, sizeof(metadata)))
624 return -EFAULT;
625
626 retval = stream->ops->set_metadata(stream, &metadata);
627 stream->metadata_set = true;
628
629 return retval;
630}
631
Vinod Koulb21c60a2011-12-23 10:36:39 +0530632static inline int
633snd_compr_tstamp(struct snd_compr_stream *stream, unsigned long arg)
634{
Richard Fitzgerald17ac8e52013-02-11 13:44:53 +0000635 struct snd_compr_tstamp tstamp = {0};
636 int ret;
Vinod Koulb21c60a2011-12-23 10:36:39 +0530637
Richard Fitzgerald17ac8e52013-02-11 13:44:53 +0000638 ret = snd_compr_update_tstamp(stream, &tstamp);
639 if (ret == 0)
640 ret = copy_to_user((struct snd_compr_tstamp __user *)arg,
641 &tstamp, sizeof(tstamp)) ? -EFAULT : 0;
642 return ret;
Vinod Koulb21c60a2011-12-23 10:36:39 +0530643}
644
645static int snd_compr_pause(struct snd_compr_stream *stream)
646{
647 int retval;
648
649 if (stream->runtime->state != SNDRV_PCM_STATE_RUNNING)
650 return -EPERM;
651 retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_PAUSE_PUSH);
Vinod Koul6b18f792012-06-12 16:16:17 +0530652 if (!retval)
Vinod Koulb21c60a2011-12-23 10:36:39 +0530653 stream->runtime->state = SNDRV_PCM_STATE_PAUSED;
Vinod Koulb21c60a2011-12-23 10:36:39 +0530654 return retval;
655}
656
657static int snd_compr_resume(struct snd_compr_stream *stream)
658{
659 int retval;
660
661 if (stream->runtime->state != SNDRV_PCM_STATE_PAUSED)
662 return -EPERM;
663 retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_PAUSE_RELEASE);
664 if (!retval)
665 stream->runtime->state = SNDRV_PCM_STATE_RUNNING;
666 return retval;
667}
668
669static int snd_compr_start(struct snd_compr_stream *stream)
670{
671 int retval;
672
673 if (stream->runtime->state != SNDRV_PCM_STATE_PREPARED)
674 return -EPERM;
675 retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_START);
676 if (!retval)
677 stream->runtime->state = SNDRV_PCM_STATE_RUNNING;
678 return retval;
679}
680
681static int snd_compr_stop(struct snd_compr_stream *stream)
682{
683 int retval;
684
685 if (stream->runtime->state == SNDRV_PCM_STATE_PREPARED ||
686 stream->runtime->state == SNDRV_PCM_STATE_SETUP)
687 return -EPERM;
688 retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_STOP);
689 if (!retval) {
Vinod Koul917f4b52013-10-24 16:37:31 +0530690 snd_compr_drain_notify(stream);
Vinod Koul8b214602012-06-12 16:16:18 +0530691 stream->runtime->total_bytes_available = 0;
692 stream->runtime->total_bytes_transferred = 0;
Vinod Koulb21c60a2011-12-23 10:36:39 +0530693 }
694 return retval;
695}
696
Vinod Koul917f4b52013-10-24 16:37:31 +0530697static int snd_compress_wait_for_drain(struct snd_compr_stream *stream)
698{
Vinod Koulf44f2a52013-11-07 10:08:22 +0100699 int ret;
700
Vinod Koul917f4b52013-10-24 16:37:31 +0530701 /*
702 * We are called with lock held. So drop the lock while we wait for
Vinod Koulcdb1ee32016-03-04 23:59:29 +0530703 * drain complete notification from the driver
Vinod Koul917f4b52013-10-24 16:37:31 +0530704 *
705 * It is expected that driver will notify the drain completion and then
706 * stream will be moved to SETUP state, even if draining resulted in an
707 * error. We can trigger next track after this.
708 */
709 stream->runtime->state = SNDRV_PCM_STATE_DRAINING;
710 mutex_unlock(&stream->device->lock);
711
Vinod Koulf44f2a52013-11-07 10:08:22 +0100712 /* we wait for drain to complete here, drain can return when
713 * interruption occurred, wait returned error or success.
714 * For the first two cases we don't do anything different here and
715 * return after waking up
716 */
717
718 ret = wait_event_interruptible(stream->runtime->sleep,
719 (stream->runtime->state != SNDRV_PCM_STATE_DRAINING));
720 if (ret == -ERESTARTSYS)
721 pr_debug("wait aborted by a signal");
722 else if (ret)
723 pr_debug("wait for drain failed with %d\n", ret);
724
Vinod Koul917f4b52013-10-24 16:37:31 +0530725
726 wake_up(&stream->runtime->sleep);
727 mutex_lock(&stream->device->lock);
728
Vinod Koulf44f2a52013-11-07 10:08:22 +0100729 return ret;
Vinod Koul917f4b52013-10-24 16:37:31 +0530730}
731
Vinod Koulb21c60a2011-12-23 10:36:39 +0530732static int snd_compr_drain(struct snd_compr_stream *stream)
733{
734 int retval;
735
736 if (stream->runtime->state == SNDRV_PCM_STATE_PREPARED ||
737 stream->runtime->state == SNDRV_PCM_STATE_SETUP)
738 return -EPERM;
Vinod Koul917f4b52013-10-24 16:37:31 +0530739
Vinod Koulb21c60a2011-12-23 10:36:39 +0530740 retval = stream->ops->trigger(stream, SND_COMPR_TRIGGER_DRAIN);
Vinod Koul917f4b52013-10-24 16:37:31 +0530741 if (retval) {
Vinod Koulf44f2a52013-11-07 10:08:22 +0100742 pr_debug("SND_COMPR_TRIGGER_DRAIN failed %d\n", retval);
Vinod Koulb21c60a2011-12-23 10:36:39 +0530743 wake_up(&stream->runtime->sleep);
Vinod Koul917f4b52013-10-24 16:37:31 +0530744 return retval;
Vinod Koulb21c60a2011-12-23 10:36:39 +0530745 }
Vinod Koul917f4b52013-10-24 16:37:31 +0530746
Vinod Koulf44f2a52013-11-07 10:08:22 +0100747 return snd_compress_wait_for_drain(stream);
Vinod Koulb21c60a2011-12-23 10:36:39 +0530748}
749
Jeeja KP9727b492013-02-14 16:52:51 +0530750static int snd_compr_next_track(struct snd_compr_stream *stream)
751{
752 int retval;
753
754 /* only a running stream can transition to next track */
755 if (stream->runtime->state != SNDRV_PCM_STATE_RUNNING)
756 return -EPERM;
757
Vinod Koulcdb1ee32016-03-04 23:59:29 +0530758 /* you can signal next track if this is intended to be a gapless stream
Jeeja KP9727b492013-02-14 16:52:51 +0530759 * and current track metadata is set
760 */
761 if (stream->metadata_set == false)
762 return -EPERM;
763
764 retval = stream->ops->trigger(stream, SND_COMPR_TRIGGER_NEXT_TRACK);
765 if (retval != 0)
766 return retval;
767 stream->metadata_set = false;
768 stream->next_track = true;
769 return 0;
770}
771
772static int snd_compr_partial_drain(struct snd_compr_stream *stream)
773{
774 int retval;
775 if (stream->runtime->state == SNDRV_PCM_STATE_PREPARED ||
776 stream->runtime->state == SNDRV_PCM_STATE_SETUP)
777 return -EPERM;
778 /* stream can be drained only when next track has been signalled */
779 if (stream->next_track == false)
780 return -EPERM;
781
782 retval = stream->ops->trigger(stream, SND_COMPR_TRIGGER_PARTIAL_DRAIN);
Vinod Koul917f4b52013-10-24 16:37:31 +0530783 if (retval) {
Vinod Koulf44f2a52013-11-07 10:08:22 +0100784 pr_debug("Partial drain returned failure\n");
Vinod Koul917f4b52013-10-24 16:37:31 +0530785 wake_up(&stream->runtime->sleep);
786 return retval;
787 }
Jeeja KP9727b492013-02-14 16:52:51 +0530788
789 stream->next_track = false;
Vinod Koul917f4b52013-10-24 16:37:31 +0530790 return snd_compress_wait_for_drain(stream);
Jeeja KP9727b492013-02-14 16:52:51 +0530791}
792
Vinod Koulb21c60a2011-12-23 10:36:39 +0530793static long snd_compr_ioctl(struct file *f, unsigned int cmd, unsigned long arg)
794{
795 struct snd_compr_file *data = f->private_data;
796 struct snd_compr_stream *stream;
797 int retval = -ENOTTY;
798
799 if (snd_BUG_ON(!data))
800 return -EFAULT;
Charles Keepax5bd05392016-05-04 14:59:09 +0100801
Vinod Koulb21c60a2011-12-23 10:36:39 +0530802 stream = &data->stream;
Charles Keepax5bd05392016-05-04 14:59:09 +0100803
Vinod Koulb21c60a2011-12-23 10:36:39 +0530804 mutex_lock(&stream->device->lock);
805 switch (_IOC_NR(cmd)) {
806 case _IOC_NR(SNDRV_COMPRESS_IOCTL_VERSION):
Vinod Koula8d30602013-07-29 15:10:22 +0530807 retval = put_user(SNDRV_COMPRESS_VERSION,
Vinod Koulb21c60a2011-12-23 10:36:39 +0530808 (int __user *)arg) ? -EFAULT : 0;
809 break;
810 case _IOC_NR(SNDRV_COMPRESS_GET_CAPS):
811 retval = snd_compr_get_caps(stream, arg);
812 break;
Takashi Iwai462b3f12016-01-25 13:59:21 +0100813#ifndef COMPR_CODEC_CAPS_OVERFLOW
Vinod Koulb21c60a2011-12-23 10:36:39 +0530814 case _IOC_NR(SNDRV_COMPRESS_GET_CODEC_CAPS):
815 retval = snd_compr_get_codec_caps(stream, arg);
816 break;
Takashi Iwai462b3f12016-01-25 13:59:21 +0100817#endif
Vinod Koulb21c60a2011-12-23 10:36:39 +0530818 case _IOC_NR(SNDRV_COMPRESS_SET_PARAMS):
819 retval = snd_compr_set_params(stream, arg);
820 break;
821 case _IOC_NR(SNDRV_COMPRESS_GET_PARAMS):
822 retval = snd_compr_get_params(stream, arg);
823 break;
Jeeja KP9727b492013-02-14 16:52:51 +0530824 case _IOC_NR(SNDRV_COMPRESS_SET_METADATA):
825 retval = snd_compr_set_metadata(stream, arg);
826 break;
827 case _IOC_NR(SNDRV_COMPRESS_GET_METADATA):
828 retval = snd_compr_get_metadata(stream, arg);
829 break;
Vinod Koulb21c60a2011-12-23 10:36:39 +0530830 case _IOC_NR(SNDRV_COMPRESS_TSTAMP):
831 retval = snd_compr_tstamp(stream, arg);
832 break;
833 case _IOC_NR(SNDRV_COMPRESS_AVAIL):
834 retval = snd_compr_ioctl_avail(stream, arg);
835 break;
836 case _IOC_NR(SNDRV_COMPRESS_PAUSE):
837 retval = snd_compr_pause(stream);
838 break;
839 case _IOC_NR(SNDRV_COMPRESS_RESUME):
840 retval = snd_compr_resume(stream);
841 break;
842 case _IOC_NR(SNDRV_COMPRESS_START):
843 retval = snd_compr_start(stream);
844 break;
845 case _IOC_NR(SNDRV_COMPRESS_STOP):
846 retval = snd_compr_stop(stream);
847 break;
848 case _IOC_NR(SNDRV_COMPRESS_DRAIN):
849 retval = snd_compr_drain(stream);
850 break;
Jeeja KP9727b492013-02-14 16:52:51 +0530851 case _IOC_NR(SNDRV_COMPRESS_PARTIAL_DRAIN):
852 retval = snd_compr_partial_drain(stream);
853 break;
854 case _IOC_NR(SNDRV_COMPRESS_NEXT_TRACK):
855 retval = snd_compr_next_track(stream);
856 break;
857
Vinod Koulb21c60a2011-12-23 10:36:39 +0530858 }
859 mutex_unlock(&stream->device->lock);
860 return retval;
861}
862
Ravindra Lokhandec1036882015-12-07 12:08:31 +0530863/* support of 32bit userspace on 64bit platforms */
864#ifdef CONFIG_COMPAT
865static long snd_compr_ioctl_compat(struct file *file, unsigned int cmd,
866 unsigned long arg)
867{
868 return snd_compr_ioctl(file, cmd, (unsigned long)compat_ptr(arg));
869}
870#endif
871
Vinod Koulb21c60a2011-12-23 10:36:39 +0530872static const struct file_operations snd_compr_file_ops = {
873 .owner = THIS_MODULE,
874 .open = snd_compr_open,
875 .release = snd_compr_free,
876 .write = snd_compr_write,
877 .read = snd_compr_read,
878 .unlocked_ioctl = snd_compr_ioctl,
Ravindra Lokhandec1036882015-12-07 12:08:31 +0530879#ifdef CONFIG_COMPAT
880 .compat_ioctl = snd_compr_ioctl_compat,
881#endif
Vinod Koulb21c60a2011-12-23 10:36:39 +0530882 .mmap = snd_compr_mmap,
883 .poll = snd_compr_poll,
884};
885
886static int snd_compress_dev_register(struct snd_device *device)
887{
888 int ret = -EINVAL;
889 char str[16];
890 struct snd_compr *compr;
891
892 if (snd_BUG_ON(!device || !device->device_data))
893 return -EBADFD;
894 compr = device->device_data;
895
Vinod Koulb21c60a2011-12-23 10:36:39 +0530896 pr_debug("reg %s for device %s, direction %d\n", str, compr->name,
897 compr->direction);
898 /* register compressed device */
Takashi Iwai40a4b262015-01-30 08:34:58 +0100899 ret = snd_register_device(SNDRV_DEVICE_TYPE_COMPRESS,
900 compr->card, compr->device,
901 &snd_compr_file_ops, compr, &compr->dev);
Vinod Koulb21c60a2011-12-23 10:36:39 +0530902 if (ret < 0) {
903 pr_err("snd_register_device failed\n %d", ret);
904 return ret;
905 }
906 return ret;
907
908}
909
910static int snd_compress_dev_disconnect(struct snd_device *device)
911{
912 struct snd_compr *compr;
913
914 compr = device->device_data;
Takashi Iwai40a4b262015-01-30 08:34:58 +0100915 snd_unregister_device(&compr->dev);
Vinod Koulb21c60a2011-12-23 10:36:39 +0530916 return 0;
917}
918
Richard Fitzgerald31742722015-11-25 13:00:23 +0000919#ifdef CONFIG_SND_VERBOSE_PROCFS
920static void snd_compress_proc_info_read(struct snd_info_entry *entry,
921 struct snd_info_buffer *buffer)
922{
923 struct snd_compr *compr = (struct snd_compr *)entry->private_data;
924
925 snd_iprintf(buffer, "card: %d\n", compr->card->number);
926 snd_iprintf(buffer, "device: %d\n", compr->device);
927 snd_iprintf(buffer, "stream: %s\n",
928 compr->direction == SND_COMPRESS_PLAYBACK
929 ? "PLAYBACK" : "CAPTURE");
930 snd_iprintf(buffer, "id: %s\n", compr->id);
931}
932
933static int snd_compress_proc_init(struct snd_compr *compr)
934{
935 struct snd_info_entry *entry;
936 char name[16];
937
938 sprintf(name, "compr%i", compr->device);
939 entry = snd_info_create_card_entry(compr->card, name,
940 compr->card->proc_root);
941 if (!entry)
942 return -ENOMEM;
943 entry->mode = S_IFDIR | S_IRUGO | S_IXUGO;
944 if (snd_info_register(entry) < 0) {
945 snd_info_free_entry(entry);
946 return -ENOMEM;
947 }
948 compr->proc_root = entry;
949
950 entry = snd_info_create_card_entry(compr->card, "info",
951 compr->proc_root);
952 if (entry) {
953 snd_info_set_text_ops(entry, compr,
954 snd_compress_proc_info_read);
955 if (snd_info_register(entry) < 0) {
956 snd_info_free_entry(entry);
957 entry = NULL;
958 }
959 }
960 compr->proc_info_entry = entry;
961
962 return 0;
963}
964
965static void snd_compress_proc_done(struct snd_compr *compr)
966{
967 snd_info_free_entry(compr->proc_info_entry);
968 compr->proc_info_entry = NULL;
969 snd_info_free_entry(compr->proc_root);
970 compr->proc_root = NULL;
971}
Richard Fitzgeralde5241a82015-11-25 13:00:24 +0000972
973static inline void snd_compress_set_id(struct snd_compr *compr, const char *id)
974{
975 strlcpy(compr->id, id, sizeof(compr->id));
976}
Richard Fitzgerald31742722015-11-25 13:00:23 +0000977#else
978static inline int snd_compress_proc_init(struct snd_compr *compr)
979{
980 return 0;
981}
982
983static inline void snd_compress_proc_done(struct snd_compr *compr)
984{
985}
Richard Fitzgeralde5241a82015-11-25 13:00:24 +0000986
987static inline void snd_compress_set_id(struct snd_compr *compr, const char *id)
988{
989}
Richard Fitzgerald31742722015-11-25 13:00:23 +0000990#endif
991
Takashi Iwai04c5d5a2015-01-30 08:16:35 +0100992static int snd_compress_dev_free(struct snd_device *device)
993{
994 struct snd_compr *compr;
995
996 compr = device->device_data;
Richard Fitzgerald31742722015-11-25 13:00:23 +0000997 snd_compress_proc_done(compr);
Takashi Iwai04c5d5a2015-01-30 08:16:35 +0100998 put_device(&compr->dev);
999 return 0;
1000}
1001
Vinod Koulb21c60a2011-12-23 10:36:39 +05301002/*
1003 * snd_compress_new: create new compress device
1004 * @card: sound card pointer
1005 * @device: device number
1006 * @dirn: device direction, should be of type enum snd_compr_direction
1007 * @compr: compress device pointer
1008 */
1009int snd_compress_new(struct snd_card *card, int device,
Richard Fitzgeralde5241a82015-11-25 13:00:24 +00001010 int dirn, const char *id, struct snd_compr *compr)
Vinod Koulb21c60a2011-12-23 10:36:39 +05301011{
1012 static struct snd_device_ops ops = {
Takashi Iwai04c5d5a2015-01-30 08:16:35 +01001013 .dev_free = snd_compress_dev_free,
Vinod Koulb21c60a2011-12-23 10:36:39 +05301014 .dev_register = snd_compress_dev_register,
1015 .dev_disconnect = snd_compress_dev_disconnect,
1016 };
Richard Fitzgerald31742722015-11-25 13:00:23 +00001017 int ret;
Vinod Koulb21c60a2011-12-23 10:36:39 +05301018
1019 compr->card = card;
1020 compr->device = device;
1021 compr->direction = dirn;
Takashi Iwai04c5d5a2015-01-30 08:16:35 +01001022
Richard Fitzgeralde5241a82015-11-25 13:00:24 +00001023 snd_compress_set_id(compr, id);
1024
Takashi Iwai04c5d5a2015-01-30 08:16:35 +01001025 snd_device_initialize(&compr->dev, card);
1026 dev_set_name(&compr->dev, "comprC%iD%i", card->number, device);
1027
Richard Fitzgerald31742722015-11-25 13:00:23 +00001028 ret = snd_device_new(card, SNDRV_DEV_COMPRESS, compr, &ops);
1029 if (ret == 0)
1030 snd_compress_proc_init(compr);
1031
1032 return ret;
Vinod Koulb21c60a2011-12-23 10:36:39 +05301033}
1034EXPORT_SYMBOL_GPL(snd_compress_new);
1035
1036static int snd_compress_add_device(struct snd_compr *device)
1037{
1038 int ret;
1039
1040 if (!device->card)
1041 return -EINVAL;
1042
1043 /* register the card */
1044 ret = snd_card_register(device->card);
1045 if (ret)
1046 goto out;
1047 return 0;
1048
1049out:
1050 pr_err("failed with %d\n", ret);
1051 return ret;
1052
1053}
1054
1055static int snd_compress_remove_device(struct snd_compr *device)
1056{
1057 return snd_card_free(device->card);
1058}
1059
1060/**
1061 * snd_compress_register - register compressed device
1062 *
1063 * @device: compressed device to register
1064 */
1065int snd_compress_register(struct snd_compr *device)
1066{
1067 int retval;
1068
Takashi Iwai04c5d5a2015-01-30 08:16:35 +01001069 if (device->name == NULL || device->ops == NULL)
Vinod Koulb21c60a2011-12-23 10:36:39 +05301070 return -EINVAL;
1071
1072 pr_debug("Registering compressed device %s\n", device->name);
1073 if (snd_BUG_ON(!device->ops->open))
1074 return -EINVAL;
1075 if (snd_BUG_ON(!device->ops->free))
1076 return -EINVAL;
1077 if (snd_BUG_ON(!device->ops->set_params))
1078 return -EINVAL;
1079 if (snd_BUG_ON(!device->ops->trigger))
1080 return -EINVAL;
1081
1082 mutex_init(&device->lock);
1083
1084 /* register a compressed card */
1085 mutex_lock(&device_mutex);
1086 retval = snd_compress_add_device(device);
1087 mutex_unlock(&device_mutex);
1088 return retval;
1089}
1090EXPORT_SYMBOL_GPL(snd_compress_register);
1091
1092int snd_compress_deregister(struct snd_compr *device)
1093{
1094 pr_debug("Removing compressed device %s\n", device->name);
1095 mutex_lock(&device_mutex);
1096 snd_compress_remove_device(device);
1097 mutex_unlock(&device_mutex);
1098 return 0;
1099}
1100EXPORT_SYMBOL_GPL(snd_compress_deregister);
1101
1102static int __init snd_compress_init(void)
1103{
1104 return 0;
1105}
1106
1107static void __exit snd_compress_exit(void)
1108{
1109}
1110
1111module_init(snd_compress_init);
1112module_exit(snd_compress_exit);
1113
1114MODULE_DESCRIPTION("ALSA Compressed offload framework");
1115MODULE_AUTHOR("Vinod Koul <vinod.koul@linux.intel.com>");
1116MODULE_LICENSE("GPL v2");