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"); 103a0830dbdSTakashi Iwai snd_card_unref(compr->card); 104b21c60a4SVinod Koul return -EINVAL; 105b21c60a4SVinod Koul } 106b21c60a4SVinod Koul 107b21c60a4SVinod Koul data = kzalloc(sizeof(*data), GFP_KERNEL); 108a0830dbdSTakashi Iwai if (!data) { 109a0830dbdSTakashi Iwai snd_card_unref(compr->card); 110b21c60a4SVinod Koul return -ENOMEM; 111a0830dbdSTakashi Iwai } 112b21c60a4SVinod Koul data->stream.ops = compr->ops; 113b21c60a4SVinod Koul data->stream.direction = dirn; 114b21c60a4SVinod Koul data->stream.private_data = compr->private_data; 115b21c60a4SVinod Koul data->stream.device = compr; 116b21c60a4SVinod Koul runtime = kzalloc(sizeof(*runtime), GFP_KERNEL); 117b21c60a4SVinod Koul if (!runtime) { 118b21c60a4SVinod Koul kfree(data); 119a0830dbdSTakashi Iwai snd_card_unref(compr->card); 120b21c60a4SVinod Koul return -ENOMEM; 121b21c60a4SVinod Koul } 122b21c60a4SVinod Koul runtime->state = SNDRV_PCM_STATE_OPEN; 123b21c60a4SVinod Koul init_waitqueue_head(&runtime->sleep); 124b21c60a4SVinod Koul data->stream.runtime = runtime; 125b21c60a4SVinod Koul f->private_data = (void *)data; 126b21c60a4SVinod Koul mutex_lock(&compr->lock); 127b21c60a4SVinod Koul ret = compr->ops->open(&data->stream); 128b21c60a4SVinod Koul mutex_unlock(&compr->lock); 129b21c60a4SVinod Koul if (ret) { 130b21c60a4SVinod Koul kfree(runtime); 131b21c60a4SVinod Koul kfree(data); 132b21c60a4SVinod Koul } 133a0830dbdSTakashi Iwai snd_card_unref(compr->card); 134a0830dbdSTakashi Iwai return 0; 135b21c60a4SVinod Koul } 136b21c60a4SVinod Koul 137b21c60a4SVinod Koul static int snd_compr_free(struct inode *inode, struct file *f) 138b21c60a4SVinod Koul { 139b21c60a4SVinod Koul struct snd_compr_file *data = f->private_data; 140b21c60a4SVinod Koul data->stream.ops->free(&data->stream); 141b21c60a4SVinod Koul kfree(data->stream.runtime->buffer); 142b21c60a4SVinod Koul kfree(data->stream.runtime); 143b21c60a4SVinod Koul kfree(data); 144b21c60a4SVinod Koul return 0; 145b21c60a4SVinod Koul } 146b21c60a4SVinod Koul 14717ac8e5cSRichard Fitzgerald static int snd_compr_update_tstamp(struct snd_compr_stream *stream, 148b21c60a4SVinod Koul struct snd_compr_tstamp *tstamp) 149b21c60a4SVinod Koul { 150b21c60a4SVinod Koul if (!stream->ops->pointer) 15117ac8e5cSRichard Fitzgerald return -ENOTSUPP; 152b21c60a4SVinod Koul stream->ops->pointer(stream, tstamp); 153b21c60a4SVinod Koul pr_debug("dsp consumed till %d total %d bytes\n", 154b21c60a4SVinod Koul tstamp->byte_offset, tstamp->copied_total); 155b21c60a4SVinod Koul stream->runtime->hw_pointer = tstamp->byte_offset; 156b21c60a4SVinod Koul stream->runtime->total_bytes_transferred = tstamp->copied_total; 15717ac8e5cSRichard Fitzgerald return 0; 158b21c60a4SVinod Koul } 159b21c60a4SVinod Koul 160b21c60a4SVinod Koul static size_t snd_compr_calc_avail(struct snd_compr_stream *stream, 161b21c60a4SVinod Koul struct snd_compr_avail *avail) 162b21c60a4SVinod Koul { 16317ac8e5cSRichard Fitzgerald memset(avail, 0, sizeof(*avail)); 164b21c60a4SVinod Koul snd_compr_update_tstamp(stream, &avail->tstamp); 16517ac8e5cSRichard Fitzgerald /* Still need to return avail even if tstamp can't be filled in */ 166b21c60a4SVinod Koul 167b21c60a4SVinod Koul /* FIXME: This needs to be different for capture stream, 168b21c60a4SVinod Koul available is # of compressed data, for playback it's 169b21c60a4SVinod Koul remainder of buffer */ 170b21c60a4SVinod Koul 171b21c60a4SVinod Koul if (stream->runtime->total_bytes_available == 0 && 172b21c60a4SVinod Koul stream->runtime->state == SNDRV_PCM_STATE_SETUP) { 173b21c60a4SVinod Koul pr_debug("detected init and someone forgot to do a write\n"); 174b21c60a4SVinod Koul return stream->runtime->buffer_size; 175b21c60a4SVinod Koul } 176b21c60a4SVinod Koul pr_debug("app wrote %lld, DSP consumed %lld\n", 177b21c60a4SVinod Koul stream->runtime->total_bytes_available, 178b21c60a4SVinod Koul stream->runtime->total_bytes_transferred); 179b21c60a4SVinod Koul if (stream->runtime->total_bytes_available == 180b21c60a4SVinod Koul stream->runtime->total_bytes_transferred) { 181b21c60a4SVinod Koul pr_debug("both pointers are same, returning full avail\n"); 182b21c60a4SVinod Koul return stream->runtime->buffer_size; 183b21c60a4SVinod Koul } 184b21c60a4SVinod Koul 1854c28e32dSCharles Keepax avail->avail = stream->runtime->buffer_size - 1864c28e32dSCharles Keepax (stream->runtime->total_bytes_available - 1874c28e32dSCharles Keepax stream->runtime->total_bytes_transferred); 1884c28e32dSCharles Keepax pr_debug("ret avail as %lld\n", avail->avail); 1894c28e32dSCharles Keepax return avail->avail; 190b21c60a4SVinod Koul } 191b21c60a4SVinod Koul 192b21c60a4SVinod Koul static inline size_t snd_compr_get_avail(struct snd_compr_stream *stream) 193b21c60a4SVinod Koul { 194b21c60a4SVinod Koul struct snd_compr_avail avail; 195b21c60a4SVinod Koul 196b21c60a4SVinod Koul return snd_compr_calc_avail(stream, &avail); 197b21c60a4SVinod Koul } 198b21c60a4SVinod Koul 199b21c60a4SVinod Koul static int 200b21c60a4SVinod Koul snd_compr_ioctl_avail(struct snd_compr_stream *stream, unsigned long arg) 201b21c60a4SVinod Koul { 202b21c60a4SVinod Koul struct snd_compr_avail ioctl_avail; 203b21c60a4SVinod Koul size_t avail; 204b21c60a4SVinod Koul 205b21c60a4SVinod Koul avail = snd_compr_calc_avail(stream, &ioctl_avail); 206b21c60a4SVinod Koul ioctl_avail.avail = avail; 207b21c60a4SVinod Koul 208b21c60a4SVinod Koul if (copy_to_user((__u64 __user *)arg, 209b21c60a4SVinod Koul &ioctl_avail, sizeof(ioctl_avail))) 210b21c60a4SVinod Koul return -EFAULT; 211b21c60a4SVinod Koul return 0; 212b21c60a4SVinod Koul } 213b21c60a4SVinod Koul 214b21c60a4SVinod Koul static int snd_compr_write_data(struct snd_compr_stream *stream, 215b21c60a4SVinod Koul const char __user *buf, size_t count) 216b21c60a4SVinod Koul { 217b21c60a4SVinod Koul void *dstn; 218b21c60a4SVinod Koul size_t copy; 219b21c60a4SVinod Koul struct snd_compr_runtime *runtime = stream->runtime; 220b21c60a4SVinod Koul 221b21c60a4SVinod Koul dstn = runtime->buffer + runtime->app_pointer; 222b21c60a4SVinod Koul pr_debug("copying %ld at %lld\n", 223b21c60a4SVinod Koul (unsigned long)count, runtime->app_pointer); 224b21c60a4SVinod Koul if (count < runtime->buffer_size - runtime->app_pointer) { 225b21c60a4SVinod Koul if (copy_from_user(dstn, buf, count)) 226b21c60a4SVinod Koul return -EFAULT; 227b21c60a4SVinod Koul runtime->app_pointer += count; 228b21c60a4SVinod Koul } else { 229b21c60a4SVinod Koul copy = runtime->buffer_size - runtime->app_pointer; 230b21c60a4SVinod Koul if (copy_from_user(dstn, buf, copy)) 231b21c60a4SVinod Koul return -EFAULT; 232b21c60a4SVinod Koul if (copy_from_user(runtime->buffer, buf + copy, count - copy)) 233b21c60a4SVinod Koul return -EFAULT; 234b21c60a4SVinod Koul runtime->app_pointer = count - copy; 235b21c60a4SVinod Koul } 236b21c60a4SVinod Koul /* if DSP cares, let it know data has been written */ 237b21c60a4SVinod Koul if (stream->ops->ack) 238b21c60a4SVinod Koul stream->ops->ack(stream, count); 239b21c60a4SVinod Koul return count; 240b21c60a4SVinod Koul } 241b21c60a4SVinod Koul 242b21c60a4SVinod Koul static ssize_t snd_compr_write(struct file *f, const char __user *buf, 243b21c60a4SVinod Koul size_t count, loff_t *offset) 244b21c60a4SVinod Koul { 245b21c60a4SVinod Koul struct snd_compr_file *data = f->private_data; 246b21c60a4SVinod Koul struct snd_compr_stream *stream; 247b21c60a4SVinod Koul size_t avail; 248b21c60a4SVinod Koul int retval; 249b21c60a4SVinod Koul 250b21c60a4SVinod Koul if (snd_BUG_ON(!data)) 251b21c60a4SVinod Koul return -EFAULT; 252b21c60a4SVinod Koul 253b21c60a4SVinod Koul stream = &data->stream; 254b21c60a4SVinod Koul mutex_lock(&stream->device->lock); 255b21c60a4SVinod Koul /* write is allowed when stream is running or has been steup */ 256b21c60a4SVinod Koul if (stream->runtime->state != SNDRV_PCM_STATE_SETUP && 257b21c60a4SVinod Koul stream->runtime->state != SNDRV_PCM_STATE_RUNNING) { 258b21c60a4SVinod Koul mutex_unlock(&stream->device->lock); 259b21c60a4SVinod Koul return -EBADFD; 260b21c60a4SVinod Koul } 261b21c60a4SVinod Koul 262b21c60a4SVinod Koul avail = snd_compr_get_avail(stream); 263b21c60a4SVinod Koul pr_debug("avail returned %ld\n", (unsigned long)avail); 264b21c60a4SVinod Koul /* calculate how much we can write to buffer */ 265b21c60a4SVinod Koul if (avail > count) 266b21c60a4SVinod Koul avail = count; 267b21c60a4SVinod Koul 268b21c60a4SVinod Koul if (stream->ops->copy) 269b21c60a4SVinod Koul retval = stream->ops->copy(stream, buf, avail); 270b21c60a4SVinod Koul else 271b21c60a4SVinod Koul retval = snd_compr_write_data(stream, buf, avail); 272b21c60a4SVinod Koul if (retval > 0) 273b21c60a4SVinod Koul stream->runtime->total_bytes_available += retval; 274b21c60a4SVinod Koul 275b21c60a4SVinod Koul /* while initiating the stream, write should be called before START 276b21c60a4SVinod Koul * call, so in setup move state */ 277b21c60a4SVinod Koul if (stream->runtime->state == SNDRV_PCM_STATE_SETUP) { 278b21c60a4SVinod Koul stream->runtime->state = SNDRV_PCM_STATE_PREPARED; 279b21c60a4SVinod Koul pr_debug("stream prepared, Houston we are good to go\n"); 280b21c60a4SVinod Koul } 281b21c60a4SVinod Koul 282b21c60a4SVinod Koul mutex_unlock(&stream->device->lock); 283b21c60a4SVinod Koul return retval; 284b21c60a4SVinod Koul } 285b21c60a4SVinod Koul 286b21c60a4SVinod Koul 287b21c60a4SVinod Koul static ssize_t snd_compr_read(struct file *f, char __user *buf, 288b21c60a4SVinod Koul size_t count, loff_t *offset) 289b21c60a4SVinod Koul { 290b21c60a4SVinod Koul return -ENXIO; 291b21c60a4SVinod Koul } 292b21c60a4SVinod Koul 293b21c60a4SVinod Koul static int snd_compr_mmap(struct file *f, struct vm_area_struct *vma) 294b21c60a4SVinod Koul { 295b21c60a4SVinod Koul return -ENXIO; 296b21c60a4SVinod Koul } 297b21c60a4SVinod Koul 298b21c60a4SVinod Koul static inline int snd_compr_get_poll(struct snd_compr_stream *stream) 299b21c60a4SVinod Koul { 300b21c60a4SVinod Koul if (stream->direction == SND_COMPRESS_PLAYBACK) 301b21c60a4SVinod Koul return POLLOUT | POLLWRNORM; 302b21c60a4SVinod Koul else 303b21c60a4SVinod Koul return POLLIN | POLLRDNORM; 304b21c60a4SVinod Koul } 305b21c60a4SVinod Koul 306b21c60a4SVinod Koul static unsigned int snd_compr_poll(struct file *f, poll_table *wait) 307b21c60a4SVinod Koul { 308b21c60a4SVinod Koul struct snd_compr_file *data = f->private_data; 309b21c60a4SVinod Koul struct snd_compr_stream *stream; 310b21c60a4SVinod Koul size_t avail; 311b21c60a4SVinod Koul int retval = 0; 312b21c60a4SVinod Koul 313b21c60a4SVinod Koul if (snd_BUG_ON(!data)) 314b21c60a4SVinod Koul return -EFAULT; 315b21c60a4SVinod Koul stream = &data->stream; 316b21c60a4SVinod Koul if (snd_BUG_ON(!stream)) 317b21c60a4SVinod Koul return -EFAULT; 318b21c60a4SVinod Koul 319b21c60a4SVinod Koul mutex_lock(&stream->device->lock); 320b21c60a4SVinod Koul if (stream->runtime->state == SNDRV_PCM_STATE_PAUSED || 321b21c60a4SVinod Koul stream->runtime->state == SNDRV_PCM_STATE_OPEN) { 322b21c60a4SVinod Koul retval = -EBADFD; 323b21c60a4SVinod Koul goto out; 324b21c60a4SVinod Koul } 325b21c60a4SVinod Koul poll_wait(f, &stream->runtime->sleep, wait); 326b21c60a4SVinod Koul 327b21c60a4SVinod Koul avail = snd_compr_get_avail(stream); 328b21c60a4SVinod Koul pr_debug("avail is %ld\n", (unsigned long)avail); 329b21c60a4SVinod Koul /* check if we have at least one fragment to fill */ 330b21c60a4SVinod Koul switch (stream->runtime->state) { 331b21c60a4SVinod Koul case SNDRV_PCM_STATE_DRAINING: 332b21c60a4SVinod Koul /* stream has been woken up after drain is complete 333b21c60a4SVinod Koul * draining done so set stream state to stopped 334b21c60a4SVinod Koul */ 335b21c60a4SVinod Koul retval = snd_compr_get_poll(stream); 336b21c60a4SVinod Koul stream->runtime->state = SNDRV_PCM_STATE_SETUP; 337b21c60a4SVinod Koul break; 338b21c60a4SVinod Koul case SNDRV_PCM_STATE_RUNNING: 339b21c60a4SVinod Koul case SNDRV_PCM_STATE_PREPARED: 340b21c60a4SVinod Koul case SNDRV_PCM_STATE_PAUSED: 341b21c60a4SVinod Koul if (avail >= stream->runtime->fragment_size) 342b21c60a4SVinod Koul retval = snd_compr_get_poll(stream); 343b21c60a4SVinod Koul break; 344b21c60a4SVinod Koul default: 345b21c60a4SVinod Koul if (stream->direction == SND_COMPRESS_PLAYBACK) 346b21c60a4SVinod Koul retval = POLLOUT | POLLWRNORM | POLLERR; 347b21c60a4SVinod Koul else 348b21c60a4SVinod Koul retval = POLLIN | POLLRDNORM | POLLERR; 349b21c60a4SVinod Koul break; 350b21c60a4SVinod Koul } 351b21c60a4SVinod Koul out: 352b21c60a4SVinod Koul mutex_unlock(&stream->device->lock); 353b21c60a4SVinod Koul return retval; 354b21c60a4SVinod Koul } 355b21c60a4SVinod Koul 356b21c60a4SVinod Koul static int 357b21c60a4SVinod Koul snd_compr_get_caps(struct snd_compr_stream *stream, unsigned long arg) 358b21c60a4SVinod Koul { 359b21c60a4SVinod Koul int retval; 360b21c60a4SVinod Koul struct snd_compr_caps caps; 361b21c60a4SVinod Koul 362b21c60a4SVinod Koul if (!stream->ops->get_caps) 363b21c60a4SVinod Koul return -ENXIO; 364b21c60a4SVinod Koul 365b21c60a4SVinod Koul retval = stream->ops->get_caps(stream, &caps); 366b21c60a4SVinod Koul if (retval) 367b21c60a4SVinod Koul goto out; 368b21c60a4SVinod Koul if (copy_to_user((void __user *)arg, &caps, sizeof(caps))) 369b21c60a4SVinod Koul retval = -EFAULT; 370b21c60a4SVinod Koul out: 371b21c60a4SVinod Koul return retval; 372b21c60a4SVinod Koul } 373b21c60a4SVinod Koul 374b21c60a4SVinod Koul static int 375b21c60a4SVinod Koul snd_compr_get_codec_caps(struct snd_compr_stream *stream, unsigned long arg) 376b21c60a4SVinod Koul { 377b21c60a4SVinod Koul int retval; 378b21c60a4SVinod Koul struct snd_compr_codec_caps *caps; 379b21c60a4SVinod Koul 380b21c60a4SVinod Koul if (!stream->ops->get_codec_caps) 381b21c60a4SVinod Koul return -ENXIO; 382b21c60a4SVinod Koul 383b21c60a4SVinod Koul caps = kmalloc(sizeof(*caps), GFP_KERNEL); 384b21c60a4SVinod Koul if (!caps) 385b21c60a4SVinod Koul return -ENOMEM; 386b21c60a4SVinod Koul 387b21c60a4SVinod Koul retval = stream->ops->get_codec_caps(stream, caps); 388b21c60a4SVinod Koul if (retval) 389b21c60a4SVinod Koul goto out; 390b21c60a4SVinod Koul if (copy_to_user((void __user *)arg, caps, sizeof(*caps))) 391b21c60a4SVinod Koul retval = -EFAULT; 392b21c60a4SVinod Koul 393b21c60a4SVinod Koul out: 394b21c60a4SVinod Koul kfree(caps); 395b21c60a4SVinod Koul return retval; 396b21c60a4SVinod Koul } 397b21c60a4SVinod Koul 398b21c60a4SVinod Koul /* revisit this with snd_pcm_preallocate_xxx */ 399b21c60a4SVinod Koul static int snd_compr_allocate_buffer(struct snd_compr_stream *stream, 400b21c60a4SVinod Koul struct snd_compr_params *params) 401b21c60a4SVinod Koul { 402b21c60a4SVinod Koul unsigned int buffer_size; 403b21c60a4SVinod Koul void *buffer; 404b21c60a4SVinod Koul 405b21c60a4SVinod Koul buffer_size = params->buffer.fragment_size * params->buffer.fragments; 406b21c60a4SVinod Koul if (stream->ops->copy) { 407b21c60a4SVinod Koul buffer = NULL; 408b21c60a4SVinod Koul /* if copy is defined the driver will be required to copy 409b21c60a4SVinod Koul * the data from core 410b21c60a4SVinod Koul */ 411b21c60a4SVinod Koul } else { 412b21c60a4SVinod Koul buffer = kmalloc(buffer_size, GFP_KERNEL); 413b21c60a4SVinod Koul if (!buffer) 414b21c60a4SVinod Koul return -ENOMEM; 415b21c60a4SVinod Koul } 416b21c60a4SVinod Koul stream->runtime->fragment_size = params->buffer.fragment_size; 417b21c60a4SVinod Koul stream->runtime->fragments = params->buffer.fragments; 418b21c60a4SVinod Koul stream->runtime->buffer = buffer; 419b21c60a4SVinod Koul stream->runtime->buffer_size = buffer_size; 420b21c60a4SVinod Koul return 0; 421b21c60a4SVinod Koul } 422b21c60a4SVinod Koul 4234dc040a0SVinod Koul static int snd_compress_check_input(struct snd_compr_params *params) 4244dc040a0SVinod Koul { 4254dc040a0SVinod Koul /* first let's check the buffer parameter's */ 4264dc040a0SVinod Koul if (params->buffer.fragment_size == 0 || 4274dc040a0SVinod Koul params->buffer.fragments > SIZE_MAX / params->buffer.fragment_size) 4284dc040a0SVinod Koul return -EINVAL; 4294dc040a0SVinod Koul 430fb4a9779SVinod Koul /* now codec parameters */ 431fb4a9779SVinod Koul if (params->codec.id == 0 || params->codec.id > SND_AUDIOCODEC_MAX) 432fb4a9779SVinod Koul return -EINVAL; 433fb4a9779SVinod Koul 434fb4a9779SVinod Koul if (params->codec.ch_in == 0 || params->codec.ch_out == 0) 435fb4a9779SVinod Koul return -EINVAL; 436fb4a9779SVinod Koul 437fb4a9779SVinod Koul if (!(params->codec.sample_rate & SNDRV_PCM_RATE_8000_192000)) 438fb4a9779SVinod Koul return -EINVAL; 439fb4a9779SVinod Koul 4404dc040a0SVinod Koul return 0; 4414dc040a0SVinod Koul } 4424dc040a0SVinod Koul 443b21c60a4SVinod Koul static int 444b21c60a4SVinod Koul snd_compr_set_params(struct snd_compr_stream *stream, unsigned long arg) 445b21c60a4SVinod Koul { 446b21c60a4SVinod Koul struct snd_compr_params *params; 447b21c60a4SVinod Koul int retval; 448b21c60a4SVinod Koul 449b21c60a4SVinod Koul if (stream->runtime->state == SNDRV_PCM_STATE_OPEN) { 450b21c60a4SVinod Koul /* 451b21c60a4SVinod Koul * we should allow parameter change only when stream has been 452b21c60a4SVinod Koul * opened not in other cases 453b21c60a4SVinod Koul */ 454b21c60a4SVinod Koul params = kmalloc(sizeof(*params), GFP_KERNEL); 455b21c60a4SVinod Koul if (!params) 456b21c60a4SVinod Koul return -ENOMEM; 457769fab2aSJesper Juhl if (copy_from_user(params, (void __user *)arg, sizeof(*params))) { 458769fab2aSJesper Juhl retval = -EFAULT; 459769fab2aSJesper Juhl goto out; 460769fab2aSJesper Juhl } 4614dc040a0SVinod Koul 4624dc040a0SVinod Koul retval = snd_compress_check_input(params); 4634dc040a0SVinod Koul if (retval) 4644dc040a0SVinod Koul goto out; 4654dc040a0SVinod Koul 466b21c60a4SVinod Koul retval = snd_compr_allocate_buffer(stream, params); 467b21c60a4SVinod Koul if (retval) { 468769fab2aSJesper Juhl retval = -ENOMEM; 469769fab2aSJesper Juhl goto out; 470b21c60a4SVinod Koul } 4714dc040a0SVinod Koul 472b21c60a4SVinod Koul retval = stream->ops->set_params(stream, params); 473b21c60a4SVinod Koul if (retval) 474b21c60a4SVinod Koul goto out; 475b21c60a4SVinod Koul stream->runtime->state = SNDRV_PCM_STATE_SETUP; 4769727b490SJeeja KP stream->metadata_set = false; 4779727b490SJeeja KP stream->next_track = false; 478769fab2aSJesper Juhl } else { 479b21c60a4SVinod Koul return -EPERM; 480769fab2aSJesper Juhl } 481b21c60a4SVinod Koul out: 482b21c60a4SVinod Koul kfree(params); 483b21c60a4SVinod Koul return retval; 484b21c60a4SVinod Koul } 485b21c60a4SVinod Koul 486b21c60a4SVinod Koul static int 487b21c60a4SVinod Koul snd_compr_get_params(struct snd_compr_stream *stream, unsigned long arg) 488b21c60a4SVinod Koul { 489b21c60a4SVinod Koul struct snd_codec *params; 490b21c60a4SVinod Koul int retval; 491b21c60a4SVinod Koul 492b21c60a4SVinod Koul if (!stream->ops->get_params) 493b21c60a4SVinod Koul return -EBADFD; 494b21c60a4SVinod Koul 495b21c60a4SVinod Koul params = kmalloc(sizeof(*params), GFP_KERNEL); 496b21c60a4SVinod Koul if (!params) 497b21c60a4SVinod Koul return -ENOMEM; 498b21c60a4SVinod Koul retval = stream->ops->get_params(stream, params); 499b21c60a4SVinod Koul if (retval) 500b21c60a4SVinod Koul goto out; 501b21c60a4SVinod Koul if (copy_to_user((char __user *)arg, params, sizeof(*params))) 502b21c60a4SVinod Koul retval = -EFAULT; 503b21c60a4SVinod Koul 504b21c60a4SVinod Koul out: 505b21c60a4SVinod Koul kfree(params); 506b21c60a4SVinod Koul return retval; 507b21c60a4SVinod Koul } 508b21c60a4SVinod Koul 5099727b490SJeeja KP static int 5109727b490SJeeja KP snd_compr_get_metadata(struct snd_compr_stream *stream, unsigned long arg) 5119727b490SJeeja KP { 5129727b490SJeeja KP struct snd_compr_metadata metadata; 5139727b490SJeeja KP int retval; 5149727b490SJeeja KP 5159727b490SJeeja KP if (!stream->ops->get_metadata) 5169727b490SJeeja KP return -ENXIO; 5179727b490SJeeja KP 5189727b490SJeeja KP if (copy_from_user(&metadata, (void __user *)arg, sizeof(metadata))) 5199727b490SJeeja KP return -EFAULT; 5209727b490SJeeja KP 5219727b490SJeeja KP retval = stream->ops->get_metadata(stream, &metadata); 5229727b490SJeeja KP if (retval != 0) 5239727b490SJeeja KP return retval; 5249727b490SJeeja KP 5259727b490SJeeja KP if (copy_to_user((void __user *)arg, &metadata, sizeof(metadata))) 5269727b490SJeeja KP return -EFAULT; 5279727b490SJeeja KP 5289727b490SJeeja KP return 0; 5299727b490SJeeja KP } 5309727b490SJeeja KP 5319727b490SJeeja KP static int 5329727b490SJeeja KP snd_compr_set_metadata(struct snd_compr_stream *stream, unsigned long arg) 5339727b490SJeeja KP { 5349727b490SJeeja KP struct snd_compr_metadata metadata; 5359727b490SJeeja KP int retval; 5369727b490SJeeja KP 5379727b490SJeeja KP if (!stream->ops->set_metadata) 5389727b490SJeeja KP return -ENXIO; 5399727b490SJeeja KP /* 5409727b490SJeeja KP * we should allow parameter change only when stream has been 5419727b490SJeeja KP * opened not in other cases 5429727b490SJeeja KP */ 5439727b490SJeeja KP if (copy_from_user(&metadata, (void __user *)arg, sizeof(metadata))) 5449727b490SJeeja KP return -EFAULT; 5459727b490SJeeja KP 5469727b490SJeeja KP retval = stream->ops->set_metadata(stream, &metadata); 5479727b490SJeeja KP stream->metadata_set = true; 5489727b490SJeeja KP 5499727b490SJeeja KP return retval; 5509727b490SJeeja KP } 5519727b490SJeeja KP 552b21c60a4SVinod Koul static inline int 553b21c60a4SVinod Koul snd_compr_tstamp(struct snd_compr_stream *stream, unsigned long arg) 554b21c60a4SVinod Koul { 55517ac8e5cSRichard Fitzgerald struct snd_compr_tstamp tstamp = {0}; 55617ac8e5cSRichard Fitzgerald int ret; 557b21c60a4SVinod Koul 55817ac8e5cSRichard Fitzgerald ret = snd_compr_update_tstamp(stream, &tstamp); 55917ac8e5cSRichard Fitzgerald if (ret == 0) 56017ac8e5cSRichard Fitzgerald ret = copy_to_user((struct snd_compr_tstamp __user *)arg, 561b21c60a4SVinod Koul &tstamp, sizeof(tstamp)) ? -EFAULT : 0; 56217ac8e5cSRichard Fitzgerald return ret; 563b21c60a4SVinod Koul } 564b21c60a4SVinod Koul 565b21c60a4SVinod Koul static int snd_compr_pause(struct snd_compr_stream *stream) 566b21c60a4SVinod Koul { 567b21c60a4SVinod Koul int retval; 568b21c60a4SVinod Koul 569b21c60a4SVinod Koul if (stream->runtime->state != SNDRV_PCM_STATE_RUNNING) 570b21c60a4SVinod Koul return -EPERM; 571b21c60a4SVinod Koul retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_PAUSE_PUSH); 5726b18f793SVinod Koul if (!retval) 573b21c60a4SVinod Koul stream->runtime->state = SNDRV_PCM_STATE_PAUSED; 574b21c60a4SVinod Koul return retval; 575b21c60a4SVinod Koul } 576b21c60a4SVinod Koul 577b21c60a4SVinod Koul static int snd_compr_resume(struct snd_compr_stream *stream) 578b21c60a4SVinod Koul { 579b21c60a4SVinod Koul int retval; 580b21c60a4SVinod Koul 581b21c60a4SVinod Koul if (stream->runtime->state != SNDRV_PCM_STATE_PAUSED) 582b21c60a4SVinod Koul return -EPERM; 583b21c60a4SVinod Koul retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_PAUSE_RELEASE); 584b21c60a4SVinod Koul if (!retval) 585b21c60a4SVinod Koul stream->runtime->state = SNDRV_PCM_STATE_RUNNING; 586b21c60a4SVinod Koul return retval; 587b21c60a4SVinod Koul } 588b21c60a4SVinod Koul 589b21c60a4SVinod Koul static int snd_compr_start(struct snd_compr_stream *stream) 590b21c60a4SVinod Koul { 591b21c60a4SVinod Koul int retval; 592b21c60a4SVinod Koul 593b21c60a4SVinod Koul if (stream->runtime->state != SNDRV_PCM_STATE_PREPARED) 594b21c60a4SVinod Koul return -EPERM; 595b21c60a4SVinod Koul retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_START); 596b21c60a4SVinod Koul if (!retval) 597b21c60a4SVinod Koul stream->runtime->state = SNDRV_PCM_STATE_RUNNING; 598b21c60a4SVinod Koul return retval; 599b21c60a4SVinod Koul } 600b21c60a4SVinod Koul 601b21c60a4SVinod Koul static int snd_compr_stop(struct snd_compr_stream *stream) 602b21c60a4SVinod Koul { 603b21c60a4SVinod Koul int retval; 604b21c60a4SVinod Koul 605b21c60a4SVinod Koul if (stream->runtime->state == SNDRV_PCM_STATE_PREPARED || 606b21c60a4SVinod Koul stream->runtime->state == SNDRV_PCM_STATE_SETUP) 607b21c60a4SVinod Koul return -EPERM; 608b21c60a4SVinod Koul retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_STOP); 609b21c60a4SVinod Koul if (!retval) { 610b21c60a4SVinod Koul stream->runtime->state = SNDRV_PCM_STATE_SETUP; 611b21c60a4SVinod Koul wake_up(&stream->runtime->sleep); 6128b21460aSVinod Koul stream->runtime->hw_pointer = 0; 6138b21460aSVinod Koul stream->runtime->app_pointer = 0; 6148b21460aSVinod Koul stream->runtime->total_bytes_available = 0; 6158b21460aSVinod Koul stream->runtime->total_bytes_transferred = 0; 616b21c60a4SVinod Koul } 617b21c60a4SVinod Koul return retval; 618b21c60a4SVinod Koul } 619b21c60a4SVinod Koul 620b21c60a4SVinod Koul static int snd_compr_drain(struct snd_compr_stream *stream) 621b21c60a4SVinod Koul { 622b21c60a4SVinod Koul int retval; 623b21c60a4SVinod Koul 624b21c60a4SVinod Koul if (stream->runtime->state == SNDRV_PCM_STATE_PREPARED || 625b21c60a4SVinod Koul stream->runtime->state == SNDRV_PCM_STATE_SETUP) 626b21c60a4SVinod Koul return -EPERM; 627b21c60a4SVinod Koul retval = stream->ops->trigger(stream, SND_COMPR_TRIGGER_DRAIN); 628b21c60a4SVinod Koul if (!retval) { 629b21c60a4SVinod Koul stream->runtime->state = SNDRV_PCM_STATE_DRAINING; 630b21c60a4SVinod Koul wake_up(&stream->runtime->sleep); 631b21c60a4SVinod Koul } 632b21c60a4SVinod Koul return retval; 633b21c60a4SVinod Koul } 634b21c60a4SVinod Koul 6359727b490SJeeja KP static int snd_compr_next_track(struct snd_compr_stream *stream) 6369727b490SJeeja KP { 6379727b490SJeeja KP int retval; 6389727b490SJeeja KP 6399727b490SJeeja KP /* only a running stream can transition to next track */ 6409727b490SJeeja KP if (stream->runtime->state != SNDRV_PCM_STATE_RUNNING) 6419727b490SJeeja KP return -EPERM; 6429727b490SJeeja KP 6439727b490SJeeja KP /* you can signal next track isf this is intended to be a gapless stream 6449727b490SJeeja KP * and current track metadata is set 6459727b490SJeeja KP */ 6469727b490SJeeja KP if (stream->metadata_set == false) 6479727b490SJeeja KP return -EPERM; 6489727b490SJeeja KP 6499727b490SJeeja KP retval = stream->ops->trigger(stream, SND_COMPR_TRIGGER_NEXT_TRACK); 6509727b490SJeeja KP if (retval != 0) 6519727b490SJeeja KP return retval; 6529727b490SJeeja KP stream->metadata_set = false; 6539727b490SJeeja KP stream->next_track = true; 6549727b490SJeeja KP return 0; 6559727b490SJeeja KP } 6569727b490SJeeja KP 6579727b490SJeeja KP static int snd_compr_partial_drain(struct snd_compr_stream *stream) 6589727b490SJeeja KP { 6599727b490SJeeja KP int retval; 6609727b490SJeeja KP if (stream->runtime->state == SNDRV_PCM_STATE_PREPARED || 6619727b490SJeeja KP stream->runtime->state == SNDRV_PCM_STATE_SETUP) 6629727b490SJeeja KP return -EPERM; 6639727b490SJeeja KP /* stream can be drained only when next track has been signalled */ 6649727b490SJeeja KP if (stream->next_track == false) 6659727b490SJeeja KP return -EPERM; 6669727b490SJeeja KP 6679727b490SJeeja KP retval = stream->ops->trigger(stream, SND_COMPR_TRIGGER_PARTIAL_DRAIN); 6689727b490SJeeja KP 6699727b490SJeeja KP stream->next_track = false; 6709727b490SJeeja KP return retval; 6719727b490SJeeja KP } 6729727b490SJeeja KP 673b21c60a4SVinod Koul static long snd_compr_ioctl(struct file *f, unsigned int cmd, unsigned long arg) 674b21c60a4SVinod Koul { 675b21c60a4SVinod Koul struct snd_compr_file *data = f->private_data; 676b21c60a4SVinod Koul struct snd_compr_stream *stream; 677b21c60a4SVinod Koul int retval = -ENOTTY; 678b21c60a4SVinod Koul 679b21c60a4SVinod Koul if (snd_BUG_ON(!data)) 680b21c60a4SVinod Koul return -EFAULT; 681b21c60a4SVinod Koul stream = &data->stream; 682b21c60a4SVinod Koul if (snd_BUG_ON(!stream)) 683b21c60a4SVinod Koul return -EFAULT; 684b21c60a4SVinod Koul mutex_lock(&stream->device->lock); 685b21c60a4SVinod Koul switch (_IOC_NR(cmd)) { 686b21c60a4SVinod Koul case _IOC_NR(SNDRV_COMPRESS_IOCTL_VERSION): 687b21c60a4SVinod Koul put_user(SNDRV_COMPRESS_VERSION, 688b21c60a4SVinod Koul (int __user *)arg) ? -EFAULT : 0; 689b21c60a4SVinod Koul break; 690b21c60a4SVinod Koul case _IOC_NR(SNDRV_COMPRESS_GET_CAPS): 691b21c60a4SVinod Koul retval = snd_compr_get_caps(stream, arg); 692b21c60a4SVinod Koul break; 693b21c60a4SVinod Koul case _IOC_NR(SNDRV_COMPRESS_GET_CODEC_CAPS): 694b21c60a4SVinod Koul retval = snd_compr_get_codec_caps(stream, arg); 695b21c60a4SVinod Koul break; 696b21c60a4SVinod Koul case _IOC_NR(SNDRV_COMPRESS_SET_PARAMS): 697b21c60a4SVinod Koul retval = snd_compr_set_params(stream, arg); 698b21c60a4SVinod Koul break; 699b21c60a4SVinod Koul case _IOC_NR(SNDRV_COMPRESS_GET_PARAMS): 700b21c60a4SVinod Koul retval = snd_compr_get_params(stream, arg); 701b21c60a4SVinod Koul break; 7029727b490SJeeja KP case _IOC_NR(SNDRV_COMPRESS_SET_METADATA): 7039727b490SJeeja KP retval = snd_compr_set_metadata(stream, arg); 7049727b490SJeeja KP break; 7059727b490SJeeja KP case _IOC_NR(SNDRV_COMPRESS_GET_METADATA): 7069727b490SJeeja KP retval = snd_compr_get_metadata(stream, arg); 7079727b490SJeeja KP break; 708b21c60a4SVinod Koul case _IOC_NR(SNDRV_COMPRESS_TSTAMP): 709b21c60a4SVinod Koul retval = snd_compr_tstamp(stream, arg); 710b21c60a4SVinod Koul break; 711b21c60a4SVinod Koul case _IOC_NR(SNDRV_COMPRESS_AVAIL): 712b21c60a4SVinod Koul retval = snd_compr_ioctl_avail(stream, arg); 713b21c60a4SVinod Koul break; 714b21c60a4SVinod Koul case _IOC_NR(SNDRV_COMPRESS_PAUSE): 715b21c60a4SVinod Koul retval = snd_compr_pause(stream); 716b21c60a4SVinod Koul break; 717b21c60a4SVinod Koul case _IOC_NR(SNDRV_COMPRESS_RESUME): 718b21c60a4SVinod Koul retval = snd_compr_resume(stream); 719b21c60a4SVinod Koul break; 720b21c60a4SVinod Koul case _IOC_NR(SNDRV_COMPRESS_START): 721b21c60a4SVinod Koul retval = snd_compr_start(stream); 722b21c60a4SVinod Koul break; 723b21c60a4SVinod Koul case _IOC_NR(SNDRV_COMPRESS_STOP): 724b21c60a4SVinod Koul retval = snd_compr_stop(stream); 725b21c60a4SVinod Koul break; 726b21c60a4SVinod Koul case _IOC_NR(SNDRV_COMPRESS_DRAIN): 727b21c60a4SVinod Koul retval = snd_compr_drain(stream); 728b21c60a4SVinod Koul break; 7299727b490SJeeja KP case _IOC_NR(SNDRV_COMPRESS_PARTIAL_DRAIN): 7309727b490SJeeja KP retval = snd_compr_partial_drain(stream); 7319727b490SJeeja KP break; 7329727b490SJeeja KP case _IOC_NR(SNDRV_COMPRESS_NEXT_TRACK): 7339727b490SJeeja KP retval = snd_compr_next_track(stream); 7349727b490SJeeja KP break; 7359727b490SJeeja KP 736b21c60a4SVinod Koul } 737b21c60a4SVinod Koul mutex_unlock(&stream->device->lock); 738b21c60a4SVinod Koul return retval; 739b21c60a4SVinod Koul } 740b21c60a4SVinod Koul 741b21c60a4SVinod Koul static const struct file_operations snd_compr_file_ops = { 742b21c60a4SVinod Koul .owner = THIS_MODULE, 743b21c60a4SVinod Koul .open = snd_compr_open, 744b21c60a4SVinod Koul .release = snd_compr_free, 745b21c60a4SVinod Koul .write = snd_compr_write, 746b21c60a4SVinod Koul .read = snd_compr_read, 747b21c60a4SVinod Koul .unlocked_ioctl = snd_compr_ioctl, 748b21c60a4SVinod Koul .mmap = snd_compr_mmap, 749b21c60a4SVinod Koul .poll = snd_compr_poll, 750b21c60a4SVinod Koul }; 751b21c60a4SVinod Koul 752b21c60a4SVinod Koul static int snd_compress_dev_register(struct snd_device *device) 753b21c60a4SVinod Koul { 754b21c60a4SVinod Koul int ret = -EINVAL; 755b21c60a4SVinod Koul char str[16]; 756b21c60a4SVinod Koul struct snd_compr *compr; 757b21c60a4SVinod Koul 758b21c60a4SVinod Koul if (snd_BUG_ON(!device || !device->device_data)) 759b21c60a4SVinod Koul return -EBADFD; 760b21c60a4SVinod Koul compr = device->device_data; 761b21c60a4SVinod Koul 762b21c60a4SVinod Koul sprintf(str, "comprC%iD%i", compr->card->number, compr->device); 763b21c60a4SVinod Koul pr_debug("reg %s for device %s, direction %d\n", str, compr->name, 764b21c60a4SVinod Koul compr->direction); 765b21c60a4SVinod Koul /* register compressed device */ 766b21c60a4SVinod Koul ret = snd_register_device(SNDRV_DEVICE_TYPE_COMPRESS, compr->card, 767b21c60a4SVinod Koul compr->device, &snd_compr_file_ops, compr, str); 768b21c60a4SVinod Koul if (ret < 0) { 769b21c60a4SVinod Koul pr_err("snd_register_device failed\n %d", ret); 770b21c60a4SVinod Koul return ret; 771b21c60a4SVinod Koul } 772b21c60a4SVinod Koul return ret; 773b21c60a4SVinod Koul 774b21c60a4SVinod Koul } 775b21c60a4SVinod Koul 776b21c60a4SVinod Koul static int snd_compress_dev_disconnect(struct snd_device *device) 777b21c60a4SVinod Koul { 778b21c60a4SVinod Koul struct snd_compr *compr; 779b21c60a4SVinod Koul 780b21c60a4SVinod Koul compr = device->device_data; 781b21c60a4SVinod Koul snd_unregister_device(compr->direction, compr->card, compr->device); 782b21c60a4SVinod Koul return 0; 783b21c60a4SVinod Koul } 784b21c60a4SVinod Koul 785b21c60a4SVinod Koul /* 786b21c60a4SVinod Koul * snd_compress_new: create new compress device 787b21c60a4SVinod Koul * @card: sound card pointer 788b21c60a4SVinod Koul * @device: device number 789b21c60a4SVinod Koul * @dirn: device direction, should be of type enum snd_compr_direction 790b21c60a4SVinod Koul * @compr: compress device pointer 791b21c60a4SVinod Koul */ 792b21c60a4SVinod Koul int snd_compress_new(struct snd_card *card, int device, 793b21c60a4SVinod Koul int dirn, struct snd_compr *compr) 794b21c60a4SVinod Koul { 795b21c60a4SVinod Koul static struct snd_device_ops ops = { 796b21c60a4SVinod Koul .dev_free = NULL, 797b21c60a4SVinod Koul .dev_register = snd_compress_dev_register, 798b21c60a4SVinod Koul .dev_disconnect = snd_compress_dev_disconnect, 799b21c60a4SVinod Koul }; 800b21c60a4SVinod Koul 801b21c60a4SVinod Koul compr->card = card; 802b21c60a4SVinod Koul compr->device = device; 803b21c60a4SVinod Koul compr->direction = dirn; 804b21c60a4SVinod Koul return snd_device_new(card, SNDRV_DEV_COMPRESS, compr, &ops); 805b21c60a4SVinod Koul } 806b21c60a4SVinod Koul EXPORT_SYMBOL_GPL(snd_compress_new); 807b21c60a4SVinod Koul 808b21c60a4SVinod Koul static int snd_compress_add_device(struct snd_compr *device) 809b21c60a4SVinod Koul { 810b21c60a4SVinod Koul int ret; 811b21c60a4SVinod Koul 812b21c60a4SVinod Koul if (!device->card) 813b21c60a4SVinod Koul return -EINVAL; 814b21c60a4SVinod Koul 815b21c60a4SVinod Koul /* register the card */ 816b21c60a4SVinod Koul ret = snd_card_register(device->card); 817b21c60a4SVinod Koul if (ret) 818b21c60a4SVinod Koul goto out; 819b21c60a4SVinod Koul return 0; 820b21c60a4SVinod Koul 821b21c60a4SVinod Koul out: 822b21c60a4SVinod Koul pr_err("failed with %d\n", ret); 823b21c60a4SVinod Koul return ret; 824b21c60a4SVinod Koul 825b21c60a4SVinod Koul } 826b21c60a4SVinod Koul 827b21c60a4SVinod Koul static int snd_compress_remove_device(struct snd_compr *device) 828b21c60a4SVinod Koul { 829b21c60a4SVinod Koul return snd_card_free(device->card); 830b21c60a4SVinod Koul } 831b21c60a4SVinod Koul 832b21c60a4SVinod Koul /** 833b21c60a4SVinod Koul * snd_compress_register - register compressed device 834b21c60a4SVinod Koul * 835b21c60a4SVinod Koul * @device: compressed device to register 836b21c60a4SVinod Koul */ 837b21c60a4SVinod Koul int snd_compress_register(struct snd_compr *device) 838b21c60a4SVinod Koul { 839b21c60a4SVinod Koul int retval; 840b21c60a4SVinod Koul 841b21c60a4SVinod Koul if (device->name == NULL || device->dev == NULL || device->ops == NULL) 842b21c60a4SVinod Koul return -EINVAL; 843b21c60a4SVinod Koul 844b21c60a4SVinod Koul pr_debug("Registering compressed device %s\n", device->name); 845b21c60a4SVinod Koul if (snd_BUG_ON(!device->ops->open)) 846b21c60a4SVinod Koul return -EINVAL; 847b21c60a4SVinod Koul if (snd_BUG_ON(!device->ops->free)) 848b21c60a4SVinod Koul return -EINVAL; 849b21c60a4SVinod Koul if (snd_BUG_ON(!device->ops->set_params)) 850b21c60a4SVinod Koul return -EINVAL; 851b21c60a4SVinod Koul if (snd_BUG_ON(!device->ops->trigger)) 852b21c60a4SVinod Koul return -EINVAL; 853b21c60a4SVinod Koul 854b21c60a4SVinod Koul mutex_init(&device->lock); 855b21c60a4SVinod Koul 856b21c60a4SVinod Koul /* register a compressed card */ 857b21c60a4SVinod Koul mutex_lock(&device_mutex); 858b21c60a4SVinod Koul retval = snd_compress_add_device(device); 859b21c60a4SVinod Koul mutex_unlock(&device_mutex); 860b21c60a4SVinod Koul return retval; 861b21c60a4SVinod Koul } 862b21c60a4SVinod Koul EXPORT_SYMBOL_GPL(snd_compress_register); 863b21c60a4SVinod Koul 864b21c60a4SVinod Koul int snd_compress_deregister(struct snd_compr *device) 865b21c60a4SVinod Koul { 866b21c60a4SVinod Koul pr_debug("Removing compressed device %s\n", device->name); 867b21c60a4SVinod Koul mutex_lock(&device_mutex); 868b21c60a4SVinod Koul snd_compress_remove_device(device); 869b21c60a4SVinod Koul mutex_unlock(&device_mutex); 870b21c60a4SVinod Koul return 0; 871b21c60a4SVinod Koul } 872b21c60a4SVinod Koul EXPORT_SYMBOL_GPL(snd_compress_deregister); 873b21c60a4SVinod Koul 874b21c60a4SVinod Koul static int __init snd_compress_init(void) 875b21c60a4SVinod Koul { 876b21c60a4SVinod Koul return 0; 877b21c60a4SVinod Koul } 878b21c60a4SVinod Koul 879b21c60a4SVinod Koul static void __exit snd_compress_exit(void) 880b21c60a4SVinod Koul { 881b21c60a4SVinod Koul } 882b21c60a4SVinod Koul 883b21c60a4SVinod Koul module_init(snd_compress_init); 884b21c60a4SVinod Koul module_exit(snd_compress_exit); 885b21c60a4SVinod Koul 886b21c60a4SVinod Koul MODULE_DESCRIPTION("ALSA Compressed offload framework"); 887b21c60a4SVinod Koul MODULE_AUTHOR("Vinod Koul <vinod.koul@linux.intel.com>"); 888b21c60a4SVinod Koul MODULE_LICENSE("GPL v2"); 889