1873e65bcSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 2b21c60a4SVinod Koul /* 3b21c60a4SVinod Koul * compress_core.c - compress offload core 4b21c60a4SVinod Koul * 5b21c60a4SVinod Koul * Copyright (C) 2011 Intel Corporation 6b21c60a4SVinod Koul * Authors: Vinod Koul <vinod.koul@linux.intel.com> 7b21c60a4SVinod Koul * Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com> 8b21c60a4SVinod Koul * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 9b21c60a4SVinod Koul * 10b21c60a4SVinod Koul * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 11b21c60a4SVinod Koul */ 12b21c60a4SVinod Koul #define FORMAT(fmt) "%s: %d: " fmt, __func__, __LINE__ 13b21c60a4SVinod Koul #define pr_fmt(fmt) KBUILD_MODNAME ": " FORMAT(fmt) 14b21c60a4SVinod Koul 15b21c60a4SVinod Koul #include <linux/file.h> 16b21c60a4SVinod Koul #include <linux/fs.h> 17b21c60a4SVinod Koul #include <linux/list.h> 18f0283b58SCharles Keepax #include <linux/math64.h> 19b21c60a4SVinod Koul #include <linux/mm.h> 20b21c60a4SVinod Koul #include <linux/mutex.h> 21b21c60a4SVinod Koul #include <linux/poll.h> 22b21c60a4SVinod Koul #include <linux/slab.h> 23b21c60a4SVinod Koul #include <linux/sched.h> 24f0283b58SCharles Keepax #include <linux/types.h> 25b21c60a4SVinod Koul #include <linux/uio.h> 26b21c60a4SVinod Koul #include <linux/uaccess.h> 27b21c60a4SVinod Koul #include <linux/module.h> 28c1036889SRavindra Lokhande #include <linux/compat.h> 29b21c60a4SVinod Koul #include <sound/core.h> 30b21c60a4SVinod Koul #include <sound/initval.h> 3131742724SRichard Fitzgerald #include <sound/info.h> 32b21c60a4SVinod Koul #include <sound/compress_params.h> 33b21c60a4SVinod Koul #include <sound/compress_offload.h> 34b21c60a4SVinod Koul #include <sound/compress_driver.h> 35b21c60a4SVinod Koul 36462b3f16STakashi Iwai /* struct snd_compr_codec_caps overflows the ioctl bit size for some 37462b3f16STakashi Iwai * architectures, so we need to disable the relevant ioctls. 38462b3f16STakashi Iwai */ 39462b3f16STakashi Iwai #if _IOC_SIZEBITS < 14 40462b3f16STakashi Iwai #define COMPR_CODEC_CAPS_OVERFLOW 41462b3f16STakashi Iwai #endif 42462b3f16STakashi Iwai 43b21c60a4SVinod Koul /* TODO: 44b21c60a4SVinod Koul * - add substream support for multiple devices in case of 45b21c60a4SVinod Koul * SND_DYNAMIC_MINORS is not used 46b21c60a4SVinod Koul * - Multiple node representation 47b21c60a4SVinod Koul * driver should be able to register multiple nodes 48b21c60a4SVinod Koul */ 49b21c60a4SVinod Koul 50b21c60a4SVinod Koul static DEFINE_MUTEX(device_mutex); 51b21c60a4SVinod Koul 52b21c60a4SVinod Koul struct snd_compr_file { 53b21c60a4SVinod Koul unsigned long caps; 54b21c60a4SVinod Koul struct snd_compr_stream stream; 55b21c60a4SVinod Koul }; 56b21c60a4SVinod Koul 57a4f2d87cSCharles Keepax static void error_delayed_work(struct work_struct *work); 58a4f2d87cSCharles Keepax 59b21c60a4SVinod Koul /* 60b21c60a4SVinod Koul * a note on stream states used: 6141eb94fdSVinod Koul * we use following states in the compressed core 62b21c60a4SVinod Koul * SNDRV_PCM_STATE_OPEN: When stream has been opened. 63b21c60a4SVinod Koul * SNDRV_PCM_STATE_SETUP: When stream has been initialized. This is done by 6441eb94fdSVinod Koul * calling SNDRV_COMPRESS_SET_PARAMS. Running streams will come to this 65b21c60a4SVinod Koul * state at stop by calling SNDRV_COMPRESS_STOP, or at end of drain. 66862bca5dSVinod Koul * SNDRV_PCM_STATE_PREPARED: When a stream has been written to (for 67862bca5dSVinod Koul * playback only). User after setting up stream writes the data buffer 68862bca5dSVinod Koul * before starting the stream. 69b21c60a4SVinod Koul * SNDRV_PCM_STATE_RUNNING: When stream has been started and is 70b21c60a4SVinod Koul * decoding/encoding and rendering/capturing data. 71b21c60a4SVinod Koul * SNDRV_PCM_STATE_DRAINING: When stream is draining current data. This is done 72b21c60a4SVinod Koul * by calling SNDRV_COMPRESS_DRAIN. 73b21c60a4SVinod Koul * SNDRV_PCM_STATE_PAUSED: When stream is paused. This is done by calling 74b21c60a4SVinod Koul * SNDRV_COMPRESS_PAUSE. It can be stopped or resumed by calling 75b21c60a4SVinod Koul * SNDRV_COMPRESS_STOP or SNDRV_COMPRESS_RESUME respectively. 76b21c60a4SVinod Koul */ 77b21c60a4SVinod Koul static int snd_compr_open(struct inode *inode, struct file *f) 78b21c60a4SVinod Koul { 79b21c60a4SVinod Koul struct snd_compr *compr; 80b21c60a4SVinod Koul struct snd_compr_file *data; 81b21c60a4SVinod Koul struct snd_compr_runtime *runtime; 82b21c60a4SVinod Koul enum snd_compr_direction dirn; 83b21c60a4SVinod Koul int maj = imajor(inode); 84b21c60a4SVinod Koul int ret; 85b21c60a4SVinod Koul 8681cb3246SDan Carpenter if ((f->f_flags & O_ACCMODE) == O_WRONLY) 87b21c60a4SVinod Koul dirn = SND_COMPRESS_PLAYBACK; 8881cb3246SDan Carpenter else if ((f->f_flags & O_ACCMODE) == O_RDONLY) 89b21c60a4SVinod Koul dirn = SND_COMPRESS_CAPTURE; 9081cb3246SDan Carpenter else 91b21c60a4SVinod Koul return -EINVAL; 92b21c60a4SVinod Koul 93b21c60a4SVinod Koul if (maj == snd_major) 94b21c60a4SVinod Koul compr = snd_lookup_minor_data(iminor(inode), 95b21c60a4SVinod Koul SNDRV_DEVICE_TYPE_COMPRESS); 96b21c60a4SVinod Koul else 97b21c60a4SVinod Koul return -EBADFD; 98b21c60a4SVinod Koul 99b21c60a4SVinod Koul if (compr == NULL) { 100b21c60a4SVinod Koul pr_err("no device data!!!\n"); 101b21c60a4SVinod Koul return -ENODEV; 102b21c60a4SVinod Koul } 103b21c60a4SVinod Koul 104b21c60a4SVinod Koul if (dirn != compr->direction) { 105b21c60a4SVinod Koul pr_err("this device doesn't support this direction\n"); 106a0830dbdSTakashi Iwai snd_card_unref(compr->card); 107b21c60a4SVinod Koul return -EINVAL; 108b21c60a4SVinod Koul } 109b21c60a4SVinod Koul 110b21c60a4SVinod Koul data = kzalloc(sizeof(*data), GFP_KERNEL); 111a0830dbdSTakashi Iwai if (!data) { 112a0830dbdSTakashi Iwai snd_card_unref(compr->card); 113b21c60a4SVinod Koul return -ENOMEM; 114a0830dbdSTakashi Iwai } 115a4f2d87cSCharles Keepax 116a4f2d87cSCharles Keepax INIT_DELAYED_WORK(&data->stream.error_work, error_delayed_work); 117a4f2d87cSCharles Keepax 118b21c60a4SVinod Koul data->stream.ops = compr->ops; 119b21c60a4SVinod Koul data->stream.direction = dirn; 120b21c60a4SVinod Koul data->stream.private_data = compr->private_data; 121b21c60a4SVinod Koul data->stream.device = compr; 122b21c60a4SVinod Koul runtime = kzalloc(sizeof(*runtime), GFP_KERNEL); 123b21c60a4SVinod Koul if (!runtime) { 124b21c60a4SVinod Koul kfree(data); 125a0830dbdSTakashi Iwai snd_card_unref(compr->card); 126b21c60a4SVinod Koul return -ENOMEM; 127b21c60a4SVinod Koul } 128b21c60a4SVinod Koul runtime->state = SNDRV_PCM_STATE_OPEN; 129b21c60a4SVinod Koul init_waitqueue_head(&runtime->sleep); 130b21c60a4SVinod Koul data->stream.runtime = runtime; 131b21c60a4SVinod Koul f->private_data = (void *)data; 132b21c60a4SVinod Koul mutex_lock(&compr->lock); 133b21c60a4SVinod Koul ret = compr->ops->open(&data->stream); 134b21c60a4SVinod Koul mutex_unlock(&compr->lock); 135b21c60a4SVinod Koul if (ret) { 136b21c60a4SVinod Koul kfree(runtime); 137b21c60a4SVinod Koul kfree(data); 138b21c60a4SVinod Koul } 139a0830dbdSTakashi Iwai snd_card_unref(compr->card); 140749d3223SCharles Keepax return ret; 141b21c60a4SVinod Koul } 142b21c60a4SVinod Koul 143b21c60a4SVinod Koul static int snd_compr_free(struct inode *inode, struct file *f) 144b21c60a4SVinod Koul { 145b21c60a4SVinod Koul struct snd_compr_file *data = f->private_data; 146b26d19e4SLiam Girdwood struct snd_compr_runtime *runtime = data->stream.runtime; 147b26d19e4SLiam Girdwood 148a4f2d87cSCharles Keepax cancel_delayed_work_sync(&data->stream.error_work); 149a4f2d87cSCharles Keepax 150b26d19e4SLiam Girdwood switch (runtime->state) { 151b26d19e4SLiam Girdwood case SNDRV_PCM_STATE_RUNNING: 152b26d19e4SLiam Girdwood case SNDRV_PCM_STATE_DRAINING: 153b26d19e4SLiam Girdwood case SNDRV_PCM_STATE_PAUSED: 154b26d19e4SLiam Girdwood data->stream.ops->trigger(&data->stream, SNDRV_PCM_TRIGGER_STOP); 155b26d19e4SLiam Girdwood break; 156b26d19e4SLiam Girdwood default: 157b26d19e4SLiam Girdwood break; 158b26d19e4SLiam Girdwood } 159b26d19e4SLiam Girdwood 160b21c60a4SVinod Koul data->stream.ops->free(&data->stream); 161d00f749bSSrinivas Kandagatla if (!data->stream.runtime->dma_buffer_p) 162b21c60a4SVinod Koul kfree(data->stream.runtime->buffer); 163b21c60a4SVinod Koul kfree(data->stream.runtime); 164b21c60a4SVinod Koul kfree(data); 165b21c60a4SVinod Koul return 0; 166b21c60a4SVinod Koul } 167b21c60a4SVinod Koul 16817ac8e5cSRichard Fitzgerald static int snd_compr_update_tstamp(struct snd_compr_stream *stream, 169b21c60a4SVinod Koul struct snd_compr_tstamp *tstamp) 170b21c60a4SVinod Koul { 171b21c60a4SVinod Koul if (!stream->ops->pointer) 17217ac8e5cSRichard Fitzgerald return -ENOTSUPP; 173b21c60a4SVinod Koul stream->ops->pointer(stream, tstamp); 174b21c60a4SVinod Koul pr_debug("dsp consumed till %d total %d bytes\n", 175b21c60a4SVinod Koul tstamp->byte_offset, tstamp->copied_total); 1765b1f79f7SCharles Keepax if (stream->direction == SND_COMPRESS_PLAYBACK) 177b21c60a4SVinod Koul stream->runtime->total_bytes_transferred = tstamp->copied_total; 1785b1f79f7SCharles Keepax else 1795b1f79f7SCharles Keepax stream->runtime->total_bytes_available = tstamp->copied_total; 18017ac8e5cSRichard Fitzgerald return 0; 181b21c60a4SVinod Koul } 182b21c60a4SVinod Koul 183b21c60a4SVinod Koul static size_t snd_compr_calc_avail(struct snd_compr_stream *stream, 184b21c60a4SVinod Koul struct snd_compr_avail *avail) 185b21c60a4SVinod Koul { 18617ac8e5cSRichard Fitzgerald memset(avail, 0, sizeof(*avail)); 187b21c60a4SVinod Koul snd_compr_update_tstamp(stream, &avail->tstamp); 18817ac8e5cSRichard Fitzgerald /* Still need to return avail even if tstamp can't be filled in */ 189b21c60a4SVinod Koul 190b21c60a4SVinod Koul if (stream->runtime->total_bytes_available == 0 && 1915b1f79f7SCharles Keepax stream->runtime->state == SNDRV_PCM_STATE_SETUP && 1925b1f79f7SCharles Keepax stream->direction == SND_COMPRESS_PLAYBACK) { 193b21c60a4SVinod Koul pr_debug("detected init and someone forgot to do a write\n"); 194b21c60a4SVinod Koul return stream->runtime->buffer_size; 195b21c60a4SVinod Koul } 196b21c60a4SVinod Koul pr_debug("app wrote %lld, DSP consumed %lld\n", 197b21c60a4SVinod Koul stream->runtime->total_bytes_available, 198b21c60a4SVinod Koul stream->runtime->total_bytes_transferred); 199b21c60a4SVinod Koul if (stream->runtime->total_bytes_available == 200b21c60a4SVinod Koul stream->runtime->total_bytes_transferred) { 2015b1f79f7SCharles Keepax if (stream->direction == SND_COMPRESS_PLAYBACK) { 202b21c60a4SVinod Koul pr_debug("both pointers are same, returning full avail\n"); 203b21c60a4SVinod Koul return stream->runtime->buffer_size; 2045b1f79f7SCharles Keepax } else { 2055b1f79f7SCharles Keepax pr_debug("both pointers are same, returning no avail\n"); 2065b1f79f7SCharles Keepax return 0; 2075b1f79f7SCharles Keepax } 208b21c60a4SVinod Koul } 209b21c60a4SVinod Koul 2105b1f79f7SCharles Keepax avail->avail = stream->runtime->total_bytes_available - 2115b1f79f7SCharles Keepax stream->runtime->total_bytes_transferred; 2125b1f79f7SCharles Keepax if (stream->direction == SND_COMPRESS_PLAYBACK) 2135b1f79f7SCharles Keepax avail->avail = stream->runtime->buffer_size - avail->avail; 2145b1f79f7SCharles Keepax 2154c28e32dSCharles Keepax pr_debug("ret avail as %lld\n", avail->avail); 2164c28e32dSCharles Keepax return avail->avail; 217b21c60a4SVinod Koul } 218b21c60a4SVinod Koul 219b21c60a4SVinod Koul static inline size_t snd_compr_get_avail(struct snd_compr_stream *stream) 220b21c60a4SVinod Koul { 221b21c60a4SVinod Koul struct snd_compr_avail avail; 222b21c60a4SVinod Koul 223b21c60a4SVinod Koul return snd_compr_calc_avail(stream, &avail); 224b21c60a4SVinod Koul } 225b21c60a4SVinod Koul 226b21c60a4SVinod Koul static int 227b21c60a4SVinod Koul snd_compr_ioctl_avail(struct snd_compr_stream *stream, unsigned long arg) 228b21c60a4SVinod Koul { 229b21c60a4SVinod Koul struct snd_compr_avail ioctl_avail; 230b21c60a4SVinod Koul size_t avail; 231b21c60a4SVinod Koul 232b21c60a4SVinod Koul avail = snd_compr_calc_avail(stream, &ioctl_avail); 233b21c60a4SVinod Koul ioctl_avail.avail = avail; 234b21c60a4SVinod Koul 235a4f2d87cSCharles Keepax switch (stream->runtime->state) { 236a4f2d87cSCharles Keepax case SNDRV_PCM_STATE_OPEN: 237a4f2d87cSCharles Keepax return -EBADFD; 238a4f2d87cSCharles Keepax case SNDRV_PCM_STATE_XRUN: 239a4f2d87cSCharles Keepax return -EPIPE; 240a4f2d87cSCharles Keepax default: 241a4f2d87cSCharles Keepax break; 242a4f2d87cSCharles Keepax } 243a4f2d87cSCharles Keepax 244b21c60a4SVinod Koul if (copy_to_user((__u64 __user *)arg, 245b21c60a4SVinod Koul &ioctl_avail, sizeof(ioctl_avail))) 246b21c60a4SVinod Koul return -EFAULT; 247b21c60a4SVinod Koul return 0; 248b21c60a4SVinod Koul } 249b21c60a4SVinod Koul 250b21c60a4SVinod Koul static int snd_compr_write_data(struct snd_compr_stream *stream, 251b21c60a4SVinod Koul const char __user *buf, size_t count) 252b21c60a4SVinod Koul { 253b21c60a4SVinod Koul void *dstn; 254b21c60a4SVinod Koul size_t copy; 255b21c60a4SVinod Koul struct snd_compr_runtime *runtime = stream->runtime; 256f0283b58SCharles Keepax /* 64-bit Modulus */ 257f0283b58SCharles Keepax u64 app_pointer = div64_u64(runtime->total_bytes_available, 258f0283b58SCharles Keepax runtime->buffer_size); 259f0283b58SCharles Keepax app_pointer = runtime->total_bytes_available - 260f0283b58SCharles Keepax (app_pointer * runtime->buffer_size); 261b21c60a4SVinod Koul 262f0283b58SCharles Keepax dstn = runtime->buffer + app_pointer; 263b21c60a4SVinod Koul pr_debug("copying %ld at %lld\n", 264f0283b58SCharles Keepax (unsigned long)count, app_pointer); 265f0283b58SCharles Keepax if (count < runtime->buffer_size - app_pointer) { 266b21c60a4SVinod Koul if (copy_from_user(dstn, buf, count)) 267b21c60a4SVinod Koul return -EFAULT; 268b21c60a4SVinod Koul } else { 269f0283b58SCharles Keepax copy = runtime->buffer_size - app_pointer; 270b21c60a4SVinod Koul if (copy_from_user(dstn, buf, copy)) 271b21c60a4SVinod Koul return -EFAULT; 272b21c60a4SVinod Koul if (copy_from_user(runtime->buffer, buf + copy, count - copy)) 273b21c60a4SVinod Koul return -EFAULT; 274b21c60a4SVinod Koul } 275b21c60a4SVinod Koul /* if DSP cares, let it know data has been written */ 276b21c60a4SVinod Koul if (stream->ops->ack) 277b21c60a4SVinod Koul stream->ops->ack(stream, count); 278b21c60a4SVinod Koul return count; 279b21c60a4SVinod Koul } 280b21c60a4SVinod Koul 281b21c60a4SVinod Koul static ssize_t snd_compr_write(struct file *f, const char __user *buf, 282b21c60a4SVinod Koul size_t count, loff_t *offset) 283b21c60a4SVinod Koul { 284b21c60a4SVinod Koul struct snd_compr_file *data = f->private_data; 285b21c60a4SVinod Koul struct snd_compr_stream *stream; 286b21c60a4SVinod Koul size_t avail; 287b21c60a4SVinod Koul int retval; 288b21c60a4SVinod Koul 289b21c60a4SVinod Koul if (snd_BUG_ON(!data)) 290b21c60a4SVinod Koul return -EFAULT; 291b21c60a4SVinod Koul 292b21c60a4SVinod Koul stream = &data->stream; 293b21c60a4SVinod Koul mutex_lock(&stream->device->lock); 294b21c60a4SVinod Koul /* write is allowed when stream is running or has been steup */ 295875f6fffSCharles Keepax switch (stream->runtime->state) { 296875f6fffSCharles Keepax case SNDRV_PCM_STATE_SETUP: 297875f6fffSCharles Keepax case SNDRV_PCM_STATE_PREPARED: 298875f6fffSCharles Keepax case SNDRV_PCM_STATE_RUNNING: 299875f6fffSCharles Keepax break; 300875f6fffSCharles Keepax default: 301b21c60a4SVinod Koul mutex_unlock(&stream->device->lock); 302b21c60a4SVinod Koul return -EBADFD; 303b21c60a4SVinod Koul } 304b21c60a4SVinod Koul 305b21c60a4SVinod Koul avail = snd_compr_get_avail(stream); 306b21c60a4SVinod Koul pr_debug("avail returned %ld\n", (unsigned long)avail); 307b21c60a4SVinod Koul /* calculate how much we can write to buffer */ 308b21c60a4SVinod Koul if (avail > count) 309b21c60a4SVinod Koul avail = count; 310b21c60a4SVinod Koul 3114daf891cSCharles Keepax if (stream->ops->copy) { 3124daf891cSCharles Keepax char __user* cbuf = (char __user*)buf; 3134daf891cSCharles Keepax retval = stream->ops->copy(stream, cbuf, avail); 3144daf891cSCharles Keepax } else { 315b21c60a4SVinod Koul retval = snd_compr_write_data(stream, buf, avail); 3164daf891cSCharles Keepax } 317b21c60a4SVinod Koul if (retval > 0) 318b21c60a4SVinod Koul stream->runtime->total_bytes_available += retval; 319b21c60a4SVinod Koul 320b21c60a4SVinod Koul /* while initiating the stream, write should be called before START 321b21c60a4SVinod Koul * call, so in setup move state */ 322b21c60a4SVinod Koul if (stream->runtime->state == SNDRV_PCM_STATE_SETUP) { 323b21c60a4SVinod Koul stream->runtime->state = SNDRV_PCM_STATE_PREPARED; 324b21c60a4SVinod Koul pr_debug("stream prepared, Houston we are good to go\n"); 325b21c60a4SVinod Koul } 326b21c60a4SVinod Koul 327b21c60a4SVinod Koul mutex_unlock(&stream->device->lock); 328b21c60a4SVinod Koul return retval; 329b21c60a4SVinod Koul } 330b21c60a4SVinod Koul 331b21c60a4SVinod Koul 332b21c60a4SVinod Koul static ssize_t snd_compr_read(struct file *f, char __user *buf, 333b21c60a4SVinod Koul size_t count, loff_t *offset) 334b21c60a4SVinod Koul { 33549bb6402SCharles Keepax struct snd_compr_file *data = f->private_data; 33649bb6402SCharles Keepax struct snd_compr_stream *stream; 33749bb6402SCharles Keepax size_t avail; 33849bb6402SCharles Keepax int retval; 33949bb6402SCharles Keepax 34049bb6402SCharles Keepax if (snd_BUG_ON(!data)) 34149bb6402SCharles Keepax return -EFAULT; 34249bb6402SCharles Keepax 34349bb6402SCharles Keepax stream = &data->stream; 34449bb6402SCharles Keepax mutex_lock(&stream->device->lock); 34549bb6402SCharles Keepax 34675481347SVinod Koul /* read is allowed when stream is running, paused, draining and setup 34775481347SVinod Koul * (yes setup is state which we transition to after stop, so if user 34875481347SVinod Koul * wants to read data after stop we allow that) 34975481347SVinod Koul */ 35075481347SVinod Koul switch (stream->runtime->state) { 35175481347SVinod Koul case SNDRV_PCM_STATE_OPEN: 35275481347SVinod Koul case SNDRV_PCM_STATE_PREPARED: 35375481347SVinod Koul case SNDRV_PCM_STATE_SUSPENDED: 35475481347SVinod Koul case SNDRV_PCM_STATE_DISCONNECTED: 35549bb6402SCharles Keepax retval = -EBADFD; 35649bb6402SCharles Keepax goto out; 357a4f2d87cSCharles Keepax case SNDRV_PCM_STATE_XRUN: 358a4f2d87cSCharles Keepax retval = -EPIPE; 359a4f2d87cSCharles Keepax goto out; 36049bb6402SCharles Keepax } 36149bb6402SCharles Keepax 36249bb6402SCharles Keepax avail = snd_compr_get_avail(stream); 36349bb6402SCharles Keepax pr_debug("avail returned %ld\n", (unsigned long)avail); 36449bb6402SCharles Keepax /* calculate how much we can read from buffer */ 36549bb6402SCharles Keepax if (avail > count) 36649bb6402SCharles Keepax avail = count; 36749bb6402SCharles Keepax 36849bb6402SCharles Keepax if (stream->ops->copy) { 36949bb6402SCharles Keepax retval = stream->ops->copy(stream, buf, avail); 37049bb6402SCharles Keepax } else { 37149bb6402SCharles Keepax retval = -ENXIO; 37249bb6402SCharles Keepax goto out; 37349bb6402SCharles Keepax } 37449bb6402SCharles Keepax if (retval > 0) 37549bb6402SCharles Keepax stream->runtime->total_bytes_transferred += retval; 37649bb6402SCharles Keepax 37749bb6402SCharles Keepax out: 37849bb6402SCharles Keepax mutex_unlock(&stream->device->lock); 37949bb6402SCharles Keepax return retval; 380b21c60a4SVinod Koul } 381b21c60a4SVinod Koul 382b21c60a4SVinod Koul static int snd_compr_mmap(struct file *f, struct vm_area_struct *vma) 383b21c60a4SVinod Koul { 384b21c60a4SVinod Koul return -ENXIO; 385b21c60a4SVinod Koul } 386b21c60a4SVinod Koul 387680ef72aSAl Viro static __poll_t snd_compr_get_poll(struct snd_compr_stream *stream) 388b21c60a4SVinod Koul { 389b21c60a4SVinod Koul if (stream->direction == SND_COMPRESS_PLAYBACK) 390a9a08845SLinus Torvalds return EPOLLOUT | EPOLLWRNORM; 391b21c60a4SVinod Koul else 392a9a08845SLinus Torvalds return EPOLLIN | EPOLLRDNORM; 393b21c60a4SVinod Koul } 394b21c60a4SVinod Koul 395680ef72aSAl Viro static __poll_t snd_compr_poll(struct file *f, poll_table *wait) 396b21c60a4SVinod Koul { 397b21c60a4SVinod Koul struct snd_compr_file *data = f->private_data; 398b21c60a4SVinod Koul struct snd_compr_stream *stream; 399b21c60a4SVinod Koul size_t avail; 400680ef72aSAl Viro __poll_t retval = 0; 401b21c60a4SVinod Koul 402b21c60a4SVinod Koul if (snd_BUG_ON(!data)) 403a9a08845SLinus Torvalds return EPOLLERR; 4045bd05390SCharles Keepax 405b21c60a4SVinod Koul stream = &data->stream; 406b21c60a4SVinod Koul 407b21c60a4SVinod Koul mutex_lock(&stream->device->lock); 408a4f2d87cSCharles Keepax 409a4f2d87cSCharles Keepax switch (stream->runtime->state) { 410a4f2d87cSCharles Keepax case SNDRV_PCM_STATE_OPEN: 411a4f2d87cSCharles Keepax case SNDRV_PCM_STATE_XRUN: 412a9a08845SLinus Torvalds retval = snd_compr_get_poll(stream) | EPOLLERR; 413b21c60a4SVinod Koul goto out; 414a4f2d87cSCharles Keepax default: 415a4f2d87cSCharles Keepax break; 416b21c60a4SVinod Koul } 417a4f2d87cSCharles Keepax 418b21c60a4SVinod Koul poll_wait(f, &stream->runtime->sleep, wait); 419b21c60a4SVinod Koul 420b21c60a4SVinod Koul avail = snd_compr_get_avail(stream); 421b21c60a4SVinod Koul pr_debug("avail is %ld\n", (unsigned long)avail); 422b21c60a4SVinod Koul /* check if we have at least one fragment to fill */ 423b21c60a4SVinod Koul switch (stream->runtime->state) { 424b21c60a4SVinod Koul case SNDRV_PCM_STATE_DRAINING: 425b21c60a4SVinod Koul /* stream has been woken up after drain is complete 426b21c60a4SVinod Koul * draining done so set stream state to stopped 427b21c60a4SVinod Koul */ 428b21c60a4SVinod Koul retval = snd_compr_get_poll(stream); 429b21c60a4SVinod Koul stream->runtime->state = SNDRV_PCM_STATE_SETUP; 430b21c60a4SVinod Koul break; 431b21c60a4SVinod Koul case SNDRV_PCM_STATE_RUNNING: 432b21c60a4SVinod Koul case SNDRV_PCM_STATE_PREPARED: 433b21c60a4SVinod Koul case SNDRV_PCM_STATE_PAUSED: 434b21c60a4SVinod Koul if (avail >= stream->runtime->fragment_size) 435b21c60a4SVinod Koul retval = snd_compr_get_poll(stream); 436b21c60a4SVinod Koul break; 437b21c60a4SVinod Koul default: 438a9a08845SLinus Torvalds retval = snd_compr_get_poll(stream) | EPOLLERR; 439b21c60a4SVinod Koul break; 440b21c60a4SVinod Koul } 441b21c60a4SVinod Koul out: 442b21c60a4SVinod Koul mutex_unlock(&stream->device->lock); 443b21c60a4SVinod Koul return retval; 444b21c60a4SVinod Koul } 445b21c60a4SVinod Koul 446b21c60a4SVinod Koul static int 447b21c60a4SVinod Koul snd_compr_get_caps(struct snd_compr_stream *stream, unsigned long arg) 448b21c60a4SVinod Koul { 449b21c60a4SVinod Koul int retval; 450b21c60a4SVinod Koul struct snd_compr_caps caps; 451b21c60a4SVinod Koul 452b21c60a4SVinod Koul if (!stream->ops->get_caps) 453b21c60a4SVinod Koul return -ENXIO; 454b21c60a4SVinod Koul 4551c62e9f2SDan Carpenter memset(&caps, 0, sizeof(caps)); 456b21c60a4SVinod Koul retval = stream->ops->get_caps(stream, &caps); 457b21c60a4SVinod Koul if (retval) 458b21c60a4SVinod Koul goto out; 459b21c60a4SVinod Koul if (copy_to_user((void __user *)arg, &caps, sizeof(caps))) 460b21c60a4SVinod Koul retval = -EFAULT; 461b21c60a4SVinod Koul out: 462b21c60a4SVinod Koul return retval; 463b21c60a4SVinod Koul } 464b21c60a4SVinod Koul 465462b3f16STakashi Iwai #ifndef COMPR_CODEC_CAPS_OVERFLOW 466b21c60a4SVinod Koul static int 467b21c60a4SVinod Koul snd_compr_get_codec_caps(struct snd_compr_stream *stream, unsigned long arg) 468b21c60a4SVinod Koul { 469b21c60a4SVinod Koul int retval; 470b21c60a4SVinod Koul struct snd_compr_codec_caps *caps; 471b21c60a4SVinod Koul 472b21c60a4SVinod Koul if (!stream->ops->get_codec_caps) 473b21c60a4SVinod Koul return -ENXIO; 474b21c60a4SVinod Koul 47547966e97STakashi Iwai caps = kzalloc(sizeof(*caps), GFP_KERNEL); 476b21c60a4SVinod Koul if (!caps) 477b21c60a4SVinod Koul return -ENOMEM; 478b21c60a4SVinod Koul 479b21c60a4SVinod Koul retval = stream->ops->get_codec_caps(stream, caps); 480b21c60a4SVinod Koul if (retval) 481b21c60a4SVinod Koul goto out; 482b21c60a4SVinod Koul if (copy_to_user((void __user *)arg, caps, sizeof(*caps))) 483b21c60a4SVinod Koul retval = -EFAULT; 484b21c60a4SVinod Koul 485b21c60a4SVinod Koul out: 486b21c60a4SVinod Koul kfree(caps); 487b21c60a4SVinod Koul return retval; 488b21c60a4SVinod Koul } 489462b3f16STakashi Iwai #endif /* !COMPR_CODEC_CAPS_OVERFLOW */ 490b21c60a4SVinod Koul 491b21c60a4SVinod Koul /* revisit this with snd_pcm_preallocate_xxx */ 492b21c60a4SVinod Koul static int snd_compr_allocate_buffer(struct snd_compr_stream *stream, 493b21c60a4SVinod Koul struct snd_compr_params *params) 494b21c60a4SVinod Koul { 495b21c60a4SVinod Koul unsigned int buffer_size; 496d00f749bSSrinivas Kandagatla void *buffer = NULL; 497b21c60a4SVinod Koul 498b21c60a4SVinod Koul buffer_size = params->buffer.fragment_size * params->buffer.fragments; 499b21c60a4SVinod Koul if (stream->ops->copy) { 500b21c60a4SVinod Koul buffer = NULL; 501b21c60a4SVinod Koul /* if copy is defined the driver will be required to copy 502b21c60a4SVinod Koul * the data from core 503b21c60a4SVinod Koul */ 504b21c60a4SVinod Koul } else { 505d00f749bSSrinivas Kandagatla if (stream->runtime->dma_buffer_p) { 506d00f749bSSrinivas Kandagatla 507d00f749bSSrinivas Kandagatla if (buffer_size > stream->runtime->dma_buffer_p->bytes) 508d00f749bSSrinivas Kandagatla dev_err(&stream->device->dev, 509d00f749bSSrinivas Kandagatla "Not enough DMA buffer"); 510d00f749bSSrinivas Kandagatla else 511d00f749bSSrinivas Kandagatla buffer = stream->runtime->dma_buffer_p->area; 512d00f749bSSrinivas Kandagatla 513d00f749bSSrinivas Kandagatla } else { 514b21c60a4SVinod Koul buffer = kmalloc(buffer_size, GFP_KERNEL); 515d00f749bSSrinivas Kandagatla } 516d00f749bSSrinivas Kandagatla 517b21c60a4SVinod Koul if (!buffer) 518b21c60a4SVinod Koul return -ENOMEM; 519b21c60a4SVinod Koul } 520b21c60a4SVinod Koul stream->runtime->fragment_size = params->buffer.fragment_size; 521b21c60a4SVinod Koul stream->runtime->fragments = params->buffer.fragments; 522b21c60a4SVinod Koul stream->runtime->buffer = buffer; 523b21c60a4SVinod Koul stream->runtime->buffer_size = buffer_size; 524b21c60a4SVinod Koul return 0; 525b21c60a4SVinod Koul } 526b21c60a4SVinod Koul 5274dc040a0SVinod Koul static int snd_compress_check_input(struct snd_compr_params *params) 5284dc040a0SVinod Koul { 5294dc040a0SVinod Koul /* first let's check the buffer parameter's */ 5304dc040a0SVinod Koul if (params->buffer.fragment_size == 0 || 531678e2b44SDan Carpenter params->buffer.fragments > INT_MAX / params->buffer.fragment_size || 532678e2b44SDan Carpenter params->buffer.fragments == 0) 5334dc040a0SVinod Koul return -EINVAL; 5344dc040a0SVinod Koul 535fb4a9779SVinod Koul /* now codec parameters */ 536fb4a9779SVinod Koul if (params->codec.id == 0 || params->codec.id > SND_AUDIOCODEC_MAX) 537fb4a9779SVinod Koul return -EINVAL; 538fb4a9779SVinod Koul 539fb4a9779SVinod Koul if (params->codec.ch_in == 0 || params->codec.ch_out == 0) 540fb4a9779SVinod Koul return -EINVAL; 541fb4a9779SVinod Koul 5424dc040a0SVinod Koul return 0; 5434dc040a0SVinod Koul } 5444dc040a0SVinod Koul 545b21c60a4SVinod Koul static int 546b21c60a4SVinod Koul snd_compr_set_params(struct snd_compr_stream *stream, unsigned long arg) 547b21c60a4SVinod Koul { 548b21c60a4SVinod Koul struct snd_compr_params *params; 549b21c60a4SVinod Koul int retval; 550b21c60a4SVinod Koul 551b21c60a4SVinod Koul if (stream->runtime->state == SNDRV_PCM_STATE_OPEN) { 552b21c60a4SVinod Koul /* 553b21c60a4SVinod Koul * we should allow parameter change only when stream has been 554b21c60a4SVinod Koul * opened not in other cases 555b21c60a4SVinod Koul */ 556c2f14ba7SMarkus Elfring params = memdup_user((void __user *)arg, sizeof(*params)); 557c2f14ba7SMarkus Elfring if (IS_ERR(params)) 558c2f14ba7SMarkus Elfring return PTR_ERR(params); 5594dc040a0SVinod Koul 5604dc040a0SVinod Koul retval = snd_compress_check_input(params); 5614dc040a0SVinod Koul if (retval) 5624dc040a0SVinod Koul goto out; 5634dc040a0SVinod Koul 564b21c60a4SVinod Koul retval = snd_compr_allocate_buffer(stream, params); 565b21c60a4SVinod Koul if (retval) { 566769fab2aSJesper Juhl retval = -ENOMEM; 567769fab2aSJesper Juhl goto out; 568b21c60a4SVinod Koul } 5694dc040a0SVinod Koul 570b21c60a4SVinod Koul retval = stream->ops->set_params(stream, params); 571b21c60a4SVinod Koul if (retval) 572b21c60a4SVinod Koul goto out; 57349bb6402SCharles Keepax 5749727b490SJeeja KP stream->metadata_set = false; 5759727b490SJeeja KP stream->next_track = false; 57649bb6402SCharles Keepax 57749bb6402SCharles Keepax stream->runtime->state = SNDRV_PCM_STATE_SETUP; 578769fab2aSJesper Juhl } else { 579b21c60a4SVinod Koul return -EPERM; 580769fab2aSJesper Juhl } 581b21c60a4SVinod Koul out: 582b21c60a4SVinod Koul kfree(params); 583b21c60a4SVinod Koul return retval; 584b21c60a4SVinod Koul } 585b21c60a4SVinod Koul 586b21c60a4SVinod Koul static int 587b21c60a4SVinod Koul snd_compr_get_params(struct snd_compr_stream *stream, unsigned long arg) 588b21c60a4SVinod Koul { 589b21c60a4SVinod Koul struct snd_codec *params; 590b21c60a4SVinod Koul int retval; 591b21c60a4SVinod Koul 592b21c60a4SVinod Koul if (!stream->ops->get_params) 593b21c60a4SVinod Koul return -EBADFD; 594b21c60a4SVinod Koul 59547966e97STakashi Iwai params = kzalloc(sizeof(*params), GFP_KERNEL); 596b21c60a4SVinod Koul if (!params) 597b21c60a4SVinod Koul return -ENOMEM; 598b21c60a4SVinod Koul retval = stream->ops->get_params(stream, params); 599b21c60a4SVinod Koul if (retval) 600b21c60a4SVinod Koul goto out; 601b21c60a4SVinod Koul if (copy_to_user((char __user *)arg, params, sizeof(*params))) 602b21c60a4SVinod Koul retval = -EFAULT; 603b21c60a4SVinod Koul 604b21c60a4SVinod Koul out: 605b21c60a4SVinod Koul kfree(params); 606b21c60a4SVinod Koul return retval; 607b21c60a4SVinod Koul } 608b21c60a4SVinod Koul 6099727b490SJeeja KP static int 6109727b490SJeeja KP snd_compr_get_metadata(struct snd_compr_stream *stream, unsigned long arg) 6119727b490SJeeja KP { 6129727b490SJeeja KP struct snd_compr_metadata metadata; 6139727b490SJeeja KP int retval; 6149727b490SJeeja KP 6159727b490SJeeja KP if (!stream->ops->get_metadata) 6169727b490SJeeja KP return -ENXIO; 6179727b490SJeeja KP 6189727b490SJeeja KP if (copy_from_user(&metadata, (void __user *)arg, sizeof(metadata))) 6199727b490SJeeja KP return -EFAULT; 6209727b490SJeeja KP 6219727b490SJeeja KP retval = stream->ops->get_metadata(stream, &metadata); 6229727b490SJeeja KP if (retval != 0) 6239727b490SJeeja KP return retval; 6249727b490SJeeja KP 6259727b490SJeeja KP if (copy_to_user((void __user *)arg, &metadata, sizeof(metadata))) 6269727b490SJeeja KP return -EFAULT; 6279727b490SJeeja KP 6289727b490SJeeja KP return 0; 6299727b490SJeeja KP } 6309727b490SJeeja KP 6319727b490SJeeja KP static int 6329727b490SJeeja KP snd_compr_set_metadata(struct snd_compr_stream *stream, unsigned long arg) 6339727b490SJeeja KP { 6349727b490SJeeja KP struct snd_compr_metadata metadata; 6359727b490SJeeja KP int retval; 6369727b490SJeeja KP 6379727b490SJeeja KP if (!stream->ops->set_metadata) 6389727b490SJeeja KP return -ENXIO; 6399727b490SJeeja KP /* 6409727b490SJeeja KP * we should allow parameter change only when stream has been 6419727b490SJeeja KP * opened not in other cases 6429727b490SJeeja KP */ 6439727b490SJeeja KP if (copy_from_user(&metadata, (void __user *)arg, sizeof(metadata))) 6449727b490SJeeja KP return -EFAULT; 6459727b490SJeeja KP 6469727b490SJeeja KP retval = stream->ops->set_metadata(stream, &metadata); 6479727b490SJeeja KP stream->metadata_set = true; 6489727b490SJeeja KP 6499727b490SJeeja KP return retval; 6509727b490SJeeja KP } 6519727b490SJeeja KP 652b21c60a4SVinod Koul static inline int 653b21c60a4SVinod Koul snd_compr_tstamp(struct snd_compr_stream *stream, unsigned long arg) 654b21c60a4SVinod Koul { 65517ac8e5cSRichard Fitzgerald struct snd_compr_tstamp tstamp = {0}; 65617ac8e5cSRichard Fitzgerald int ret; 657b21c60a4SVinod Koul 65817ac8e5cSRichard Fitzgerald ret = snd_compr_update_tstamp(stream, &tstamp); 65917ac8e5cSRichard Fitzgerald if (ret == 0) 66017ac8e5cSRichard Fitzgerald ret = copy_to_user((struct snd_compr_tstamp __user *)arg, 661b21c60a4SVinod Koul &tstamp, sizeof(tstamp)) ? -EFAULT : 0; 66217ac8e5cSRichard Fitzgerald return ret; 663b21c60a4SVinod Koul } 664b21c60a4SVinod Koul 665b21c60a4SVinod Koul static int snd_compr_pause(struct snd_compr_stream *stream) 666b21c60a4SVinod Koul { 667b21c60a4SVinod Koul int retval; 668b21c60a4SVinod Koul 669b21c60a4SVinod Koul if (stream->runtime->state != SNDRV_PCM_STATE_RUNNING) 670b21c60a4SVinod Koul return -EPERM; 671b21c60a4SVinod Koul retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_PAUSE_PUSH); 6726b18f793SVinod Koul if (!retval) 673b21c60a4SVinod Koul stream->runtime->state = SNDRV_PCM_STATE_PAUSED; 674b21c60a4SVinod Koul return retval; 675b21c60a4SVinod Koul } 676b21c60a4SVinod Koul 677b21c60a4SVinod Koul static int snd_compr_resume(struct snd_compr_stream *stream) 678b21c60a4SVinod Koul { 679b21c60a4SVinod Koul int retval; 680b21c60a4SVinod Koul 681b21c60a4SVinod Koul if (stream->runtime->state != SNDRV_PCM_STATE_PAUSED) 682b21c60a4SVinod Koul return -EPERM; 683b21c60a4SVinod Koul retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_PAUSE_RELEASE); 684b21c60a4SVinod Koul if (!retval) 685b21c60a4SVinod Koul stream->runtime->state = SNDRV_PCM_STATE_RUNNING; 686b21c60a4SVinod Koul return retval; 687b21c60a4SVinod Koul } 688b21c60a4SVinod Koul 689b21c60a4SVinod Koul static int snd_compr_start(struct snd_compr_stream *stream) 690b21c60a4SVinod Koul { 691b21c60a4SVinod Koul int retval; 692b21c60a4SVinod Koul 6934475f8c4SCharles Keepax switch (stream->runtime->state) { 6944475f8c4SCharles Keepax case SNDRV_PCM_STATE_SETUP: 6954475f8c4SCharles Keepax if (stream->direction != SND_COMPRESS_CAPTURE) 696b21c60a4SVinod Koul return -EPERM; 6974475f8c4SCharles Keepax break; 6984475f8c4SCharles Keepax case SNDRV_PCM_STATE_PREPARED: 6994475f8c4SCharles Keepax break; 7004475f8c4SCharles Keepax default: 7014475f8c4SCharles Keepax return -EPERM; 7024475f8c4SCharles Keepax } 7034475f8c4SCharles Keepax 704b21c60a4SVinod Koul retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_START); 705b21c60a4SVinod Koul if (!retval) 706b21c60a4SVinod Koul stream->runtime->state = SNDRV_PCM_STATE_RUNNING; 707b21c60a4SVinod Koul return retval; 708b21c60a4SVinod Koul } 709b21c60a4SVinod Koul 710b21c60a4SVinod Koul static int snd_compr_stop(struct snd_compr_stream *stream) 711b21c60a4SVinod Koul { 712b21c60a4SVinod Koul int retval; 713b21c60a4SVinod Koul 71426c3f154SCharles Keepax switch (stream->runtime->state) { 71526c3f154SCharles Keepax case SNDRV_PCM_STATE_OPEN: 71626c3f154SCharles Keepax case SNDRV_PCM_STATE_SETUP: 71726c3f154SCharles Keepax case SNDRV_PCM_STATE_PREPARED: 718b21c60a4SVinod Koul return -EPERM; 71926c3f154SCharles Keepax default: 72026c3f154SCharles Keepax break; 72126c3f154SCharles Keepax } 72226c3f154SCharles Keepax 723b21c60a4SVinod Koul retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_STOP); 724b21c60a4SVinod Koul if (!retval) { 725917f4b5cSVinod Koul snd_compr_drain_notify(stream); 7268b21460aSVinod Koul stream->runtime->total_bytes_available = 0; 7278b21460aSVinod Koul stream->runtime->total_bytes_transferred = 0; 728b21c60a4SVinod Koul } 729b21c60a4SVinod Koul return retval; 730b21c60a4SVinod Koul } 731b21c60a4SVinod Koul 732a4f2d87cSCharles Keepax static void error_delayed_work(struct work_struct *work) 733a4f2d87cSCharles Keepax { 734a4f2d87cSCharles Keepax struct snd_compr_stream *stream; 735a4f2d87cSCharles Keepax 736a4f2d87cSCharles Keepax stream = container_of(work, struct snd_compr_stream, error_work.work); 737a4f2d87cSCharles Keepax 738a4f2d87cSCharles Keepax mutex_lock(&stream->device->lock); 739a4f2d87cSCharles Keepax 740a4f2d87cSCharles Keepax stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_STOP); 741a4f2d87cSCharles Keepax wake_up(&stream->runtime->sleep); 742a4f2d87cSCharles Keepax 743a4f2d87cSCharles Keepax mutex_unlock(&stream->device->lock); 744a4f2d87cSCharles Keepax } 745a4f2d87cSCharles Keepax 746a4f2d87cSCharles Keepax /* 747a4f2d87cSCharles Keepax * snd_compr_stop_error: Report a fatal error on a stream 748a4f2d87cSCharles Keepax * @stream: pointer to stream 749a4f2d87cSCharles Keepax * @state: state to transition the stream to 750a4f2d87cSCharles Keepax * 751a4f2d87cSCharles Keepax * Stop the stream and set its state. 752a4f2d87cSCharles Keepax * 753a4f2d87cSCharles Keepax * Should be called with compressed device lock held. 754a4f2d87cSCharles Keepax */ 755a4f2d87cSCharles Keepax int snd_compr_stop_error(struct snd_compr_stream *stream, 756a4f2d87cSCharles Keepax snd_pcm_state_t state) 757a4f2d87cSCharles Keepax { 758a4f2d87cSCharles Keepax if (stream->runtime->state == state) 759a4f2d87cSCharles Keepax return 0; 760a4f2d87cSCharles Keepax 761a4f2d87cSCharles Keepax stream->runtime->state = state; 762a4f2d87cSCharles Keepax 763a4f2d87cSCharles Keepax pr_debug("Changing state to: %d\n", state); 764a4f2d87cSCharles Keepax 765a4f2d87cSCharles Keepax queue_delayed_work(system_power_efficient_wq, &stream->error_work, 0); 766a4f2d87cSCharles Keepax 767a4f2d87cSCharles Keepax return 0; 768a4f2d87cSCharles Keepax } 769a4f2d87cSCharles Keepax EXPORT_SYMBOL_GPL(snd_compr_stop_error); 770a4f2d87cSCharles Keepax 771917f4b5cSVinod Koul static int snd_compress_wait_for_drain(struct snd_compr_stream *stream) 772917f4b5cSVinod Koul { 773f44f2a54SVinod Koul int ret; 774f44f2a54SVinod Koul 775917f4b5cSVinod Koul /* 776917f4b5cSVinod Koul * We are called with lock held. So drop the lock while we wait for 777cdb1ee3fSVinod Koul * drain complete notification from the driver 778917f4b5cSVinod Koul * 779917f4b5cSVinod Koul * It is expected that driver will notify the drain completion and then 780917f4b5cSVinod Koul * stream will be moved to SETUP state, even if draining resulted in an 781917f4b5cSVinod Koul * error. We can trigger next track after this. 782917f4b5cSVinod Koul */ 783917f4b5cSVinod Koul stream->runtime->state = SNDRV_PCM_STATE_DRAINING; 784917f4b5cSVinod Koul mutex_unlock(&stream->device->lock); 785917f4b5cSVinod Koul 786f44f2a54SVinod Koul /* we wait for drain to complete here, drain can return when 787f44f2a54SVinod Koul * interruption occurred, wait returned error or success. 788f44f2a54SVinod Koul * For the first two cases we don't do anything different here and 789f44f2a54SVinod Koul * return after waking up 790f44f2a54SVinod Koul */ 791f44f2a54SVinod Koul 792f44f2a54SVinod Koul ret = wait_event_interruptible(stream->runtime->sleep, 793f44f2a54SVinod Koul (stream->runtime->state != SNDRV_PCM_STATE_DRAINING)); 794f44f2a54SVinod Koul if (ret == -ERESTARTSYS) 795c5a905d3SColin Ian King pr_debug("wait aborted by a signal\n"); 796f44f2a54SVinod Koul else if (ret) 797f44f2a54SVinod Koul pr_debug("wait for drain failed with %d\n", ret); 798f44f2a54SVinod Koul 799917f4b5cSVinod Koul 800917f4b5cSVinod Koul wake_up(&stream->runtime->sleep); 801917f4b5cSVinod Koul mutex_lock(&stream->device->lock); 802917f4b5cSVinod Koul 803f44f2a54SVinod Koul return ret; 804917f4b5cSVinod Koul } 805917f4b5cSVinod Koul 806b21c60a4SVinod Koul static int snd_compr_drain(struct snd_compr_stream *stream) 807b21c60a4SVinod Koul { 808b21c60a4SVinod Koul int retval; 809b21c60a4SVinod Koul 81026c3f154SCharles Keepax switch (stream->runtime->state) { 81126c3f154SCharles Keepax case SNDRV_PCM_STATE_OPEN: 81226c3f154SCharles Keepax case SNDRV_PCM_STATE_SETUP: 81326c3f154SCharles Keepax case SNDRV_PCM_STATE_PREPARED: 814b21c60a4SVinod Koul return -EPERM; 81526c3f154SCharles Keepax default: 81626c3f154SCharles Keepax break; 81726c3f154SCharles Keepax } 818917f4b5cSVinod Koul 819b21c60a4SVinod Koul retval = stream->ops->trigger(stream, SND_COMPR_TRIGGER_DRAIN); 820917f4b5cSVinod Koul if (retval) { 821f44f2a54SVinod Koul pr_debug("SND_COMPR_TRIGGER_DRAIN failed %d\n", retval); 822b21c60a4SVinod Koul wake_up(&stream->runtime->sleep); 823917f4b5cSVinod Koul return retval; 824b21c60a4SVinod Koul } 825917f4b5cSVinod Koul 826f44f2a54SVinod Koul return snd_compress_wait_for_drain(stream); 827b21c60a4SVinod Koul } 828b21c60a4SVinod Koul 8299727b490SJeeja KP static int snd_compr_next_track(struct snd_compr_stream *stream) 8309727b490SJeeja KP { 8319727b490SJeeja KP int retval; 8329727b490SJeeja KP 8339727b490SJeeja KP /* only a running stream can transition to next track */ 8349727b490SJeeja KP if (stream->runtime->state != SNDRV_PCM_STATE_RUNNING) 8359727b490SJeeja KP return -EPERM; 8369727b490SJeeja KP 837a70ab8a8SCharles Keepax /* next track doesn't have any meaning for capture streams */ 838a70ab8a8SCharles Keepax if (stream->direction == SND_COMPRESS_CAPTURE) 839a70ab8a8SCharles Keepax return -EPERM; 840a70ab8a8SCharles Keepax 841cdb1ee3fSVinod Koul /* you can signal next track if this is intended to be a gapless stream 8429727b490SJeeja KP * and current track metadata is set 8439727b490SJeeja KP */ 8449727b490SJeeja KP if (stream->metadata_set == false) 8459727b490SJeeja KP return -EPERM; 8469727b490SJeeja KP 8479727b490SJeeja KP retval = stream->ops->trigger(stream, SND_COMPR_TRIGGER_NEXT_TRACK); 8489727b490SJeeja KP if (retval != 0) 8499727b490SJeeja KP return retval; 8509727b490SJeeja KP stream->metadata_set = false; 8519727b490SJeeja KP stream->next_track = true; 8529727b490SJeeja KP return 0; 8539727b490SJeeja KP } 8549727b490SJeeja KP 8559727b490SJeeja KP static int snd_compr_partial_drain(struct snd_compr_stream *stream) 8569727b490SJeeja KP { 8579727b490SJeeja KP int retval; 85826c3f154SCharles Keepax 85926c3f154SCharles Keepax switch (stream->runtime->state) { 86026c3f154SCharles Keepax case SNDRV_PCM_STATE_OPEN: 86126c3f154SCharles Keepax case SNDRV_PCM_STATE_SETUP: 86226c3f154SCharles Keepax case SNDRV_PCM_STATE_PREPARED: 8639727b490SJeeja KP return -EPERM; 86426c3f154SCharles Keepax default: 86526c3f154SCharles Keepax break; 86626c3f154SCharles Keepax } 86726c3f154SCharles Keepax 868a70ab8a8SCharles Keepax /* partial drain doesn't have any meaning for capture streams */ 869a70ab8a8SCharles Keepax if (stream->direction == SND_COMPRESS_CAPTURE) 870a70ab8a8SCharles Keepax return -EPERM; 871a70ab8a8SCharles Keepax 8729727b490SJeeja KP /* stream can be drained only when next track has been signalled */ 8739727b490SJeeja KP if (stream->next_track == false) 8749727b490SJeeja KP return -EPERM; 8759727b490SJeeja KP 8769727b490SJeeja KP retval = stream->ops->trigger(stream, SND_COMPR_TRIGGER_PARTIAL_DRAIN); 877917f4b5cSVinod Koul if (retval) { 878f44f2a54SVinod Koul pr_debug("Partial drain returned failure\n"); 879917f4b5cSVinod Koul wake_up(&stream->runtime->sleep); 880917f4b5cSVinod Koul return retval; 881917f4b5cSVinod Koul } 8829727b490SJeeja KP 8839727b490SJeeja KP stream->next_track = false; 884917f4b5cSVinod Koul return snd_compress_wait_for_drain(stream); 8859727b490SJeeja KP } 8869727b490SJeeja KP 887b21c60a4SVinod Koul static long snd_compr_ioctl(struct file *f, unsigned int cmd, unsigned long arg) 888b21c60a4SVinod Koul { 889b21c60a4SVinod Koul struct snd_compr_file *data = f->private_data; 890b21c60a4SVinod Koul struct snd_compr_stream *stream; 891b21c60a4SVinod Koul int retval = -ENOTTY; 892b21c60a4SVinod Koul 893b21c60a4SVinod Koul if (snd_BUG_ON(!data)) 894b21c60a4SVinod Koul return -EFAULT; 8955bd05390SCharles Keepax 896b21c60a4SVinod Koul stream = &data->stream; 8975bd05390SCharles Keepax 898b21c60a4SVinod Koul mutex_lock(&stream->device->lock); 899b21c60a4SVinod Koul switch (_IOC_NR(cmd)) { 900b21c60a4SVinod Koul case _IOC_NR(SNDRV_COMPRESS_IOCTL_VERSION): 901a8d30608SVinod Koul retval = put_user(SNDRV_COMPRESS_VERSION, 902b21c60a4SVinod Koul (int __user *)arg) ? -EFAULT : 0; 903b21c60a4SVinod Koul break; 904b21c60a4SVinod Koul case _IOC_NR(SNDRV_COMPRESS_GET_CAPS): 905b21c60a4SVinod Koul retval = snd_compr_get_caps(stream, arg); 906b21c60a4SVinod Koul break; 907462b3f16STakashi Iwai #ifndef COMPR_CODEC_CAPS_OVERFLOW 908b21c60a4SVinod Koul case _IOC_NR(SNDRV_COMPRESS_GET_CODEC_CAPS): 909b21c60a4SVinod Koul retval = snd_compr_get_codec_caps(stream, arg); 910b21c60a4SVinod Koul break; 911462b3f16STakashi Iwai #endif 912b21c60a4SVinod Koul case _IOC_NR(SNDRV_COMPRESS_SET_PARAMS): 913b21c60a4SVinod Koul retval = snd_compr_set_params(stream, arg); 914b21c60a4SVinod Koul break; 915b21c60a4SVinod Koul case _IOC_NR(SNDRV_COMPRESS_GET_PARAMS): 916b21c60a4SVinod Koul retval = snd_compr_get_params(stream, arg); 917b21c60a4SVinod Koul break; 9189727b490SJeeja KP case _IOC_NR(SNDRV_COMPRESS_SET_METADATA): 9199727b490SJeeja KP retval = snd_compr_set_metadata(stream, arg); 9209727b490SJeeja KP break; 9219727b490SJeeja KP case _IOC_NR(SNDRV_COMPRESS_GET_METADATA): 9229727b490SJeeja KP retval = snd_compr_get_metadata(stream, arg); 9239727b490SJeeja KP break; 924b21c60a4SVinod Koul case _IOC_NR(SNDRV_COMPRESS_TSTAMP): 925b21c60a4SVinod Koul retval = snd_compr_tstamp(stream, arg); 926b21c60a4SVinod Koul break; 927b21c60a4SVinod Koul case _IOC_NR(SNDRV_COMPRESS_AVAIL): 928b21c60a4SVinod Koul retval = snd_compr_ioctl_avail(stream, arg); 929b21c60a4SVinod Koul break; 930b21c60a4SVinod Koul case _IOC_NR(SNDRV_COMPRESS_PAUSE): 931b21c60a4SVinod Koul retval = snd_compr_pause(stream); 932b21c60a4SVinod Koul break; 933b21c60a4SVinod Koul case _IOC_NR(SNDRV_COMPRESS_RESUME): 934b21c60a4SVinod Koul retval = snd_compr_resume(stream); 935b21c60a4SVinod Koul break; 936b21c60a4SVinod Koul case _IOC_NR(SNDRV_COMPRESS_START): 937b21c60a4SVinod Koul retval = snd_compr_start(stream); 938b21c60a4SVinod Koul break; 939b21c60a4SVinod Koul case _IOC_NR(SNDRV_COMPRESS_STOP): 940b21c60a4SVinod Koul retval = snd_compr_stop(stream); 941b21c60a4SVinod Koul break; 942b21c60a4SVinod Koul case _IOC_NR(SNDRV_COMPRESS_DRAIN): 943b21c60a4SVinod Koul retval = snd_compr_drain(stream); 944b21c60a4SVinod Koul break; 9459727b490SJeeja KP case _IOC_NR(SNDRV_COMPRESS_PARTIAL_DRAIN): 9469727b490SJeeja KP retval = snd_compr_partial_drain(stream); 9479727b490SJeeja KP break; 9489727b490SJeeja KP case _IOC_NR(SNDRV_COMPRESS_NEXT_TRACK): 9499727b490SJeeja KP retval = snd_compr_next_track(stream); 9509727b490SJeeja KP break; 9519727b490SJeeja KP 952b21c60a4SVinod Koul } 953b21c60a4SVinod Koul mutex_unlock(&stream->device->lock); 954b21c60a4SVinod Koul return retval; 955b21c60a4SVinod Koul } 956b21c60a4SVinod Koul 957c1036889SRavindra Lokhande /* support of 32bit userspace on 64bit platforms */ 958c1036889SRavindra Lokhande #ifdef CONFIG_COMPAT 959c1036889SRavindra Lokhande static long snd_compr_ioctl_compat(struct file *file, unsigned int cmd, 960c1036889SRavindra Lokhande unsigned long arg) 961c1036889SRavindra Lokhande { 962c1036889SRavindra Lokhande return snd_compr_ioctl(file, cmd, (unsigned long)compat_ptr(arg)); 963c1036889SRavindra Lokhande } 964c1036889SRavindra Lokhande #endif 965c1036889SRavindra Lokhande 966b21c60a4SVinod Koul static const struct file_operations snd_compr_file_ops = { 967b21c60a4SVinod Koul .owner = THIS_MODULE, 968b21c60a4SVinod Koul .open = snd_compr_open, 969b21c60a4SVinod Koul .release = snd_compr_free, 970b21c60a4SVinod Koul .write = snd_compr_write, 971b21c60a4SVinod Koul .read = snd_compr_read, 972b21c60a4SVinod Koul .unlocked_ioctl = snd_compr_ioctl, 973c1036889SRavindra Lokhande #ifdef CONFIG_COMPAT 974c1036889SRavindra Lokhande .compat_ioctl = snd_compr_ioctl_compat, 975c1036889SRavindra Lokhande #endif 976b21c60a4SVinod Koul .mmap = snd_compr_mmap, 977b21c60a4SVinod Koul .poll = snd_compr_poll, 978b21c60a4SVinod Koul }; 979b21c60a4SVinod Koul 980b21c60a4SVinod Koul static int snd_compress_dev_register(struct snd_device *device) 981b21c60a4SVinod Koul { 982b21c60a4SVinod Koul int ret = -EINVAL; 983b21c60a4SVinod Koul struct snd_compr *compr; 984b21c60a4SVinod Koul 985b21c60a4SVinod Koul if (snd_BUG_ON(!device || !device->device_data)) 986b21c60a4SVinod Koul return -EBADFD; 987b21c60a4SVinod Koul compr = device->device_data; 988b21c60a4SVinod Koul 989a931b9ceSGuneshwor Singh pr_debug("reg device %s, direction %d\n", compr->name, 990b21c60a4SVinod Koul compr->direction); 991b21c60a4SVinod Koul /* register compressed device */ 99240a4b263STakashi Iwai ret = snd_register_device(SNDRV_DEVICE_TYPE_COMPRESS, 99304c5d5a4STakashi Iwai compr->card, compr->device, 99440a4b263STakashi Iwai &snd_compr_file_ops, compr, &compr->dev); 995b21c60a4SVinod Koul if (ret < 0) { 996c5a905d3SColin Ian King pr_err("snd_register_device failed %d\n", ret); 997b21c60a4SVinod Koul return ret; 998b21c60a4SVinod Koul } 999b21c60a4SVinod Koul return ret; 1000b21c60a4SVinod Koul 1001b21c60a4SVinod Koul } 1002b21c60a4SVinod Koul 1003b21c60a4SVinod Koul static int snd_compress_dev_disconnect(struct snd_device *device) 1004b21c60a4SVinod Koul { 1005b21c60a4SVinod Koul struct snd_compr *compr; 1006b21c60a4SVinod Koul 1007b21c60a4SVinod Koul compr = device->device_data; 100840a4b263STakashi Iwai snd_unregister_device(&compr->dev); 1009b21c60a4SVinod Koul return 0; 1010b21c60a4SVinod Koul } 1011b21c60a4SVinod Koul 101231742724SRichard Fitzgerald #ifdef CONFIG_SND_VERBOSE_PROCFS 101331742724SRichard Fitzgerald static void snd_compress_proc_info_read(struct snd_info_entry *entry, 101431742724SRichard Fitzgerald struct snd_info_buffer *buffer) 101531742724SRichard Fitzgerald { 101631742724SRichard Fitzgerald struct snd_compr *compr = (struct snd_compr *)entry->private_data; 101731742724SRichard Fitzgerald 101831742724SRichard Fitzgerald snd_iprintf(buffer, "card: %d\n", compr->card->number); 101931742724SRichard Fitzgerald snd_iprintf(buffer, "device: %d\n", compr->device); 102031742724SRichard Fitzgerald snd_iprintf(buffer, "stream: %s\n", 102131742724SRichard Fitzgerald compr->direction == SND_COMPRESS_PLAYBACK 102231742724SRichard Fitzgerald ? "PLAYBACK" : "CAPTURE"); 102331742724SRichard Fitzgerald snd_iprintf(buffer, "id: %s\n", compr->id); 102431742724SRichard Fitzgerald } 102531742724SRichard Fitzgerald 102631742724SRichard Fitzgerald static int snd_compress_proc_init(struct snd_compr *compr) 102731742724SRichard Fitzgerald { 102831742724SRichard Fitzgerald struct snd_info_entry *entry; 102931742724SRichard Fitzgerald char name[16]; 103031742724SRichard Fitzgerald 103131742724SRichard Fitzgerald sprintf(name, "compr%i", compr->device); 103231742724SRichard Fitzgerald entry = snd_info_create_card_entry(compr->card, name, 103331742724SRichard Fitzgerald compr->card->proc_root); 103431742724SRichard Fitzgerald if (!entry) 103531742724SRichard Fitzgerald return -ENOMEM; 10366a73cf46SJoe Perches entry->mode = S_IFDIR | 0555; 103731742724SRichard Fitzgerald compr->proc_root = entry; 103831742724SRichard Fitzgerald 103931742724SRichard Fitzgerald entry = snd_info_create_card_entry(compr->card, "info", 104031742724SRichard Fitzgerald compr->proc_root); 10414a471d7cSTakashi Iwai if (entry) 104231742724SRichard Fitzgerald snd_info_set_text_ops(entry, compr, 104331742724SRichard Fitzgerald snd_compress_proc_info_read); 104431742724SRichard Fitzgerald compr->proc_info_entry = entry; 104531742724SRichard Fitzgerald 104631742724SRichard Fitzgerald return 0; 104731742724SRichard Fitzgerald } 104831742724SRichard Fitzgerald 104931742724SRichard Fitzgerald static void snd_compress_proc_done(struct snd_compr *compr) 105031742724SRichard Fitzgerald { 105131742724SRichard Fitzgerald snd_info_free_entry(compr->proc_info_entry); 105231742724SRichard Fitzgerald compr->proc_info_entry = NULL; 105331742724SRichard Fitzgerald snd_info_free_entry(compr->proc_root); 105431742724SRichard Fitzgerald compr->proc_root = NULL; 105531742724SRichard Fitzgerald } 1056e5241a8cSRichard Fitzgerald 1057e5241a8cSRichard Fitzgerald static inline void snd_compress_set_id(struct snd_compr *compr, const char *id) 1058e5241a8cSRichard Fitzgerald { 1059e5241a8cSRichard Fitzgerald strlcpy(compr->id, id, sizeof(compr->id)); 1060e5241a8cSRichard Fitzgerald } 106131742724SRichard Fitzgerald #else 106231742724SRichard Fitzgerald static inline int snd_compress_proc_init(struct snd_compr *compr) 106331742724SRichard Fitzgerald { 106431742724SRichard Fitzgerald return 0; 106531742724SRichard Fitzgerald } 106631742724SRichard Fitzgerald 106731742724SRichard Fitzgerald static inline void snd_compress_proc_done(struct snd_compr *compr) 106831742724SRichard Fitzgerald { 106931742724SRichard Fitzgerald } 1070e5241a8cSRichard Fitzgerald 1071e5241a8cSRichard Fitzgerald static inline void snd_compress_set_id(struct snd_compr *compr, const char *id) 1072e5241a8cSRichard Fitzgerald { 1073e5241a8cSRichard Fitzgerald } 107431742724SRichard Fitzgerald #endif 107531742724SRichard Fitzgerald 107604c5d5a4STakashi Iwai static int snd_compress_dev_free(struct snd_device *device) 107704c5d5a4STakashi Iwai { 107804c5d5a4STakashi Iwai struct snd_compr *compr; 107904c5d5a4STakashi Iwai 108004c5d5a4STakashi Iwai compr = device->device_data; 108131742724SRichard Fitzgerald snd_compress_proc_done(compr); 108204c5d5a4STakashi Iwai put_device(&compr->dev); 108304c5d5a4STakashi Iwai return 0; 108404c5d5a4STakashi Iwai } 108504c5d5a4STakashi Iwai 1086b21c60a4SVinod Koul /* 1087b21c60a4SVinod Koul * snd_compress_new: create new compress device 1088b21c60a4SVinod Koul * @card: sound card pointer 1089b21c60a4SVinod Koul * @device: device number 1090b21c60a4SVinod Koul * @dirn: device direction, should be of type enum snd_compr_direction 1091b21c60a4SVinod Koul * @compr: compress device pointer 1092b21c60a4SVinod Koul */ 1093b21c60a4SVinod Koul int snd_compress_new(struct snd_card *card, int device, 1094e5241a8cSRichard Fitzgerald int dirn, const char *id, struct snd_compr *compr) 1095b21c60a4SVinod Koul { 1096b21c60a4SVinod Koul static struct snd_device_ops ops = { 109704c5d5a4STakashi Iwai .dev_free = snd_compress_dev_free, 1098b21c60a4SVinod Koul .dev_register = snd_compress_dev_register, 1099b21c60a4SVinod Koul .dev_disconnect = snd_compress_dev_disconnect, 1100b21c60a4SVinod Koul }; 110131742724SRichard Fitzgerald int ret; 1102b21c60a4SVinod Koul 1103b21c60a4SVinod Koul compr->card = card; 1104b21c60a4SVinod Koul compr->device = device; 1105b21c60a4SVinod Koul compr->direction = dirn; 110604c5d5a4STakashi Iwai 1107e5241a8cSRichard Fitzgerald snd_compress_set_id(compr, id); 1108e5241a8cSRichard Fitzgerald 110904c5d5a4STakashi Iwai snd_device_initialize(&compr->dev, card); 111004c5d5a4STakashi Iwai dev_set_name(&compr->dev, "comprC%iD%i", card->number, device); 111104c5d5a4STakashi Iwai 111231742724SRichard Fitzgerald ret = snd_device_new(card, SNDRV_DEV_COMPRESS, compr, &ops); 111331742724SRichard Fitzgerald if (ret == 0) 111431742724SRichard Fitzgerald snd_compress_proc_init(compr); 111531742724SRichard Fitzgerald 111631742724SRichard Fitzgerald return ret; 1117b21c60a4SVinod Koul } 1118b21c60a4SVinod Koul EXPORT_SYMBOL_GPL(snd_compress_new); 1119b21c60a4SVinod Koul 1120b21c60a4SVinod Koul static int snd_compress_add_device(struct snd_compr *device) 1121b21c60a4SVinod Koul { 1122b21c60a4SVinod Koul int ret; 1123b21c60a4SVinod Koul 1124b21c60a4SVinod Koul if (!device->card) 1125b21c60a4SVinod Koul return -EINVAL; 1126b21c60a4SVinod Koul 1127b21c60a4SVinod Koul /* register the card */ 1128b21c60a4SVinod Koul ret = snd_card_register(device->card); 1129b21c60a4SVinod Koul if (ret) 1130b21c60a4SVinod Koul goto out; 1131b21c60a4SVinod Koul return 0; 1132b21c60a4SVinod Koul 1133b21c60a4SVinod Koul out: 1134b21c60a4SVinod Koul pr_err("failed with %d\n", ret); 1135b21c60a4SVinod Koul return ret; 1136b21c60a4SVinod Koul 1137b21c60a4SVinod Koul } 1138b21c60a4SVinod Koul 1139b21c60a4SVinod Koul static int snd_compress_remove_device(struct snd_compr *device) 1140b21c60a4SVinod Koul { 1141b21c60a4SVinod Koul return snd_card_free(device->card); 1142b21c60a4SVinod Koul } 1143b21c60a4SVinod Koul 1144b21c60a4SVinod Koul /** 1145b21c60a4SVinod Koul * snd_compress_register - register compressed device 1146b21c60a4SVinod Koul * 1147b21c60a4SVinod Koul * @device: compressed device to register 1148b21c60a4SVinod Koul */ 1149b21c60a4SVinod Koul int snd_compress_register(struct snd_compr *device) 1150b21c60a4SVinod Koul { 1151b21c60a4SVinod Koul int retval; 1152b21c60a4SVinod Koul 115304c5d5a4STakashi Iwai if (device->name == NULL || device->ops == NULL) 1154b21c60a4SVinod Koul return -EINVAL; 1155b21c60a4SVinod Koul 1156b21c60a4SVinod Koul pr_debug("Registering compressed device %s\n", device->name); 1157b21c60a4SVinod Koul if (snd_BUG_ON(!device->ops->open)) 1158b21c60a4SVinod Koul return -EINVAL; 1159b21c60a4SVinod Koul if (snd_BUG_ON(!device->ops->free)) 1160b21c60a4SVinod Koul return -EINVAL; 1161b21c60a4SVinod Koul if (snd_BUG_ON(!device->ops->set_params)) 1162b21c60a4SVinod Koul return -EINVAL; 1163b21c60a4SVinod Koul if (snd_BUG_ON(!device->ops->trigger)) 1164b21c60a4SVinod Koul return -EINVAL; 1165b21c60a4SVinod Koul 1166b21c60a4SVinod Koul mutex_init(&device->lock); 1167b21c60a4SVinod Koul 1168b21c60a4SVinod Koul /* register a compressed card */ 1169b21c60a4SVinod Koul mutex_lock(&device_mutex); 1170b21c60a4SVinod Koul retval = snd_compress_add_device(device); 1171b21c60a4SVinod Koul mutex_unlock(&device_mutex); 1172b21c60a4SVinod Koul return retval; 1173b21c60a4SVinod Koul } 1174b21c60a4SVinod Koul EXPORT_SYMBOL_GPL(snd_compress_register); 1175b21c60a4SVinod Koul 1176b21c60a4SVinod Koul int snd_compress_deregister(struct snd_compr *device) 1177b21c60a4SVinod Koul { 1178b21c60a4SVinod Koul pr_debug("Removing compressed device %s\n", device->name); 1179b21c60a4SVinod Koul mutex_lock(&device_mutex); 1180b21c60a4SVinod Koul snd_compress_remove_device(device); 1181b21c60a4SVinod Koul mutex_unlock(&device_mutex); 1182b21c60a4SVinod Koul return 0; 1183b21c60a4SVinod Koul } 1184b21c60a4SVinod Koul EXPORT_SYMBOL_GPL(snd_compress_deregister); 1185b21c60a4SVinod Koul 1186b21c60a4SVinod Koul MODULE_DESCRIPTION("ALSA Compressed offload framework"); 1187b21c60a4SVinod Koul MODULE_AUTHOR("Vinod Koul <vinod.koul@linux.intel.com>"); 1188b21c60a4SVinod Koul MODULE_LICENSE("GPL v2"); 1189