162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * compress_core.c - compress offload core 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2011 Intel Corporation 662306a36Sopenharmony_ci * Authors: Vinod Koul <vinod.koul@linux.intel.com> 762306a36Sopenharmony_ci * Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com> 862306a36Sopenharmony_ci * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1162306a36Sopenharmony_ci */ 1262306a36Sopenharmony_ci#define FORMAT(fmt) "%s: %d: " fmt, __func__, __LINE__ 1362306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " FORMAT(fmt) 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#include <linux/file.h> 1662306a36Sopenharmony_ci#include <linux/fs.h> 1762306a36Sopenharmony_ci#include <linux/list.h> 1862306a36Sopenharmony_ci#include <linux/math64.h> 1962306a36Sopenharmony_ci#include <linux/mm.h> 2062306a36Sopenharmony_ci#include <linux/mutex.h> 2162306a36Sopenharmony_ci#include <linux/poll.h> 2262306a36Sopenharmony_ci#include <linux/slab.h> 2362306a36Sopenharmony_ci#include <linux/sched.h> 2462306a36Sopenharmony_ci#include <linux/types.h> 2562306a36Sopenharmony_ci#include <linux/uio.h> 2662306a36Sopenharmony_ci#include <linux/uaccess.h> 2762306a36Sopenharmony_ci#include <linux/module.h> 2862306a36Sopenharmony_ci#include <linux/compat.h> 2962306a36Sopenharmony_ci#include <sound/core.h> 3062306a36Sopenharmony_ci#include <sound/initval.h> 3162306a36Sopenharmony_ci#include <sound/info.h> 3262306a36Sopenharmony_ci#include <sound/compress_params.h> 3362306a36Sopenharmony_ci#include <sound/compress_offload.h> 3462306a36Sopenharmony_ci#include <sound/compress_driver.h> 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci/* struct snd_compr_codec_caps overflows the ioctl bit size for some 3762306a36Sopenharmony_ci * architectures, so we need to disable the relevant ioctls. 3862306a36Sopenharmony_ci */ 3962306a36Sopenharmony_ci#if _IOC_SIZEBITS < 14 4062306a36Sopenharmony_ci#define COMPR_CODEC_CAPS_OVERFLOW 4162306a36Sopenharmony_ci#endif 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci/* TODO: 4462306a36Sopenharmony_ci * - add substream support for multiple devices in case of 4562306a36Sopenharmony_ci * SND_DYNAMIC_MINORS is not used 4662306a36Sopenharmony_ci * - Multiple node representation 4762306a36Sopenharmony_ci * driver should be able to register multiple nodes 4862306a36Sopenharmony_ci */ 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_cistruct snd_compr_file { 5162306a36Sopenharmony_ci unsigned long caps; 5262306a36Sopenharmony_ci struct snd_compr_stream stream; 5362306a36Sopenharmony_ci}; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_cistatic void error_delayed_work(struct work_struct *work); 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci/* 5862306a36Sopenharmony_ci * a note on stream states used: 5962306a36Sopenharmony_ci * we use following states in the compressed core 6062306a36Sopenharmony_ci * SNDRV_PCM_STATE_OPEN: When stream has been opened. 6162306a36Sopenharmony_ci * SNDRV_PCM_STATE_SETUP: When stream has been initialized. This is done by 6262306a36Sopenharmony_ci * calling SNDRV_COMPRESS_SET_PARAMS. Running streams will come to this 6362306a36Sopenharmony_ci * state at stop by calling SNDRV_COMPRESS_STOP, or at end of drain. 6462306a36Sopenharmony_ci * SNDRV_PCM_STATE_PREPARED: When a stream has been written to (for 6562306a36Sopenharmony_ci * playback only). User after setting up stream writes the data buffer 6662306a36Sopenharmony_ci * before starting the stream. 6762306a36Sopenharmony_ci * SNDRV_PCM_STATE_RUNNING: When stream has been started and is 6862306a36Sopenharmony_ci * decoding/encoding and rendering/capturing data. 6962306a36Sopenharmony_ci * SNDRV_PCM_STATE_DRAINING: When stream is draining current data. This is done 7062306a36Sopenharmony_ci * by calling SNDRV_COMPRESS_DRAIN. 7162306a36Sopenharmony_ci * SNDRV_PCM_STATE_PAUSED: When stream is paused. This is done by calling 7262306a36Sopenharmony_ci * SNDRV_COMPRESS_PAUSE. It can be stopped or resumed by calling 7362306a36Sopenharmony_ci * SNDRV_COMPRESS_STOP or SNDRV_COMPRESS_RESUME respectively. 7462306a36Sopenharmony_ci */ 7562306a36Sopenharmony_cistatic int snd_compr_open(struct inode *inode, struct file *f) 7662306a36Sopenharmony_ci{ 7762306a36Sopenharmony_ci struct snd_compr *compr; 7862306a36Sopenharmony_ci struct snd_compr_file *data; 7962306a36Sopenharmony_ci struct snd_compr_runtime *runtime; 8062306a36Sopenharmony_ci enum snd_compr_direction dirn; 8162306a36Sopenharmony_ci int maj = imajor(inode); 8262306a36Sopenharmony_ci int ret; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci if ((f->f_flags & O_ACCMODE) == O_WRONLY) 8562306a36Sopenharmony_ci dirn = SND_COMPRESS_PLAYBACK; 8662306a36Sopenharmony_ci else if ((f->f_flags & O_ACCMODE) == O_RDONLY) 8762306a36Sopenharmony_ci dirn = SND_COMPRESS_CAPTURE; 8862306a36Sopenharmony_ci else 8962306a36Sopenharmony_ci return -EINVAL; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci if (maj == snd_major) 9262306a36Sopenharmony_ci compr = snd_lookup_minor_data(iminor(inode), 9362306a36Sopenharmony_ci SNDRV_DEVICE_TYPE_COMPRESS); 9462306a36Sopenharmony_ci else 9562306a36Sopenharmony_ci return -EBADFD; 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci if (compr == NULL) { 9862306a36Sopenharmony_ci pr_err("no device data!!!\n"); 9962306a36Sopenharmony_ci return -ENODEV; 10062306a36Sopenharmony_ci } 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci if (dirn != compr->direction) { 10362306a36Sopenharmony_ci pr_err("this device doesn't support this direction\n"); 10462306a36Sopenharmony_ci snd_card_unref(compr->card); 10562306a36Sopenharmony_ci return -EINVAL; 10662306a36Sopenharmony_ci } 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci data = kzalloc(sizeof(*data), GFP_KERNEL); 10962306a36Sopenharmony_ci if (!data) { 11062306a36Sopenharmony_ci snd_card_unref(compr->card); 11162306a36Sopenharmony_ci return -ENOMEM; 11262306a36Sopenharmony_ci } 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci INIT_DELAYED_WORK(&data->stream.error_work, error_delayed_work); 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci data->stream.ops = compr->ops; 11762306a36Sopenharmony_ci data->stream.direction = dirn; 11862306a36Sopenharmony_ci data->stream.private_data = compr->private_data; 11962306a36Sopenharmony_ci data->stream.device = compr; 12062306a36Sopenharmony_ci runtime = kzalloc(sizeof(*runtime), GFP_KERNEL); 12162306a36Sopenharmony_ci if (!runtime) { 12262306a36Sopenharmony_ci kfree(data); 12362306a36Sopenharmony_ci snd_card_unref(compr->card); 12462306a36Sopenharmony_ci return -ENOMEM; 12562306a36Sopenharmony_ci } 12662306a36Sopenharmony_ci runtime->state = SNDRV_PCM_STATE_OPEN; 12762306a36Sopenharmony_ci init_waitqueue_head(&runtime->sleep); 12862306a36Sopenharmony_ci data->stream.runtime = runtime; 12962306a36Sopenharmony_ci f->private_data = (void *)data; 13062306a36Sopenharmony_ci mutex_lock(&compr->lock); 13162306a36Sopenharmony_ci ret = compr->ops->open(&data->stream); 13262306a36Sopenharmony_ci mutex_unlock(&compr->lock); 13362306a36Sopenharmony_ci if (ret) { 13462306a36Sopenharmony_ci kfree(runtime); 13562306a36Sopenharmony_ci kfree(data); 13662306a36Sopenharmony_ci } 13762306a36Sopenharmony_ci snd_card_unref(compr->card); 13862306a36Sopenharmony_ci return ret; 13962306a36Sopenharmony_ci} 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_cistatic int snd_compr_free(struct inode *inode, struct file *f) 14262306a36Sopenharmony_ci{ 14362306a36Sopenharmony_ci struct snd_compr_file *data = f->private_data; 14462306a36Sopenharmony_ci struct snd_compr_runtime *runtime = data->stream.runtime; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci cancel_delayed_work_sync(&data->stream.error_work); 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci switch (runtime->state) { 14962306a36Sopenharmony_ci case SNDRV_PCM_STATE_RUNNING: 15062306a36Sopenharmony_ci case SNDRV_PCM_STATE_DRAINING: 15162306a36Sopenharmony_ci case SNDRV_PCM_STATE_PAUSED: 15262306a36Sopenharmony_ci data->stream.ops->trigger(&data->stream, SNDRV_PCM_TRIGGER_STOP); 15362306a36Sopenharmony_ci break; 15462306a36Sopenharmony_ci default: 15562306a36Sopenharmony_ci break; 15662306a36Sopenharmony_ci } 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci data->stream.ops->free(&data->stream); 15962306a36Sopenharmony_ci if (!data->stream.runtime->dma_buffer_p) 16062306a36Sopenharmony_ci kfree(data->stream.runtime->buffer); 16162306a36Sopenharmony_ci kfree(data->stream.runtime); 16262306a36Sopenharmony_ci kfree(data); 16362306a36Sopenharmony_ci return 0; 16462306a36Sopenharmony_ci} 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_cistatic int snd_compr_update_tstamp(struct snd_compr_stream *stream, 16762306a36Sopenharmony_ci struct snd_compr_tstamp *tstamp) 16862306a36Sopenharmony_ci{ 16962306a36Sopenharmony_ci if (!stream->ops->pointer) 17062306a36Sopenharmony_ci return -ENOTSUPP; 17162306a36Sopenharmony_ci stream->ops->pointer(stream, tstamp); 17262306a36Sopenharmony_ci pr_debug("dsp consumed till %d total %d bytes\n", 17362306a36Sopenharmony_ci tstamp->byte_offset, tstamp->copied_total); 17462306a36Sopenharmony_ci if (stream->direction == SND_COMPRESS_PLAYBACK) 17562306a36Sopenharmony_ci stream->runtime->total_bytes_transferred = tstamp->copied_total; 17662306a36Sopenharmony_ci else 17762306a36Sopenharmony_ci stream->runtime->total_bytes_available = tstamp->copied_total; 17862306a36Sopenharmony_ci return 0; 17962306a36Sopenharmony_ci} 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_cistatic size_t snd_compr_calc_avail(struct snd_compr_stream *stream, 18262306a36Sopenharmony_ci struct snd_compr_avail *avail) 18362306a36Sopenharmony_ci{ 18462306a36Sopenharmony_ci memset(avail, 0, sizeof(*avail)); 18562306a36Sopenharmony_ci snd_compr_update_tstamp(stream, &avail->tstamp); 18662306a36Sopenharmony_ci /* Still need to return avail even if tstamp can't be filled in */ 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci if (stream->runtime->total_bytes_available == 0 && 18962306a36Sopenharmony_ci stream->runtime->state == SNDRV_PCM_STATE_SETUP && 19062306a36Sopenharmony_ci stream->direction == SND_COMPRESS_PLAYBACK) { 19162306a36Sopenharmony_ci pr_debug("detected init and someone forgot to do a write\n"); 19262306a36Sopenharmony_ci return stream->runtime->buffer_size; 19362306a36Sopenharmony_ci } 19462306a36Sopenharmony_ci pr_debug("app wrote %lld, DSP consumed %lld\n", 19562306a36Sopenharmony_ci stream->runtime->total_bytes_available, 19662306a36Sopenharmony_ci stream->runtime->total_bytes_transferred); 19762306a36Sopenharmony_ci if (stream->runtime->total_bytes_available == 19862306a36Sopenharmony_ci stream->runtime->total_bytes_transferred) { 19962306a36Sopenharmony_ci if (stream->direction == SND_COMPRESS_PLAYBACK) { 20062306a36Sopenharmony_ci pr_debug("both pointers are same, returning full avail\n"); 20162306a36Sopenharmony_ci return stream->runtime->buffer_size; 20262306a36Sopenharmony_ci } else { 20362306a36Sopenharmony_ci pr_debug("both pointers are same, returning no avail\n"); 20462306a36Sopenharmony_ci return 0; 20562306a36Sopenharmony_ci } 20662306a36Sopenharmony_ci } 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci avail->avail = stream->runtime->total_bytes_available - 20962306a36Sopenharmony_ci stream->runtime->total_bytes_transferred; 21062306a36Sopenharmony_ci if (stream->direction == SND_COMPRESS_PLAYBACK) 21162306a36Sopenharmony_ci avail->avail = stream->runtime->buffer_size - avail->avail; 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci pr_debug("ret avail as %lld\n", avail->avail); 21462306a36Sopenharmony_ci return avail->avail; 21562306a36Sopenharmony_ci} 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_cistatic inline size_t snd_compr_get_avail(struct snd_compr_stream *stream) 21862306a36Sopenharmony_ci{ 21962306a36Sopenharmony_ci struct snd_compr_avail avail; 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci return snd_compr_calc_avail(stream, &avail); 22262306a36Sopenharmony_ci} 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_cistatic int 22562306a36Sopenharmony_cisnd_compr_ioctl_avail(struct snd_compr_stream *stream, unsigned long arg) 22662306a36Sopenharmony_ci{ 22762306a36Sopenharmony_ci struct snd_compr_avail ioctl_avail; 22862306a36Sopenharmony_ci size_t avail; 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci avail = snd_compr_calc_avail(stream, &ioctl_avail); 23162306a36Sopenharmony_ci ioctl_avail.avail = avail; 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci switch (stream->runtime->state) { 23462306a36Sopenharmony_ci case SNDRV_PCM_STATE_OPEN: 23562306a36Sopenharmony_ci return -EBADFD; 23662306a36Sopenharmony_ci case SNDRV_PCM_STATE_XRUN: 23762306a36Sopenharmony_ci return -EPIPE; 23862306a36Sopenharmony_ci default: 23962306a36Sopenharmony_ci break; 24062306a36Sopenharmony_ci } 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci if (copy_to_user((__u64 __user *)arg, 24362306a36Sopenharmony_ci &ioctl_avail, sizeof(ioctl_avail))) 24462306a36Sopenharmony_ci return -EFAULT; 24562306a36Sopenharmony_ci return 0; 24662306a36Sopenharmony_ci} 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_cistatic int snd_compr_write_data(struct snd_compr_stream *stream, 24962306a36Sopenharmony_ci const char __user *buf, size_t count) 25062306a36Sopenharmony_ci{ 25162306a36Sopenharmony_ci void *dstn; 25262306a36Sopenharmony_ci size_t copy; 25362306a36Sopenharmony_ci struct snd_compr_runtime *runtime = stream->runtime; 25462306a36Sopenharmony_ci /* 64-bit Modulus */ 25562306a36Sopenharmony_ci u64 app_pointer = div64_u64(runtime->total_bytes_available, 25662306a36Sopenharmony_ci runtime->buffer_size); 25762306a36Sopenharmony_ci app_pointer = runtime->total_bytes_available - 25862306a36Sopenharmony_ci (app_pointer * runtime->buffer_size); 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci dstn = runtime->buffer + app_pointer; 26162306a36Sopenharmony_ci pr_debug("copying %ld at %lld\n", 26262306a36Sopenharmony_ci (unsigned long)count, app_pointer); 26362306a36Sopenharmony_ci if (count < runtime->buffer_size - app_pointer) { 26462306a36Sopenharmony_ci if (copy_from_user(dstn, buf, count)) 26562306a36Sopenharmony_ci return -EFAULT; 26662306a36Sopenharmony_ci } else { 26762306a36Sopenharmony_ci copy = runtime->buffer_size - app_pointer; 26862306a36Sopenharmony_ci if (copy_from_user(dstn, buf, copy)) 26962306a36Sopenharmony_ci return -EFAULT; 27062306a36Sopenharmony_ci if (copy_from_user(runtime->buffer, buf + copy, count - copy)) 27162306a36Sopenharmony_ci return -EFAULT; 27262306a36Sopenharmony_ci } 27362306a36Sopenharmony_ci /* if DSP cares, let it know data has been written */ 27462306a36Sopenharmony_ci if (stream->ops->ack) 27562306a36Sopenharmony_ci stream->ops->ack(stream, count); 27662306a36Sopenharmony_ci return count; 27762306a36Sopenharmony_ci} 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_cistatic ssize_t snd_compr_write(struct file *f, const char __user *buf, 28062306a36Sopenharmony_ci size_t count, loff_t *offset) 28162306a36Sopenharmony_ci{ 28262306a36Sopenharmony_ci struct snd_compr_file *data = f->private_data; 28362306a36Sopenharmony_ci struct snd_compr_stream *stream; 28462306a36Sopenharmony_ci size_t avail; 28562306a36Sopenharmony_ci int retval; 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci if (snd_BUG_ON(!data)) 28862306a36Sopenharmony_ci return -EFAULT; 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci stream = &data->stream; 29162306a36Sopenharmony_ci mutex_lock(&stream->device->lock); 29262306a36Sopenharmony_ci /* write is allowed when stream is running or has been steup */ 29362306a36Sopenharmony_ci switch (stream->runtime->state) { 29462306a36Sopenharmony_ci case SNDRV_PCM_STATE_SETUP: 29562306a36Sopenharmony_ci case SNDRV_PCM_STATE_PREPARED: 29662306a36Sopenharmony_ci case SNDRV_PCM_STATE_RUNNING: 29762306a36Sopenharmony_ci break; 29862306a36Sopenharmony_ci default: 29962306a36Sopenharmony_ci mutex_unlock(&stream->device->lock); 30062306a36Sopenharmony_ci return -EBADFD; 30162306a36Sopenharmony_ci } 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci avail = snd_compr_get_avail(stream); 30462306a36Sopenharmony_ci pr_debug("avail returned %ld\n", (unsigned long)avail); 30562306a36Sopenharmony_ci /* calculate how much we can write to buffer */ 30662306a36Sopenharmony_ci if (avail > count) 30762306a36Sopenharmony_ci avail = count; 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci if (stream->ops->copy) { 31062306a36Sopenharmony_ci char __user* cbuf = (char __user*)buf; 31162306a36Sopenharmony_ci retval = stream->ops->copy(stream, cbuf, avail); 31262306a36Sopenharmony_ci } else { 31362306a36Sopenharmony_ci retval = snd_compr_write_data(stream, buf, avail); 31462306a36Sopenharmony_ci } 31562306a36Sopenharmony_ci if (retval > 0) 31662306a36Sopenharmony_ci stream->runtime->total_bytes_available += retval; 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci /* while initiating the stream, write should be called before START 31962306a36Sopenharmony_ci * call, so in setup move state */ 32062306a36Sopenharmony_ci if (stream->runtime->state == SNDRV_PCM_STATE_SETUP) { 32162306a36Sopenharmony_ci stream->runtime->state = SNDRV_PCM_STATE_PREPARED; 32262306a36Sopenharmony_ci pr_debug("stream prepared, Houston we are good to go\n"); 32362306a36Sopenharmony_ci } 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci mutex_unlock(&stream->device->lock); 32662306a36Sopenharmony_ci return retval; 32762306a36Sopenharmony_ci} 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_cistatic ssize_t snd_compr_read(struct file *f, char __user *buf, 33162306a36Sopenharmony_ci size_t count, loff_t *offset) 33262306a36Sopenharmony_ci{ 33362306a36Sopenharmony_ci struct snd_compr_file *data = f->private_data; 33462306a36Sopenharmony_ci struct snd_compr_stream *stream; 33562306a36Sopenharmony_ci size_t avail; 33662306a36Sopenharmony_ci int retval; 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci if (snd_BUG_ON(!data)) 33962306a36Sopenharmony_ci return -EFAULT; 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci stream = &data->stream; 34262306a36Sopenharmony_ci mutex_lock(&stream->device->lock); 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci /* read is allowed when stream is running, paused, draining and setup 34562306a36Sopenharmony_ci * (yes setup is state which we transition to after stop, so if user 34662306a36Sopenharmony_ci * wants to read data after stop we allow that) 34762306a36Sopenharmony_ci */ 34862306a36Sopenharmony_ci switch (stream->runtime->state) { 34962306a36Sopenharmony_ci case SNDRV_PCM_STATE_OPEN: 35062306a36Sopenharmony_ci case SNDRV_PCM_STATE_PREPARED: 35162306a36Sopenharmony_ci case SNDRV_PCM_STATE_SUSPENDED: 35262306a36Sopenharmony_ci case SNDRV_PCM_STATE_DISCONNECTED: 35362306a36Sopenharmony_ci retval = -EBADFD; 35462306a36Sopenharmony_ci goto out; 35562306a36Sopenharmony_ci case SNDRV_PCM_STATE_XRUN: 35662306a36Sopenharmony_ci retval = -EPIPE; 35762306a36Sopenharmony_ci goto out; 35862306a36Sopenharmony_ci } 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci avail = snd_compr_get_avail(stream); 36162306a36Sopenharmony_ci pr_debug("avail returned %ld\n", (unsigned long)avail); 36262306a36Sopenharmony_ci /* calculate how much we can read from buffer */ 36362306a36Sopenharmony_ci if (avail > count) 36462306a36Sopenharmony_ci avail = count; 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci if (stream->ops->copy) { 36762306a36Sopenharmony_ci retval = stream->ops->copy(stream, buf, avail); 36862306a36Sopenharmony_ci } else { 36962306a36Sopenharmony_ci retval = -ENXIO; 37062306a36Sopenharmony_ci goto out; 37162306a36Sopenharmony_ci } 37262306a36Sopenharmony_ci if (retval > 0) 37362306a36Sopenharmony_ci stream->runtime->total_bytes_transferred += retval; 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ciout: 37662306a36Sopenharmony_ci mutex_unlock(&stream->device->lock); 37762306a36Sopenharmony_ci return retval; 37862306a36Sopenharmony_ci} 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_cistatic int snd_compr_mmap(struct file *f, struct vm_area_struct *vma) 38162306a36Sopenharmony_ci{ 38262306a36Sopenharmony_ci return -ENXIO; 38362306a36Sopenharmony_ci} 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_cistatic __poll_t snd_compr_get_poll(struct snd_compr_stream *stream) 38662306a36Sopenharmony_ci{ 38762306a36Sopenharmony_ci if (stream->direction == SND_COMPRESS_PLAYBACK) 38862306a36Sopenharmony_ci return EPOLLOUT | EPOLLWRNORM; 38962306a36Sopenharmony_ci else 39062306a36Sopenharmony_ci return EPOLLIN | EPOLLRDNORM; 39162306a36Sopenharmony_ci} 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_cistatic __poll_t snd_compr_poll(struct file *f, poll_table *wait) 39462306a36Sopenharmony_ci{ 39562306a36Sopenharmony_ci struct snd_compr_file *data = f->private_data; 39662306a36Sopenharmony_ci struct snd_compr_stream *stream; 39762306a36Sopenharmony_ci size_t avail; 39862306a36Sopenharmony_ci __poll_t retval = 0; 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci if (snd_BUG_ON(!data)) 40162306a36Sopenharmony_ci return EPOLLERR; 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci stream = &data->stream; 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci mutex_lock(&stream->device->lock); 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci switch (stream->runtime->state) { 40862306a36Sopenharmony_ci case SNDRV_PCM_STATE_OPEN: 40962306a36Sopenharmony_ci case SNDRV_PCM_STATE_XRUN: 41062306a36Sopenharmony_ci retval = snd_compr_get_poll(stream) | EPOLLERR; 41162306a36Sopenharmony_ci goto out; 41262306a36Sopenharmony_ci default: 41362306a36Sopenharmony_ci break; 41462306a36Sopenharmony_ci } 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci poll_wait(f, &stream->runtime->sleep, wait); 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci avail = snd_compr_get_avail(stream); 41962306a36Sopenharmony_ci pr_debug("avail is %ld\n", (unsigned long)avail); 42062306a36Sopenharmony_ci /* check if we have at least one fragment to fill */ 42162306a36Sopenharmony_ci switch (stream->runtime->state) { 42262306a36Sopenharmony_ci case SNDRV_PCM_STATE_DRAINING: 42362306a36Sopenharmony_ci /* stream has been woken up after drain is complete 42462306a36Sopenharmony_ci * draining done so set stream state to stopped 42562306a36Sopenharmony_ci */ 42662306a36Sopenharmony_ci retval = snd_compr_get_poll(stream); 42762306a36Sopenharmony_ci stream->runtime->state = SNDRV_PCM_STATE_SETUP; 42862306a36Sopenharmony_ci break; 42962306a36Sopenharmony_ci case SNDRV_PCM_STATE_RUNNING: 43062306a36Sopenharmony_ci case SNDRV_PCM_STATE_PREPARED: 43162306a36Sopenharmony_ci case SNDRV_PCM_STATE_PAUSED: 43262306a36Sopenharmony_ci if (avail >= stream->runtime->fragment_size) 43362306a36Sopenharmony_ci retval = snd_compr_get_poll(stream); 43462306a36Sopenharmony_ci break; 43562306a36Sopenharmony_ci default: 43662306a36Sopenharmony_ci retval = snd_compr_get_poll(stream) | EPOLLERR; 43762306a36Sopenharmony_ci break; 43862306a36Sopenharmony_ci } 43962306a36Sopenharmony_ciout: 44062306a36Sopenharmony_ci mutex_unlock(&stream->device->lock); 44162306a36Sopenharmony_ci return retval; 44262306a36Sopenharmony_ci} 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_cistatic int 44562306a36Sopenharmony_cisnd_compr_get_caps(struct snd_compr_stream *stream, unsigned long arg) 44662306a36Sopenharmony_ci{ 44762306a36Sopenharmony_ci int retval; 44862306a36Sopenharmony_ci struct snd_compr_caps caps; 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci if (!stream->ops->get_caps) 45162306a36Sopenharmony_ci return -ENXIO; 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci memset(&caps, 0, sizeof(caps)); 45462306a36Sopenharmony_ci retval = stream->ops->get_caps(stream, &caps); 45562306a36Sopenharmony_ci if (retval) 45662306a36Sopenharmony_ci goto out; 45762306a36Sopenharmony_ci if (copy_to_user((void __user *)arg, &caps, sizeof(caps))) 45862306a36Sopenharmony_ci retval = -EFAULT; 45962306a36Sopenharmony_ciout: 46062306a36Sopenharmony_ci return retval; 46162306a36Sopenharmony_ci} 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci#ifndef COMPR_CODEC_CAPS_OVERFLOW 46462306a36Sopenharmony_cistatic int 46562306a36Sopenharmony_cisnd_compr_get_codec_caps(struct snd_compr_stream *stream, unsigned long arg) 46662306a36Sopenharmony_ci{ 46762306a36Sopenharmony_ci int retval; 46862306a36Sopenharmony_ci struct snd_compr_codec_caps *caps; 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci if (!stream->ops->get_codec_caps) 47162306a36Sopenharmony_ci return -ENXIO; 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci caps = kzalloc(sizeof(*caps), GFP_KERNEL); 47462306a36Sopenharmony_ci if (!caps) 47562306a36Sopenharmony_ci return -ENOMEM; 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci retval = stream->ops->get_codec_caps(stream, caps); 47862306a36Sopenharmony_ci if (retval) 47962306a36Sopenharmony_ci goto out; 48062306a36Sopenharmony_ci if (copy_to_user((void __user *)arg, caps, sizeof(*caps))) 48162306a36Sopenharmony_ci retval = -EFAULT; 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ciout: 48462306a36Sopenharmony_ci kfree(caps); 48562306a36Sopenharmony_ci return retval; 48662306a36Sopenharmony_ci} 48762306a36Sopenharmony_ci#endif /* !COMPR_CODEC_CAPS_OVERFLOW */ 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ciint snd_compr_malloc_pages(struct snd_compr_stream *stream, size_t size) 49062306a36Sopenharmony_ci{ 49162306a36Sopenharmony_ci struct snd_dma_buffer *dmab; 49262306a36Sopenharmony_ci int ret; 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci if (snd_BUG_ON(!(stream) || !(stream)->runtime)) 49562306a36Sopenharmony_ci return -EINVAL; 49662306a36Sopenharmony_ci dmab = kzalloc(sizeof(*dmab), GFP_KERNEL); 49762306a36Sopenharmony_ci if (!dmab) 49862306a36Sopenharmony_ci return -ENOMEM; 49962306a36Sopenharmony_ci dmab->dev = stream->dma_buffer.dev; 50062306a36Sopenharmony_ci ret = snd_dma_alloc_pages(dmab->dev.type, dmab->dev.dev, size, dmab); 50162306a36Sopenharmony_ci if (ret < 0) { 50262306a36Sopenharmony_ci kfree(dmab); 50362306a36Sopenharmony_ci return ret; 50462306a36Sopenharmony_ci } 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci snd_compr_set_runtime_buffer(stream, dmab); 50762306a36Sopenharmony_ci stream->runtime->dma_bytes = size; 50862306a36Sopenharmony_ci return 1; 50962306a36Sopenharmony_ci} 51062306a36Sopenharmony_ciEXPORT_SYMBOL(snd_compr_malloc_pages); 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ciint snd_compr_free_pages(struct snd_compr_stream *stream) 51362306a36Sopenharmony_ci{ 51462306a36Sopenharmony_ci struct snd_compr_runtime *runtime; 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci if (snd_BUG_ON(!(stream) || !(stream)->runtime)) 51762306a36Sopenharmony_ci return -EINVAL; 51862306a36Sopenharmony_ci runtime = stream->runtime; 51962306a36Sopenharmony_ci if (runtime->dma_area == NULL) 52062306a36Sopenharmony_ci return 0; 52162306a36Sopenharmony_ci if (runtime->dma_buffer_p != &stream->dma_buffer) { 52262306a36Sopenharmony_ci /* It's a newly allocated buffer. Release it now. */ 52362306a36Sopenharmony_ci snd_dma_free_pages(runtime->dma_buffer_p); 52462306a36Sopenharmony_ci kfree(runtime->dma_buffer_p); 52562306a36Sopenharmony_ci } 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci snd_compr_set_runtime_buffer(stream, NULL); 52862306a36Sopenharmony_ci return 0; 52962306a36Sopenharmony_ci} 53062306a36Sopenharmony_ciEXPORT_SYMBOL(snd_compr_free_pages); 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci/* revisit this with snd_pcm_preallocate_xxx */ 53362306a36Sopenharmony_cistatic int snd_compr_allocate_buffer(struct snd_compr_stream *stream, 53462306a36Sopenharmony_ci struct snd_compr_params *params) 53562306a36Sopenharmony_ci{ 53662306a36Sopenharmony_ci unsigned int buffer_size; 53762306a36Sopenharmony_ci void *buffer = NULL; 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci buffer_size = params->buffer.fragment_size * params->buffer.fragments; 54062306a36Sopenharmony_ci if (stream->ops->copy) { 54162306a36Sopenharmony_ci buffer = NULL; 54262306a36Sopenharmony_ci /* if copy is defined the driver will be required to copy 54362306a36Sopenharmony_ci * the data from core 54462306a36Sopenharmony_ci */ 54562306a36Sopenharmony_ci } else { 54662306a36Sopenharmony_ci if (stream->runtime->dma_buffer_p) { 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci if (buffer_size > stream->runtime->dma_buffer_p->bytes) 54962306a36Sopenharmony_ci dev_err(stream->device->dev, 55062306a36Sopenharmony_ci "Not enough DMA buffer"); 55162306a36Sopenharmony_ci else 55262306a36Sopenharmony_ci buffer = stream->runtime->dma_buffer_p->area; 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci } else { 55562306a36Sopenharmony_ci buffer = kmalloc(buffer_size, GFP_KERNEL); 55662306a36Sopenharmony_ci } 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci if (!buffer) 55962306a36Sopenharmony_ci return -ENOMEM; 56062306a36Sopenharmony_ci } 56162306a36Sopenharmony_ci stream->runtime->fragment_size = params->buffer.fragment_size; 56262306a36Sopenharmony_ci stream->runtime->fragments = params->buffer.fragments; 56362306a36Sopenharmony_ci stream->runtime->buffer = buffer; 56462306a36Sopenharmony_ci stream->runtime->buffer_size = buffer_size; 56562306a36Sopenharmony_ci return 0; 56662306a36Sopenharmony_ci} 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_cistatic int snd_compress_check_input(struct snd_compr_params *params) 56962306a36Sopenharmony_ci{ 57062306a36Sopenharmony_ci /* first let's check the buffer parameter's */ 57162306a36Sopenharmony_ci if (params->buffer.fragment_size == 0 || 57262306a36Sopenharmony_ci params->buffer.fragments > U32_MAX / params->buffer.fragment_size || 57362306a36Sopenharmony_ci params->buffer.fragments == 0) 57462306a36Sopenharmony_ci return -EINVAL; 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci /* now codec parameters */ 57762306a36Sopenharmony_ci if (params->codec.id == 0 || params->codec.id > SND_AUDIOCODEC_MAX) 57862306a36Sopenharmony_ci return -EINVAL; 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci if (params->codec.ch_in == 0 || params->codec.ch_out == 0) 58162306a36Sopenharmony_ci return -EINVAL; 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci return 0; 58462306a36Sopenharmony_ci} 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_cistatic int 58762306a36Sopenharmony_cisnd_compr_set_params(struct snd_compr_stream *stream, unsigned long arg) 58862306a36Sopenharmony_ci{ 58962306a36Sopenharmony_ci struct snd_compr_params *params; 59062306a36Sopenharmony_ci int retval; 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci if (stream->runtime->state == SNDRV_PCM_STATE_OPEN || stream->next_track) { 59362306a36Sopenharmony_ci /* 59462306a36Sopenharmony_ci * we should allow parameter change only when stream has been 59562306a36Sopenharmony_ci * opened not in other cases 59662306a36Sopenharmony_ci */ 59762306a36Sopenharmony_ci params = memdup_user((void __user *)arg, sizeof(*params)); 59862306a36Sopenharmony_ci if (IS_ERR(params)) 59962306a36Sopenharmony_ci return PTR_ERR(params); 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci retval = snd_compress_check_input(params); 60262306a36Sopenharmony_ci if (retval) 60362306a36Sopenharmony_ci goto out; 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ci retval = snd_compr_allocate_buffer(stream, params); 60662306a36Sopenharmony_ci if (retval) { 60762306a36Sopenharmony_ci retval = -ENOMEM; 60862306a36Sopenharmony_ci goto out; 60962306a36Sopenharmony_ci } 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci retval = stream->ops->set_params(stream, params); 61262306a36Sopenharmony_ci if (retval) 61362306a36Sopenharmony_ci goto out; 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci if (stream->next_track) 61662306a36Sopenharmony_ci goto out; 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci stream->metadata_set = false; 61962306a36Sopenharmony_ci stream->next_track = false; 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci stream->runtime->state = SNDRV_PCM_STATE_SETUP; 62262306a36Sopenharmony_ci } else { 62362306a36Sopenharmony_ci return -EPERM; 62462306a36Sopenharmony_ci } 62562306a36Sopenharmony_ciout: 62662306a36Sopenharmony_ci kfree(params); 62762306a36Sopenharmony_ci return retval; 62862306a36Sopenharmony_ci} 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_cistatic int 63162306a36Sopenharmony_cisnd_compr_get_params(struct snd_compr_stream *stream, unsigned long arg) 63262306a36Sopenharmony_ci{ 63362306a36Sopenharmony_ci struct snd_codec *params; 63462306a36Sopenharmony_ci int retval; 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci if (!stream->ops->get_params) 63762306a36Sopenharmony_ci return -EBADFD; 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci params = kzalloc(sizeof(*params), GFP_KERNEL); 64062306a36Sopenharmony_ci if (!params) 64162306a36Sopenharmony_ci return -ENOMEM; 64262306a36Sopenharmony_ci retval = stream->ops->get_params(stream, params); 64362306a36Sopenharmony_ci if (retval) 64462306a36Sopenharmony_ci goto out; 64562306a36Sopenharmony_ci if (copy_to_user((char __user *)arg, params, sizeof(*params))) 64662306a36Sopenharmony_ci retval = -EFAULT; 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ciout: 64962306a36Sopenharmony_ci kfree(params); 65062306a36Sopenharmony_ci return retval; 65162306a36Sopenharmony_ci} 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_cistatic int 65462306a36Sopenharmony_cisnd_compr_get_metadata(struct snd_compr_stream *stream, unsigned long arg) 65562306a36Sopenharmony_ci{ 65662306a36Sopenharmony_ci struct snd_compr_metadata metadata; 65762306a36Sopenharmony_ci int retval; 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci if (!stream->ops->get_metadata) 66062306a36Sopenharmony_ci return -ENXIO; 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci if (copy_from_user(&metadata, (void __user *)arg, sizeof(metadata))) 66362306a36Sopenharmony_ci return -EFAULT; 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci retval = stream->ops->get_metadata(stream, &metadata); 66662306a36Sopenharmony_ci if (retval != 0) 66762306a36Sopenharmony_ci return retval; 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_ci if (copy_to_user((void __user *)arg, &metadata, sizeof(metadata))) 67062306a36Sopenharmony_ci return -EFAULT; 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_ci return 0; 67362306a36Sopenharmony_ci} 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_cistatic int 67662306a36Sopenharmony_cisnd_compr_set_metadata(struct snd_compr_stream *stream, unsigned long arg) 67762306a36Sopenharmony_ci{ 67862306a36Sopenharmony_ci struct snd_compr_metadata metadata; 67962306a36Sopenharmony_ci int retval; 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci if (!stream->ops->set_metadata) 68262306a36Sopenharmony_ci return -ENXIO; 68362306a36Sopenharmony_ci /* 68462306a36Sopenharmony_ci * we should allow parameter change only when stream has been 68562306a36Sopenharmony_ci * opened not in other cases 68662306a36Sopenharmony_ci */ 68762306a36Sopenharmony_ci if (copy_from_user(&metadata, (void __user *)arg, sizeof(metadata))) 68862306a36Sopenharmony_ci return -EFAULT; 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_ci retval = stream->ops->set_metadata(stream, &metadata); 69162306a36Sopenharmony_ci stream->metadata_set = true; 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci return retval; 69462306a36Sopenharmony_ci} 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_cistatic inline int 69762306a36Sopenharmony_cisnd_compr_tstamp(struct snd_compr_stream *stream, unsigned long arg) 69862306a36Sopenharmony_ci{ 69962306a36Sopenharmony_ci struct snd_compr_tstamp tstamp = {0}; 70062306a36Sopenharmony_ci int ret; 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_ci ret = snd_compr_update_tstamp(stream, &tstamp); 70362306a36Sopenharmony_ci if (ret == 0) 70462306a36Sopenharmony_ci ret = copy_to_user((struct snd_compr_tstamp __user *)arg, 70562306a36Sopenharmony_ci &tstamp, sizeof(tstamp)) ? -EFAULT : 0; 70662306a36Sopenharmony_ci return ret; 70762306a36Sopenharmony_ci} 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_cistatic int snd_compr_pause(struct snd_compr_stream *stream) 71062306a36Sopenharmony_ci{ 71162306a36Sopenharmony_ci int retval; 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_ci switch (stream->runtime->state) { 71462306a36Sopenharmony_ci case SNDRV_PCM_STATE_RUNNING: 71562306a36Sopenharmony_ci retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_PAUSE_PUSH); 71662306a36Sopenharmony_ci if (!retval) 71762306a36Sopenharmony_ci stream->runtime->state = SNDRV_PCM_STATE_PAUSED; 71862306a36Sopenharmony_ci break; 71962306a36Sopenharmony_ci case SNDRV_PCM_STATE_DRAINING: 72062306a36Sopenharmony_ci if (!stream->device->use_pause_in_draining) 72162306a36Sopenharmony_ci return -EPERM; 72262306a36Sopenharmony_ci retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_PAUSE_PUSH); 72362306a36Sopenharmony_ci if (!retval) 72462306a36Sopenharmony_ci stream->pause_in_draining = true; 72562306a36Sopenharmony_ci break; 72662306a36Sopenharmony_ci default: 72762306a36Sopenharmony_ci return -EPERM; 72862306a36Sopenharmony_ci } 72962306a36Sopenharmony_ci return retval; 73062306a36Sopenharmony_ci} 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_cistatic int snd_compr_resume(struct snd_compr_stream *stream) 73362306a36Sopenharmony_ci{ 73462306a36Sopenharmony_ci int retval; 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_ci switch (stream->runtime->state) { 73762306a36Sopenharmony_ci case SNDRV_PCM_STATE_PAUSED: 73862306a36Sopenharmony_ci retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_PAUSE_RELEASE); 73962306a36Sopenharmony_ci if (!retval) 74062306a36Sopenharmony_ci stream->runtime->state = SNDRV_PCM_STATE_RUNNING; 74162306a36Sopenharmony_ci break; 74262306a36Sopenharmony_ci case SNDRV_PCM_STATE_DRAINING: 74362306a36Sopenharmony_ci if (!stream->pause_in_draining) 74462306a36Sopenharmony_ci return -EPERM; 74562306a36Sopenharmony_ci retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_PAUSE_RELEASE); 74662306a36Sopenharmony_ci if (!retval) 74762306a36Sopenharmony_ci stream->pause_in_draining = false; 74862306a36Sopenharmony_ci break; 74962306a36Sopenharmony_ci default: 75062306a36Sopenharmony_ci return -EPERM; 75162306a36Sopenharmony_ci } 75262306a36Sopenharmony_ci return retval; 75362306a36Sopenharmony_ci} 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_cistatic int snd_compr_start(struct snd_compr_stream *stream) 75662306a36Sopenharmony_ci{ 75762306a36Sopenharmony_ci int retval; 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ci switch (stream->runtime->state) { 76062306a36Sopenharmony_ci case SNDRV_PCM_STATE_SETUP: 76162306a36Sopenharmony_ci if (stream->direction != SND_COMPRESS_CAPTURE) 76262306a36Sopenharmony_ci return -EPERM; 76362306a36Sopenharmony_ci break; 76462306a36Sopenharmony_ci case SNDRV_PCM_STATE_PREPARED: 76562306a36Sopenharmony_ci break; 76662306a36Sopenharmony_ci default: 76762306a36Sopenharmony_ci return -EPERM; 76862306a36Sopenharmony_ci } 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_ci retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_START); 77162306a36Sopenharmony_ci if (!retval) 77262306a36Sopenharmony_ci stream->runtime->state = SNDRV_PCM_STATE_RUNNING; 77362306a36Sopenharmony_ci return retval; 77462306a36Sopenharmony_ci} 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_cistatic int snd_compr_stop(struct snd_compr_stream *stream) 77762306a36Sopenharmony_ci{ 77862306a36Sopenharmony_ci int retval; 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_ci switch (stream->runtime->state) { 78162306a36Sopenharmony_ci case SNDRV_PCM_STATE_OPEN: 78262306a36Sopenharmony_ci case SNDRV_PCM_STATE_SETUP: 78362306a36Sopenharmony_ci case SNDRV_PCM_STATE_PREPARED: 78462306a36Sopenharmony_ci return -EPERM; 78562306a36Sopenharmony_ci default: 78662306a36Sopenharmony_ci break; 78762306a36Sopenharmony_ci } 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_ci retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_STOP); 79062306a36Sopenharmony_ci if (!retval) { 79162306a36Sopenharmony_ci /* clear flags and stop any drain wait */ 79262306a36Sopenharmony_ci stream->partial_drain = false; 79362306a36Sopenharmony_ci stream->metadata_set = false; 79462306a36Sopenharmony_ci stream->pause_in_draining = false; 79562306a36Sopenharmony_ci snd_compr_drain_notify(stream); 79662306a36Sopenharmony_ci stream->runtime->total_bytes_available = 0; 79762306a36Sopenharmony_ci stream->runtime->total_bytes_transferred = 0; 79862306a36Sopenharmony_ci } 79962306a36Sopenharmony_ci return retval; 80062306a36Sopenharmony_ci} 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_cistatic void error_delayed_work(struct work_struct *work) 80362306a36Sopenharmony_ci{ 80462306a36Sopenharmony_ci struct snd_compr_stream *stream; 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_ci stream = container_of(work, struct snd_compr_stream, error_work.work); 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_ci mutex_lock(&stream->device->lock); 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_ci stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_STOP); 81162306a36Sopenharmony_ci wake_up(&stream->runtime->sleep); 81262306a36Sopenharmony_ci 81362306a36Sopenharmony_ci mutex_unlock(&stream->device->lock); 81462306a36Sopenharmony_ci} 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_ci/** 81762306a36Sopenharmony_ci * snd_compr_stop_error: Report a fatal error on a stream 81862306a36Sopenharmony_ci * @stream: pointer to stream 81962306a36Sopenharmony_ci * @state: state to transition the stream to 82062306a36Sopenharmony_ci * 82162306a36Sopenharmony_ci * Stop the stream and set its state. 82262306a36Sopenharmony_ci * 82362306a36Sopenharmony_ci * Should be called with compressed device lock held. 82462306a36Sopenharmony_ci * 82562306a36Sopenharmony_ci * Return: zero if successful, or a negative error code 82662306a36Sopenharmony_ci */ 82762306a36Sopenharmony_ciint snd_compr_stop_error(struct snd_compr_stream *stream, 82862306a36Sopenharmony_ci snd_pcm_state_t state) 82962306a36Sopenharmony_ci{ 83062306a36Sopenharmony_ci if (stream->runtime->state == state) 83162306a36Sopenharmony_ci return 0; 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_ci stream->runtime->state = state; 83462306a36Sopenharmony_ci 83562306a36Sopenharmony_ci pr_debug("Changing state to: %d\n", state); 83662306a36Sopenharmony_ci 83762306a36Sopenharmony_ci queue_delayed_work(system_power_efficient_wq, &stream->error_work, 0); 83862306a36Sopenharmony_ci 83962306a36Sopenharmony_ci return 0; 84062306a36Sopenharmony_ci} 84162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_compr_stop_error); 84262306a36Sopenharmony_ci 84362306a36Sopenharmony_cistatic int snd_compress_wait_for_drain(struct snd_compr_stream *stream) 84462306a36Sopenharmony_ci{ 84562306a36Sopenharmony_ci int ret; 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_ci /* 84862306a36Sopenharmony_ci * We are called with lock held. So drop the lock while we wait for 84962306a36Sopenharmony_ci * drain complete notification from the driver 85062306a36Sopenharmony_ci * 85162306a36Sopenharmony_ci * It is expected that driver will notify the drain completion and then 85262306a36Sopenharmony_ci * stream will be moved to SETUP state, even if draining resulted in an 85362306a36Sopenharmony_ci * error. We can trigger next track after this. 85462306a36Sopenharmony_ci */ 85562306a36Sopenharmony_ci stream->runtime->state = SNDRV_PCM_STATE_DRAINING; 85662306a36Sopenharmony_ci mutex_unlock(&stream->device->lock); 85762306a36Sopenharmony_ci 85862306a36Sopenharmony_ci /* we wait for drain to complete here, drain can return when 85962306a36Sopenharmony_ci * interruption occurred, wait returned error or success. 86062306a36Sopenharmony_ci * For the first two cases we don't do anything different here and 86162306a36Sopenharmony_ci * return after waking up 86262306a36Sopenharmony_ci */ 86362306a36Sopenharmony_ci 86462306a36Sopenharmony_ci ret = wait_event_interruptible(stream->runtime->sleep, 86562306a36Sopenharmony_ci (stream->runtime->state != SNDRV_PCM_STATE_DRAINING)); 86662306a36Sopenharmony_ci if (ret == -ERESTARTSYS) 86762306a36Sopenharmony_ci pr_debug("wait aborted by a signal\n"); 86862306a36Sopenharmony_ci else if (ret) 86962306a36Sopenharmony_ci pr_debug("wait for drain failed with %d\n", ret); 87062306a36Sopenharmony_ci 87162306a36Sopenharmony_ci 87262306a36Sopenharmony_ci wake_up(&stream->runtime->sleep); 87362306a36Sopenharmony_ci mutex_lock(&stream->device->lock); 87462306a36Sopenharmony_ci 87562306a36Sopenharmony_ci return ret; 87662306a36Sopenharmony_ci} 87762306a36Sopenharmony_ci 87862306a36Sopenharmony_cistatic int snd_compr_drain(struct snd_compr_stream *stream) 87962306a36Sopenharmony_ci{ 88062306a36Sopenharmony_ci int retval; 88162306a36Sopenharmony_ci 88262306a36Sopenharmony_ci switch (stream->runtime->state) { 88362306a36Sopenharmony_ci case SNDRV_PCM_STATE_OPEN: 88462306a36Sopenharmony_ci case SNDRV_PCM_STATE_SETUP: 88562306a36Sopenharmony_ci case SNDRV_PCM_STATE_PREPARED: 88662306a36Sopenharmony_ci case SNDRV_PCM_STATE_PAUSED: 88762306a36Sopenharmony_ci return -EPERM; 88862306a36Sopenharmony_ci case SNDRV_PCM_STATE_XRUN: 88962306a36Sopenharmony_ci return -EPIPE; 89062306a36Sopenharmony_ci default: 89162306a36Sopenharmony_ci break; 89262306a36Sopenharmony_ci } 89362306a36Sopenharmony_ci 89462306a36Sopenharmony_ci retval = stream->ops->trigger(stream, SND_COMPR_TRIGGER_DRAIN); 89562306a36Sopenharmony_ci if (retval) { 89662306a36Sopenharmony_ci pr_debug("SND_COMPR_TRIGGER_DRAIN failed %d\n", retval); 89762306a36Sopenharmony_ci wake_up(&stream->runtime->sleep); 89862306a36Sopenharmony_ci return retval; 89962306a36Sopenharmony_ci } 90062306a36Sopenharmony_ci 90162306a36Sopenharmony_ci return snd_compress_wait_for_drain(stream); 90262306a36Sopenharmony_ci} 90362306a36Sopenharmony_ci 90462306a36Sopenharmony_cistatic int snd_compr_next_track(struct snd_compr_stream *stream) 90562306a36Sopenharmony_ci{ 90662306a36Sopenharmony_ci int retval; 90762306a36Sopenharmony_ci 90862306a36Sopenharmony_ci /* only a running stream can transition to next track */ 90962306a36Sopenharmony_ci if (stream->runtime->state != SNDRV_PCM_STATE_RUNNING) 91062306a36Sopenharmony_ci return -EPERM; 91162306a36Sopenharmony_ci 91262306a36Sopenharmony_ci /* next track doesn't have any meaning for capture streams */ 91362306a36Sopenharmony_ci if (stream->direction == SND_COMPRESS_CAPTURE) 91462306a36Sopenharmony_ci return -EPERM; 91562306a36Sopenharmony_ci 91662306a36Sopenharmony_ci /* you can signal next track if this is intended to be a gapless stream 91762306a36Sopenharmony_ci * and current track metadata is set 91862306a36Sopenharmony_ci */ 91962306a36Sopenharmony_ci if (stream->metadata_set == false) 92062306a36Sopenharmony_ci return -EPERM; 92162306a36Sopenharmony_ci 92262306a36Sopenharmony_ci retval = stream->ops->trigger(stream, SND_COMPR_TRIGGER_NEXT_TRACK); 92362306a36Sopenharmony_ci if (retval != 0) 92462306a36Sopenharmony_ci return retval; 92562306a36Sopenharmony_ci stream->metadata_set = false; 92662306a36Sopenharmony_ci stream->next_track = true; 92762306a36Sopenharmony_ci return 0; 92862306a36Sopenharmony_ci} 92962306a36Sopenharmony_ci 93062306a36Sopenharmony_cistatic int snd_compr_partial_drain(struct snd_compr_stream *stream) 93162306a36Sopenharmony_ci{ 93262306a36Sopenharmony_ci int retval; 93362306a36Sopenharmony_ci 93462306a36Sopenharmony_ci switch (stream->runtime->state) { 93562306a36Sopenharmony_ci case SNDRV_PCM_STATE_OPEN: 93662306a36Sopenharmony_ci case SNDRV_PCM_STATE_SETUP: 93762306a36Sopenharmony_ci case SNDRV_PCM_STATE_PREPARED: 93862306a36Sopenharmony_ci case SNDRV_PCM_STATE_PAUSED: 93962306a36Sopenharmony_ci return -EPERM; 94062306a36Sopenharmony_ci case SNDRV_PCM_STATE_XRUN: 94162306a36Sopenharmony_ci return -EPIPE; 94262306a36Sopenharmony_ci default: 94362306a36Sopenharmony_ci break; 94462306a36Sopenharmony_ci } 94562306a36Sopenharmony_ci 94662306a36Sopenharmony_ci /* partial drain doesn't have any meaning for capture streams */ 94762306a36Sopenharmony_ci if (stream->direction == SND_COMPRESS_CAPTURE) 94862306a36Sopenharmony_ci return -EPERM; 94962306a36Sopenharmony_ci 95062306a36Sopenharmony_ci /* stream can be drained only when next track has been signalled */ 95162306a36Sopenharmony_ci if (stream->next_track == false) 95262306a36Sopenharmony_ci return -EPERM; 95362306a36Sopenharmony_ci 95462306a36Sopenharmony_ci stream->partial_drain = true; 95562306a36Sopenharmony_ci retval = stream->ops->trigger(stream, SND_COMPR_TRIGGER_PARTIAL_DRAIN); 95662306a36Sopenharmony_ci if (retval) { 95762306a36Sopenharmony_ci pr_debug("Partial drain returned failure\n"); 95862306a36Sopenharmony_ci wake_up(&stream->runtime->sleep); 95962306a36Sopenharmony_ci return retval; 96062306a36Sopenharmony_ci } 96162306a36Sopenharmony_ci 96262306a36Sopenharmony_ci stream->next_track = false; 96362306a36Sopenharmony_ci return snd_compress_wait_for_drain(stream); 96462306a36Sopenharmony_ci} 96562306a36Sopenharmony_ci 96662306a36Sopenharmony_cistatic long snd_compr_ioctl(struct file *f, unsigned int cmd, unsigned long arg) 96762306a36Sopenharmony_ci{ 96862306a36Sopenharmony_ci struct snd_compr_file *data = f->private_data; 96962306a36Sopenharmony_ci struct snd_compr_stream *stream; 97062306a36Sopenharmony_ci int retval = -ENOTTY; 97162306a36Sopenharmony_ci 97262306a36Sopenharmony_ci if (snd_BUG_ON(!data)) 97362306a36Sopenharmony_ci return -EFAULT; 97462306a36Sopenharmony_ci 97562306a36Sopenharmony_ci stream = &data->stream; 97662306a36Sopenharmony_ci 97762306a36Sopenharmony_ci mutex_lock(&stream->device->lock); 97862306a36Sopenharmony_ci switch (_IOC_NR(cmd)) { 97962306a36Sopenharmony_ci case _IOC_NR(SNDRV_COMPRESS_IOCTL_VERSION): 98062306a36Sopenharmony_ci retval = put_user(SNDRV_COMPRESS_VERSION, 98162306a36Sopenharmony_ci (int __user *)arg) ? -EFAULT : 0; 98262306a36Sopenharmony_ci break; 98362306a36Sopenharmony_ci case _IOC_NR(SNDRV_COMPRESS_GET_CAPS): 98462306a36Sopenharmony_ci retval = snd_compr_get_caps(stream, arg); 98562306a36Sopenharmony_ci break; 98662306a36Sopenharmony_ci#ifndef COMPR_CODEC_CAPS_OVERFLOW 98762306a36Sopenharmony_ci case _IOC_NR(SNDRV_COMPRESS_GET_CODEC_CAPS): 98862306a36Sopenharmony_ci retval = snd_compr_get_codec_caps(stream, arg); 98962306a36Sopenharmony_ci break; 99062306a36Sopenharmony_ci#endif 99162306a36Sopenharmony_ci case _IOC_NR(SNDRV_COMPRESS_SET_PARAMS): 99262306a36Sopenharmony_ci retval = snd_compr_set_params(stream, arg); 99362306a36Sopenharmony_ci break; 99462306a36Sopenharmony_ci case _IOC_NR(SNDRV_COMPRESS_GET_PARAMS): 99562306a36Sopenharmony_ci retval = snd_compr_get_params(stream, arg); 99662306a36Sopenharmony_ci break; 99762306a36Sopenharmony_ci case _IOC_NR(SNDRV_COMPRESS_SET_METADATA): 99862306a36Sopenharmony_ci retval = snd_compr_set_metadata(stream, arg); 99962306a36Sopenharmony_ci break; 100062306a36Sopenharmony_ci case _IOC_NR(SNDRV_COMPRESS_GET_METADATA): 100162306a36Sopenharmony_ci retval = snd_compr_get_metadata(stream, arg); 100262306a36Sopenharmony_ci break; 100362306a36Sopenharmony_ci case _IOC_NR(SNDRV_COMPRESS_TSTAMP): 100462306a36Sopenharmony_ci retval = snd_compr_tstamp(stream, arg); 100562306a36Sopenharmony_ci break; 100662306a36Sopenharmony_ci case _IOC_NR(SNDRV_COMPRESS_AVAIL): 100762306a36Sopenharmony_ci retval = snd_compr_ioctl_avail(stream, arg); 100862306a36Sopenharmony_ci break; 100962306a36Sopenharmony_ci case _IOC_NR(SNDRV_COMPRESS_PAUSE): 101062306a36Sopenharmony_ci retval = snd_compr_pause(stream); 101162306a36Sopenharmony_ci break; 101262306a36Sopenharmony_ci case _IOC_NR(SNDRV_COMPRESS_RESUME): 101362306a36Sopenharmony_ci retval = snd_compr_resume(stream); 101462306a36Sopenharmony_ci break; 101562306a36Sopenharmony_ci case _IOC_NR(SNDRV_COMPRESS_START): 101662306a36Sopenharmony_ci retval = snd_compr_start(stream); 101762306a36Sopenharmony_ci break; 101862306a36Sopenharmony_ci case _IOC_NR(SNDRV_COMPRESS_STOP): 101962306a36Sopenharmony_ci retval = snd_compr_stop(stream); 102062306a36Sopenharmony_ci break; 102162306a36Sopenharmony_ci case _IOC_NR(SNDRV_COMPRESS_DRAIN): 102262306a36Sopenharmony_ci retval = snd_compr_drain(stream); 102362306a36Sopenharmony_ci break; 102462306a36Sopenharmony_ci case _IOC_NR(SNDRV_COMPRESS_PARTIAL_DRAIN): 102562306a36Sopenharmony_ci retval = snd_compr_partial_drain(stream); 102662306a36Sopenharmony_ci break; 102762306a36Sopenharmony_ci case _IOC_NR(SNDRV_COMPRESS_NEXT_TRACK): 102862306a36Sopenharmony_ci retval = snd_compr_next_track(stream); 102962306a36Sopenharmony_ci break; 103062306a36Sopenharmony_ci 103162306a36Sopenharmony_ci } 103262306a36Sopenharmony_ci mutex_unlock(&stream->device->lock); 103362306a36Sopenharmony_ci return retval; 103462306a36Sopenharmony_ci} 103562306a36Sopenharmony_ci 103662306a36Sopenharmony_ci/* support of 32bit userspace on 64bit platforms */ 103762306a36Sopenharmony_ci#ifdef CONFIG_COMPAT 103862306a36Sopenharmony_cistatic long snd_compr_ioctl_compat(struct file *file, unsigned int cmd, 103962306a36Sopenharmony_ci unsigned long arg) 104062306a36Sopenharmony_ci{ 104162306a36Sopenharmony_ci return snd_compr_ioctl(file, cmd, (unsigned long)compat_ptr(arg)); 104262306a36Sopenharmony_ci} 104362306a36Sopenharmony_ci#endif 104462306a36Sopenharmony_ci 104562306a36Sopenharmony_cistatic const struct file_operations snd_compr_file_ops = { 104662306a36Sopenharmony_ci .owner = THIS_MODULE, 104762306a36Sopenharmony_ci .open = snd_compr_open, 104862306a36Sopenharmony_ci .release = snd_compr_free, 104962306a36Sopenharmony_ci .write = snd_compr_write, 105062306a36Sopenharmony_ci .read = snd_compr_read, 105162306a36Sopenharmony_ci .unlocked_ioctl = snd_compr_ioctl, 105262306a36Sopenharmony_ci#ifdef CONFIG_COMPAT 105362306a36Sopenharmony_ci .compat_ioctl = snd_compr_ioctl_compat, 105462306a36Sopenharmony_ci#endif 105562306a36Sopenharmony_ci .mmap = snd_compr_mmap, 105662306a36Sopenharmony_ci .poll = snd_compr_poll, 105762306a36Sopenharmony_ci}; 105862306a36Sopenharmony_ci 105962306a36Sopenharmony_cistatic int snd_compress_dev_register(struct snd_device *device) 106062306a36Sopenharmony_ci{ 106162306a36Sopenharmony_ci int ret; 106262306a36Sopenharmony_ci struct snd_compr *compr; 106362306a36Sopenharmony_ci 106462306a36Sopenharmony_ci if (snd_BUG_ON(!device || !device->device_data)) 106562306a36Sopenharmony_ci return -EBADFD; 106662306a36Sopenharmony_ci compr = device->device_data; 106762306a36Sopenharmony_ci 106862306a36Sopenharmony_ci pr_debug("reg device %s, direction %d\n", compr->name, 106962306a36Sopenharmony_ci compr->direction); 107062306a36Sopenharmony_ci /* register compressed device */ 107162306a36Sopenharmony_ci ret = snd_register_device(SNDRV_DEVICE_TYPE_COMPRESS, 107262306a36Sopenharmony_ci compr->card, compr->device, 107362306a36Sopenharmony_ci &snd_compr_file_ops, compr, compr->dev); 107462306a36Sopenharmony_ci if (ret < 0) { 107562306a36Sopenharmony_ci pr_err("snd_register_device failed %d\n", ret); 107662306a36Sopenharmony_ci return ret; 107762306a36Sopenharmony_ci } 107862306a36Sopenharmony_ci return ret; 107962306a36Sopenharmony_ci 108062306a36Sopenharmony_ci} 108162306a36Sopenharmony_ci 108262306a36Sopenharmony_cistatic int snd_compress_dev_disconnect(struct snd_device *device) 108362306a36Sopenharmony_ci{ 108462306a36Sopenharmony_ci struct snd_compr *compr; 108562306a36Sopenharmony_ci 108662306a36Sopenharmony_ci compr = device->device_data; 108762306a36Sopenharmony_ci snd_unregister_device(compr->dev); 108862306a36Sopenharmony_ci return 0; 108962306a36Sopenharmony_ci} 109062306a36Sopenharmony_ci 109162306a36Sopenharmony_ci#ifdef CONFIG_SND_VERBOSE_PROCFS 109262306a36Sopenharmony_cistatic void snd_compress_proc_info_read(struct snd_info_entry *entry, 109362306a36Sopenharmony_ci struct snd_info_buffer *buffer) 109462306a36Sopenharmony_ci{ 109562306a36Sopenharmony_ci struct snd_compr *compr = (struct snd_compr *)entry->private_data; 109662306a36Sopenharmony_ci 109762306a36Sopenharmony_ci snd_iprintf(buffer, "card: %d\n", compr->card->number); 109862306a36Sopenharmony_ci snd_iprintf(buffer, "device: %d\n", compr->device); 109962306a36Sopenharmony_ci snd_iprintf(buffer, "stream: %s\n", 110062306a36Sopenharmony_ci compr->direction == SND_COMPRESS_PLAYBACK 110162306a36Sopenharmony_ci ? "PLAYBACK" : "CAPTURE"); 110262306a36Sopenharmony_ci snd_iprintf(buffer, "id: %s\n", compr->id); 110362306a36Sopenharmony_ci} 110462306a36Sopenharmony_ci 110562306a36Sopenharmony_cistatic int snd_compress_proc_init(struct snd_compr *compr) 110662306a36Sopenharmony_ci{ 110762306a36Sopenharmony_ci struct snd_info_entry *entry; 110862306a36Sopenharmony_ci char name[16]; 110962306a36Sopenharmony_ci 111062306a36Sopenharmony_ci sprintf(name, "compr%i", compr->device); 111162306a36Sopenharmony_ci entry = snd_info_create_card_entry(compr->card, name, 111262306a36Sopenharmony_ci compr->card->proc_root); 111362306a36Sopenharmony_ci if (!entry) 111462306a36Sopenharmony_ci return -ENOMEM; 111562306a36Sopenharmony_ci entry->mode = S_IFDIR | 0555; 111662306a36Sopenharmony_ci compr->proc_root = entry; 111762306a36Sopenharmony_ci 111862306a36Sopenharmony_ci entry = snd_info_create_card_entry(compr->card, "info", 111962306a36Sopenharmony_ci compr->proc_root); 112062306a36Sopenharmony_ci if (entry) 112162306a36Sopenharmony_ci snd_info_set_text_ops(entry, compr, 112262306a36Sopenharmony_ci snd_compress_proc_info_read); 112362306a36Sopenharmony_ci compr->proc_info_entry = entry; 112462306a36Sopenharmony_ci 112562306a36Sopenharmony_ci return 0; 112662306a36Sopenharmony_ci} 112762306a36Sopenharmony_ci 112862306a36Sopenharmony_cistatic void snd_compress_proc_done(struct snd_compr *compr) 112962306a36Sopenharmony_ci{ 113062306a36Sopenharmony_ci snd_info_free_entry(compr->proc_info_entry); 113162306a36Sopenharmony_ci compr->proc_info_entry = NULL; 113262306a36Sopenharmony_ci snd_info_free_entry(compr->proc_root); 113362306a36Sopenharmony_ci compr->proc_root = NULL; 113462306a36Sopenharmony_ci} 113562306a36Sopenharmony_ci 113662306a36Sopenharmony_cistatic inline void snd_compress_set_id(struct snd_compr *compr, const char *id) 113762306a36Sopenharmony_ci{ 113862306a36Sopenharmony_ci strscpy(compr->id, id, sizeof(compr->id)); 113962306a36Sopenharmony_ci} 114062306a36Sopenharmony_ci#else 114162306a36Sopenharmony_cistatic inline int snd_compress_proc_init(struct snd_compr *compr) 114262306a36Sopenharmony_ci{ 114362306a36Sopenharmony_ci return 0; 114462306a36Sopenharmony_ci} 114562306a36Sopenharmony_ci 114662306a36Sopenharmony_cistatic inline void snd_compress_proc_done(struct snd_compr *compr) 114762306a36Sopenharmony_ci{ 114862306a36Sopenharmony_ci} 114962306a36Sopenharmony_ci 115062306a36Sopenharmony_cistatic inline void snd_compress_set_id(struct snd_compr *compr, const char *id) 115162306a36Sopenharmony_ci{ 115262306a36Sopenharmony_ci} 115362306a36Sopenharmony_ci#endif 115462306a36Sopenharmony_ci 115562306a36Sopenharmony_cistatic int snd_compress_dev_free(struct snd_device *device) 115662306a36Sopenharmony_ci{ 115762306a36Sopenharmony_ci struct snd_compr *compr; 115862306a36Sopenharmony_ci 115962306a36Sopenharmony_ci compr = device->device_data; 116062306a36Sopenharmony_ci snd_compress_proc_done(compr); 116162306a36Sopenharmony_ci put_device(compr->dev); 116262306a36Sopenharmony_ci return 0; 116362306a36Sopenharmony_ci} 116462306a36Sopenharmony_ci 116562306a36Sopenharmony_ci/** 116662306a36Sopenharmony_ci * snd_compress_new: create new compress device 116762306a36Sopenharmony_ci * @card: sound card pointer 116862306a36Sopenharmony_ci * @device: device number 116962306a36Sopenharmony_ci * @dirn: device direction, should be of type enum snd_compr_direction 117062306a36Sopenharmony_ci * @id: ID string 117162306a36Sopenharmony_ci * @compr: compress device pointer 117262306a36Sopenharmony_ci * 117362306a36Sopenharmony_ci * Return: zero if successful, or a negative error code 117462306a36Sopenharmony_ci */ 117562306a36Sopenharmony_ciint snd_compress_new(struct snd_card *card, int device, 117662306a36Sopenharmony_ci int dirn, const char *id, struct snd_compr *compr) 117762306a36Sopenharmony_ci{ 117862306a36Sopenharmony_ci static const struct snd_device_ops ops = { 117962306a36Sopenharmony_ci .dev_free = snd_compress_dev_free, 118062306a36Sopenharmony_ci .dev_register = snd_compress_dev_register, 118162306a36Sopenharmony_ci .dev_disconnect = snd_compress_dev_disconnect, 118262306a36Sopenharmony_ci }; 118362306a36Sopenharmony_ci int ret; 118462306a36Sopenharmony_ci 118562306a36Sopenharmony_ci compr->card = card; 118662306a36Sopenharmony_ci compr->device = device; 118762306a36Sopenharmony_ci compr->direction = dirn; 118862306a36Sopenharmony_ci mutex_init(&compr->lock); 118962306a36Sopenharmony_ci 119062306a36Sopenharmony_ci snd_compress_set_id(compr, id); 119162306a36Sopenharmony_ci 119262306a36Sopenharmony_ci ret = snd_device_alloc(&compr->dev, card); 119362306a36Sopenharmony_ci if (ret) 119462306a36Sopenharmony_ci return ret; 119562306a36Sopenharmony_ci dev_set_name(compr->dev, "comprC%iD%i", card->number, device); 119662306a36Sopenharmony_ci 119762306a36Sopenharmony_ci ret = snd_device_new(card, SNDRV_DEV_COMPRESS, compr, &ops); 119862306a36Sopenharmony_ci if (ret == 0) 119962306a36Sopenharmony_ci snd_compress_proc_init(compr); 120062306a36Sopenharmony_ci else 120162306a36Sopenharmony_ci put_device(compr->dev); 120262306a36Sopenharmony_ci 120362306a36Sopenharmony_ci return ret; 120462306a36Sopenharmony_ci} 120562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_compress_new); 120662306a36Sopenharmony_ci 120762306a36Sopenharmony_ciMODULE_DESCRIPTION("ALSA Compressed offload framework"); 120862306a36Sopenharmony_ciMODULE_AUTHOR("Vinod Koul <vinod.koul@linux.intel.com>"); 120962306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 1210