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> 41c1036889SRavindra Lokhande #include <linux/compat.h> 42b21c60a4SVinod Koul #include <sound/core.h> 43b21c60a4SVinod Koul #include <sound/initval.h> 4431742724SRichard Fitzgerald #include <sound/info.h> 45b21c60a4SVinod Koul #include <sound/compress_params.h> 46b21c60a4SVinod Koul #include <sound/compress_offload.h> 47b21c60a4SVinod Koul #include <sound/compress_driver.h> 48b21c60a4SVinod Koul 49462b3f16STakashi Iwai /* struct snd_compr_codec_caps overflows the ioctl bit size for some 50462b3f16STakashi Iwai * architectures, so we need to disable the relevant ioctls. 51462b3f16STakashi Iwai */ 52462b3f16STakashi Iwai #if _IOC_SIZEBITS < 14 53462b3f16STakashi Iwai #define COMPR_CODEC_CAPS_OVERFLOW 54462b3f16STakashi Iwai #endif 55462b3f16STakashi Iwai 56b21c60a4SVinod Koul /* TODO: 57b21c60a4SVinod Koul * - add substream support for multiple devices in case of 58b21c60a4SVinod Koul * SND_DYNAMIC_MINORS is not used 59b21c60a4SVinod Koul * - Multiple node representation 60b21c60a4SVinod Koul * driver should be able to register multiple nodes 61b21c60a4SVinod Koul */ 62b21c60a4SVinod Koul 63b21c60a4SVinod Koul static DEFINE_MUTEX(device_mutex); 64b21c60a4SVinod Koul 65b21c60a4SVinod Koul struct snd_compr_file { 66b21c60a4SVinod Koul unsigned long caps; 67b21c60a4SVinod Koul struct snd_compr_stream stream; 68b21c60a4SVinod Koul }; 69b21c60a4SVinod Koul 70b21c60a4SVinod Koul /* 71b21c60a4SVinod Koul * a note on stream states used: 7241eb94fdSVinod Koul * we use following states in the compressed core 73b21c60a4SVinod Koul * SNDRV_PCM_STATE_OPEN: When stream has been opened. 74b21c60a4SVinod Koul * SNDRV_PCM_STATE_SETUP: When stream has been initialized. This is done by 7541eb94fdSVinod Koul * calling SNDRV_COMPRESS_SET_PARAMS. Running streams will come to this 76b21c60a4SVinod Koul * state at stop by calling SNDRV_COMPRESS_STOP, or at end of drain. 77862bca5dSVinod Koul * SNDRV_PCM_STATE_PREPARED: When a stream has been written to (for 78862bca5dSVinod Koul * playback only). User after setting up stream writes the data buffer 79862bca5dSVinod Koul * before starting the stream. 80b21c60a4SVinod Koul * SNDRV_PCM_STATE_RUNNING: When stream has been started and is 81b21c60a4SVinod Koul * decoding/encoding and rendering/capturing data. 82b21c60a4SVinod Koul * SNDRV_PCM_STATE_DRAINING: When stream is draining current data. This is done 83b21c60a4SVinod Koul * by calling SNDRV_COMPRESS_DRAIN. 84b21c60a4SVinod Koul * SNDRV_PCM_STATE_PAUSED: When stream is paused. This is done by calling 85b21c60a4SVinod Koul * SNDRV_COMPRESS_PAUSE. It can be stopped or resumed by calling 86b21c60a4SVinod Koul * SNDRV_COMPRESS_STOP or SNDRV_COMPRESS_RESUME respectively. 87b21c60a4SVinod Koul */ 88b21c60a4SVinod Koul static int snd_compr_open(struct inode *inode, struct file *f) 89b21c60a4SVinod Koul { 90b21c60a4SVinod Koul struct snd_compr *compr; 91b21c60a4SVinod Koul struct snd_compr_file *data; 92b21c60a4SVinod Koul struct snd_compr_runtime *runtime; 93b21c60a4SVinod Koul enum snd_compr_direction dirn; 94b21c60a4SVinod Koul int maj = imajor(inode); 95b21c60a4SVinod Koul int ret; 96b21c60a4SVinod Koul 9781cb3246SDan Carpenter if ((f->f_flags & O_ACCMODE) == O_WRONLY) 98b21c60a4SVinod Koul dirn = SND_COMPRESS_PLAYBACK; 9981cb3246SDan Carpenter else if ((f->f_flags & O_ACCMODE) == O_RDONLY) 100b21c60a4SVinod Koul dirn = SND_COMPRESS_CAPTURE; 10181cb3246SDan Carpenter else 102b21c60a4SVinod Koul return -EINVAL; 103b21c60a4SVinod Koul 104b21c60a4SVinod Koul if (maj == snd_major) 105b21c60a4SVinod Koul compr = snd_lookup_minor_data(iminor(inode), 106b21c60a4SVinod Koul SNDRV_DEVICE_TYPE_COMPRESS); 107b21c60a4SVinod Koul else 108b21c60a4SVinod Koul return -EBADFD; 109b21c60a4SVinod Koul 110b21c60a4SVinod Koul if (compr == NULL) { 111b21c60a4SVinod Koul pr_err("no device data!!!\n"); 112b21c60a4SVinod Koul return -ENODEV; 113b21c60a4SVinod Koul } 114b21c60a4SVinod Koul 115b21c60a4SVinod Koul if (dirn != compr->direction) { 116b21c60a4SVinod Koul pr_err("this device doesn't support this direction\n"); 117a0830dbdSTakashi Iwai snd_card_unref(compr->card); 118b21c60a4SVinod Koul return -EINVAL; 119b21c60a4SVinod Koul } 120b21c60a4SVinod Koul 121b21c60a4SVinod Koul data = kzalloc(sizeof(*data), GFP_KERNEL); 122a0830dbdSTakashi Iwai if (!data) { 123a0830dbdSTakashi Iwai snd_card_unref(compr->card); 124b21c60a4SVinod Koul return -ENOMEM; 125a0830dbdSTakashi Iwai } 126b21c60a4SVinod Koul data->stream.ops = compr->ops; 127b21c60a4SVinod Koul data->stream.direction = dirn; 128b21c60a4SVinod Koul data->stream.private_data = compr->private_data; 129b21c60a4SVinod Koul data->stream.device = compr; 130b21c60a4SVinod Koul runtime = kzalloc(sizeof(*runtime), GFP_KERNEL); 131b21c60a4SVinod Koul if (!runtime) { 132b21c60a4SVinod Koul kfree(data); 133a0830dbdSTakashi Iwai snd_card_unref(compr->card); 134b21c60a4SVinod Koul return -ENOMEM; 135b21c60a4SVinod Koul } 136b21c60a4SVinod Koul runtime->state = SNDRV_PCM_STATE_OPEN; 137b21c60a4SVinod Koul init_waitqueue_head(&runtime->sleep); 138b21c60a4SVinod Koul data->stream.runtime = runtime; 139b21c60a4SVinod Koul f->private_data = (void *)data; 140b21c60a4SVinod Koul mutex_lock(&compr->lock); 141b21c60a4SVinod Koul ret = compr->ops->open(&data->stream); 142b21c60a4SVinod Koul mutex_unlock(&compr->lock); 143b21c60a4SVinod Koul if (ret) { 144b21c60a4SVinod Koul kfree(runtime); 145b21c60a4SVinod Koul kfree(data); 146b21c60a4SVinod Koul } 147a0830dbdSTakashi Iwai snd_card_unref(compr->card); 148749d3223SCharles Keepax return ret; 149b21c60a4SVinod Koul } 150b21c60a4SVinod Koul 151b21c60a4SVinod Koul static int snd_compr_free(struct inode *inode, struct file *f) 152b21c60a4SVinod Koul { 153b21c60a4SVinod Koul struct snd_compr_file *data = f->private_data; 154b26d19e4SLiam Girdwood struct snd_compr_runtime *runtime = data->stream.runtime; 155b26d19e4SLiam Girdwood 156b26d19e4SLiam Girdwood switch (runtime->state) { 157b26d19e4SLiam Girdwood case SNDRV_PCM_STATE_RUNNING: 158b26d19e4SLiam Girdwood case SNDRV_PCM_STATE_DRAINING: 159b26d19e4SLiam Girdwood case SNDRV_PCM_STATE_PAUSED: 160b26d19e4SLiam Girdwood data->stream.ops->trigger(&data->stream, SNDRV_PCM_TRIGGER_STOP); 161b26d19e4SLiam Girdwood break; 162b26d19e4SLiam Girdwood default: 163b26d19e4SLiam Girdwood break; 164b26d19e4SLiam Girdwood } 165b26d19e4SLiam Girdwood 166b21c60a4SVinod Koul data->stream.ops->free(&data->stream); 167b21c60a4SVinod Koul kfree(data->stream.runtime->buffer); 168b21c60a4SVinod Koul kfree(data->stream.runtime); 169b21c60a4SVinod Koul kfree(data); 170b21c60a4SVinod Koul return 0; 171b21c60a4SVinod Koul } 172b21c60a4SVinod Koul 17317ac8e5cSRichard Fitzgerald static int snd_compr_update_tstamp(struct snd_compr_stream *stream, 174b21c60a4SVinod Koul struct snd_compr_tstamp *tstamp) 175b21c60a4SVinod Koul { 176b21c60a4SVinod Koul if (!stream->ops->pointer) 17717ac8e5cSRichard Fitzgerald return -ENOTSUPP; 178b21c60a4SVinod Koul stream->ops->pointer(stream, tstamp); 179b21c60a4SVinod Koul pr_debug("dsp consumed till %d total %d bytes\n", 180b21c60a4SVinod Koul tstamp->byte_offset, tstamp->copied_total); 1815b1f79f7SCharles Keepax if (stream->direction == SND_COMPRESS_PLAYBACK) 182b21c60a4SVinod Koul stream->runtime->total_bytes_transferred = tstamp->copied_total; 1835b1f79f7SCharles Keepax else 1845b1f79f7SCharles Keepax stream->runtime->total_bytes_available = tstamp->copied_total; 18517ac8e5cSRichard Fitzgerald return 0; 186b21c60a4SVinod Koul } 187b21c60a4SVinod Koul 188b21c60a4SVinod Koul static size_t snd_compr_calc_avail(struct snd_compr_stream *stream, 189b21c60a4SVinod Koul struct snd_compr_avail *avail) 190b21c60a4SVinod Koul { 19117ac8e5cSRichard Fitzgerald memset(avail, 0, sizeof(*avail)); 192b21c60a4SVinod Koul snd_compr_update_tstamp(stream, &avail->tstamp); 19317ac8e5cSRichard Fitzgerald /* Still need to return avail even if tstamp can't be filled in */ 194b21c60a4SVinod Koul 195b21c60a4SVinod Koul if (stream->runtime->total_bytes_available == 0 && 1965b1f79f7SCharles Keepax stream->runtime->state == SNDRV_PCM_STATE_SETUP && 1975b1f79f7SCharles Keepax stream->direction == SND_COMPRESS_PLAYBACK) { 198b21c60a4SVinod Koul pr_debug("detected init and someone forgot to do a write\n"); 199b21c60a4SVinod Koul return stream->runtime->buffer_size; 200b21c60a4SVinod Koul } 201b21c60a4SVinod Koul pr_debug("app wrote %lld, DSP consumed %lld\n", 202b21c60a4SVinod Koul stream->runtime->total_bytes_available, 203b21c60a4SVinod Koul stream->runtime->total_bytes_transferred); 204b21c60a4SVinod Koul if (stream->runtime->total_bytes_available == 205b21c60a4SVinod Koul stream->runtime->total_bytes_transferred) { 2065b1f79f7SCharles Keepax if (stream->direction == SND_COMPRESS_PLAYBACK) { 207b21c60a4SVinod Koul pr_debug("both pointers are same, returning full avail\n"); 208b21c60a4SVinod Koul return stream->runtime->buffer_size; 2095b1f79f7SCharles Keepax } else { 2105b1f79f7SCharles Keepax pr_debug("both pointers are same, returning no avail\n"); 2115b1f79f7SCharles Keepax return 0; 2125b1f79f7SCharles Keepax } 213b21c60a4SVinod Koul } 214b21c60a4SVinod Koul 2155b1f79f7SCharles Keepax avail->avail = stream->runtime->total_bytes_available - 2165b1f79f7SCharles Keepax stream->runtime->total_bytes_transferred; 2175b1f79f7SCharles Keepax if (stream->direction == SND_COMPRESS_PLAYBACK) 2185b1f79f7SCharles Keepax avail->avail = stream->runtime->buffer_size - avail->avail; 2195b1f79f7SCharles Keepax 2204c28e32dSCharles Keepax pr_debug("ret avail as %lld\n", avail->avail); 2214c28e32dSCharles Keepax return avail->avail; 222b21c60a4SVinod Koul } 223b21c60a4SVinod Koul 224b21c60a4SVinod Koul static inline size_t snd_compr_get_avail(struct snd_compr_stream *stream) 225b21c60a4SVinod Koul { 226b21c60a4SVinod Koul struct snd_compr_avail avail; 227b21c60a4SVinod Koul 228b21c60a4SVinod Koul return snd_compr_calc_avail(stream, &avail); 229b21c60a4SVinod Koul } 230b21c60a4SVinod Koul 231b21c60a4SVinod Koul static int 232b21c60a4SVinod Koul snd_compr_ioctl_avail(struct snd_compr_stream *stream, unsigned long arg) 233b21c60a4SVinod Koul { 234b21c60a4SVinod Koul struct snd_compr_avail ioctl_avail; 235b21c60a4SVinod Koul size_t avail; 236b21c60a4SVinod Koul 237b21c60a4SVinod Koul avail = snd_compr_calc_avail(stream, &ioctl_avail); 238b21c60a4SVinod Koul ioctl_avail.avail = avail; 239b21c60a4SVinod Koul 240b21c60a4SVinod Koul if (copy_to_user((__u64 __user *)arg, 241b21c60a4SVinod Koul &ioctl_avail, sizeof(ioctl_avail))) 242b21c60a4SVinod Koul return -EFAULT; 243b21c60a4SVinod Koul return 0; 244b21c60a4SVinod Koul } 245b21c60a4SVinod Koul 246b21c60a4SVinod Koul static int snd_compr_write_data(struct snd_compr_stream *stream, 247b21c60a4SVinod Koul const char __user *buf, size_t count) 248b21c60a4SVinod Koul { 249b21c60a4SVinod Koul void *dstn; 250b21c60a4SVinod Koul size_t copy; 251b21c60a4SVinod Koul struct snd_compr_runtime *runtime = stream->runtime; 252f0283b58SCharles Keepax /* 64-bit Modulus */ 253f0283b58SCharles Keepax u64 app_pointer = div64_u64(runtime->total_bytes_available, 254f0283b58SCharles Keepax runtime->buffer_size); 255f0283b58SCharles Keepax app_pointer = runtime->total_bytes_available - 256f0283b58SCharles Keepax (app_pointer * runtime->buffer_size); 257b21c60a4SVinod Koul 258f0283b58SCharles Keepax dstn = runtime->buffer + app_pointer; 259b21c60a4SVinod Koul pr_debug("copying %ld at %lld\n", 260f0283b58SCharles Keepax (unsigned long)count, app_pointer); 261f0283b58SCharles Keepax if (count < runtime->buffer_size - app_pointer) { 262b21c60a4SVinod Koul if (copy_from_user(dstn, buf, count)) 263b21c60a4SVinod Koul return -EFAULT; 264b21c60a4SVinod Koul } else { 265f0283b58SCharles Keepax copy = runtime->buffer_size - app_pointer; 266b21c60a4SVinod Koul if (copy_from_user(dstn, buf, copy)) 267b21c60a4SVinod Koul return -EFAULT; 268b21c60a4SVinod Koul if (copy_from_user(runtime->buffer, buf + copy, count - copy)) 269b21c60a4SVinod Koul return -EFAULT; 270b21c60a4SVinod Koul } 271b21c60a4SVinod Koul /* if DSP cares, let it know data has been written */ 272b21c60a4SVinod Koul if (stream->ops->ack) 273b21c60a4SVinod Koul stream->ops->ack(stream, count); 274b21c60a4SVinod Koul return count; 275b21c60a4SVinod Koul } 276b21c60a4SVinod Koul 277b21c60a4SVinod Koul static ssize_t snd_compr_write(struct file *f, const char __user *buf, 278b21c60a4SVinod Koul size_t count, loff_t *offset) 279b21c60a4SVinod Koul { 280b21c60a4SVinod Koul struct snd_compr_file *data = f->private_data; 281b21c60a4SVinod Koul struct snd_compr_stream *stream; 282b21c60a4SVinod Koul size_t avail; 283b21c60a4SVinod Koul int retval; 284b21c60a4SVinod Koul 285b21c60a4SVinod Koul if (snd_BUG_ON(!data)) 286b21c60a4SVinod Koul return -EFAULT; 287b21c60a4SVinod Koul 288b21c60a4SVinod Koul stream = &data->stream; 289b21c60a4SVinod Koul mutex_lock(&stream->device->lock); 290b21c60a4SVinod Koul /* write is allowed when stream is running or has been steup */ 291b21c60a4SVinod Koul if (stream->runtime->state != SNDRV_PCM_STATE_SETUP && 29235383a24SEric Laurent stream->runtime->state != SNDRV_PCM_STATE_PREPARED && 293b21c60a4SVinod Koul stream->runtime->state != SNDRV_PCM_STATE_RUNNING) { 294b21c60a4SVinod Koul mutex_unlock(&stream->device->lock); 295b21c60a4SVinod Koul return -EBADFD; 296b21c60a4SVinod Koul } 297b21c60a4SVinod Koul 298b21c60a4SVinod Koul avail = snd_compr_get_avail(stream); 299b21c60a4SVinod Koul pr_debug("avail returned %ld\n", (unsigned long)avail); 300b21c60a4SVinod Koul /* calculate how much we can write to buffer */ 301b21c60a4SVinod Koul if (avail > count) 302b21c60a4SVinod Koul avail = count; 303b21c60a4SVinod Koul 3044daf891cSCharles Keepax if (stream->ops->copy) { 3054daf891cSCharles Keepax char __user* cbuf = (char __user*)buf; 3064daf891cSCharles Keepax retval = stream->ops->copy(stream, cbuf, avail); 3074daf891cSCharles Keepax } else { 308b21c60a4SVinod Koul retval = snd_compr_write_data(stream, buf, avail); 3094daf891cSCharles Keepax } 310b21c60a4SVinod Koul if (retval > 0) 311b21c60a4SVinod Koul stream->runtime->total_bytes_available += retval; 312b21c60a4SVinod Koul 313b21c60a4SVinod Koul /* while initiating the stream, write should be called before START 314b21c60a4SVinod Koul * call, so in setup move state */ 315b21c60a4SVinod Koul if (stream->runtime->state == SNDRV_PCM_STATE_SETUP) { 316b21c60a4SVinod Koul stream->runtime->state = SNDRV_PCM_STATE_PREPARED; 317b21c60a4SVinod Koul pr_debug("stream prepared, Houston we are good to go\n"); 318b21c60a4SVinod Koul } 319b21c60a4SVinod Koul 320b21c60a4SVinod Koul mutex_unlock(&stream->device->lock); 321b21c60a4SVinod Koul return retval; 322b21c60a4SVinod Koul } 323b21c60a4SVinod Koul 324b21c60a4SVinod Koul 325b21c60a4SVinod Koul static ssize_t snd_compr_read(struct file *f, char __user *buf, 326b21c60a4SVinod Koul size_t count, loff_t *offset) 327b21c60a4SVinod Koul { 32849bb6402SCharles Keepax struct snd_compr_file *data = f->private_data; 32949bb6402SCharles Keepax struct snd_compr_stream *stream; 33049bb6402SCharles Keepax size_t avail; 33149bb6402SCharles Keepax int retval; 33249bb6402SCharles Keepax 33349bb6402SCharles Keepax if (snd_BUG_ON(!data)) 33449bb6402SCharles Keepax return -EFAULT; 33549bb6402SCharles Keepax 33649bb6402SCharles Keepax stream = &data->stream; 33749bb6402SCharles Keepax mutex_lock(&stream->device->lock); 33849bb6402SCharles Keepax 33975481347SVinod Koul /* read is allowed when stream is running, paused, draining and setup 34075481347SVinod Koul * (yes setup is state which we transition to after stop, so if user 34175481347SVinod Koul * wants to read data after stop we allow that) 34275481347SVinod Koul */ 34375481347SVinod Koul switch (stream->runtime->state) { 34475481347SVinod Koul case SNDRV_PCM_STATE_OPEN: 34575481347SVinod Koul case SNDRV_PCM_STATE_PREPARED: 34675481347SVinod Koul case SNDRV_PCM_STATE_XRUN: 34775481347SVinod Koul case SNDRV_PCM_STATE_SUSPENDED: 34875481347SVinod Koul case SNDRV_PCM_STATE_DISCONNECTED: 34949bb6402SCharles Keepax retval = -EBADFD; 35049bb6402SCharles Keepax goto out; 35149bb6402SCharles Keepax } 35249bb6402SCharles Keepax 35349bb6402SCharles Keepax avail = snd_compr_get_avail(stream); 35449bb6402SCharles Keepax pr_debug("avail returned %ld\n", (unsigned long)avail); 35549bb6402SCharles Keepax /* calculate how much we can read from buffer */ 35649bb6402SCharles Keepax if (avail > count) 35749bb6402SCharles Keepax avail = count; 35849bb6402SCharles Keepax 35949bb6402SCharles Keepax if (stream->ops->copy) { 36049bb6402SCharles Keepax retval = stream->ops->copy(stream, buf, avail); 36149bb6402SCharles Keepax } else { 36249bb6402SCharles Keepax retval = -ENXIO; 36349bb6402SCharles Keepax goto out; 36449bb6402SCharles Keepax } 36549bb6402SCharles Keepax if (retval > 0) 36649bb6402SCharles Keepax stream->runtime->total_bytes_transferred += retval; 36749bb6402SCharles Keepax 36849bb6402SCharles Keepax out: 36949bb6402SCharles Keepax mutex_unlock(&stream->device->lock); 37049bb6402SCharles Keepax return retval; 371b21c60a4SVinod Koul } 372b21c60a4SVinod Koul 373b21c60a4SVinod Koul static int snd_compr_mmap(struct file *f, struct vm_area_struct *vma) 374b21c60a4SVinod Koul { 375b21c60a4SVinod Koul return -ENXIO; 376b21c60a4SVinod Koul } 377b21c60a4SVinod Koul 378b21c60a4SVinod Koul static inline int snd_compr_get_poll(struct snd_compr_stream *stream) 379b21c60a4SVinod Koul { 380b21c60a4SVinod Koul if (stream->direction == SND_COMPRESS_PLAYBACK) 381b21c60a4SVinod Koul return POLLOUT | POLLWRNORM; 382b21c60a4SVinod Koul else 383b21c60a4SVinod Koul return POLLIN | POLLRDNORM; 384b21c60a4SVinod Koul } 385b21c60a4SVinod Koul 386b21c60a4SVinod Koul static unsigned int snd_compr_poll(struct file *f, poll_table *wait) 387b21c60a4SVinod Koul { 388b21c60a4SVinod Koul struct snd_compr_file *data = f->private_data; 389b21c60a4SVinod Koul struct snd_compr_stream *stream; 390b21c60a4SVinod Koul size_t avail; 391b21c60a4SVinod Koul int retval = 0; 392b21c60a4SVinod Koul 393b21c60a4SVinod Koul if (snd_BUG_ON(!data)) 394b21c60a4SVinod Koul return -EFAULT; 395b21c60a4SVinod Koul stream = &data->stream; 396b21c60a4SVinod Koul if (snd_BUG_ON(!stream)) 397b21c60a4SVinod Koul return -EFAULT; 398b21c60a4SVinod Koul 399b21c60a4SVinod Koul mutex_lock(&stream->device->lock); 400c15b149aSRichard Fitzgerald if (stream->runtime->state == SNDRV_PCM_STATE_OPEN) { 401b21c60a4SVinod Koul retval = -EBADFD; 402b21c60a4SVinod Koul goto out; 403b21c60a4SVinod Koul } 404b21c60a4SVinod Koul poll_wait(f, &stream->runtime->sleep, wait); 405b21c60a4SVinod Koul 406b21c60a4SVinod Koul avail = snd_compr_get_avail(stream); 407b21c60a4SVinod Koul pr_debug("avail is %ld\n", (unsigned long)avail); 408b21c60a4SVinod Koul /* check if we have at least one fragment to fill */ 409b21c60a4SVinod Koul switch (stream->runtime->state) { 410b21c60a4SVinod Koul case SNDRV_PCM_STATE_DRAINING: 411b21c60a4SVinod Koul /* stream has been woken up after drain is complete 412b21c60a4SVinod Koul * draining done so set stream state to stopped 413b21c60a4SVinod Koul */ 414b21c60a4SVinod Koul retval = snd_compr_get_poll(stream); 415b21c60a4SVinod Koul stream->runtime->state = SNDRV_PCM_STATE_SETUP; 416b21c60a4SVinod Koul break; 417b21c60a4SVinod Koul case SNDRV_PCM_STATE_RUNNING: 418b21c60a4SVinod Koul case SNDRV_PCM_STATE_PREPARED: 419b21c60a4SVinod Koul case SNDRV_PCM_STATE_PAUSED: 420b21c60a4SVinod Koul if (avail >= stream->runtime->fragment_size) 421b21c60a4SVinod Koul retval = snd_compr_get_poll(stream); 422b21c60a4SVinod Koul break; 423b21c60a4SVinod Koul default: 424b21c60a4SVinod Koul if (stream->direction == SND_COMPRESS_PLAYBACK) 425b21c60a4SVinod Koul retval = POLLOUT | POLLWRNORM | POLLERR; 426b21c60a4SVinod Koul else 427b21c60a4SVinod Koul retval = POLLIN | POLLRDNORM | POLLERR; 428b21c60a4SVinod Koul break; 429b21c60a4SVinod Koul } 430b21c60a4SVinod Koul out: 431b21c60a4SVinod Koul mutex_unlock(&stream->device->lock); 432b21c60a4SVinod Koul return retval; 433b21c60a4SVinod Koul } 434b21c60a4SVinod Koul 435b21c60a4SVinod Koul static int 436b21c60a4SVinod Koul snd_compr_get_caps(struct snd_compr_stream *stream, unsigned long arg) 437b21c60a4SVinod Koul { 438b21c60a4SVinod Koul int retval; 439b21c60a4SVinod Koul struct snd_compr_caps caps; 440b21c60a4SVinod Koul 441b21c60a4SVinod Koul if (!stream->ops->get_caps) 442b21c60a4SVinod Koul return -ENXIO; 443b21c60a4SVinod Koul 4441c62e9f2SDan Carpenter memset(&caps, 0, sizeof(caps)); 445b21c60a4SVinod Koul retval = stream->ops->get_caps(stream, &caps); 446b21c60a4SVinod Koul if (retval) 447b21c60a4SVinod Koul goto out; 448b21c60a4SVinod Koul if (copy_to_user((void __user *)arg, &caps, sizeof(caps))) 449b21c60a4SVinod Koul retval = -EFAULT; 450b21c60a4SVinod Koul out: 451b21c60a4SVinod Koul return retval; 452b21c60a4SVinod Koul } 453b21c60a4SVinod Koul 454462b3f16STakashi Iwai #ifndef COMPR_CODEC_CAPS_OVERFLOW 455b21c60a4SVinod Koul static int 456b21c60a4SVinod Koul snd_compr_get_codec_caps(struct snd_compr_stream *stream, unsigned long arg) 457b21c60a4SVinod Koul { 458b21c60a4SVinod Koul int retval; 459b21c60a4SVinod Koul struct snd_compr_codec_caps *caps; 460b21c60a4SVinod Koul 461b21c60a4SVinod Koul if (!stream->ops->get_codec_caps) 462b21c60a4SVinod Koul return -ENXIO; 463b21c60a4SVinod Koul 46447966e97STakashi Iwai caps = kzalloc(sizeof(*caps), GFP_KERNEL); 465b21c60a4SVinod Koul if (!caps) 466b21c60a4SVinod Koul return -ENOMEM; 467b21c60a4SVinod Koul 468b21c60a4SVinod Koul retval = stream->ops->get_codec_caps(stream, caps); 469b21c60a4SVinod Koul if (retval) 470b21c60a4SVinod Koul goto out; 471b21c60a4SVinod Koul if (copy_to_user((void __user *)arg, caps, sizeof(*caps))) 472b21c60a4SVinod Koul retval = -EFAULT; 473b21c60a4SVinod Koul 474b21c60a4SVinod Koul out: 475b21c60a4SVinod Koul kfree(caps); 476b21c60a4SVinod Koul return retval; 477b21c60a4SVinod Koul } 478462b3f16STakashi Iwai #endif /* !COMPR_CODEC_CAPS_OVERFLOW */ 479b21c60a4SVinod Koul 480b21c60a4SVinod Koul /* revisit this with snd_pcm_preallocate_xxx */ 481b21c60a4SVinod Koul static int snd_compr_allocate_buffer(struct snd_compr_stream *stream, 482b21c60a4SVinod Koul struct snd_compr_params *params) 483b21c60a4SVinod Koul { 484b21c60a4SVinod Koul unsigned int buffer_size; 485b21c60a4SVinod Koul void *buffer; 486b21c60a4SVinod Koul 487b21c60a4SVinod Koul buffer_size = params->buffer.fragment_size * params->buffer.fragments; 488b21c60a4SVinod Koul if (stream->ops->copy) { 489b21c60a4SVinod Koul buffer = NULL; 490b21c60a4SVinod Koul /* if copy is defined the driver will be required to copy 491b21c60a4SVinod Koul * the data from core 492b21c60a4SVinod Koul */ 493b21c60a4SVinod Koul } else { 494b21c60a4SVinod Koul buffer = kmalloc(buffer_size, GFP_KERNEL); 495b21c60a4SVinod Koul if (!buffer) 496b21c60a4SVinod Koul return -ENOMEM; 497b21c60a4SVinod Koul } 498b21c60a4SVinod Koul stream->runtime->fragment_size = params->buffer.fragment_size; 499b21c60a4SVinod Koul stream->runtime->fragments = params->buffer.fragments; 500b21c60a4SVinod Koul stream->runtime->buffer = buffer; 501b21c60a4SVinod Koul stream->runtime->buffer_size = buffer_size; 502b21c60a4SVinod Koul return 0; 503b21c60a4SVinod Koul } 504b21c60a4SVinod Koul 5054dc040a0SVinod Koul static int snd_compress_check_input(struct snd_compr_params *params) 5064dc040a0SVinod Koul { 5074dc040a0SVinod Koul /* first let's check the buffer parameter's */ 5084dc040a0SVinod Koul if (params->buffer.fragment_size == 0 || 5096217e5edSDan Carpenter params->buffer.fragments > INT_MAX / params->buffer.fragment_size) 5104dc040a0SVinod Koul return -EINVAL; 5114dc040a0SVinod Koul 512fb4a9779SVinod Koul /* now codec parameters */ 513fb4a9779SVinod Koul if (params->codec.id == 0 || params->codec.id > SND_AUDIOCODEC_MAX) 514fb4a9779SVinod Koul return -EINVAL; 515fb4a9779SVinod Koul 516fb4a9779SVinod Koul if (params->codec.ch_in == 0 || params->codec.ch_out == 0) 517fb4a9779SVinod Koul return -EINVAL; 518fb4a9779SVinod Koul 5194dc040a0SVinod Koul return 0; 5204dc040a0SVinod Koul } 5214dc040a0SVinod Koul 522b21c60a4SVinod Koul static int 523b21c60a4SVinod Koul snd_compr_set_params(struct snd_compr_stream *stream, unsigned long arg) 524b21c60a4SVinod Koul { 525b21c60a4SVinod Koul struct snd_compr_params *params; 526b21c60a4SVinod Koul int retval; 527b21c60a4SVinod Koul 528b21c60a4SVinod Koul if (stream->runtime->state == SNDRV_PCM_STATE_OPEN) { 529b21c60a4SVinod Koul /* 530b21c60a4SVinod Koul * we should allow parameter change only when stream has been 531b21c60a4SVinod Koul * opened not in other cases 532b21c60a4SVinod Koul */ 533b21c60a4SVinod Koul params = kmalloc(sizeof(*params), GFP_KERNEL); 534b21c60a4SVinod Koul if (!params) 535b21c60a4SVinod Koul return -ENOMEM; 536769fab2aSJesper Juhl if (copy_from_user(params, (void __user *)arg, sizeof(*params))) { 537769fab2aSJesper Juhl retval = -EFAULT; 538769fab2aSJesper Juhl goto out; 539769fab2aSJesper Juhl } 5404dc040a0SVinod Koul 5414dc040a0SVinod Koul retval = snd_compress_check_input(params); 5424dc040a0SVinod Koul if (retval) 5434dc040a0SVinod Koul goto out; 5444dc040a0SVinod Koul 545b21c60a4SVinod Koul retval = snd_compr_allocate_buffer(stream, params); 546b21c60a4SVinod Koul if (retval) { 547769fab2aSJesper Juhl retval = -ENOMEM; 548769fab2aSJesper Juhl goto out; 549b21c60a4SVinod Koul } 5504dc040a0SVinod Koul 551b21c60a4SVinod Koul retval = stream->ops->set_params(stream, params); 552b21c60a4SVinod Koul if (retval) 553b21c60a4SVinod Koul goto out; 55449bb6402SCharles Keepax 5559727b490SJeeja KP stream->metadata_set = false; 5569727b490SJeeja KP stream->next_track = false; 55749bb6402SCharles Keepax 55849bb6402SCharles Keepax if (stream->direction == SND_COMPRESS_PLAYBACK) 55949bb6402SCharles Keepax stream->runtime->state = SNDRV_PCM_STATE_SETUP; 56049bb6402SCharles Keepax else 56149bb6402SCharles Keepax stream->runtime->state = SNDRV_PCM_STATE_PREPARED; 562769fab2aSJesper Juhl } else { 563b21c60a4SVinod Koul return -EPERM; 564769fab2aSJesper Juhl } 565b21c60a4SVinod Koul out: 566b21c60a4SVinod Koul kfree(params); 567b21c60a4SVinod Koul return retval; 568b21c60a4SVinod Koul } 569b21c60a4SVinod Koul 570b21c60a4SVinod Koul static int 571b21c60a4SVinod Koul snd_compr_get_params(struct snd_compr_stream *stream, unsigned long arg) 572b21c60a4SVinod Koul { 573b21c60a4SVinod Koul struct snd_codec *params; 574b21c60a4SVinod Koul int retval; 575b21c60a4SVinod Koul 576b21c60a4SVinod Koul if (!stream->ops->get_params) 577b21c60a4SVinod Koul return -EBADFD; 578b21c60a4SVinod Koul 57947966e97STakashi Iwai params = kzalloc(sizeof(*params), GFP_KERNEL); 580b21c60a4SVinod Koul if (!params) 581b21c60a4SVinod Koul return -ENOMEM; 582b21c60a4SVinod Koul retval = stream->ops->get_params(stream, params); 583b21c60a4SVinod Koul if (retval) 584b21c60a4SVinod Koul goto out; 585b21c60a4SVinod Koul if (copy_to_user((char __user *)arg, params, sizeof(*params))) 586b21c60a4SVinod Koul retval = -EFAULT; 587b21c60a4SVinod Koul 588b21c60a4SVinod Koul out: 589b21c60a4SVinod Koul kfree(params); 590b21c60a4SVinod Koul return retval; 591b21c60a4SVinod Koul } 592b21c60a4SVinod Koul 5939727b490SJeeja KP static int 5949727b490SJeeja KP snd_compr_get_metadata(struct snd_compr_stream *stream, unsigned long arg) 5959727b490SJeeja KP { 5969727b490SJeeja KP struct snd_compr_metadata metadata; 5979727b490SJeeja KP int retval; 5989727b490SJeeja KP 5999727b490SJeeja KP if (!stream->ops->get_metadata) 6009727b490SJeeja KP return -ENXIO; 6019727b490SJeeja KP 6029727b490SJeeja KP if (copy_from_user(&metadata, (void __user *)arg, sizeof(metadata))) 6039727b490SJeeja KP return -EFAULT; 6049727b490SJeeja KP 6059727b490SJeeja KP retval = stream->ops->get_metadata(stream, &metadata); 6069727b490SJeeja KP if (retval != 0) 6079727b490SJeeja KP return retval; 6089727b490SJeeja KP 6099727b490SJeeja KP if (copy_to_user((void __user *)arg, &metadata, sizeof(metadata))) 6109727b490SJeeja KP return -EFAULT; 6119727b490SJeeja KP 6129727b490SJeeja KP return 0; 6139727b490SJeeja KP } 6149727b490SJeeja KP 6159727b490SJeeja KP static int 6169727b490SJeeja KP snd_compr_set_metadata(struct snd_compr_stream *stream, unsigned long arg) 6179727b490SJeeja KP { 6189727b490SJeeja KP struct snd_compr_metadata metadata; 6199727b490SJeeja KP int retval; 6209727b490SJeeja KP 6219727b490SJeeja KP if (!stream->ops->set_metadata) 6229727b490SJeeja KP return -ENXIO; 6239727b490SJeeja KP /* 6249727b490SJeeja KP * we should allow parameter change only when stream has been 6259727b490SJeeja KP * opened not in other cases 6269727b490SJeeja KP */ 6279727b490SJeeja KP if (copy_from_user(&metadata, (void __user *)arg, sizeof(metadata))) 6289727b490SJeeja KP return -EFAULT; 6299727b490SJeeja KP 6309727b490SJeeja KP retval = stream->ops->set_metadata(stream, &metadata); 6319727b490SJeeja KP stream->metadata_set = true; 6329727b490SJeeja KP 6339727b490SJeeja KP return retval; 6349727b490SJeeja KP } 6359727b490SJeeja KP 636b21c60a4SVinod Koul static inline int 637b21c60a4SVinod Koul snd_compr_tstamp(struct snd_compr_stream *stream, unsigned long arg) 638b21c60a4SVinod Koul { 63917ac8e5cSRichard Fitzgerald struct snd_compr_tstamp tstamp = {0}; 64017ac8e5cSRichard Fitzgerald int ret; 641b21c60a4SVinod Koul 64217ac8e5cSRichard Fitzgerald ret = snd_compr_update_tstamp(stream, &tstamp); 64317ac8e5cSRichard Fitzgerald if (ret == 0) 64417ac8e5cSRichard Fitzgerald ret = copy_to_user((struct snd_compr_tstamp __user *)arg, 645b21c60a4SVinod Koul &tstamp, sizeof(tstamp)) ? -EFAULT : 0; 64617ac8e5cSRichard Fitzgerald return ret; 647b21c60a4SVinod Koul } 648b21c60a4SVinod Koul 649b21c60a4SVinod Koul static int snd_compr_pause(struct snd_compr_stream *stream) 650b21c60a4SVinod Koul { 651b21c60a4SVinod Koul int retval; 652b21c60a4SVinod Koul 653b21c60a4SVinod Koul if (stream->runtime->state != SNDRV_PCM_STATE_RUNNING) 654b21c60a4SVinod Koul return -EPERM; 655b21c60a4SVinod Koul retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_PAUSE_PUSH); 6566b18f793SVinod Koul if (!retval) 657b21c60a4SVinod Koul stream->runtime->state = SNDRV_PCM_STATE_PAUSED; 658b21c60a4SVinod Koul return retval; 659b21c60a4SVinod Koul } 660b21c60a4SVinod Koul 661b21c60a4SVinod Koul static int snd_compr_resume(struct snd_compr_stream *stream) 662b21c60a4SVinod Koul { 663b21c60a4SVinod Koul int retval; 664b21c60a4SVinod Koul 665b21c60a4SVinod Koul if (stream->runtime->state != SNDRV_PCM_STATE_PAUSED) 666b21c60a4SVinod Koul return -EPERM; 667b21c60a4SVinod Koul retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_PAUSE_RELEASE); 668b21c60a4SVinod Koul if (!retval) 669b21c60a4SVinod Koul stream->runtime->state = SNDRV_PCM_STATE_RUNNING; 670b21c60a4SVinod Koul return retval; 671b21c60a4SVinod Koul } 672b21c60a4SVinod Koul 673b21c60a4SVinod Koul static int snd_compr_start(struct snd_compr_stream *stream) 674b21c60a4SVinod Koul { 675b21c60a4SVinod Koul int retval; 676b21c60a4SVinod Koul 677b21c60a4SVinod Koul if (stream->runtime->state != SNDRV_PCM_STATE_PREPARED) 678b21c60a4SVinod Koul return -EPERM; 679b21c60a4SVinod Koul retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_START); 680b21c60a4SVinod Koul if (!retval) 681b21c60a4SVinod Koul stream->runtime->state = SNDRV_PCM_STATE_RUNNING; 682b21c60a4SVinod Koul return retval; 683b21c60a4SVinod Koul } 684b21c60a4SVinod Koul 685b21c60a4SVinod Koul static int snd_compr_stop(struct snd_compr_stream *stream) 686b21c60a4SVinod Koul { 687b21c60a4SVinod Koul int retval; 688b21c60a4SVinod Koul 689b21c60a4SVinod Koul if (stream->runtime->state == SNDRV_PCM_STATE_PREPARED || 690b21c60a4SVinod Koul stream->runtime->state == SNDRV_PCM_STATE_SETUP) 691b21c60a4SVinod Koul return -EPERM; 692b21c60a4SVinod Koul retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_STOP); 693b21c60a4SVinod Koul if (!retval) { 694917f4b5cSVinod Koul snd_compr_drain_notify(stream); 6958b21460aSVinod Koul stream->runtime->total_bytes_available = 0; 6968b21460aSVinod Koul stream->runtime->total_bytes_transferred = 0; 697b21c60a4SVinod Koul } 698b21c60a4SVinod Koul return retval; 699b21c60a4SVinod Koul } 700b21c60a4SVinod Koul 701917f4b5cSVinod Koul static int snd_compress_wait_for_drain(struct snd_compr_stream *stream) 702917f4b5cSVinod Koul { 703f44f2a54SVinod Koul int ret; 704f44f2a54SVinod Koul 705917f4b5cSVinod Koul /* 706917f4b5cSVinod Koul * We are called with lock held. So drop the lock while we wait for 707cdb1ee3fSVinod Koul * drain complete notification from the driver 708917f4b5cSVinod Koul * 709917f4b5cSVinod Koul * It is expected that driver will notify the drain completion and then 710917f4b5cSVinod Koul * stream will be moved to SETUP state, even if draining resulted in an 711917f4b5cSVinod Koul * error. We can trigger next track after this. 712917f4b5cSVinod Koul */ 713917f4b5cSVinod Koul stream->runtime->state = SNDRV_PCM_STATE_DRAINING; 714917f4b5cSVinod Koul mutex_unlock(&stream->device->lock); 715917f4b5cSVinod Koul 716f44f2a54SVinod Koul /* we wait for drain to complete here, drain can return when 717f44f2a54SVinod Koul * interruption occurred, wait returned error or success. 718f44f2a54SVinod Koul * For the first two cases we don't do anything different here and 719f44f2a54SVinod Koul * return after waking up 720f44f2a54SVinod Koul */ 721f44f2a54SVinod Koul 722f44f2a54SVinod Koul ret = wait_event_interruptible(stream->runtime->sleep, 723f44f2a54SVinod Koul (stream->runtime->state != SNDRV_PCM_STATE_DRAINING)); 724f44f2a54SVinod Koul if (ret == -ERESTARTSYS) 725f44f2a54SVinod Koul pr_debug("wait aborted by a signal"); 726f44f2a54SVinod Koul else if (ret) 727f44f2a54SVinod Koul pr_debug("wait for drain failed with %d\n", ret); 728f44f2a54SVinod Koul 729917f4b5cSVinod Koul 730917f4b5cSVinod Koul wake_up(&stream->runtime->sleep); 731917f4b5cSVinod Koul mutex_lock(&stream->device->lock); 732917f4b5cSVinod Koul 733f44f2a54SVinod Koul return ret; 734917f4b5cSVinod Koul } 735917f4b5cSVinod Koul 736b21c60a4SVinod Koul static int snd_compr_drain(struct snd_compr_stream *stream) 737b21c60a4SVinod Koul { 738b21c60a4SVinod Koul int retval; 739b21c60a4SVinod Koul 740b21c60a4SVinod Koul if (stream->runtime->state == SNDRV_PCM_STATE_PREPARED || 741b21c60a4SVinod Koul stream->runtime->state == SNDRV_PCM_STATE_SETUP) 742b21c60a4SVinod Koul return -EPERM; 743917f4b5cSVinod Koul 744b21c60a4SVinod Koul retval = stream->ops->trigger(stream, SND_COMPR_TRIGGER_DRAIN); 745917f4b5cSVinod Koul if (retval) { 746f44f2a54SVinod Koul pr_debug("SND_COMPR_TRIGGER_DRAIN failed %d\n", retval); 747b21c60a4SVinod Koul wake_up(&stream->runtime->sleep); 748917f4b5cSVinod Koul return retval; 749b21c60a4SVinod Koul } 750917f4b5cSVinod Koul 751f44f2a54SVinod Koul return snd_compress_wait_for_drain(stream); 752b21c60a4SVinod Koul } 753b21c60a4SVinod Koul 7549727b490SJeeja KP static int snd_compr_next_track(struct snd_compr_stream *stream) 7559727b490SJeeja KP { 7569727b490SJeeja KP int retval; 7579727b490SJeeja KP 7589727b490SJeeja KP /* only a running stream can transition to next track */ 7599727b490SJeeja KP if (stream->runtime->state != SNDRV_PCM_STATE_RUNNING) 7609727b490SJeeja KP return -EPERM; 7619727b490SJeeja KP 762cdb1ee3fSVinod Koul /* you can signal next track if this is intended to be a gapless stream 7639727b490SJeeja KP * and current track metadata is set 7649727b490SJeeja KP */ 7659727b490SJeeja KP if (stream->metadata_set == false) 7669727b490SJeeja KP return -EPERM; 7679727b490SJeeja KP 7689727b490SJeeja KP retval = stream->ops->trigger(stream, SND_COMPR_TRIGGER_NEXT_TRACK); 7699727b490SJeeja KP if (retval != 0) 7709727b490SJeeja KP return retval; 7719727b490SJeeja KP stream->metadata_set = false; 7729727b490SJeeja KP stream->next_track = true; 7739727b490SJeeja KP return 0; 7749727b490SJeeja KP } 7759727b490SJeeja KP 7769727b490SJeeja KP static int snd_compr_partial_drain(struct snd_compr_stream *stream) 7779727b490SJeeja KP { 7789727b490SJeeja KP int retval; 7799727b490SJeeja KP if (stream->runtime->state == SNDRV_PCM_STATE_PREPARED || 7809727b490SJeeja KP stream->runtime->state == SNDRV_PCM_STATE_SETUP) 7819727b490SJeeja KP return -EPERM; 7829727b490SJeeja KP /* stream can be drained only when next track has been signalled */ 7839727b490SJeeja KP if (stream->next_track == false) 7849727b490SJeeja KP return -EPERM; 7859727b490SJeeja KP 7869727b490SJeeja KP retval = stream->ops->trigger(stream, SND_COMPR_TRIGGER_PARTIAL_DRAIN); 787917f4b5cSVinod Koul if (retval) { 788f44f2a54SVinod Koul pr_debug("Partial drain returned failure\n"); 789917f4b5cSVinod Koul wake_up(&stream->runtime->sleep); 790917f4b5cSVinod Koul return retval; 791917f4b5cSVinod Koul } 7929727b490SJeeja KP 7939727b490SJeeja KP stream->next_track = false; 794917f4b5cSVinod Koul return snd_compress_wait_for_drain(stream); 7959727b490SJeeja KP } 7969727b490SJeeja KP 797b21c60a4SVinod Koul static long snd_compr_ioctl(struct file *f, unsigned int cmd, unsigned long arg) 798b21c60a4SVinod Koul { 799b21c60a4SVinod Koul struct snd_compr_file *data = f->private_data; 800b21c60a4SVinod Koul struct snd_compr_stream *stream; 801b21c60a4SVinod Koul int retval = -ENOTTY; 802b21c60a4SVinod Koul 803b21c60a4SVinod Koul if (snd_BUG_ON(!data)) 804b21c60a4SVinod Koul return -EFAULT; 805b21c60a4SVinod Koul stream = &data->stream; 806b21c60a4SVinod Koul if (snd_BUG_ON(!stream)) 807b21c60a4SVinod Koul return -EFAULT; 808b21c60a4SVinod Koul mutex_lock(&stream->device->lock); 809b21c60a4SVinod Koul switch (_IOC_NR(cmd)) { 810b21c60a4SVinod Koul case _IOC_NR(SNDRV_COMPRESS_IOCTL_VERSION): 811a8d30608SVinod Koul retval = put_user(SNDRV_COMPRESS_VERSION, 812b21c60a4SVinod Koul (int __user *)arg) ? -EFAULT : 0; 813b21c60a4SVinod Koul break; 814b21c60a4SVinod Koul case _IOC_NR(SNDRV_COMPRESS_GET_CAPS): 815b21c60a4SVinod Koul retval = snd_compr_get_caps(stream, arg); 816b21c60a4SVinod Koul break; 817462b3f16STakashi Iwai #ifndef COMPR_CODEC_CAPS_OVERFLOW 818b21c60a4SVinod Koul case _IOC_NR(SNDRV_COMPRESS_GET_CODEC_CAPS): 819b21c60a4SVinod Koul retval = snd_compr_get_codec_caps(stream, arg); 820b21c60a4SVinod Koul break; 821462b3f16STakashi Iwai #endif 822b21c60a4SVinod Koul case _IOC_NR(SNDRV_COMPRESS_SET_PARAMS): 823b21c60a4SVinod Koul retval = snd_compr_set_params(stream, arg); 824b21c60a4SVinod Koul break; 825b21c60a4SVinod Koul case _IOC_NR(SNDRV_COMPRESS_GET_PARAMS): 826b21c60a4SVinod Koul retval = snd_compr_get_params(stream, arg); 827b21c60a4SVinod Koul break; 8289727b490SJeeja KP case _IOC_NR(SNDRV_COMPRESS_SET_METADATA): 8299727b490SJeeja KP retval = snd_compr_set_metadata(stream, arg); 8309727b490SJeeja KP break; 8319727b490SJeeja KP case _IOC_NR(SNDRV_COMPRESS_GET_METADATA): 8329727b490SJeeja KP retval = snd_compr_get_metadata(stream, arg); 8339727b490SJeeja KP break; 834b21c60a4SVinod Koul case _IOC_NR(SNDRV_COMPRESS_TSTAMP): 835b21c60a4SVinod Koul retval = snd_compr_tstamp(stream, arg); 836b21c60a4SVinod Koul break; 837b21c60a4SVinod Koul case _IOC_NR(SNDRV_COMPRESS_AVAIL): 838b21c60a4SVinod Koul retval = snd_compr_ioctl_avail(stream, arg); 839b21c60a4SVinod Koul break; 840b21c60a4SVinod Koul case _IOC_NR(SNDRV_COMPRESS_PAUSE): 841b21c60a4SVinod Koul retval = snd_compr_pause(stream); 842b21c60a4SVinod Koul break; 843b21c60a4SVinod Koul case _IOC_NR(SNDRV_COMPRESS_RESUME): 844b21c60a4SVinod Koul retval = snd_compr_resume(stream); 845b21c60a4SVinod Koul break; 846b21c60a4SVinod Koul case _IOC_NR(SNDRV_COMPRESS_START): 847b21c60a4SVinod Koul retval = snd_compr_start(stream); 848b21c60a4SVinod Koul break; 849b21c60a4SVinod Koul case _IOC_NR(SNDRV_COMPRESS_STOP): 850b21c60a4SVinod Koul retval = snd_compr_stop(stream); 851b21c60a4SVinod Koul break; 852b21c60a4SVinod Koul case _IOC_NR(SNDRV_COMPRESS_DRAIN): 853b21c60a4SVinod Koul retval = snd_compr_drain(stream); 854b21c60a4SVinod Koul break; 8559727b490SJeeja KP case _IOC_NR(SNDRV_COMPRESS_PARTIAL_DRAIN): 8569727b490SJeeja KP retval = snd_compr_partial_drain(stream); 8579727b490SJeeja KP break; 8589727b490SJeeja KP case _IOC_NR(SNDRV_COMPRESS_NEXT_TRACK): 8599727b490SJeeja KP retval = snd_compr_next_track(stream); 8609727b490SJeeja KP break; 8619727b490SJeeja KP 862b21c60a4SVinod Koul } 863b21c60a4SVinod Koul mutex_unlock(&stream->device->lock); 864b21c60a4SVinod Koul return retval; 865b21c60a4SVinod Koul } 866b21c60a4SVinod Koul 867c1036889SRavindra Lokhande /* support of 32bit userspace on 64bit platforms */ 868c1036889SRavindra Lokhande #ifdef CONFIG_COMPAT 869c1036889SRavindra Lokhande static long snd_compr_ioctl_compat(struct file *file, unsigned int cmd, 870c1036889SRavindra Lokhande unsigned long arg) 871c1036889SRavindra Lokhande { 872c1036889SRavindra Lokhande return snd_compr_ioctl(file, cmd, (unsigned long)compat_ptr(arg)); 873c1036889SRavindra Lokhande } 874c1036889SRavindra Lokhande #endif 875c1036889SRavindra Lokhande 876b21c60a4SVinod Koul static const struct file_operations snd_compr_file_ops = { 877b21c60a4SVinod Koul .owner = THIS_MODULE, 878b21c60a4SVinod Koul .open = snd_compr_open, 879b21c60a4SVinod Koul .release = snd_compr_free, 880b21c60a4SVinod Koul .write = snd_compr_write, 881b21c60a4SVinod Koul .read = snd_compr_read, 882b21c60a4SVinod Koul .unlocked_ioctl = snd_compr_ioctl, 883c1036889SRavindra Lokhande #ifdef CONFIG_COMPAT 884c1036889SRavindra Lokhande .compat_ioctl = snd_compr_ioctl_compat, 885c1036889SRavindra Lokhande #endif 886b21c60a4SVinod Koul .mmap = snd_compr_mmap, 887b21c60a4SVinod Koul .poll = snd_compr_poll, 888b21c60a4SVinod Koul }; 889b21c60a4SVinod Koul 890b21c60a4SVinod Koul static int snd_compress_dev_register(struct snd_device *device) 891b21c60a4SVinod Koul { 892b21c60a4SVinod Koul int ret = -EINVAL; 893b21c60a4SVinod Koul char str[16]; 894b21c60a4SVinod Koul struct snd_compr *compr; 895b21c60a4SVinod Koul 896b21c60a4SVinod Koul if (snd_BUG_ON(!device || !device->device_data)) 897b21c60a4SVinod Koul return -EBADFD; 898b21c60a4SVinod Koul compr = device->device_data; 899b21c60a4SVinod Koul 900b21c60a4SVinod Koul pr_debug("reg %s for device %s, direction %d\n", str, compr->name, 901b21c60a4SVinod Koul compr->direction); 902b21c60a4SVinod Koul /* register compressed device */ 90340a4b263STakashi Iwai ret = snd_register_device(SNDRV_DEVICE_TYPE_COMPRESS, 90404c5d5a4STakashi Iwai compr->card, compr->device, 90540a4b263STakashi Iwai &snd_compr_file_ops, compr, &compr->dev); 906b21c60a4SVinod Koul if (ret < 0) { 907b21c60a4SVinod Koul pr_err("snd_register_device failed\n %d", ret); 908b21c60a4SVinod Koul return ret; 909b21c60a4SVinod Koul } 910b21c60a4SVinod Koul return ret; 911b21c60a4SVinod Koul 912b21c60a4SVinod Koul } 913b21c60a4SVinod Koul 914b21c60a4SVinod Koul static int snd_compress_dev_disconnect(struct snd_device *device) 915b21c60a4SVinod Koul { 916b21c60a4SVinod Koul struct snd_compr *compr; 917b21c60a4SVinod Koul 918b21c60a4SVinod Koul compr = device->device_data; 91940a4b263STakashi Iwai snd_unregister_device(&compr->dev); 920b21c60a4SVinod Koul return 0; 921b21c60a4SVinod Koul } 922b21c60a4SVinod Koul 92331742724SRichard Fitzgerald #ifdef CONFIG_SND_VERBOSE_PROCFS 92431742724SRichard Fitzgerald static void snd_compress_proc_info_read(struct snd_info_entry *entry, 92531742724SRichard Fitzgerald struct snd_info_buffer *buffer) 92631742724SRichard Fitzgerald { 92731742724SRichard Fitzgerald struct snd_compr *compr = (struct snd_compr *)entry->private_data; 92831742724SRichard Fitzgerald 92931742724SRichard Fitzgerald snd_iprintf(buffer, "card: %d\n", compr->card->number); 93031742724SRichard Fitzgerald snd_iprintf(buffer, "device: %d\n", compr->device); 93131742724SRichard Fitzgerald snd_iprintf(buffer, "stream: %s\n", 93231742724SRichard Fitzgerald compr->direction == SND_COMPRESS_PLAYBACK 93331742724SRichard Fitzgerald ? "PLAYBACK" : "CAPTURE"); 93431742724SRichard Fitzgerald snd_iprintf(buffer, "id: %s\n", compr->id); 93531742724SRichard Fitzgerald } 93631742724SRichard Fitzgerald 93731742724SRichard Fitzgerald static int snd_compress_proc_init(struct snd_compr *compr) 93831742724SRichard Fitzgerald { 93931742724SRichard Fitzgerald struct snd_info_entry *entry; 94031742724SRichard Fitzgerald char name[16]; 94131742724SRichard Fitzgerald 94231742724SRichard Fitzgerald sprintf(name, "compr%i", compr->device); 94331742724SRichard Fitzgerald entry = snd_info_create_card_entry(compr->card, name, 94431742724SRichard Fitzgerald compr->card->proc_root); 94531742724SRichard Fitzgerald if (!entry) 94631742724SRichard Fitzgerald return -ENOMEM; 94731742724SRichard Fitzgerald entry->mode = S_IFDIR | S_IRUGO | S_IXUGO; 94831742724SRichard Fitzgerald if (snd_info_register(entry) < 0) { 94931742724SRichard Fitzgerald snd_info_free_entry(entry); 95031742724SRichard Fitzgerald return -ENOMEM; 95131742724SRichard Fitzgerald } 95231742724SRichard Fitzgerald compr->proc_root = entry; 95331742724SRichard Fitzgerald 95431742724SRichard Fitzgerald entry = snd_info_create_card_entry(compr->card, "info", 95531742724SRichard Fitzgerald compr->proc_root); 95631742724SRichard Fitzgerald if (entry) { 95731742724SRichard Fitzgerald snd_info_set_text_ops(entry, compr, 95831742724SRichard Fitzgerald snd_compress_proc_info_read); 95931742724SRichard Fitzgerald if (snd_info_register(entry) < 0) { 96031742724SRichard Fitzgerald snd_info_free_entry(entry); 96131742724SRichard Fitzgerald entry = NULL; 96231742724SRichard Fitzgerald } 96331742724SRichard Fitzgerald } 96431742724SRichard Fitzgerald compr->proc_info_entry = entry; 96531742724SRichard Fitzgerald 96631742724SRichard Fitzgerald return 0; 96731742724SRichard Fitzgerald } 96831742724SRichard Fitzgerald 96931742724SRichard Fitzgerald static void snd_compress_proc_done(struct snd_compr *compr) 97031742724SRichard Fitzgerald { 97131742724SRichard Fitzgerald snd_info_free_entry(compr->proc_info_entry); 97231742724SRichard Fitzgerald compr->proc_info_entry = NULL; 97331742724SRichard Fitzgerald snd_info_free_entry(compr->proc_root); 97431742724SRichard Fitzgerald compr->proc_root = NULL; 97531742724SRichard Fitzgerald } 976e5241a8cSRichard Fitzgerald 977e5241a8cSRichard Fitzgerald static inline void snd_compress_set_id(struct snd_compr *compr, const char *id) 978e5241a8cSRichard Fitzgerald { 979e5241a8cSRichard Fitzgerald strlcpy(compr->id, id, sizeof(compr->id)); 980e5241a8cSRichard Fitzgerald } 98131742724SRichard Fitzgerald #else 98231742724SRichard Fitzgerald static inline int snd_compress_proc_init(struct snd_compr *compr) 98331742724SRichard Fitzgerald { 98431742724SRichard Fitzgerald return 0; 98531742724SRichard Fitzgerald } 98631742724SRichard Fitzgerald 98731742724SRichard Fitzgerald static inline void snd_compress_proc_done(struct snd_compr *compr) 98831742724SRichard Fitzgerald { 98931742724SRichard Fitzgerald } 990e5241a8cSRichard Fitzgerald 991e5241a8cSRichard Fitzgerald static inline void snd_compress_set_id(struct snd_compr *compr, const char *id) 992e5241a8cSRichard Fitzgerald { 993e5241a8cSRichard Fitzgerald } 99431742724SRichard Fitzgerald #endif 99531742724SRichard Fitzgerald 99604c5d5a4STakashi Iwai static int snd_compress_dev_free(struct snd_device *device) 99704c5d5a4STakashi Iwai { 99804c5d5a4STakashi Iwai struct snd_compr *compr; 99904c5d5a4STakashi Iwai 100004c5d5a4STakashi Iwai compr = device->device_data; 100131742724SRichard Fitzgerald snd_compress_proc_done(compr); 100204c5d5a4STakashi Iwai put_device(&compr->dev); 100304c5d5a4STakashi Iwai return 0; 100404c5d5a4STakashi Iwai } 100504c5d5a4STakashi Iwai 1006b21c60a4SVinod Koul /* 1007b21c60a4SVinod Koul * snd_compress_new: create new compress device 1008b21c60a4SVinod Koul * @card: sound card pointer 1009b21c60a4SVinod Koul * @device: device number 1010b21c60a4SVinod Koul * @dirn: device direction, should be of type enum snd_compr_direction 1011b21c60a4SVinod Koul * @compr: compress device pointer 1012b21c60a4SVinod Koul */ 1013b21c60a4SVinod Koul int snd_compress_new(struct snd_card *card, int device, 1014e5241a8cSRichard Fitzgerald int dirn, const char *id, struct snd_compr *compr) 1015b21c60a4SVinod Koul { 1016b21c60a4SVinod Koul static struct snd_device_ops ops = { 101704c5d5a4STakashi Iwai .dev_free = snd_compress_dev_free, 1018b21c60a4SVinod Koul .dev_register = snd_compress_dev_register, 1019b21c60a4SVinod Koul .dev_disconnect = snd_compress_dev_disconnect, 1020b21c60a4SVinod Koul }; 102131742724SRichard Fitzgerald int ret; 1022b21c60a4SVinod Koul 1023b21c60a4SVinod Koul compr->card = card; 1024b21c60a4SVinod Koul compr->device = device; 1025b21c60a4SVinod Koul compr->direction = dirn; 102604c5d5a4STakashi Iwai 1027e5241a8cSRichard Fitzgerald snd_compress_set_id(compr, id); 1028e5241a8cSRichard Fitzgerald 102904c5d5a4STakashi Iwai snd_device_initialize(&compr->dev, card); 103004c5d5a4STakashi Iwai dev_set_name(&compr->dev, "comprC%iD%i", card->number, device); 103104c5d5a4STakashi Iwai 103231742724SRichard Fitzgerald ret = snd_device_new(card, SNDRV_DEV_COMPRESS, compr, &ops); 103331742724SRichard Fitzgerald if (ret == 0) 103431742724SRichard Fitzgerald snd_compress_proc_init(compr); 103531742724SRichard Fitzgerald 103631742724SRichard Fitzgerald return ret; 1037b21c60a4SVinod Koul } 1038b21c60a4SVinod Koul EXPORT_SYMBOL_GPL(snd_compress_new); 1039b21c60a4SVinod Koul 1040b21c60a4SVinod Koul static int snd_compress_add_device(struct snd_compr *device) 1041b21c60a4SVinod Koul { 1042b21c60a4SVinod Koul int ret; 1043b21c60a4SVinod Koul 1044b21c60a4SVinod Koul if (!device->card) 1045b21c60a4SVinod Koul return -EINVAL; 1046b21c60a4SVinod Koul 1047b21c60a4SVinod Koul /* register the card */ 1048b21c60a4SVinod Koul ret = snd_card_register(device->card); 1049b21c60a4SVinod Koul if (ret) 1050b21c60a4SVinod Koul goto out; 1051b21c60a4SVinod Koul return 0; 1052b21c60a4SVinod Koul 1053b21c60a4SVinod Koul out: 1054b21c60a4SVinod Koul pr_err("failed with %d\n", ret); 1055b21c60a4SVinod Koul return ret; 1056b21c60a4SVinod Koul 1057b21c60a4SVinod Koul } 1058b21c60a4SVinod Koul 1059b21c60a4SVinod Koul static int snd_compress_remove_device(struct snd_compr *device) 1060b21c60a4SVinod Koul { 1061b21c60a4SVinod Koul return snd_card_free(device->card); 1062b21c60a4SVinod Koul } 1063b21c60a4SVinod Koul 1064b21c60a4SVinod Koul /** 1065b21c60a4SVinod Koul * snd_compress_register - register compressed device 1066b21c60a4SVinod Koul * 1067b21c60a4SVinod Koul * @device: compressed device to register 1068b21c60a4SVinod Koul */ 1069b21c60a4SVinod Koul int snd_compress_register(struct snd_compr *device) 1070b21c60a4SVinod Koul { 1071b21c60a4SVinod Koul int retval; 1072b21c60a4SVinod Koul 107304c5d5a4STakashi Iwai if (device->name == NULL || device->ops == NULL) 1074b21c60a4SVinod Koul return -EINVAL; 1075b21c60a4SVinod Koul 1076b21c60a4SVinod Koul pr_debug("Registering compressed device %s\n", device->name); 1077b21c60a4SVinod Koul if (snd_BUG_ON(!device->ops->open)) 1078b21c60a4SVinod Koul return -EINVAL; 1079b21c60a4SVinod Koul if (snd_BUG_ON(!device->ops->free)) 1080b21c60a4SVinod Koul return -EINVAL; 1081b21c60a4SVinod Koul if (snd_BUG_ON(!device->ops->set_params)) 1082b21c60a4SVinod Koul return -EINVAL; 1083b21c60a4SVinod Koul if (snd_BUG_ON(!device->ops->trigger)) 1084b21c60a4SVinod Koul return -EINVAL; 1085b21c60a4SVinod Koul 1086b21c60a4SVinod Koul mutex_init(&device->lock); 1087b21c60a4SVinod Koul 1088b21c60a4SVinod Koul /* register a compressed card */ 1089b21c60a4SVinod Koul mutex_lock(&device_mutex); 1090b21c60a4SVinod Koul retval = snd_compress_add_device(device); 1091b21c60a4SVinod Koul mutex_unlock(&device_mutex); 1092b21c60a4SVinod Koul return retval; 1093b21c60a4SVinod Koul } 1094b21c60a4SVinod Koul EXPORT_SYMBOL_GPL(snd_compress_register); 1095b21c60a4SVinod Koul 1096b21c60a4SVinod Koul int snd_compress_deregister(struct snd_compr *device) 1097b21c60a4SVinod Koul { 1098b21c60a4SVinod Koul pr_debug("Removing compressed device %s\n", device->name); 1099b21c60a4SVinod Koul mutex_lock(&device_mutex); 1100b21c60a4SVinod Koul snd_compress_remove_device(device); 1101b21c60a4SVinod Koul mutex_unlock(&device_mutex); 1102b21c60a4SVinod Koul return 0; 1103b21c60a4SVinod Koul } 1104b21c60a4SVinod Koul EXPORT_SYMBOL_GPL(snd_compress_deregister); 1105b21c60a4SVinod Koul 1106b21c60a4SVinod Koul static int __init snd_compress_init(void) 1107b21c60a4SVinod Koul { 1108b21c60a4SVinod Koul return 0; 1109b21c60a4SVinod Koul } 1110b21c60a4SVinod Koul 1111b21c60a4SVinod Koul static void __exit snd_compress_exit(void) 1112b21c60a4SVinod Koul { 1113b21c60a4SVinod Koul } 1114b21c60a4SVinod Koul 1115b21c60a4SVinod Koul module_init(snd_compress_init); 1116b21c60a4SVinod Koul module_exit(snd_compress_exit); 1117b21c60a4SVinod Koul 1118b21c60a4SVinod Koul MODULE_DESCRIPTION("ALSA Compressed offload framework"); 1119b21c60a4SVinod Koul MODULE_AUTHOR("Vinod Koul <vinod.koul@linux.intel.com>"); 1120b21c60a4SVinod Koul MODULE_LICENSE("GPL v2"); 1121