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