a58cf35941307620cc418ae5201a24329945d5ea
[linux-2.6.git] / sound / core / compress_offload.c
1 /*
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>
31 #include <linux/mm.h>
32 #include <linux/mutex.h>
33 #include <linux/poll.h>
34 #include <linux/slab.h>
35 #include <linux/sched.h>
36 #include <linux/uio.h>
37 #include <linux/uaccess.h>
38 #include <linux/module.h>
39 #include <sound/core.h>
40 #include <sound/initval.h>
41 #include <sound/compress_params.h>
42 #include <sound/compress_offload.h>
43 #include <sound/compress_driver.h>
44
45 /* TODO:
46  * - add substream support for multiple devices in case of
47  *      SND_DYNAMIC_MINORS is not used
48  * - Multiple node representation
49  *      driver should be able to register multiple nodes
50  */
51
52 static DEFINE_MUTEX(device_mutex);
53
54 struct snd_compr_file {
55         unsigned long caps;
56         struct snd_compr_stream stream;
57 };
58
59 /*
60  * a note on stream states used:
61  * we use follwing states in the compressed core
62  * SNDRV_PCM_STATE_OPEN: When stream has been opened.
63  * SNDRV_PCM_STATE_SETUP: When stream has been initialized. This is done by
64  *      calling SNDRV_COMPRESS_SET_PARAMS. running streams will come to this
65  *      state at stop by calling SNDRV_COMPRESS_STOP, or at end of drain.
66  * SNDRV_PCM_STATE_RUNNING: When stream has been started and is
67  *      decoding/encoding and rendering/capturing data.
68  * SNDRV_PCM_STATE_DRAINING: When stream is draining current data. This is done
69  *      by calling SNDRV_COMPRESS_DRAIN.
70  * SNDRV_PCM_STATE_PAUSED: When stream is paused. This is done by calling
71  *      SNDRV_COMPRESS_PAUSE. It can be stopped or resumed by calling
72  *      SNDRV_COMPRESS_STOP or SNDRV_COMPRESS_RESUME respectively.
73  */
74 static int snd_compr_open(struct inode *inode, struct file *f)
75 {
76         struct snd_compr *compr;
77         struct snd_compr_file *data;
78         struct snd_compr_runtime *runtime;
79         enum snd_compr_direction dirn;
80         int maj = imajor(inode);
81         int ret;
82
83         if (f->f_flags & O_WRONLY)
84                 dirn = SND_COMPRESS_PLAYBACK;
85         else if (f->f_flags & O_RDONLY)
86                 dirn = SND_COMPRESS_CAPTURE;
87         else {
88                 pr_err("invalid direction\n");
89                 return -EINVAL;
90         }
91
92         if (maj == snd_major)
93                 compr = snd_lookup_minor_data(iminor(inode),
94                                         SNDRV_DEVICE_TYPE_COMPRESS);
95         else
96                 return -EBADFD;
97
98         if (compr == NULL) {
99                 pr_err("no device data!!!\n");
100                 return -ENODEV;
101         }
102
103         if (dirn != compr->direction) {
104                 pr_err("this device doesn't support this direction\n");
105                 snd_card_unref(compr->card);
106                 return -EINVAL;
107         }
108
109         data = kzalloc(sizeof(*data), GFP_KERNEL);
110         if (!data) {
111                 snd_card_unref(compr->card);
112                 return -ENOMEM;
113         }
114         data->stream.ops = compr->ops;
115         data->stream.direction = dirn;
116         data->stream.private_data = compr->private_data;
117         data->stream.device = compr;
118         runtime = kzalloc(sizeof(*runtime), GFP_KERNEL);
119         if (!runtime) {
120                 kfree(data);
121                 snd_card_unref(compr->card);
122                 return -ENOMEM;
123         }
124         runtime->state = SNDRV_PCM_STATE_OPEN;
125         init_waitqueue_head(&runtime->sleep);
126         data->stream.runtime = runtime;
127         f->private_data = (void *)data;
128         mutex_lock(&compr->lock);
129         ret = compr->ops->open(&data->stream);
130         mutex_unlock(&compr->lock);
131         if (ret) {
132                 kfree(runtime);
133                 kfree(data);
134         }
135         snd_card_unref(compr->card);
136         return 0;
137 }
138
139 static int snd_compr_free(struct inode *inode, struct file *f)
140 {
141         struct snd_compr_file *data = f->private_data;
142         data->stream.ops->free(&data->stream);
143         kfree(data->stream.runtime->buffer);
144         kfree(data->stream.runtime);
145         kfree(data);
146         return 0;
147 }
148
149 static void snd_compr_update_tstamp(struct snd_compr_stream *stream,
150                 struct snd_compr_tstamp *tstamp)
151 {
152         if (!stream->ops->pointer)
153                 return;
154         stream->ops->pointer(stream, tstamp);
155         pr_debug("dsp consumed till %d total %d bytes\n",
156                 tstamp->byte_offset, tstamp->copied_total);
157         stream->runtime->hw_pointer = tstamp->byte_offset;
158         stream->runtime->total_bytes_transferred = tstamp->copied_total;
159 }
160
161 static size_t snd_compr_calc_avail(struct snd_compr_stream *stream,
162                 struct snd_compr_avail *avail)
163 {
164         long avail_calc; /*this needs to be signed variable */
165
166         snd_compr_update_tstamp(stream, &avail->tstamp);
167
168         /* FIXME: This needs to be different for capture stream,
169            available is # of compressed data, for playback it's
170            remainder of buffer */
171
172         if (stream->runtime->total_bytes_available == 0 &&
173                         stream->runtime->state == SNDRV_PCM_STATE_SETUP) {
174                 pr_debug("detected init and someone forgot to do a write\n");
175                 return stream->runtime->buffer_size;
176         }
177         pr_debug("app wrote %lld, DSP consumed %lld\n",
178                         stream->runtime->total_bytes_available,
179                         stream->runtime->total_bytes_transferred);
180         if (stream->runtime->total_bytes_available ==
181                                 stream->runtime->total_bytes_transferred) {
182                 pr_debug("both pointers are same, returning full avail\n");
183                 return stream->runtime->buffer_size;
184         }
185
186         /* FIXME: this routine isn't consistent, in one test we use
187          * cumulative values and in the other byte offsets. Do we
188          * really need the byte offsets if the cumulative values have
189          * been updated? In the PCM interface app_ptr and hw_ptr are
190          * already cumulative */
191
192         avail_calc = stream->runtime->buffer_size -
193                 (stream->runtime->app_pointer - stream->runtime->hw_pointer);
194         pr_debug("calc avail as %ld, app_ptr %lld, hw+ptr %lld\n", avail_calc,
195                         stream->runtime->app_pointer,
196                         stream->runtime->hw_pointer);
197         if (avail_calc >= stream->runtime->buffer_size)
198                 avail_calc -= stream->runtime->buffer_size;
199         pr_debug("ret avail as %ld\n", avail_calc);
200         avail->avail = avail_calc;
201         return avail_calc;
202 }
203
204 static inline size_t snd_compr_get_avail(struct snd_compr_stream *stream)
205 {
206         struct snd_compr_avail avail;
207
208         return snd_compr_calc_avail(stream, &avail);
209 }
210
211 static int
212 snd_compr_ioctl_avail(struct snd_compr_stream *stream, unsigned long arg)
213 {
214         struct snd_compr_avail ioctl_avail;
215         size_t avail;
216
217         avail = snd_compr_calc_avail(stream, &ioctl_avail);
218         ioctl_avail.avail = avail;
219
220         if (copy_to_user((__u64 __user *)arg,
221                                 &ioctl_avail, sizeof(ioctl_avail)))
222                 return -EFAULT;
223         return 0;
224 }
225
226 static int snd_compr_write_data(struct snd_compr_stream *stream,
227                const char __user *buf, size_t count)
228 {
229         void *dstn;
230         size_t copy;
231         struct snd_compr_runtime *runtime = stream->runtime;
232
233         dstn = runtime->buffer + runtime->app_pointer;
234         pr_debug("copying %ld at %lld\n",
235                         (unsigned long)count, runtime->app_pointer);
236         if (count < runtime->buffer_size - runtime->app_pointer) {
237                 if (copy_from_user(dstn, buf, count))
238                         return -EFAULT;
239                 runtime->app_pointer += count;
240         } else {
241                 copy = runtime->buffer_size - runtime->app_pointer;
242                 if (copy_from_user(dstn, buf, copy))
243                         return -EFAULT;
244                 if (copy_from_user(runtime->buffer, buf + copy, count - copy))
245                         return -EFAULT;
246                 runtime->app_pointer = count - copy;
247         }
248         /* if DSP cares, let it know data has been written */
249         if (stream->ops->ack)
250                 stream->ops->ack(stream, count);
251         return count;
252 }
253
254 static ssize_t snd_compr_write(struct file *f, const char __user *buf,
255                 size_t count, loff_t *offset)
256 {
257         struct snd_compr_file *data = f->private_data;
258         struct snd_compr_stream *stream;
259         size_t avail;
260         int retval;
261
262         if (snd_BUG_ON(!data))
263                 return -EFAULT;
264
265         stream = &data->stream;
266         mutex_lock(&stream->device->lock);
267         /* write is allowed when stream is running or has been steup */
268         if (stream->runtime->state != SNDRV_PCM_STATE_SETUP &&
269                         stream->runtime->state != SNDRV_PCM_STATE_RUNNING) {
270                 mutex_unlock(&stream->device->lock);
271                 return -EBADFD;
272         }
273
274         avail = snd_compr_get_avail(stream);
275         pr_debug("avail returned %ld\n", (unsigned long)avail);
276         /* calculate how much we can write to buffer */
277         if (avail > count)
278                 avail = count;
279
280         if (stream->ops->copy)
281                 retval = stream->ops->copy(stream, buf, avail);
282         else
283                 retval = snd_compr_write_data(stream, buf, avail);
284         if (retval > 0)
285                 stream->runtime->total_bytes_available += retval;
286
287         /* while initiating the stream, write should be called before START
288          * call, so in setup move state */
289         if (stream->runtime->state == SNDRV_PCM_STATE_SETUP) {
290                 stream->runtime->state = SNDRV_PCM_STATE_PREPARED;
291                 pr_debug("stream prepared, Houston we are good to go\n");
292         }
293
294         mutex_unlock(&stream->device->lock);
295         return retval;
296 }
297
298
299 static ssize_t snd_compr_read(struct file *f, char __user *buf,
300                 size_t count, loff_t *offset)
301 {
302         return -ENXIO;
303 }
304
305 static int snd_compr_mmap(struct file *f, struct vm_area_struct *vma)
306 {
307         return -ENXIO;
308 }
309
310 static inline int snd_compr_get_poll(struct snd_compr_stream *stream)
311 {
312         if (stream->direction == SND_COMPRESS_PLAYBACK)
313                 return POLLOUT | POLLWRNORM;
314         else
315                 return POLLIN | POLLRDNORM;
316 }
317
318 static unsigned int snd_compr_poll(struct file *f, poll_table *wait)
319 {
320         struct snd_compr_file *data = f->private_data;
321         struct snd_compr_stream *stream;
322         size_t avail;
323         int retval = 0;
324
325         if (snd_BUG_ON(!data))
326                 return -EFAULT;
327         stream = &data->stream;
328         if (snd_BUG_ON(!stream))
329                 return -EFAULT;
330
331         mutex_lock(&stream->device->lock);
332         if (stream->runtime->state == SNDRV_PCM_STATE_PAUSED ||
333                         stream->runtime->state == SNDRV_PCM_STATE_OPEN) {
334                 retval = -EBADFD;
335                 goto out;
336         }
337         poll_wait(f, &stream->runtime->sleep, wait);
338
339         avail = snd_compr_get_avail(stream);
340         pr_debug("avail is %ld\n", (unsigned long)avail);
341         /* check if we have at least one fragment to fill */
342         switch (stream->runtime->state) {
343         case SNDRV_PCM_STATE_DRAINING:
344                 /* stream has been woken up after drain is complete
345                  * draining done so set stream state to stopped
346                  */
347                 retval = snd_compr_get_poll(stream);
348                 stream->runtime->state = SNDRV_PCM_STATE_SETUP;
349                 break;
350         case SNDRV_PCM_STATE_RUNNING:
351         case SNDRV_PCM_STATE_PREPARED:
352         case SNDRV_PCM_STATE_PAUSED:
353                 if (avail >= stream->runtime->fragment_size)
354                         retval = snd_compr_get_poll(stream);
355                 break;
356         default:
357                 if (stream->direction == SND_COMPRESS_PLAYBACK)
358                         retval = POLLOUT | POLLWRNORM | POLLERR;
359                 else
360                         retval = POLLIN | POLLRDNORM | POLLERR;
361                 break;
362         }
363 out:
364         mutex_unlock(&stream->device->lock);
365         return retval;
366 }
367
368 static int
369 snd_compr_get_caps(struct snd_compr_stream *stream, unsigned long arg)
370 {
371         int retval;
372         struct snd_compr_caps caps;
373
374         if (!stream->ops->get_caps)
375                 return -ENXIO;
376
377         retval = stream->ops->get_caps(stream, &caps);
378         if (retval)
379                 goto out;
380         if (copy_to_user((void __user *)arg, &caps, sizeof(caps)))
381                 retval = -EFAULT;
382 out:
383         return retval;
384 }
385
386 static int
387 snd_compr_get_codec_caps(struct snd_compr_stream *stream, unsigned long arg)
388 {
389         int retval;
390         struct snd_compr_codec_caps *caps;
391
392         if (!stream->ops->get_codec_caps)
393                 return -ENXIO;
394
395         caps = kmalloc(sizeof(*caps), GFP_KERNEL);
396         if (!caps)
397                 return -ENOMEM;
398
399         retval = stream->ops->get_codec_caps(stream, caps);
400         if (retval)
401                 goto out;
402         if (copy_to_user((void __user *)arg, caps, sizeof(*caps)))
403                 retval = -EFAULT;
404
405 out:
406         kfree(caps);
407         return retval;
408 }
409
410 /* revisit this with snd_pcm_preallocate_xxx */
411 static int snd_compr_allocate_buffer(struct snd_compr_stream *stream,
412                 struct snd_compr_params *params)
413 {
414         unsigned int buffer_size;
415         void *buffer;
416
417         buffer_size = params->buffer.fragment_size * params->buffer.fragments;
418         if (stream->ops->copy) {
419                 buffer = NULL;
420                 /* if copy is defined the driver will be required to copy
421                  * the data from core
422                  */
423         } else {
424                 buffer = kmalloc(buffer_size, GFP_KERNEL);
425                 if (!buffer)
426                         return -ENOMEM;
427         }
428         stream->runtime->fragment_size = params->buffer.fragment_size;
429         stream->runtime->fragments = params->buffer.fragments;
430         stream->runtime->buffer = buffer;
431         stream->runtime->buffer_size = buffer_size;
432         return 0;
433 }
434
435 static int
436 snd_compr_set_params(struct snd_compr_stream *stream, unsigned long arg)
437 {
438         struct snd_compr_params *params;
439         int retval;
440
441         if (stream->runtime->state == SNDRV_PCM_STATE_OPEN) {
442                 /*
443                  * we should allow parameter change only when stream has been
444                  * opened not in other cases
445                  */
446                 params = kmalloc(sizeof(*params), GFP_KERNEL);
447                 if (!params)
448                         return -ENOMEM;
449                 if (copy_from_user(params, (void __user *)arg, sizeof(*params))) {
450                         retval = -EFAULT;
451                         goto out;
452                 }
453                 retval = snd_compr_allocate_buffer(stream, params);
454                 if (retval) {
455                         retval = -ENOMEM;
456                         goto out;
457                 }
458                 retval = stream->ops->set_params(stream, params);
459                 if (retval)
460                         goto out;
461                 stream->runtime->state = SNDRV_PCM_STATE_SETUP;
462         } else {
463                 return -EPERM;
464         }
465 out:
466         kfree(params);
467         return retval;
468 }
469
470 static int
471 snd_compr_get_params(struct snd_compr_stream *stream, unsigned long arg)
472 {
473         struct snd_codec *params;
474         int retval;
475
476         if (!stream->ops->get_params)
477                 return -EBADFD;
478
479         params = kmalloc(sizeof(*params), GFP_KERNEL);
480         if (!params)
481                 return -ENOMEM;
482         retval = stream->ops->get_params(stream, params);
483         if (retval)
484                 goto out;
485         if (copy_to_user((char __user *)arg, params, sizeof(*params)))
486                 retval = -EFAULT;
487
488 out:
489         kfree(params);
490         return retval;
491 }
492
493 static inline int
494 snd_compr_tstamp(struct snd_compr_stream *stream, unsigned long arg)
495 {
496         struct snd_compr_tstamp tstamp;
497
498         snd_compr_update_tstamp(stream, &tstamp);
499         return copy_to_user((struct snd_compr_tstamp __user *)arg,
500                 &tstamp, sizeof(tstamp)) ? -EFAULT : 0;
501 }
502
503 static int snd_compr_pause(struct snd_compr_stream *stream)
504 {
505         int retval;
506
507         if (stream->runtime->state != SNDRV_PCM_STATE_RUNNING)
508                 return -EPERM;
509         retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_PAUSE_PUSH);
510         if (!retval) {
511                 stream->runtime->state = SNDRV_PCM_STATE_PAUSED;
512                 wake_up(&stream->runtime->sleep);
513         }
514         return retval;
515 }
516
517 static int snd_compr_resume(struct snd_compr_stream *stream)
518 {
519         int retval;
520
521         if (stream->runtime->state != SNDRV_PCM_STATE_PAUSED)
522                 return -EPERM;
523         retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_PAUSE_RELEASE);
524         if (!retval)
525                 stream->runtime->state = SNDRV_PCM_STATE_RUNNING;
526         return retval;
527 }
528
529 static int snd_compr_start(struct snd_compr_stream *stream)
530 {
531         int retval;
532
533         if (stream->runtime->state != SNDRV_PCM_STATE_PREPARED)
534                 return -EPERM;
535         retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_START);
536         if (!retval)
537                 stream->runtime->state = SNDRV_PCM_STATE_RUNNING;
538         return retval;
539 }
540
541 static int snd_compr_stop(struct snd_compr_stream *stream)
542 {
543         int retval;
544
545         if (stream->runtime->state == SNDRV_PCM_STATE_PREPARED ||
546                         stream->runtime->state == SNDRV_PCM_STATE_SETUP)
547                 return -EPERM;
548         retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_STOP);
549         if (!retval) {
550                 stream->runtime->state = SNDRV_PCM_STATE_SETUP;
551                 wake_up(&stream->runtime->sleep);
552         }
553         return retval;
554 }
555
556 static int snd_compr_drain(struct snd_compr_stream *stream)
557 {
558         int retval;
559
560         if (stream->runtime->state == SNDRV_PCM_STATE_PREPARED ||
561                         stream->runtime->state == SNDRV_PCM_STATE_SETUP)
562                 return -EPERM;
563         retval = stream->ops->trigger(stream, SND_COMPR_TRIGGER_DRAIN);
564         if (!retval) {
565                 stream->runtime->state = SNDRV_PCM_STATE_DRAINING;
566                 wake_up(&stream->runtime->sleep);
567         }
568         return retval;
569 }
570
571 static long snd_compr_ioctl(struct file *f, unsigned int cmd, unsigned long arg)
572 {
573         struct snd_compr_file *data = f->private_data;
574         struct snd_compr_stream *stream;
575         int retval = -ENOTTY;
576
577         if (snd_BUG_ON(!data))
578                 return -EFAULT;
579         stream = &data->stream;
580         if (snd_BUG_ON(!stream))
581                 return -EFAULT;
582         mutex_lock(&stream->device->lock);
583         switch (_IOC_NR(cmd)) {
584         case _IOC_NR(SNDRV_COMPRESS_IOCTL_VERSION):
585                 put_user(SNDRV_COMPRESS_VERSION,
586                                 (int __user *)arg) ? -EFAULT : 0;
587                 break;
588         case _IOC_NR(SNDRV_COMPRESS_GET_CAPS):
589                 retval = snd_compr_get_caps(stream, arg);
590                 break;
591         case _IOC_NR(SNDRV_COMPRESS_GET_CODEC_CAPS):
592                 retval = snd_compr_get_codec_caps(stream, arg);
593                 break;
594         case _IOC_NR(SNDRV_COMPRESS_SET_PARAMS):
595                 retval = snd_compr_set_params(stream, arg);
596                 break;
597         case _IOC_NR(SNDRV_COMPRESS_GET_PARAMS):
598                 retval = snd_compr_get_params(stream, arg);
599                 break;
600         case _IOC_NR(SNDRV_COMPRESS_TSTAMP):
601                 retval = snd_compr_tstamp(stream, arg);
602                 break;
603         case _IOC_NR(SNDRV_COMPRESS_AVAIL):
604                 retval = snd_compr_ioctl_avail(stream, arg);
605                 break;
606         case _IOC_NR(SNDRV_COMPRESS_PAUSE):
607                 retval = snd_compr_pause(stream);
608                 break;
609         case _IOC_NR(SNDRV_COMPRESS_RESUME):
610                 retval = snd_compr_resume(stream);
611                 break;
612         case _IOC_NR(SNDRV_COMPRESS_START):
613                 retval = snd_compr_start(stream);
614                 break;
615         case _IOC_NR(SNDRV_COMPRESS_STOP):
616                 retval = snd_compr_stop(stream);
617                 break;
618         case _IOC_NR(SNDRV_COMPRESS_DRAIN):
619                 retval = snd_compr_drain(stream);
620                 break;
621         }
622         mutex_unlock(&stream->device->lock);
623         return retval;
624 }
625
626 static const struct file_operations snd_compr_file_ops = {
627                 .owner =        THIS_MODULE,
628                 .open =         snd_compr_open,
629                 .release =      snd_compr_free,
630                 .write =        snd_compr_write,
631                 .read =         snd_compr_read,
632                 .unlocked_ioctl = snd_compr_ioctl,
633                 .mmap =         snd_compr_mmap,
634                 .poll =         snd_compr_poll,
635 };
636
637 static int snd_compress_dev_register(struct snd_device *device)
638 {
639         int ret = -EINVAL;
640         char str[16];
641         struct snd_compr *compr;
642
643         if (snd_BUG_ON(!device || !device->device_data))
644                 return -EBADFD;
645         compr = device->device_data;
646
647         sprintf(str, "comprC%iD%i", compr->card->number, compr->device);
648         pr_debug("reg %s for device %s, direction %d\n", str, compr->name,
649                         compr->direction);
650         /* register compressed device */
651         ret = snd_register_device(SNDRV_DEVICE_TYPE_COMPRESS, compr->card,
652                         compr->device, &snd_compr_file_ops, compr, str);
653         if (ret < 0) {
654                 pr_err("snd_register_device failed\n %d", ret);
655                 return ret;
656         }
657         return ret;
658
659 }
660
661 static int snd_compress_dev_disconnect(struct snd_device *device)
662 {
663         struct snd_compr *compr;
664
665         compr = device->device_data;
666         snd_unregister_device(compr->direction, compr->card, compr->device);
667         return 0;
668 }
669
670 /*
671  * snd_compress_new: create new compress device
672  * @card: sound card pointer
673  * @device: device number
674  * @dirn: device direction, should be of type enum snd_compr_direction
675  * @compr: compress device pointer
676  */
677 int snd_compress_new(struct snd_card *card, int device,
678                         int dirn, struct snd_compr *compr)
679 {
680         static struct snd_device_ops ops = {
681                 .dev_free = NULL,
682                 .dev_register = snd_compress_dev_register,
683                 .dev_disconnect = snd_compress_dev_disconnect,
684         };
685
686         compr->card = card;
687         compr->device = device;
688         compr->direction = dirn;
689         return snd_device_new(card, SNDRV_DEV_COMPRESS, compr, &ops);
690 }
691 EXPORT_SYMBOL_GPL(snd_compress_new);
692
693 static int snd_compress_add_device(struct snd_compr *device)
694 {
695         int ret;
696
697         if (!device->card)
698                 return -EINVAL;
699
700         /* register the card */
701         ret = snd_card_register(device->card);
702         if (ret)
703                 goto out;
704         return 0;
705
706 out:
707         pr_err("failed with %d\n", ret);
708         return ret;
709
710 }
711
712 static int snd_compress_remove_device(struct snd_compr *device)
713 {
714         return snd_card_free(device->card);
715 }
716
717 /**
718  * snd_compress_register - register compressed device
719  *
720  * @device: compressed device to register
721  */
722 int snd_compress_register(struct snd_compr *device)
723 {
724         int retval;
725
726         if (device->name == NULL || device->dev == NULL || device->ops == NULL)
727                 return -EINVAL;
728
729         pr_debug("Registering compressed device %s\n", device->name);
730         if (snd_BUG_ON(!device->ops->open))
731                 return -EINVAL;
732         if (snd_BUG_ON(!device->ops->free))
733                 return -EINVAL;
734         if (snd_BUG_ON(!device->ops->set_params))
735                 return -EINVAL;
736         if (snd_BUG_ON(!device->ops->trigger))
737                 return -EINVAL;
738
739         mutex_init(&device->lock);
740
741         /* register a compressed card */
742         mutex_lock(&device_mutex);
743         retval = snd_compress_add_device(device);
744         mutex_unlock(&device_mutex);
745         return retval;
746 }
747 EXPORT_SYMBOL_GPL(snd_compress_register);
748
749 int snd_compress_deregister(struct snd_compr *device)
750 {
751         pr_debug("Removing compressed device %s\n", device->name);
752         mutex_lock(&device_mutex);
753         snd_compress_remove_device(device);
754         mutex_unlock(&device_mutex);
755         return 0;
756 }
757 EXPORT_SYMBOL_GPL(snd_compress_deregister);
758
759 static int __init snd_compress_init(void)
760 {
761         return 0;
762 }
763
764 static void __exit snd_compress_exit(void)
765 {
766 }
767
768 module_init(snd_compress_init);
769 module_exit(snd_compress_exit);
770
771 MODULE_DESCRIPTION("ALSA Compressed offload framework");
772 MODULE_AUTHOR("Vinod Koul <vinod.koul@linux.intel.com>");
773 MODULE_LICENSE("GPL v2");