blob: 987594a6b482a1b11e3935eec295431205058409 [file] [log] [blame]
Vinod Koula3760512011-09-02 11:36:24 +05301/*
2 * 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#include <linux/file.h>
26#include <linux/fs.h>
27#include <linux/list.h>
28#include <linux/miscdevice.h>
29#include <linux/mm.h>
30#include <linux/mutex.h>
31#include <linux/poll.h>
32#include <linux/slab.h>
33#include <linux/sched.h>
34#include <linux/uio.h>
35#include <linux/uaccess.h>
36#include <sound/snd_compress_params.h>
37#include <sound/compress_offload.h>
38#include <sound/compress_driver.h>
39
40/* TODO:
41 * - Integrate with alsa, compressed devices should register as alsa devices
42 * as /dev/snd_compr_xxx
43 * - Integrate with ASoC:
44 * Opening compressed path should also start the codec dai
45 * TBD how the cpu dai will be viewed and started.
46 * ASoC should always be optional part
47 * (we should be able to use this framework in non asoc systems
48 * - Multiple node representation
49 * driver should be able to register multiple nodes
50 * - Version numbering for API
51 */
52
53static DEFINE_MUTEX(device_mutex);
54static LIST_HEAD(device_list);
55static LIST_HEAD(misc_list);
56
57/*
58 * currently we are using misc device for registration and exposing ioctls
59 * this is temporary and will be moved to snd
60 * the device should be registered as /dev/snd_compr.....
61 */
62
63struct snd_compr_misc {
64 struct miscdevice misc;
65 struct list_head list;
66 struct snd_compr *compr;
67};
68
69struct snd_ioctl_data {
70 struct snd_compr_misc *misc;
71 unsigned long caps;
72 unsigned int minor;
73 struct snd_compr_stream stream;
74};
75
76static struct snd_compr_misc *snd_compr_get_device(unsigned int minor)
77{
78 struct snd_compr_misc *misc;
79
80 list_for_each_entry(misc, &misc_list, list) {
81 if (minor == misc->misc.minor)
82 return misc;
83 }
84 return NULL;
85}
86
87static int snd_compr_open(struct inode *inode, struct file *f)
88{
89 unsigned int minor = iminor(inode);
90 struct snd_compr_misc *misc = snd_compr_get_device(minor);
91 struct snd_ioctl_data *data;
92 struct snd_compr_runtime *runtime;
93 unsigned int direction;
94 int ret;
95
96 mutex_lock(&device_mutex);
97 if (f->f_flags & O_WRONLY)
98 direction = SNDRV_PCM_STREAM_PLAYBACK;
99 else {
100 ret = -ENXIO;
101 goto out;
102 }
Vinod Koulf15a95c2011-09-09 06:03:51 +0530103 /* curently only encoded playback is supported, above needs to be
104 * removed once we have recording support */
Vinod Koula3760512011-09-02 11:36:24 +0530105
106 data = kzalloc(sizeof(*data), GFP_KERNEL);
107 if (!data) {
108 ret = -ENOMEM;
109 goto out;
110 }
111 data->misc = misc;
112 data->minor = minor;
113 data->stream.ops = misc->compr->ops;
114 data->stream.direction = direction;
115 data->stream.private_data = misc->compr->private_data;
116 data->stream.device = misc->compr;
117 runtime = kzalloc(sizeof(*runtime), GFP_KERNEL);
118 if (!runtime) {
119 ret = -ENOMEM;
120 kfree(data);
121 goto out;
122 }
Vinod Koulf15a95c2011-09-09 06:03:51 +0530123 runtime->state = SNDRV_PCM_STATE_OPEN;
124 init_waitqueue_head(&runtime->sleep);
125 data->stream.runtime = runtime;
126 f->private_data = (void *)data;
Vinod Koula3760512011-09-02 11:36:24 +0530127 ret = misc->compr->ops->open(&data->stream);
128 if (ret) {
129 kfree(runtime);
130 kfree(data);
131 goto out;
132 }
Vinod Koula3760512011-09-02 11:36:24 +0530133out:
134 mutex_unlock(&device_mutex);
135 return ret;
136}
137
138static int snd_compr_free(struct inode *inode, struct file *f)
139{
140 struct snd_ioctl_data *data = f->private_data;
141 mutex_lock(&device_mutex);
142 data->stream.ops->free(&data->stream);
143 kfree(data->stream.runtime->buffer);
144 kfree(data->stream.runtime);
145 kfree(data);
146 mutex_unlock(&device_mutex);
147 return 0;
148}
149
150static void snd_compr_update_tstamp(struct snd_compr_stream *stream,
151 struct snd_compr_tstamp *tstamp)
152{
153 stream->ops->pointer(stream, tstamp);
154 stream->runtime->hw_pointer = tstamp->copied_bytes;
155}
156
157static size_t snd_compr_calc_avail(struct snd_compr_stream *stream,
158 struct snd_compr_avail *avail)
159{
160 size_t avail_calc;
161
162 snd_compr_update_tstamp(stream, &avail->tstamp);
Vinod Koulf15a95c2011-09-09 06:03:51 +0530163 avail_calc = stream->runtime->app_pointer - stream->runtime->hw_pointer;
Vinod Koula3760512011-09-02 11:36:24 +0530164 if (avail_calc < 0)
165 avail_calc = stream->runtime->buffer_size + avail_calc;
166 avail->avail = avail_calc;
167 return avail_calc;
168}
169
170static size_t snd_compr_get_avail(struct snd_compr_stream *stream)
171{
172 struct snd_compr_avail avail;
173
174 return snd_compr_calc_avail(stream, &avail);
175}
176
177static int
178snd_compr_ioctl_avail(struct snd_compr_stream *stream, unsigned long arg)
179{
180 struct snd_compr_avail ioctl_avail;
181
182 snd_compr_calc_avail(stream, &ioctl_avail);
183
184 if (copy_to_user((unsigned long __user *)arg, &ioctl_avail, sizeof(ioctl_avail)))
185 return -EFAULT;
186 return 0;
187}
188
189static int snd_compr_write_data(struct snd_compr_stream *stream,
190 const char __user *buf, size_t count)
191{
192 void *dstn;
193 size_t copy;
194
195 dstn = stream->runtime->buffer + stream->runtime->app_pointer;
Vinod Koulf15a95c2011-09-09 06:03:51 +0530196 if (count < stream->runtime->buffer_size - stream->runtime->app_pointer) {
Vinod Koula3760512011-09-02 11:36:24 +0530197 if (copy_from_user(dstn, buf, count))
198 return -EFAULT;
199 stream->runtime->app_pointer += count;
200 } else {
201 copy = stream->runtime->buffer_size - stream->runtime->app_pointer;
202 if (copy_from_user(dstn, buf, copy))
203 return -EFAULT;
204 if (copy_from_user(stream->runtime->buffer, buf + copy, count - copy))
205 return -EFAULT;
206 stream->runtime->app_pointer = count - copy;
207 }
208 /* if DSP cares, let it know data has been written */
209 if (stream->ops->ack)
210 stream->ops->ack(stream);
211 return count;
212}
213
Vinod Koulf15a95c2011-09-09 06:03:51 +0530214static ssize_t snd_compr_write(struct file *f, const char __user *buf,
Vinod Koula3760512011-09-02 11:36:24 +0530215 size_t count, loff_t *offset)
216{
217 struct snd_ioctl_data *data = f->private_data;
218 struct snd_compr_stream *stream;
219 size_t avail;
220 int retval;
221
222 BUG_ON(!data);
223 stream = &data->stream;
Vinod Koulf15a95c2011-09-09 06:03:51 +0530224 mutex_lock(&stream->device->lock);
Vinod Koula3760512011-09-02 11:36:24 +0530225 /* write is allowed when stream is running or has been steup */
226 if (stream->runtime->state != SNDRV_PCM_STATE_SETUP &&
Vinod Koulf15a95c2011-09-09 06:03:51 +0530227 stream->runtime->state != SNDRV_PCM_STATE_RUNNING) {
228 mutex_unlock(&stream->device->lock);
Vinod Koula3760512011-09-02 11:36:24 +0530229 return -EPERM;
Vinod Koulf15a95c2011-09-09 06:03:51 +0530230 }
Vinod Koula3760512011-09-02 11:36:24 +0530231
232 avail = snd_compr_get_avail(stream);
233 /* calculate how much we can write to buffer */
234 if (avail > count)
235 avail = count;
236
237 if (stream->ops->copy)
238 retval = stream->ops->copy(stream, buf, avail);
239 else
240 retval = snd_compr_write_data(stream, buf, avail);
241
Vinod Koulf15a95c2011-09-09 06:03:51 +0530242 /* while initiating the stream, write should be called before START
243 * call, so in setup move state */
244 if (stream->runtime->state == SNDRV_PCM_STATE_SETUP)
245 stream->runtime->state = SNDRV_PCM_STATE_PREPARED;
246
Vinod Koula3760512011-09-02 11:36:24 +0530247 mutex_unlock(&stream->device->lock);
248 return retval;
249}
250
251
Vinod Koulf15a95c2011-09-09 06:03:51 +0530252static ssize_t snd_compr_read(struct file *f, char __user *buf,
Vinod Koula3760512011-09-02 11:36:24 +0530253 size_t count, loff_t *offset)
254{
255 return -ENXIO;
256}
257
258static int snd_compr_mmap(struct file *f, struct vm_area_struct *vma)
259{
260 return -ENXIO;
261}
262
263unsigned int snd_compr_poll(struct file *f, poll_table *wait)
264{
265 struct snd_ioctl_data *data = f->private_data;
266 struct snd_compr_stream *stream;
Vinod Koulf15a95c2011-09-09 06:03:51 +0530267 int retval = 0;
Vinod Koula3760512011-09-02 11:36:24 +0530268
269 BUG_ON(!data);
270 stream = &data->stream;
271
Vinod Koulf15a95c2011-09-09 06:03:51 +0530272 mutex_lock(&stream->device->lock);
273 if (stream->runtime->state != SNDRV_PCM_STATE_RUNNING) {
274 retval = -ENXIO;
275 goto out;
276 }
Vinod Koula3760512011-09-02 11:36:24 +0530277 poll_wait(f, &stream->runtime->sleep, wait);
278
279 /* this would change after read is implemented, we would need to
280 * check for direction here */
281 if (stream->runtime->state != SNDRV_PCM_STATE_RUNNING)
Vinod Koulf15a95c2011-09-09 06:03:51 +0530282 retval = POLLOUT | POLLWRNORM;
283out:
284 mutex_unlock(&stream->device->lock);
285 return retval;
Vinod Koula3760512011-09-02 11:36:24 +0530286}
287
Vinod Koulf15a95c2011-09-09 06:03:51 +0530288void snd_compr_fragment_elapsed(struct snd_compr_stream *stream)
Vinod Koula3760512011-09-02 11:36:24 +0530289{
290 size_t avail;
291
Vinod Koulf15a95c2011-09-09 06:03:51 +0530292 if (stream->direction != SNDRV_PCM_STREAM_PLAYBACK)
293 return;
Vinod Koula3760512011-09-02 11:36:24 +0530294 avail = snd_compr_get_avail(stream);
295 if (avail >= stream->runtime->fragment_size)
296 wake_up(&stream->runtime->sleep);
297}
Vinod Koulf15a95c2011-09-09 06:03:51 +0530298EXPORT_SYMBOL_GPL(snd_compr_fragment_elapsed);
299
300void snd_compr_frame_elapsed(struct snd_compr_stream *stream)
301{
302 size_t avail;
303
304 if (stream->direction != SNDRV_PCM_STREAM_CAPTURE)
305 return;
306 avail = snd_compr_get_avail(stream);
307 if (avail)
308 wake_up(&stream->runtime->sleep);
309}
310EXPORT_SYMBOL_GPL(snd_compr_frame_elapsed);
Vinod Koula3760512011-09-02 11:36:24 +0530311
312static int snd_compr_get_caps(struct snd_compr_stream *stream, unsigned long arg)
313{
314 int retval;
315 struct snd_compr_caps caps;
316
317 if (!stream->ops->get_caps)
318 return -ENXIO;
319
320 retval = stream->ops->get_caps(stream, &caps);
321 if (retval)
322 goto out;
323 if (copy_to_user((void __user *)arg, &caps, sizeof(caps)))
324 retval = -EFAULT;
325out:
326 return retval;
327}
328
329static int snd_compr_get_codec_caps(struct snd_compr_stream *stream, unsigned long arg)
330{
331 int retval;
332 struct snd_compr_codec_caps *caps;
333
334 if (!stream->ops->get_codec_caps)
335 return -ENXIO;
336
337 caps = kmalloc(sizeof(*caps), GFP_KERNEL);
338 if (!caps)
339 return -ENOMEM;
340
341 retval = stream->ops->get_codec_caps(stream, caps);
342 if (retval)
343 goto out;
344 if (copy_to_user((void __user *)arg, caps, sizeof(*caps)))
345 retval = -EFAULT;
346
347out:
348 kfree(caps);
349 return retval;
350}
351
352/* revisit this with snd_pcm_preallocate_xxx */
353static int snd_compr_allocate_buffer(struct snd_compr_stream *stream,
354 struct snd_compr_params *params)
355{
356 unsigned int buffer_size;
357 void *buffer;
358
Vinod Koulf15a95c2011-09-09 06:03:51 +0530359 buffer_size = params->buffer.fragment_size * params->buffer.fragments;
Vinod Koula3760512011-09-02 11:36:24 +0530360 if (stream->ops->copy) {
361 buffer = NULL;
362 /* if copy is defined the driver will be required to copy
363 * the data from core
364 */
365 } else {
366 buffer = kmalloc(buffer_size, GFP_KERNEL);
367 if (!buffer)
368 return -ENOMEM;
369 }
370 stream->runtime->fragment_size = params->buffer.fragment_size;
Vinod Koulf15a95c2011-09-09 06:03:51 +0530371 stream->runtime->fragments = params->buffer.fragments;
Vinod Koula3760512011-09-02 11:36:24 +0530372 stream->runtime->buffer = buffer;
373 stream->runtime->buffer_size = buffer_size;
374 return 0;
375}
376
377static int snd_compr_set_params(struct snd_compr_stream *stream, unsigned long arg)
378{
379 struct snd_compr_params *params;
380 int retval;
381
382 if (stream->runtime->state == SNDRV_PCM_STATE_OPEN) {
383 /*
384 * we should allow parameter change only when stream has been
385 * opened not in other cases
386 */
387 params = kmalloc(sizeof(*params), GFP_KERNEL);
388 if (!params)
389 return -ENOMEM;
Vinod Koulf15a95c2011-09-09 06:03:51 +0530390 if (copy_from_user(params, (void __user *)arg, sizeof(*params)))
Vinod Koula3760512011-09-02 11:36:24 +0530391 return -EFAULT;
392 retval = snd_compr_allocate_buffer(stream, params);
393 if (retval) {
394 kfree(params);
395 return -ENOMEM;
396 }
397 retval = stream->ops->set_params(stream, params);
398 if (retval)
399 goto out;
400 stream->runtime->state = SNDRV_PCM_STATE_SETUP;
401 } else
402 return -EPERM;
403out:
404 kfree(params);
405 return retval;
406}
407
408static int snd_compr_get_params(struct snd_compr_stream *stream, unsigned long arg)
409{
410 struct snd_compr_params *params;
411 int retval;
412
413 if (!stream->ops->get_params)
414 return -ENXIO;
415
416 params = kmalloc(sizeof(*params), GFP_KERNEL);
417 if (!params)
418 return -ENOMEM;
419 retval = stream->ops->get_params(stream, params);
420 if (retval)
421 goto out;
422 if (copy_to_user((char __user *)arg, params, sizeof(*params)))
423 retval = -EFAULT;
424
425out:
426 kfree(params);
427 return retval;
428}
429
430static int snd_compr_tstamp(struct snd_compr_stream *stream, unsigned long arg)
431{
432 struct snd_compr_tstamp tstamp;
433
434 snd_compr_update_tstamp(stream, &tstamp);
435 if (copy_to_user((struct snd_compr_tstamp __user *)arg, &tstamp, sizeof(tstamp)))
436 return -EFAULT;
437 return 0;
438}
439
440static int snd_compr_pause(struct snd_compr_stream *stream)
441{
442 int retval;
443
444 if (stream->runtime->state == SNDRV_PCM_STATE_PAUSED)
445 return 0;
446 retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_PAUSE_PUSH);
447 if (!retval) {
448 stream->runtime->state = SNDRV_PCM_STATE_PAUSED;
449 wake_up(&stream->runtime->sleep);
450 }
451 return retval;
452}
453
454static int snd_compr_resume(struct snd_compr_stream *stream)
455{
456 int retval;
457
458 if (stream->runtime->state != SNDRV_PCM_STATE_PAUSED)
459 return -EPERM;
460 retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_PAUSE_RELEASE);
461 if (!retval)
462 stream->runtime->state = SNDRV_PCM_STATE_RUNNING;
463 return retval;
464}
465
466static int snd_compr_start(struct snd_compr_stream *stream)
467{
468 int retval;
469
470 if (stream->runtime->state != SNDRV_PCM_STATE_PREPARED)
471 return -EPERM;
472 retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_START);
473 if (!retval)
474 stream->runtime->state = SNDRV_PCM_STATE_RUNNING;
475 return retval;
476}
477
478static int snd_compr_stop(struct snd_compr_stream *stream)
479{
480 int retval;
481
482 if (stream->runtime->state != SNDRV_PCM_STATE_PREPARED)
483 return -EPERM;
484 retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_STOP);
485 if (!retval) {
486 stream->runtime->state = SNDRV_PCM_STATE_SETUP;
487 wake_up(&stream->runtime->sleep);
488 }
489 return retval;
490}
491
492static int snd_compr_drain(struct snd_compr_stream *stream)
493{
494 int retval;
495
496 if (stream->runtime->state != SNDRV_PCM_STATE_PREPARED ||
497 stream->runtime->state != SNDRV_PCM_STATE_PAUSED)
498 return -EPERM;
499 retval = stream->ops->trigger(stream, SND_COMPR_TRIGGER_DRAIN);
500 if (!retval) {
501 stream->runtime->state = SNDRV_PCM_STATE_SETUP;
502 wake_up(&stream->runtime->sleep);
503 }
504 return retval;
505}
506
507static long snd_compr_ioctl(struct file *f, unsigned int cmd, unsigned long arg)
508{
509 struct snd_ioctl_data *data = f->private_data;
510 struct snd_compr_stream *stream;
511 int retval = -ENOTTY;
512
513 BUG_ON(!data);
514 stream = &data->stream;
515 mutex_lock(&stream->device->lock);
516 switch (_IOC_NR(cmd)) {
517 case _IOC_NR(SNDRV_COMPRESS_GET_CAPS):
518 retval = snd_compr_get_caps(stream, arg);
519 break;
520 case _IOC_NR(SNDRV_COMPRESS_GET_CODEC_CAPS):
521 retval = snd_compr_get_codec_caps(stream, arg);
522 break;
523 case _IOC_NR(SNDRV_COMPRESS_SET_PARAMS):
524 retval = snd_compr_set_params(stream, arg);
525 break;
526 case _IOC_NR(SNDRV_COMPRESS_GET_PARAMS):
527 retval = snd_compr_get_params(stream, arg);
528 break;
529 case _IOC_NR(SNDRV_COMPRESS_TSTAMP):
530 retval = snd_compr_tstamp(stream, arg);
531 break;
532 case _IOC_NR(SNDRV_COMPRESS_AVAIL):
533 retval = snd_compr_ioctl_avail(stream, arg);
534 case _IOC_NR(SNDRV_COMPRESS_PAUSE):
535 retval = snd_compr_pause(stream);
536 break;
537 case _IOC_NR(SNDRV_COMPRESS_RESUME):
538 retval = snd_compr_resume(stream);
539 break;
540 case _IOC_NR(SNDRV_COMPRESS_START):
541 retval = snd_compr_start(stream);
542 break;
543 case _IOC_NR(SNDRV_COMPRESS_STOP):
544 retval = snd_compr_stop(stream);
545 break;
546 case _IOC_NR(SNDRV_COMPRESS_DRAIN):
547 cmd = SND_COMPR_TRIGGER_DRAIN;
548 retval = snd_compr_drain(stream);
549 break;
550 }
551 mutex_unlock(&stream->device->lock);
552 return retval;
553}
554
555static const struct file_operations snd_comp_file = {
556 .owner = THIS_MODULE,
557 .open = snd_compr_open,
558 .release = snd_compr_free,
559 .read = snd_compr_read,
560 .write = snd_compr_write,
561 .unlocked_ioctl = snd_compr_ioctl,
562 .mmap = snd_compr_mmap,
563 .poll = snd_compr_poll,
564};
565
566static int snd_compress_add_device(struct snd_compr *device)
567{
568 int ret;
569
570 struct snd_compr_misc *misc = kzalloc(sizeof(*misc), GFP_KERNEL);
571
572 misc->misc.name = device->name;
573 misc->misc.fops = &snd_comp_file;
574 misc->misc.minor = MISC_DYNAMIC_MINOR;
575 misc->compr = device;
576 ret = misc_register(&misc->misc);
577 if (ret) {
578 pr_err("couldn't register misc device\n");
579 kfree(misc);
580 } else {
581 pr_debug("Got minor %d\n", misc->misc.minor);
582 list_add_tail(&misc->list, &misc_list);
583 }
584 return ret;
585}
586
587static int snd_compress_remove_device(struct snd_compr *device)
588{
589 struct snd_compr_misc *misc, *__misc;
590
591 list_for_each_entry_safe(misc, __misc, &misc_list, list) {
592 if (device == misc->compr) {
593 misc_deregister(&misc->misc);
594 list_del(&device->list);
595 kfree(misc);
596 }
597 }
598 return 0;
599}
600/**
601 * snd_compress_register - register compressed device
602 *
603 * @device: compressed device to register
604 */
605int snd_compress_register(struct snd_compr *device)
606{
607 int retval;
608
609 if (device->name == NULL || device->dev == NULL || device->ops == NULL)
610 return -EINVAL;
611 BUG_ON(!device->ops->open);
612 BUG_ON(!device->ops->free);
613 BUG_ON(!device->ops->set_params);
614 BUG_ON(!device->ops->get_params);
615 BUG_ON(!device->ops->trigger);
616 BUG_ON(!device->ops->pointer);
617 BUG_ON(!device->ops->get_caps);
618 BUG_ON(!device->ops->get_codec_caps);
619
620 INIT_LIST_HEAD(&device->list);
621 /* todo register the compressed streams */
622 /* todo integrate with asoc */
623
624 /* register a compressed card TBD if this needs change */
625
626 pr_debug("Registering compressed device %s\n", device->name);
627 mutex_lock(&device_mutex);
628 /* register a msic device for now */
629 retval = snd_compress_add_device(device);
630 if (!retval)
631 list_add_tail(&device->list, &device_list);
632 mutex_unlock(&device_mutex);
633 return retval;
634}
635EXPORT_SYMBOL_GPL(snd_compress_register);
636
637int snd_compress_deregister(struct snd_compr *device)
638{
639 pr_debug("Removing compressed device %s\n", device->name);
640 mutex_lock(&device_mutex);
641 snd_compress_remove_device(device);
642 list_del(&device->list);
643 mutex_unlock(&device_mutex);
644 return 0;
645}
646EXPORT_SYMBOL_GPL(snd_compress_deregister);
647
648static int __init snd_compress_init(void)
649{
650 return 0;
651}
652
653static void __exit snd_compress_exit(void)
654{
655}
656
657module_init(snd_compress_init);
658module_exit(snd_compress_exit);