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> 31f0283b58SCharles Keepax #include <linux/math64.h> 32b21c60a4SVinod Koul #include <linux/mm.h> 33b21c60a4SVinod Koul #include <linux/mutex.h> 34b21c60a4SVinod Koul #include <linux/poll.h> 35b21c60a4SVinod Koul #include <linux/slab.h> 36b21c60a4SVinod Koul #include <linux/sched.h> 37f0283b58SCharles Keepax #include <linux/types.h> 38b21c60a4SVinod Koul #include <linux/uio.h> 39b21c60a4SVinod Koul #include <linux/uaccess.h> 40b21c60a4SVinod Koul #include <linux/module.h> 41b21c60a4SVinod Koul #include <sound/core.h> 42b21c60a4SVinod Koul #include <sound/initval.h> 43b21c60a4SVinod Koul #include <sound/compress_params.h> 44b21c60a4SVinod Koul #include <sound/compress_offload.h> 45b21c60a4SVinod Koul #include <sound/compress_driver.h> 46b21c60a4SVinod Koul 47b21c60a4SVinod Koul /* TODO: 48b21c60a4SVinod Koul * - add substream support for multiple devices in case of 49b21c60a4SVinod Koul * SND_DYNAMIC_MINORS is not used 50b21c60a4SVinod Koul * - Multiple node representation 51b21c60a4SVinod Koul * driver should be able to register multiple nodes 52b21c60a4SVinod Koul */ 53b21c60a4SVinod Koul 54b21c60a4SVinod Koul static DEFINE_MUTEX(device_mutex); 55b21c60a4SVinod Koul 56b21c60a4SVinod Koul struct snd_compr_file { 57b21c60a4SVinod Koul unsigned long caps; 58b21c60a4SVinod Koul struct snd_compr_stream stream; 59b21c60a4SVinod Koul }; 60b21c60a4SVinod Koul 61b21c60a4SVinod Koul /* 62b21c60a4SVinod Koul * a note on stream states used: 63b21c60a4SVinod Koul * we use follwing states in the compressed core 64b21c60a4SVinod Koul * SNDRV_PCM_STATE_OPEN: When stream has been opened. 65b21c60a4SVinod Koul * SNDRV_PCM_STATE_SETUP: When stream has been initialized. This is done by 66b21c60a4SVinod Koul * calling SNDRV_COMPRESS_SET_PARAMS. running streams will come to this 67b21c60a4SVinod Koul * state at stop by calling SNDRV_COMPRESS_STOP, or at end of drain. 68b21c60a4SVinod Koul * SNDRV_PCM_STATE_RUNNING: When stream has been started and is 69b21c60a4SVinod Koul * decoding/encoding and rendering/capturing data. 70b21c60a4SVinod Koul * SNDRV_PCM_STATE_DRAINING: When stream is draining current data. This is done 71b21c60a4SVinod Koul * by calling SNDRV_COMPRESS_DRAIN. 72b21c60a4SVinod Koul * SNDRV_PCM_STATE_PAUSED: When stream is paused. This is done by calling 73b21c60a4SVinod Koul * SNDRV_COMPRESS_PAUSE. It can be stopped or resumed by calling 74b21c60a4SVinod Koul * SNDRV_COMPRESS_STOP or SNDRV_COMPRESS_RESUME respectively. 75b21c60a4SVinod Koul */ 76b21c60a4SVinod Koul static int snd_compr_open(struct inode *inode, struct file *f) 77b21c60a4SVinod Koul { 78b21c60a4SVinod Koul struct snd_compr *compr; 79b21c60a4SVinod Koul struct snd_compr_file *data; 80b21c60a4SVinod Koul struct snd_compr_runtime *runtime; 81b21c60a4SVinod Koul enum snd_compr_direction dirn; 82b21c60a4SVinod Koul int maj = imajor(inode); 83b21c60a4SVinod Koul int ret; 84b21c60a4SVinod Koul 8581cb3246SDan Carpenter if ((f->f_flags & O_ACCMODE) == O_WRONLY) 86b21c60a4SVinod Koul dirn = SND_COMPRESS_PLAYBACK; 8781cb3246SDan Carpenter else if ((f->f_flags & O_ACCMODE) == O_RDONLY) 88b21c60a4SVinod Koul dirn = SND_COMPRESS_CAPTURE; 8981cb3246SDan Carpenter else 90b21c60a4SVinod Koul return -EINVAL; 91b21c60a4SVinod Koul 92b21c60a4SVinod Koul if (maj == snd_major) 93b21c60a4SVinod Koul compr = snd_lookup_minor_data(iminor(inode), 94b21c60a4SVinod Koul SNDRV_DEVICE_TYPE_COMPRESS); 95b21c60a4SVinod Koul else 96b21c60a4SVinod Koul return -EBADFD; 97b21c60a4SVinod Koul 98b21c60a4SVinod Koul if (compr == NULL) { 99b21c60a4SVinod Koul pr_err("no device data!!!\n"); 100b21c60a4SVinod Koul return -ENODEV; 101b21c60a4SVinod Koul } 102b21c60a4SVinod Koul 103b21c60a4SVinod Koul if (dirn != compr->direction) { 104b21c60a4SVinod Koul pr_err("this device doesn't support this direction\n"); 105a0830dbdSTakashi Iwai snd_card_unref(compr->card); 106b21c60a4SVinod Koul return -EINVAL; 107b21c60a4SVinod Koul } 108b21c60a4SVinod Koul 109b21c60a4SVinod Koul data = kzalloc(sizeof(*data), GFP_KERNEL); 110a0830dbdSTakashi Iwai if (!data) { 111a0830dbdSTakashi Iwai snd_card_unref(compr->card); 112b21c60a4SVinod Koul return -ENOMEM; 113a0830dbdSTakashi Iwai } 114b21c60a4SVinod Koul data->stream.ops = compr->ops; 115b21c60a4SVinod Koul data->stream.direction = dirn; 116b21c60a4SVinod Koul data->stream.private_data = compr->private_data; 117b21c60a4SVinod Koul data->stream.device = compr; 118b21c60a4SVinod Koul runtime = kzalloc(sizeof(*runtime), GFP_KERNEL); 119b21c60a4SVinod Koul if (!runtime) { 120b21c60a4SVinod Koul kfree(data); 121a0830dbdSTakashi Iwai snd_card_unref(compr->card); 122b21c60a4SVinod Koul return -ENOMEM; 123b21c60a4SVinod Koul } 124b21c60a4SVinod Koul runtime->state = SNDRV_PCM_STATE_OPEN; 125b21c60a4SVinod Koul init_waitqueue_head(&runtime->sleep); 126b21c60a4SVinod Koul data->stream.runtime = runtime; 127b21c60a4SVinod Koul f->private_data = (void *)data; 128b21c60a4SVinod Koul mutex_lock(&compr->lock); 129b21c60a4SVinod Koul ret = compr->ops->open(&data->stream); 130b21c60a4SVinod Koul mutex_unlock(&compr->lock); 131b21c60a4SVinod Koul if (ret) { 132b21c60a4SVinod Koul kfree(runtime); 133b21c60a4SVinod Koul kfree(data); 134b21c60a4SVinod Koul } 135a0830dbdSTakashi Iwai snd_card_unref(compr->card); 136749d3223SCharles Keepax return ret; 137b21c60a4SVinod Koul } 138b21c60a4SVinod Koul 139b21c60a4SVinod Koul static int snd_compr_free(struct inode *inode, struct file *f) 140b21c60a4SVinod Koul { 141b21c60a4SVinod Koul struct snd_compr_file *data = f->private_data; 142b26d19e4SLiam Girdwood struct snd_compr_runtime *runtime = data->stream.runtime; 143b26d19e4SLiam Girdwood 144b26d19e4SLiam Girdwood switch (runtime->state) { 145b26d19e4SLiam Girdwood case SNDRV_PCM_STATE_RUNNING: 146b26d19e4SLiam Girdwood case SNDRV_PCM_STATE_DRAINING: 147b26d19e4SLiam Girdwood case SNDRV_PCM_STATE_PAUSED: 148b26d19e4SLiam Girdwood data->stream.ops->trigger(&data->stream, SNDRV_PCM_TRIGGER_STOP); 149b26d19e4SLiam Girdwood break; 150b26d19e4SLiam Girdwood default: 151b26d19e4SLiam Girdwood break; 152b26d19e4SLiam Girdwood } 153b26d19e4SLiam Girdwood 154b21c60a4SVinod Koul data->stream.ops->free(&data->stream); 155b21c60a4SVinod Koul kfree(data->stream.runtime->buffer); 156b21c60a4SVinod Koul kfree(data->stream.runtime); 157b21c60a4SVinod Koul kfree(data); 158b21c60a4SVinod Koul return 0; 159b21c60a4SVinod Koul } 160b21c60a4SVinod Koul 16117ac8e5cSRichard Fitzgerald static int snd_compr_update_tstamp(struct snd_compr_stream *stream, 162b21c60a4SVinod Koul struct snd_compr_tstamp *tstamp) 163b21c60a4SVinod Koul { 164b21c60a4SVinod Koul if (!stream->ops->pointer) 16517ac8e5cSRichard Fitzgerald return -ENOTSUPP; 166b21c60a4SVinod Koul stream->ops->pointer(stream, tstamp); 167b21c60a4SVinod Koul pr_debug("dsp consumed till %d total %d bytes\n", 168b21c60a4SVinod Koul tstamp->byte_offset, tstamp->copied_total); 1695b1f79f7SCharles Keepax if (stream->direction == SND_COMPRESS_PLAYBACK) 170b21c60a4SVinod Koul stream->runtime->total_bytes_transferred = tstamp->copied_total; 1715b1f79f7SCharles Keepax else 1725b1f79f7SCharles Keepax stream->runtime->total_bytes_available = tstamp->copied_total; 17317ac8e5cSRichard Fitzgerald return 0; 174b21c60a4SVinod Koul } 175b21c60a4SVinod Koul 176b21c60a4SVinod Koul static size_t snd_compr_calc_avail(struct snd_compr_stream *stream, 177b21c60a4SVinod Koul struct snd_compr_avail *avail) 178b21c60a4SVinod Koul { 17917ac8e5cSRichard Fitzgerald memset(avail, 0, sizeof(*avail)); 180b21c60a4SVinod Koul snd_compr_update_tstamp(stream, &avail->tstamp); 18117ac8e5cSRichard Fitzgerald /* Still need to return avail even if tstamp can't be filled in */ 182b21c60a4SVinod Koul 183b21c60a4SVinod Koul if (stream->runtime->total_bytes_available == 0 && 1845b1f79f7SCharles Keepax stream->runtime->state == SNDRV_PCM_STATE_SETUP && 1855b1f79f7SCharles Keepax stream->direction == SND_COMPRESS_PLAYBACK) { 186b21c60a4SVinod Koul pr_debug("detected init and someone forgot to do a write\n"); 187b21c60a4SVinod Koul return stream->runtime->buffer_size; 188b21c60a4SVinod Koul } 189b21c60a4SVinod Koul pr_debug("app wrote %lld, DSP consumed %lld\n", 190b21c60a4SVinod Koul stream->runtime->total_bytes_available, 191b21c60a4SVinod Koul stream->runtime->total_bytes_transferred); 192b21c60a4SVinod Koul if (stream->runtime->total_bytes_available == 193b21c60a4SVinod Koul stream->runtime->total_bytes_transferred) { 1945b1f79f7SCharles Keepax if (stream->direction == SND_COMPRESS_PLAYBACK) { 195b21c60a4SVinod Koul pr_debug("both pointers are same, returning full avail\n"); 196b21c60a4SVinod Koul return stream->runtime->buffer_size; 1975b1f79f7SCharles Keepax } else { 1985b1f79f7SCharles Keepax pr_debug("both pointers are same, returning no avail\n"); 1995b1f79f7SCharles Keepax return 0; 2005b1f79f7SCharles Keepax } 201b21c60a4SVinod Koul } 202b21c60a4SVinod Koul 2035b1f79f7SCharles Keepax avail->avail = stream->runtime->total_bytes_available - 2045b1f79f7SCharles Keepax stream->runtime->total_bytes_transferred; 2055b1f79f7SCharles Keepax if (stream->direction == SND_COMPRESS_PLAYBACK) 2065b1f79f7SCharles Keepax avail->avail = stream->runtime->buffer_size - avail->avail; 2075b1f79f7SCharles Keepax 2084c28e32dSCharles Keepax pr_debug("ret avail as %lld\n", avail->avail); 2094c28e32dSCharles Keepax return avail->avail; 210b21c60a4SVinod Koul } 211b21c60a4SVinod Koul 212b21c60a4SVinod Koul static inline size_t snd_compr_get_avail(struct snd_compr_stream *stream) 213b21c60a4SVinod Koul { 214b21c60a4SVinod Koul struct snd_compr_avail avail; 215b21c60a4SVinod Koul 216b21c60a4SVinod Koul return snd_compr_calc_avail(stream, &avail); 217b21c60a4SVinod Koul } 218b21c60a4SVinod Koul 219b21c60a4SVinod Koul static int 220b21c60a4SVinod Koul snd_compr_ioctl_avail(struct snd_compr_stream *stream, unsigned long arg) 221b21c60a4SVinod Koul { 222b21c60a4SVinod Koul struct snd_compr_avail ioctl_avail; 223b21c60a4SVinod Koul size_t avail; 224b21c60a4SVinod Koul 225b21c60a4SVinod Koul avail = snd_compr_calc_avail(stream, &ioctl_avail); 226b21c60a4SVinod Koul ioctl_avail.avail = avail; 227b21c60a4SVinod Koul 228b21c60a4SVinod Koul if (copy_to_user((__u64 __user *)arg, 229b21c60a4SVinod Koul &ioctl_avail, sizeof(ioctl_avail))) 230b21c60a4SVinod Koul return -EFAULT; 231b21c60a4SVinod Koul return 0; 232b21c60a4SVinod Koul } 233b21c60a4SVinod Koul 234b21c60a4SVinod Koul static int snd_compr_write_data(struct snd_compr_stream *stream, 235b21c60a4SVinod Koul const char __user *buf, size_t count) 236b21c60a4SVinod Koul { 237b21c60a4SVinod Koul void *dstn; 238b21c60a4SVinod Koul size_t copy; 239b21c60a4SVinod Koul struct snd_compr_runtime *runtime = stream->runtime; 240f0283b58SCharles Keepax /* 64-bit Modulus */ 241f0283b58SCharles Keepax u64 app_pointer = div64_u64(runtime->total_bytes_available, 242f0283b58SCharles Keepax runtime->buffer_size); 243f0283b58SCharles Keepax app_pointer = runtime->total_bytes_available - 244f0283b58SCharles Keepax (app_pointer * runtime->buffer_size); 245b21c60a4SVinod Koul 246f0283b58SCharles Keepax dstn = runtime->buffer + app_pointer; 247b21c60a4SVinod Koul pr_debug("copying %ld at %lld\n", 248f0283b58SCharles Keepax (unsigned long)count, app_pointer); 249f0283b58SCharles Keepax if (count < runtime->buffer_size - app_pointer) { 250b21c60a4SVinod Koul if (copy_from_user(dstn, buf, count)) 251b21c60a4SVinod Koul return -EFAULT; 252b21c60a4SVinod Koul } else { 253f0283b58SCharles Keepax copy = runtime->buffer_size - app_pointer; 254b21c60a4SVinod Koul if (copy_from_user(dstn, buf, copy)) 255b21c60a4SVinod Koul return -EFAULT; 256b21c60a4SVinod Koul if (copy_from_user(runtime->buffer, buf + copy, count - copy)) 257b21c60a4SVinod Koul return -EFAULT; 258b21c60a4SVinod Koul } 259b21c60a4SVinod Koul /* if DSP cares, let it know data has been written */ 260b21c60a4SVinod Koul if (stream->ops->ack) 261b21c60a4SVinod Koul stream->ops->ack(stream, count); 262b21c60a4SVinod Koul return count; 263b21c60a4SVinod Koul } 264b21c60a4SVinod Koul 265b21c60a4SVinod Koul static ssize_t snd_compr_write(struct file *f, const char __user *buf, 266b21c60a4SVinod Koul size_t count, loff_t *offset) 267b21c60a4SVinod Koul { 268b21c60a4SVinod Koul struct snd_compr_file *data = f->private_data; 269b21c60a4SVinod Koul struct snd_compr_stream *stream; 270b21c60a4SVinod Koul size_t avail; 271b21c60a4SVinod Koul int retval; 272b21c60a4SVinod Koul 273b21c60a4SVinod Koul if (snd_BUG_ON(!data)) 274b21c60a4SVinod Koul return -EFAULT; 275b21c60a4SVinod Koul 276b21c60a4SVinod Koul stream = &data->stream; 277b21c60a4SVinod Koul mutex_lock(&stream->device->lock); 278b21c60a4SVinod Koul /* write is allowed when stream is running or has been steup */ 279b21c60a4SVinod Koul if (stream->runtime->state != SNDRV_PCM_STATE_SETUP && 280b21c60a4SVinod Koul stream->runtime->state != SNDRV_PCM_STATE_RUNNING) { 281b21c60a4SVinod Koul mutex_unlock(&stream->device->lock); 282b21c60a4SVinod Koul return -EBADFD; 283b21c60a4SVinod Koul } 284b21c60a4SVinod Koul 285b21c60a4SVinod Koul avail = snd_compr_get_avail(stream); 286b21c60a4SVinod Koul pr_debug("avail returned %ld\n", (unsigned long)avail); 287b21c60a4SVinod Koul /* calculate how much we can write to buffer */ 288b21c60a4SVinod Koul if (avail > count) 289b21c60a4SVinod Koul avail = count; 290b21c60a4SVinod Koul 2914daf891cSCharles Keepax if (stream->ops->copy) { 2924daf891cSCharles Keepax char __user* cbuf = (char __user*)buf; 2934daf891cSCharles Keepax retval = stream->ops->copy(stream, cbuf, avail); 2944daf891cSCharles Keepax } else { 295b21c60a4SVinod Koul retval = snd_compr_write_data(stream, buf, avail); 2964daf891cSCharles Keepax } 297b21c60a4SVinod Koul if (retval > 0) 298b21c60a4SVinod Koul stream->runtime->total_bytes_available += retval; 299b21c60a4SVinod Koul 300b21c60a4SVinod Koul /* while initiating the stream, write should be called before START 301b21c60a4SVinod Koul * call, so in setup move state */ 302b21c60a4SVinod Koul if (stream->runtime->state == SNDRV_PCM_STATE_SETUP) { 303b21c60a4SVinod Koul stream->runtime->state = SNDRV_PCM_STATE_PREPARED; 304b21c60a4SVinod Koul pr_debug("stream prepared, Houston we are good to go\n"); 305b21c60a4SVinod Koul } 306b21c60a4SVinod Koul 307b21c60a4SVinod Koul mutex_unlock(&stream->device->lock); 308b21c60a4SVinod Koul return retval; 309b21c60a4SVinod Koul } 310b21c60a4SVinod Koul 311b21c60a4SVinod Koul 312b21c60a4SVinod Koul static ssize_t snd_compr_read(struct file *f, char __user *buf, 313b21c60a4SVinod Koul size_t count, loff_t *offset) 314b21c60a4SVinod Koul { 31549bb6402SCharles Keepax struct snd_compr_file *data = f->private_data; 31649bb6402SCharles Keepax struct snd_compr_stream *stream; 31749bb6402SCharles Keepax size_t avail; 31849bb6402SCharles Keepax int retval; 31949bb6402SCharles Keepax 32049bb6402SCharles Keepax if (snd_BUG_ON(!data)) 32149bb6402SCharles Keepax return -EFAULT; 32249bb6402SCharles Keepax 32349bb6402SCharles Keepax stream = &data->stream; 32449bb6402SCharles Keepax mutex_lock(&stream->device->lock); 32549bb6402SCharles Keepax 32675481347SVinod Koul /* read is allowed when stream is running, paused, draining and setup 32775481347SVinod Koul * (yes setup is state which we transition to after stop, so if user 32875481347SVinod Koul * wants to read data after stop we allow that) 32975481347SVinod Koul */ 33075481347SVinod Koul switch (stream->runtime->state) { 33175481347SVinod Koul case SNDRV_PCM_STATE_OPEN: 33275481347SVinod Koul case SNDRV_PCM_STATE_PREPARED: 33375481347SVinod Koul case SNDRV_PCM_STATE_XRUN: 33475481347SVinod Koul case SNDRV_PCM_STATE_SUSPENDED: 33575481347SVinod Koul case SNDRV_PCM_STATE_DISCONNECTED: 33649bb6402SCharles Keepax retval = -EBADFD; 33749bb6402SCharles Keepax goto out; 33849bb6402SCharles Keepax } 33949bb6402SCharles Keepax 34049bb6402SCharles Keepax avail = snd_compr_get_avail(stream); 34149bb6402SCharles Keepax pr_debug("avail returned %ld\n", (unsigned long)avail); 34249bb6402SCharles Keepax /* calculate how much we can read from buffer */ 34349bb6402SCharles Keepax if (avail > count) 34449bb6402SCharles Keepax avail = count; 34549bb6402SCharles Keepax 34649bb6402SCharles Keepax if (stream->ops->copy) { 34749bb6402SCharles Keepax retval = stream->ops->copy(stream, buf, avail); 34849bb6402SCharles Keepax } else { 34949bb6402SCharles Keepax retval = -ENXIO; 35049bb6402SCharles Keepax goto out; 35149bb6402SCharles Keepax } 35249bb6402SCharles Keepax if (retval > 0) 35349bb6402SCharles Keepax stream->runtime->total_bytes_transferred += retval; 35449bb6402SCharles Keepax 35549bb6402SCharles Keepax out: 35649bb6402SCharles Keepax mutex_unlock(&stream->device->lock); 35749bb6402SCharles Keepax return retval; 358b21c60a4SVinod Koul } 359b21c60a4SVinod Koul 360b21c60a4SVinod Koul static int snd_compr_mmap(struct file *f, struct vm_area_struct *vma) 361b21c60a4SVinod Koul { 362b21c60a4SVinod Koul return -ENXIO; 363b21c60a4SVinod Koul } 364b21c60a4SVinod Koul 365b21c60a4SVinod Koul static inline int snd_compr_get_poll(struct snd_compr_stream *stream) 366b21c60a4SVinod Koul { 367b21c60a4SVinod Koul if (stream->direction == SND_COMPRESS_PLAYBACK) 368b21c60a4SVinod Koul return POLLOUT | POLLWRNORM; 369b21c60a4SVinod Koul else 370b21c60a4SVinod Koul return POLLIN | POLLRDNORM; 371b21c60a4SVinod Koul } 372b21c60a4SVinod Koul 373b21c60a4SVinod Koul static unsigned int snd_compr_poll(struct file *f, poll_table *wait) 374b21c60a4SVinod Koul { 375b21c60a4SVinod Koul struct snd_compr_file *data = f->private_data; 376b21c60a4SVinod Koul struct snd_compr_stream *stream; 377b21c60a4SVinod Koul size_t avail; 378b21c60a4SVinod Koul int retval = 0; 379b21c60a4SVinod Koul 380b21c60a4SVinod Koul if (snd_BUG_ON(!data)) 381b21c60a4SVinod Koul return -EFAULT; 382b21c60a4SVinod Koul stream = &data->stream; 383b21c60a4SVinod Koul if (snd_BUG_ON(!stream)) 384b21c60a4SVinod Koul return -EFAULT; 385b21c60a4SVinod Koul 386b21c60a4SVinod Koul mutex_lock(&stream->device->lock); 387c15b149aSRichard Fitzgerald if (stream->runtime->state == SNDRV_PCM_STATE_OPEN) { 388b21c60a4SVinod Koul retval = -EBADFD; 389b21c60a4SVinod Koul goto out; 390b21c60a4SVinod Koul } 391b21c60a4SVinod Koul poll_wait(f, &stream->runtime->sleep, wait); 392b21c60a4SVinod Koul 393b21c60a4SVinod Koul avail = snd_compr_get_avail(stream); 394b21c60a4SVinod Koul pr_debug("avail is %ld\n", (unsigned long)avail); 395b21c60a4SVinod Koul /* check if we have at least one fragment to fill */ 396b21c60a4SVinod Koul switch (stream->runtime->state) { 397b21c60a4SVinod Koul case SNDRV_PCM_STATE_DRAINING: 398b21c60a4SVinod Koul /* stream has been woken up after drain is complete 399b21c60a4SVinod Koul * draining done so set stream state to stopped 400b21c60a4SVinod Koul */ 401b21c60a4SVinod Koul retval = snd_compr_get_poll(stream); 402b21c60a4SVinod Koul stream->runtime->state = SNDRV_PCM_STATE_SETUP; 403b21c60a4SVinod Koul break; 404b21c60a4SVinod Koul case SNDRV_PCM_STATE_RUNNING: 405b21c60a4SVinod Koul case SNDRV_PCM_STATE_PREPARED: 406b21c60a4SVinod Koul case SNDRV_PCM_STATE_PAUSED: 407b21c60a4SVinod Koul if (avail >= stream->runtime->fragment_size) 408b21c60a4SVinod Koul retval = snd_compr_get_poll(stream); 409b21c60a4SVinod Koul break; 410b21c60a4SVinod Koul default: 411b21c60a4SVinod Koul if (stream->direction == SND_COMPRESS_PLAYBACK) 412b21c60a4SVinod Koul retval = POLLOUT | POLLWRNORM | POLLERR; 413b21c60a4SVinod Koul else 414b21c60a4SVinod Koul retval = POLLIN | POLLRDNORM | POLLERR; 415b21c60a4SVinod Koul break; 416b21c60a4SVinod Koul } 417b21c60a4SVinod Koul out: 418b21c60a4SVinod Koul mutex_unlock(&stream->device->lock); 419b21c60a4SVinod Koul return retval; 420b21c60a4SVinod Koul } 421b21c60a4SVinod Koul 422b21c60a4SVinod Koul static int 423b21c60a4SVinod Koul snd_compr_get_caps(struct snd_compr_stream *stream, unsigned long arg) 424b21c60a4SVinod Koul { 425b21c60a4SVinod Koul int retval; 426b21c60a4SVinod Koul struct snd_compr_caps caps; 427b21c60a4SVinod Koul 428b21c60a4SVinod Koul if (!stream->ops->get_caps) 429b21c60a4SVinod Koul return -ENXIO; 430b21c60a4SVinod Koul 4311c62e9f2SDan Carpenter memset(&caps, 0, sizeof(caps)); 432b21c60a4SVinod Koul retval = stream->ops->get_caps(stream, &caps); 433b21c60a4SVinod Koul if (retval) 434b21c60a4SVinod Koul goto out; 435b21c60a4SVinod Koul if (copy_to_user((void __user *)arg, &caps, sizeof(caps))) 436b21c60a4SVinod Koul retval = -EFAULT; 437b21c60a4SVinod Koul out: 438b21c60a4SVinod Koul return retval; 439b21c60a4SVinod Koul } 440b21c60a4SVinod Koul 441b21c60a4SVinod Koul static int 442b21c60a4SVinod Koul snd_compr_get_codec_caps(struct snd_compr_stream *stream, unsigned long arg) 443b21c60a4SVinod Koul { 444b21c60a4SVinod Koul int retval; 445b21c60a4SVinod Koul struct snd_compr_codec_caps *caps; 446b21c60a4SVinod Koul 447b21c60a4SVinod Koul if (!stream->ops->get_codec_caps) 448b21c60a4SVinod Koul return -ENXIO; 449b21c60a4SVinod Koul 45047966e97STakashi Iwai caps = kzalloc(sizeof(*caps), GFP_KERNEL); 451b21c60a4SVinod Koul if (!caps) 452b21c60a4SVinod Koul return -ENOMEM; 453b21c60a4SVinod Koul 454b21c60a4SVinod Koul retval = stream->ops->get_codec_caps(stream, caps); 455b21c60a4SVinod Koul if (retval) 456b21c60a4SVinod Koul goto out; 457b21c60a4SVinod Koul if (copy_to_user((void __user *)arg, caps, sizeof(*caps))) 458b21c60a4SVinod Koul retval = -EFAULT; 459b21c60a4SVinod Koul 460b21c60a4SVinod Koul out: 461b21c60a4SVinod Koul kfree(caps); 462b21c60a4SVinod Koul return retval; 463b21c60a4SVinod Koul } 464b21c60a4SVinod Koul 465b21c60a4SVinod Koul /* revisit this with snd_pcm_preallocate_xxx */ 466b21c60a4SVinod Koul static int snd_compr_allocate_buffer(struct snd_compr_stream *stream, 467b21c60a4SVinod Koul struct snd_compr_params *params) 468b21c60a4SVinod Koul { 469b21c60a4SVinod Koul unsigned int buffer_size; 470b21c60a4SVinod Koul void *buffer; 471b21c60a4SVinod Koul 472b21c60a4SVinod Koul buffer_size = params->buffer.fragment_size * params->buffer.fragments; 473b21c60a4SVinod Koul if (stream->ops->copy) { 474b21c60a4SVinod Koul buffer = NULL; 475b21c60a4SVinod Koul /* if copy is defined the driver will be required to copy 476b21c60a4SVinod Koul * the data from core 477b21c60a4SVinod Koul */ 478b21c60a4SVinod Koul } else { 479b21c60a4SVinod Koul buffer = kmalloc(buffer_size, GFP_KERNEL); 480b21c60a4SVinod Koul if (!buffer) 481b21c60a4SVinod Koul return -ENOMEM; 482b21c60a4SVinod Koul } 483b21c60a4SVinod Koul stream->runtime->fragment_size = params->buffer.fragment_size; 484b21c60a4SVinod Koul stream->runtime->fragments = params->buffer.fragments; 485b21c60a4SVinod Koul stream->runtime->buffer = buffer; 486b21c60a4SVinod Koul stream->runtime->buffer_size = buffer_size; 487b21c60a4SVinod Koul return 0; 488b21c60a4SVinod Koul } 489b21c60a4SVinod Koul 4904dc040a0SVinod Koul static int snd_compress_check_input(struct snd_compr_params *params) 4914dc040a0SVinod Koul { 4924dc040a0SVinod Koul /* first let's check the buffer parameter's */ 4934dc040a0SVinod Koul if (params->buffer.fragment_size == 0 || 4946217e5edSDan Carpenter params->buffer.fragments > INT_MAX / params->buffer.fragment_size) 4954dc040a0SVinod Koul return -EINVAL; 4964dc040a0SVinod Koul 497fb4a9779SVinod Koul /* now codec parameters */ 498fb4a9779SVinod Koul if (params->codec.id == 0 || params->codec.id > SND_AUDIOCODEC_MAX) 499fb4a9779SVinod Koul return -EINVAL; 500fb4a9779SVinod Koul 501fb4a9779SVinod Koul if (params->codec.ch_in == 0 || params->codec.ch_out == 0) 502fb4a9779SVinod Koul return -EINVAL; 503fb4a9779SVinod Koul 5044dc040a0SVinod Koul return 0; 5054dc040a0SVinod Koul } 5064dc040a0SVinod Koul 507b21c60a4SVinod Koul static int 508b21c60a4SVinod Koul snd_compr_set_params(struct snd_compr_stream *stream, unsigned long arg) 509b21c60a4SVinod Koul { 510b21c60a4SVinod Koul struct snd_compr_params *params; 511b21c60a4SVinod Koul int retval; 512b21c60a4SVinod Koul 513b21c60a4SVinod Koul if (stream->runtime->state == SNDRV_PCM_STATE_OPEN) { 514b21c60a4SVinod Koul /* 515b21c60a4SVinod Koul * we should allow parameter change only when stream has been 516b21c60a4SVinod Koul * opened not in other cases 517b21c60a4SVinod Koul */ 518b21c60a4SVinod Koul params = kmalloc(sizeof(*params), GFP_KERNEL); 519b21c60a4SVinod Koul if (!params) 520b21c60a4SVinod Koul return -ENOMEM; 521769fab2aSJesper Juhl if (copy_from_user(params, (void __user *)arg, sizeof(*params))) { 522769fab2aSJesper Juhl retval = -EFAULT; 523769fab2aSJesper Juhl goto out; 524769fab2aSJesper Juhl } 5254dc040a0SVinod Koul 5264dc040a0SVinod Koul retval = snd_compress_check_input(params); 5274dc040a0SVinod Koul if (retval) 5284dc040a0SVinod Koul goto out; 5294dc040a0SVinod Koul 530b21c60a4SVinod Koul retval = snd_compr_allocate_buffer(stream, params); 531b21c60a4SVinod Koul if (retval) { 532769fab2aSJesper Juhl retval = -ENOMEM; 533769fab2aSJesper Juhl goto out; 534b21c60a4SVinod Koul } 5354dc040a0SVinod Koul 536b21c60a4SVinod Koul retval = stream->ops->set_params(stream, params); 537b21c60a4SVinod Koul if (retval) 538b21c60a4SVinod Koul goto out; 53949bb6402SCharles Keepax 5409727b490SJeeja KP stream->metadata_set = false; 5419727b490SJeeja KP stream->next_track = false; 54249bb6402SCharles Keepax 54349bb6402SCharles Keepax if (stream->direction == SND_COMPRESS_PLAYBACK) 54449bb6402SCharles Keepax stream->runtime->state = SNDRV_PCM_STATE_SETUP; 54549bb6402SCharles Keepax else 54649bb6402SCharles Keepax stream->runtime->state = SNDRV_PCM_STATE_PREPARED; 547769fab2aSJesper Juhl } else { 548b21c60a4SVinod Koul return -EPERM; 549769fab2aSJesper Juhl } 550b21c60a4SVinod Koul out: 551b21c60a4SVinod Koul kfree(params); 552b21c60a4SVinod Koul return retval; 553b21c60a4SVinod Koul } 554b21c60a4SVinod Koul 555b21c60a4SVinod Koul static int 556b21c60a4SVinod Koul snd_compr_get_params(struct snd_compr_stream *stream, unsigned long arg) 557b21c60a4SVinod Koul { 558b21c60a4SVinod Koul struct snd_codec *params; 559b21c60a4SVinod Koul int retval; 560b21c60a4SVinod Koul 561b21c60a4SVinod Koul if (!stream->ops->get_params) 562b21c60a4SVinod Koul return -EBADFD; 563b21c60a4SVinod Koul 56447966e97STakashi Iwai params = kzalloc(sizeof(*params), GFP_KERNEL); 565b21c60a4SVinod Koul if (!params) 566b21c60a4SVinod Koul return -ENOMEM; 567b21c60a4SVinod Koul retval = stream->ops->get_params(stream, params); 568b21c60a4SVinod Koul if (retval) 569b21c60a4SVinod Koul goto out; 570b21c60a4SVinod Koul if (copy_to_user((char __user *)arg, params, sizeof(*params))) 571b21c60a4SVinod Koul retval = -EFAULT; 572b21c60a4SVinod Koul 573b21c60a4SVinod Koul out: 574b21c60a4SVinod Koul kfree(params); 575b21c60a4SVinod Koul return retval; 576b21c60a4SVinod Koul } 577b21c60a4SVinod Koul 5789727b490SJeeja KP static int 5799727b490SJeeja KP snd_compr_get_metadata(struct snd_compr_stream *stream, unsigned long arg) 5809727b490SJeeja KP { 5819727b490SJeeja KP struct snd_compr_metadata metadata; 5829727b490SJeeja KP int retval; 5839727b490SJeeja KP 5849727b490SJeeja KP if (!stream->ops->get_metadata) 5859727b490SJeeja KP return -ENXIO; 5869727b490SJeeja KP 5879727b490SJeeja KP if (copy_from_user(&metadata, (void __user *)arg, sizeof(metadata))) 5889727b490SJeeja KP return -EFAULT; 5899727b490SJeeja KP 5909727b490SJeeja KP retval = stream->ops->get_metadata(stream, &metadata); 5919727b490SJeeja KP if (retval != 0) 5929727b490SJeeja KP return retval; 5939727b490SJeeja KP 5949727b490SJeeja KP if (copy_to_user((void __user *)arg, &metadata, sizeof(metadata))) 5959727b490SJeeja KP return -EFAULT; 5969727b490SJeeja KP 5979727b490SJeeja KP return 0; 5989727b490SJeeja KP } 5999727b490SJeeja KP 6009727b490SJeeja KP static int 6019727b490SJeeja KP snd_compr_set_metadata(struct snd_compr_stream *stream, unsigned long arg) 6029727b490SJeeja KP { 6039727b490SJeeja KP struct snd_compr_metadata metadata; 6049727b490SJeeja KP int retval; 6059727b490SJeeja KP 6069727b490SJeeja KP if (!stream->ops->set_metadata) 6079727b490SJeeja KP return -ENXIO; 6089727b490SJeeja KP /* 6099727b490SJeeja KP * we should allow parameter change only when stream has been 6109727b490SJeeja KP * opened not in other cases 6119727b490SJeeja KP */ 6129727b490SJeeja KP if (copy_from_user(&metadata, (void __user *)arg, sizeof(metadata))) 6139727b490SJeeja KP return -EFAULT; 6149727b490SJeeja KP 6159727b490SJeeja KP retval = stream->ops->set_metadata(stream, &metadata); 6169727b490SJeeja KP stream->metadata_set = true; 6179727b490SJeeja KP 6189727b490SJeeja KP return retval; 6199727b490SJeeja KP } 6209727b490SJeeja KP 621b21c60a4SVinod Koul static inline int 622b21c60a4SVinod Koul snd_compr_tstamp(struct snd_compr_stream *stream, unsigned long arg) 623b21c60a4SVinod Koul { 62417ac8e5cSRichard Fitzgerald struct snd_compr_tstamp tstamp = {0}; 62517ac8e5cSRichard Fitzgerald int ret; 626b21c60a4SVinod Koul 62717ac8e5cSRichard Fitzgerald ret = snd_compr_update_tstamp(stream, &tstamp); 62817ac8e5cSRichard Fitzgerald if (ret == 0) 62917ac8e5cSRichard Fitzgerald ret = copy_to_user((struct snd_compr_tstamp __user *)arg, 630b21c60a4SVinod Koul &tstamp, sizeof(tstamp)) ? -EFAULT : 0; 63117ac8e5cSRichard Fitzgerald return ret; 632b21c60a4SVinod Koul } 633b21c60a4SVinod Koul 634b21c60a4SVinod Koul static int snd_compr_pause(struct snd_compr_stream *stream) 635b21c60a4SVinod Koul { 636b21c60a4SVinod Koul int retval; 637b21c60a4SVinod Koul 638b21c60a4SVinod Koul if (stream->runtime->state != SNDRV_PCM_STATE_RUNNING) 639b21c60a4SVinod Koul return -EPERM; 640b21c60a4SVinod Koul retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_PAUSE_PUSH); 6416b18f793SVinod Koul if (!retval) 642b21c60a4SVinod Koul stream->runtime->state = SNDRV_PCM_STATE_PAUSED; 643b21c60a4SVinod Koul return retval; 644b21c60a4SVinod Koul } 645b21c60a4SVinod Koul 646b21c60a4SVinod Koul static int snd_compr_resume(struct snd_compr_stream *stream) 647b21c60a4SVinod Koul { 648b21c60a4SVinod Koul int retval; 649b21c60a4SVinod Koul 650b21c60a4SVinod Koul if (stream->runtime->state != SNDRV_PCM_STATE_PAUSED) 651b21c60a4SVinod Koul return -EPERM; 652b21c60a4SVinod Koul retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_PAUSE_RELEASE); 653b21c60a4SVinod Koul if (!retval) 654b21c60a4SVinod Koul stream->runtime->state = SNDRV_PCM_STATE_RUNNING; 655b21c60a4SVinod Koul return retval; 656b21c60a4SVinod Koul } 657b21c60a4SVinod Koul 658b21c60a4SVinod Koul static int snd_compr_start(struct snd_compr_stream *stream) 659b21c60a4SVinod Koul { 660b21c60a4SVinod Koul int retval; 661b21c60a4SVinod Koul 662b21c60a4SVinod Koul if (stream->runtime->state != SNDRV_PCM_STATE_PREPARED) 663b21c60a4SVinod Koul return -EPERM; 664b21c60a4SVinod Koul retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_START); 665b21c60a4SVinod Koul if (!retval) 666b21c60a4SVinod Koul stream->runtime->state = SNDRV_PCM_STATE_RUNNING; 667b21c60a4SVinod Koul return retval; 668b21c60a4SVinod Koul } 669b21c60a4SVinod Koul 670b21c60a4SVinod Koul static int snd_compr_stop(struct snd_compr_stream *stream) 671b21c60a4SVinod Koul { 672b21c60a4SVinod Koul int retval; 673b21c60a4SVinod Koul 674b21c60a4SVinod Koul if (stream->runtime->state == SNDRV_PCM_STATE_PREPARED || 675b21c60a4SVinod Koul stream->runtime->state == SNDRV_PCM_STATE_SETUP) 676b21c60a4SVinod Koul return -EPERM; 677b21c60a4SVinod Koul retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_STOP); 678b21c60a4SVinod Koul if (!retval) { 679917f4b5cSVinod Koul snd_compr_drain_notify(stream); 6808b21460aSVinod Koul stream->runtime->total_bytes_available = 0; 6818b21460aSVinod Koul stream->runtime->total_bytes_transferred = 0; 682b21c60a4SVinod Koul } 683b21c60a4SVinod Koul return retval; 684b21c60a4SVinod Koul } 685b21c60a4SVinod Koul 686917f4b5cSVinod Koul static int snd_compress_wait_for_drain(struct snd_compr_stream *stream) 687917f4b5cSVinod Koul { 688f44f2a54SVinod Koul int ret; 689f44f2a54SVinod Koul 690917f4b5cSVinod Koul /* 691917f4b5cSVinod Koul * We are called with lock held. So drop the lock while we wait for 692917f4b5cSVinod Koul * drain complete notfication from the driver 693917f4b5cSVinod Koul * 694917f4b5cSVinod Koul * It is expected that driver will notify the drain completion and then 695917f4b5cSVinod Koul * stream will be moved to SETUP state, even if draining resulted in an 696917f4b5cSVinod Koul * error. We can trigger next track after this. 697917f4b5cSVinod Koul */ 698917f4b5cSVinod Koul stream->runtime->state = SNDRV_PCM_STATE_DRAINING; 699917f4b5cSVinod Koul mutex_unlock(&stream->device->lock); 700917f4b5cSVinod Koul 701f44f2a54SVinod Koul /* we wait for drain to complete here, drain can return when 702f44f2a54SVinod Koul * interruption occurred, wait returned error or success. 703f44f2a54SVinod Koul * For the first two cases we don't do anything different here and 704f44f2a54SVinod Koul * return after waking up 705f44f2a54SVinod Koul */ 706f44f2a54SVinod Koul 707f44f2a54SVinod Koul ret = wait_event_interruptible(stream->runtime->sleep, 708f44f2a54SVinod Koul (stream->runtime->state != SNDRV_PCM_STATE_DRAINING)); 709f44f2a54SVinod Koul if (ret == -ERESTARTSYS) 710f44f2a54SVinod Koul pr_debug("wait aborted by a signal"); 711f44f2a54SVinod Koul else if (ret) 712f44f2a54SVinod Koul pr_debug("wait for drain failed with %d\n", ret); 713f44f2a54SVinod Koul 714917f4b5cSVinod Koul 715917f4b5cSVinod Koul wake_up(&stream->runtime->sleep); 716917f4b5cSVinod Koul mutex_lock(&stream->device->lock); 717917f4b5cSVinod Koul 718f44f2a54SVinod Koul return ret; 719917f4b5cSVinod Koul } 720917f4b5cSVinod Koul 721b21c60a4SVinod Koul static int snd_compr_drain(struct snd_compr_stream *stream) 722b21c60a4SVinod Koul { 723b21c60a4SVinod Koul int retval; 724b21c60a4SVinod Koul 725b21c60a4SVinod Koul if (stream->runtime->state == SNDRV_PCM_STATE_PREPARED || 726b21c60a4SVinod Koul stream->runtime->state == SNDRV_PCM_STATE_SETUP) 727b21c60a4SVinod Koul return -EPERM; 728917f4b5cSVinod Koul 729b21c60a4SVinod Koul retval = stream->ops->trigger(stream, SND_COMPR_TRIGGER_DRAIN); 730917f4b5cSVinod Koul if (retval) { 731f44f2a54SVinod Koul pr_debug("SND_COMPR_TRIGGER_DRAIN failed %d\n", retval); 732b21c60a4SVinod Koul wake_up(&stream->runtime->sleep); 733917f4b5cSVinod Koul return retval; 734b21c60a4SVinod Koul } 735917f4b5cSVinod Koul 736f44f2a54SVinod Koul return snd_compress_wait_for_drain(stream); 737b21c60a4SVinod Koul } 738b21c60a4SVinod Koul 7399727b490SJeeja KP static int snd_compr_next_track(struct snd_compr_stream *stream) 7409727b490SJeeja KP { 7419727b490SJeeja KP int retval; 7429727b490SJeeja KP 7439727b490SJeeja KP /* only a running stream can transition to next track */ 7449727b490SJeeja KP if (stream->runtime->state != SNDRV_PCM_STATE_RUNNING) 7459727b490SJeeja KP return -EPERM; 7469727b490SJeeja KP 7479727b490SJeeja KP /* you can signal next track isf this is intended to be a gapless stream 7489727b490SJeeja KP * and current track metadata is set 7499727b490SJeeja KP */ 7509727b490SJeeja KP if (stream->metadata_set == false) 7519727b490SJeeja KP return -EPERM; 7529727b490SJeeja KP 7539727b490SJeeja KP retval = stream->ops->trigger(stream, SND_COMPR_TRIGGER_NEXT_TRACK); 7549727b490SJeeja KP if (retval != 0) 7559727b490SJeeja KP return retval; 7569727b490SJeeja KP stream->metadata_set = false; 7579727b490SJeeja KP stream->next_track = true; 7589727b490SJeeja KP return 0; 7599727b490SJeeja KP } 7609727b490SJeeja KP 7619727b490SJeeja KP static int snd_compr_partial_drain(struct snd_compr_stream *stream) 7629727b490SJeeja KP { 7639727b490SJeeja KP int retval; 7649727b490SJeeja KP if (stream->runtime->state == SNDRV_PCM_STATE_PREPARED || 7659727b490SJeeja KP stream->runtime->state == SNDRV_PCM_STATE_SETUP) 7669727b490SJeeja KP return -EPERM; 7679727b490SJeeja KP /* stream can be drained only when next track has been signalled */ 7689727b490SJeeja KP if (stream->next_track == false) 7699727b490SJeeja KP return -EPERM; 7709727b490SJeeja KP 7719727b490SJeeja KP retval = stream->ops->trigger(stream, SND_COMPR_TRIGGER_PARTIAL_DRAIN); 772917f4b5cSVinod Koul if (retval) { 773f44f2a54SVinod Koul pr_debug("Partial drain returned failure\n"); 774917f4b5cSVinod Koul wake_up(&stream->runtime->sleep); 775917f4b5cSVinod Koul return retval; 776917f4b5cSVinod Koul } 7779727b490SJeeja KP 7789727b490SJeeja KP stream->next_track = false; 779917f4b5cSVinod Koul return snd_compress_wait_for_drain(stream); 7809727b490SJeeja KP } 7819727b490SJeeja KP 782b21c60a4SVinod Koul static long snd_compr_ioctl(struct file *f, unsigned int cmd, unsigned long arg) 783b21c60a4SVinod Koul { 784b21c60a4SVinod Koul struct snd_compr_file *data = f->private_data; 785b21c60a4SVinod Koul struct snd_compr_stream *stream; 786b21c60a4SVinod Koul int retval = -ENOTTY; 787b21c60a4SVinod Koul 788b21c60a4SVinod Koul if (snd_BUG_ON(!data)) 789b21c60a4SVinod Koul return -EFAULT; 790b21c60a4SVinod Koul stream = &data->stream; 791b21c60a4SVinod Koul if (snd_BUG_ON(!stream)) 792b21c60a4SVinod Koul return -EFAULT; 793b21c60a4SVinod Koul mutex_lock(&stream->device->lock); 794b21c60a4SVinod Koul switch (_IOC_NR(cmd)) { 795b21c60a4SVinod Koul case _IOC_NR(SNDRV_COMPRESS_IOCTL_VERSION): 796a8d30608SVinod Koul retval = put_user(SNDRV_COMPRESS_VERSION, 797b21c60a4SVinod Koul (int __user *)arg) ? -EFAULT : 0; 798b21c60a4SVinod Koul break; 799b21c60a4SVinod Koul case _IOC_NR(SNDRV_COMPRESS_GET_CAPS): 800b21c60a4SVinod Koul retval = snd_compr_get_caps(stream, arg); 801b21c60a4SVinod Koul break; 802b21c60a4SVinod Koul case _IOC_NR(SNDRV_COMPRESS_GET_CODEC_CAPS): 803b21c60a4SVinod Koul retval = snd_compr_get_codec_caps(stream, arg); 804b21c60a4SVinod Koul break; 805b21c60a4SVinod Koul case _IOC_NR(SNDRV_COMPRESS_SET_PARAMS): 806b21c60a4SVinod Koul retval = snd_compr_set_params(stream, arg); 807b21c60a4SVinod Koul break; 808b21c60a4SVinod Koul case _IOC_NR(SNDRV_COMPRESS_GET_PARAMS): 809b21c60a4SVinod Koul retval = snd_compr_get_params(stream, arg); 810b21c60a4SVinod Koul break; 8119727b490SJeeja KP case _IOC_NR(SNDRV_COMPRESS_SET_METADATA): 8129727b490SJeeja KP retval = snd_compr_set_metadata(stream, arg); 8139727b490SJeeja KP break; 8149727b490SJeeja KP case _IOC_NR(SNDRV_COMPRESS_GET_METADATA): 8159727b490SJeeja KP retval = snd_compr_get_metadata(stream, arg); 8169727b490SJeeja KP break; 817b21c60a4SVinod Koul case _IOC_NR(SNDRV_COMPRESS_TSTAMP): 818b21c60a4SVinod Koul retval = snd_compr_tstamp(stream, arg); 819b21c60a4SVinod Koul break; 820b21c60a4SVinod Koul case _IOC_NR(SNDRV_COMPRESS_AVAIL): 821b21c60a4SVinod Koul retval = snd_compr_ioctl_avail(stream, arg); 822b21c60a4SVinod Koul break; 823b21c60a4SVinod Koul case _IOC_NR(SNDRV_COMPRESS_PAUSE): 824b21c60a4SVinod Koul retval = snd_compr_pause(stream); 825b21c60a4SVinod Koul break; 826b21c60a4SVinod Koul case _IOC_NR(SNDRV_COMPRESS_RESUME): 827b21c60a4SVinod Koul retval = snd_compr_resume(stream); 828b21c60a4SVinod Koul break; 829b21c60a4SVinod Koul case _IOC_NR(SNDRV_COMPRESS_START): 830b21c60a4SVinod Koul retval = snd_compr_start(stream); 831b21c60a4SVinod Koul break; 832b21c60a4SVinod Koul case _IOC_NR(SNDRV_COMPRESS_STOP): 833b21c60a4SVinod Koul retval = snd_compr_stop(stream); 834b21c60a4SVinod Koul break; 835b21c60a4SVinod Koul case _IOC_NR(SNDRV_COMPRESS_DRAIN): 836b21c60a4SVinod Koul retval = snd_compr_drain(stream); 837b21c60a4SVinod Koul break; 8389727b490SJeeja KP case _IOC_NR(SNDRV_COMPRESS_PARTIAL_DRAIN): 8399727b490SJeeja KP retval = snd_compr_partial_drain(stream); 8409727b490SJeeja KP break; 8419727b490SJeeja KP case _IOC_NR(SNDRV_COMPRESS_NEXT_TRACK): 8429727b490SJeeja KP retval = snd_compr_next_track(stream); 8439727b490SJeeja KP break; 8449727b490SJeeja KP 845b21c60a4SVinod Koul } 846b21c60a4SVinod Koul mutex_unlock(&stream->device->lock); 847b21c60a4SVinod Koul return retval; 848b21c60a4SVinod Koul } 849b21c60a4SVinod Koul 850b21c60a4SVinod Koul static const struct file_operations snd_compr_file_ops = { 851b21c60a4SVinod Koul .owner = THIS_MODULE, 852b21c60a4SVinod Koul .open = snd_compr_open, 853b21c60a4SVinod Koul .release = snd_compr_free, 854b21c60a4SVinod Koul .write = snd_compr_write, 855b21c60a4SVinod Koul .read = snd_compr_read, 856b21c60a4SVinod Koul .unlocked_ioctl = snd_compr_ioctl, 857b21c60a4SVinod Koul .mmap = snd_compr_mmap, 858b21c60a4SVinod Koul .poll = snd_compr_poll, 859b21c60a4SVinod Koul }; 860b21c60a4SVinod Koul 861b21c60a4SVinod Koul static int snd_compress_dev_register(struct snd_device *device) 862b21c60a4SVinod Koul { 863b21c60a4SVinod Koul int ret = -EINVAL; 864b21c60a4SVinod Koul char str[16]; 865b21c60a4SVinod Koul struct snd_compr *compr; 866b21c60a4SVinod Koul 867b21c60a4SVinod Koul if (snd_BUG_ON(!device || !device->device_data)) 868b21c60a4SVinod Koul return -EBADFD; 869b21c60a4SVinod Koul compr = device->device_data; 870b21c60a4SVinod Koul 871b21c60a4SVinod Koul sprintf(str, "comprC%iD%i", compr->card->number, compr->device); 872b21c60a4SVinod Koul pr_debug("reg %s for device %s, direction %d\n", str, compr->name, 873b21c60a4SVinod Koul compr->direction); 874b21c60a4SVinod Koul /* register compressed device */ 875b21c60a4SVinod Koul ret = snd_register_device(SNDRV_DEVICE_TYPE_COMPRESS, compr->card, 876b21c60a4SVinod Koul compr->device, &snd_compr_file_ops, compr, str); 877b21c60a4SVinod Koul if (ret < 0) { 878b21c60a4SVinod Koul pr_err("snd_register_device failed\n %d", ret); 879b21c60a4SVinod Koul return ret; 880b21c60a4SVinod Koul } 881b21c60a4SVinod Koul return ret; 882b21c60a4SVinod Koul 883b21c60a4SVinod Koul } 884b21c60a4SVinod Koul 885b21c60a4SVinod Koul static int snd_compress_dev_disconnect(struct snd_device *device) 886b21c60a4SVinod Koul { 887b21c60a4SVinod Koul struct snd_compr *compr; 888b21c60a4SVinod Koul 889b21c60a4SVinod Koul compr = device->device_data; 8904028b6c4SLiam Girdwood snd_unregister_device(SNDRV_DEVICE_TYPE_COMPRESS, compr->card, 8914028b6c4SLiam Girdwood compr->device); 892b21c60a4SVinod Koul return 0; 893b21c60a4SVinod Koul } 894b21c60a4SVinod Koul 895b21c60a4SVinod Koul /* 896b21c60a4SVinod Koul * snd_compress_new: create new compress device 897b21c60a4SVinod Koul * @card: sound card pointer 898b21c60a4SVinod Koul * @device: device number 899b21c60a4SVinod Koul * @dirn: device direction, should be of type enum snd_compr_direction 900b21c60a4SVinod Koul * @compr: compress device pointer 901b21c60a4SVinod Koul */ 902b21c60a4SVinod Koul int snd_compress_new(struct snd_card *card, int device, 903b21c60a4SVinod Koul int dirn, struct snd_compr *compr) 904b21c60a4SVinod Koul { 905b21c60a4SVinod Koul static struct snd_device_ops ops = { 906b21c60a4SVinod Koul .dev_free = NULL, 907b21c60a4SVinod Koul .dev_register = snd_compress_dev_register, 908b21c60a4SVinod Koul .dev_disconnect = snd_compress_dev_disconnect, 909b21c60a4SVinod Koul }; 910b21c60a4SVinod Koul 911b21c60a4SVinod Koul compr->card = card; 912b21c60a4SVinod Koul compr->device = device; 913b21c60a4SVinod Koul compr->direction = dirn; 914b21c60a4SVinod Koul return snd_device_new(card, SNDRV_DEV_COMPRESS, compr, &ops); 915b21c60a4SVinod Koul } 916b21c60a4SVinod Koul EXPORT_SYMBOL_GPL(snd_compress_new); 917b21c60a4SVinod Koul 918b21c60a4SVinod Koul static int snd_compress_add_device(struct snd_compr *device) 919b21c60a4SVinod Koul { 920b21c60a4SVinod Koul int ret; 921b21c60a4SVinod Koul 922b21c60a4SVinod Koul if (!device->card) 923b21c60a4SVinod Koul return -EINVAL; 924b21c60a4SVinod Koul 925b21c60a4SVinod Koul /* register the card */ 926b21c60a4SVinod Koul ret = snd_card_register(device->card); 927b21c60a4SVinod Koul if (ret) 928b21c60a4SVinod Koul goto out; 929b21c60a4SVinod Koul return 0; 930b21c60a4SVinod Koul 931b21c60a4SVinod Koul out: 932b21c60a4SVinod Koul pr_err("failed with %d\n", ret); 933b21c60a4SVinod Koul return ret; 934b21c60a4SVinod Koul 935b21c60a4SVinod Koul } 936b21c60a4SVinod Koul 937b21c60a4SVinod Koul static int snd_compress_remove_device(struct snd_compr *device) 938b21c60a4SVinod Koul { 939b21c60a4SVinod Koul return snd_card_free(device->card); 940b21c60a4SVinod Koul } 941b21c60a4SVinod Koul 942b21c60a4SVinod Koul /** 943b21c60a4SVinod Koul * snd_compress_register - register compressed device 944b21c60a4SVinod Koul * 945b21c60a4SVinod Koul * @device: compressed device to register 946b21c60a4SVinod Koul */ 947b21c60a4SVinod Koul int snd_compress_register(struct snd_compr *device) 948b21c60a4SVinod Koul { 949b21c60a4SVinod Koul int retval; 950b21c60a4SVinod Koul 951b21c60a4SVinod Koul if (device->name == NULL || device->dev == NULL || device->ops == NULL) 952b21c60a4SVinod Koul return -EINVAL; 953b21c60a4SVinod Koul 954b21c60a4SVinod Koul pr_debug("Registering compressed device %s\n", device->name); 955b21c60a4SVinod Koul if (snd_BUG_ON(!device->ops->open)) 956b21c60a4SVinod Koul return -EINVAL; 957b21c60a4SVinod Koul if (snd_BUG_ON(!device->ops->free)) 958b21c60a4SVinod Koul return -EINVAL; 959b21c60a4SVinod Koul if (snd_BUG_ON(!device->ops->set_params)) 960b21c60a4SVinod Koul return -EINVAL; 961b21c60a4SVinod Koul if (snd_BUG_ON(!device->ops->trigger)) 962b21c60a4SVinod Koul return -EINVAL; 963b21c60a4SVinod Koul 964b21c60a4SVinod Koul mutex_init(&device->lock); 965b21c60a4SVinod Koul 966b21c60a4SVinod Koul /* register a compressed card */ 967b21c60a4SVinod Koul mutex_lock(&device_mutex); 968b21c60a4SVinod Koul retval = snd_compress_add_device(device); 969b21c60a4SVinod Koul mutex_unlock(&device_mutex); 970b21c60a4SVinod Koul return retval; 971b21c60a4SVinod Koul } 972b21c60a4SVinod Koul EXPORT_SYMBOL_GPL(snd_compress_register); 973b21c60a4SVinod Koul 974b21c60a4SVinod Koul int snd_compress_deregister(struct snd_compr *device) 975b21c60a4SVinod Koul { 976b21c60a4SVinod Koul pr_debug("Removing compressed device %s\n", device->name); 977b21c60a4SVinod Koul mutex_lock(&device_mutex); 978b21c60a4SVinod Koul snd_compress_remove_device(device); 979b21c60a4SVinod Koul mutex_unlock(&device_mutex); 980b21c60a4SVinod Koul return 0; 981b21c60a4SVinod Koul } 982b21c60a4SVinod Koul EXPORT_SYMBOL_GPL(snd_compress_deregister); 983b21c60a4SVinod Koul 984b21c60a4SVinod Koul static int __init snd_compress_init(void) 985b21c60a4SVinod Koul { 986b21c60a4SVinod Koul return 0; 987b21c60a4SVinod Koul } 988b21c60a4SVinod Koul 989b21c60a4SVinod Koul static void __exit snd_compress_exit(void) 990b21c60a4SVinod Koul { 991b21c60a4SVinod Koul } 992b21c60a4SVinod Koul 993b21c60a4SVinod Koul module_init(snd_compress_init); 994b21c60a4SVinod Koul module_exit(snd_compress_exit); 995b21c60a4SVinod Koul 996b21c60a4SVinod Koul MODULE_DESCRIPTION("ALSA Compressed offload framework"); 997b21c60a4SVinod Koul MODULE_AUTHOR("Vinod Koul <vinod.koul@linux.intel.com>"); 998b21c60a4SVinod Koul MODULE_LICENSE("GPL v2"); 999