18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * compress_core.c - compress offload core 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2011 Intel Corporation 68c2ecf20Sopenharmony_ci * Authors: Vinod Koul <vinod.koul@linux.intel.com> 78c2ecf20Sopenharmony_ci * Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com> 88c2ecf20Sopenharmony_ci * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 118c2ecf20Sopenharmony_ci */ 128c2ecf20Sopenharmony_ci#define FORMAT(fmt) "%s: %d: " fmt, __func__, __LINE__ 138c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " FORMAT(fmt) 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#include <linux/file.h> 168c2ecf20Sopenharmony_ci#include <linux/fs.h> 178c2ecf20Sopenharmony_ci#include <linux/list.h> 188c2ecf20Sopenharmony_ci#include <linux/math64.h> 198c2ecf20Sopenharmony_ci#include <linux/mm.h> 208c2ecf20Sopenharmony_ci#include <linux/mutex.h> 218c2ecf20Sopenharmony_ci#include <linux/poll.h> 228c2ecf20Sopenharmony_ci#include <linux/slab.h> 238c2ecf20Sopenharmony_ci#include <linux/sched.h> 248c2ecf20Sopenharmony_ci#include <linux/types.h> 258c2ecf20Sopenharmony_ci#include <linux/uio.h> 268c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 278c2ecf20Sopenharmony_ci#include <linux/module.h> 288c2ecf20Sopenharmony_ci#include <linux/compat.h> 298c2ecf20Sopenharmony_ci#include <sound/core.h> 308c2ecf20Sopenharmony_ci#include <sound/initval.h> 318c2ecf20Sopenharmony_ci#include <sound/info.h> 328c2ecf20Sopenharmony_ci#include <sound/compress_params.h> 338c2ecf20Sopenharmony_ci#include <sound/compress_offload.h> 348c2ecf20Sopenharmony_ci#include <sound/compress_driver.h> 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci/* struct snd_compr_codec_caps overflows the ioctl bit size for some 378c2ecf20Sopenharmony_ci * architectures, so we need to disable the relevant ioctls. 388c2ecf20Sopenharmony_ci */ 398c2ecf20Sopenharmony_ci#if _IOC_SIZEBITS < 14 408c2ecf20Sopenharmony_ci#define COMPR_CODEC_CAPS_OVERFLOW 418c2ecf20Sopenharmony_ci#endif 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci/* TODO: 448c2ecf20Sopenharmony_ci * - add substream support for multiple devices in case of 458c2ecf20Sopenharmony_ci * SND_DYNAMIC_MINORS is not used 468c2ecf20Sopenharmony_ci * - Multiple node representation 478c2ecf20Sopenharmony_ci * driver should be able to register multiple nodes 488c2ecf20Sopenharmony_ci */ 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(device_mutex); 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_cistruct snd_compr_file { 538c2ecf20Sopenharmony_ci unsigned long caps; 548c2ecf20Sopenharmony_ci struct snd_compr_stream stream; 558c2ecf20Sopenharmony_ci}; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_cistatic void error_delayed_work(struct work_struct *work); 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci/* 608c2ecf20Sopenharmony_ci * a note on stream states used: 618c2ecf20Sopenharmony_ci * we use following states in the compressed core 628c2ecf20Sopenharmony_ci * SNDRV_PCM_STATE_OPEN: When stream has been opened. 638c2ecf20Sopenharmony_ci * SNDRV_PCM_STATE_SETUP: When stream has been initialized. This is done by 648c2ecf20Sopenharmony_ci * calling SNDRV_COMPRESS_SET_PARAMS. Running streams will come to this 658c2ecf20Sopenharmony_ci * state at stop by calling SNDRV_COMPRESS_STOP, or at end of drain. 668c2ecf20Sopenharmony_ci * SNDRV_PCM_STATE_PREPARED: When a stream has been written to (for 678c2ecf20Sopenharmony_ci * playback only). User after setting up stream writes the data buffer 688c2ecf20Sopenharmony_ci * before starting the stream. 698c2ecf20Sopenharmony_ci * SNDRV_PCM_STATE_RUNNING: When stream has been started and is 708c2ecf20Sopenharmony_ci * decoding/encoding and rendering/capturing data. 718c2ecf20Sopenharmony_ci * SNDRV_PCM_STATE_DRAINING: When stream is draining current data. This is done 728c2ecf20Sopenharmony_ci * by calling SNDRV_COMPRESS_DRAIN. 738c2ecf20Sopenharmony_ci * SNDRV_PCM_STATE_PAUSED: When stream is paused. This is done by calling 748c2ecf20Sopenharmony_ci * SNDRV_COMPRESS_PAUSE. It can be stopped or resumed by calling 758c2ecf20Sopenharmony_ci * SNDRV_COMPRESS_STOP or SNDRV_COMPRESS_RESUME respectively. 768c2ecf20Sopenharmony_ci */ 778c2ecf20Sopenharmony_cistatic int snd_compr_open(struct inode *inode, struct file *f) 788c2ecf20Sopenharmony_ci{ 798c2ecf20Sopenharmony_ci struct snd_compr *compr; 808c2ecf20Sopenharmony_ci struct snd_compr_file *data; 818c2ecf20Sopenharmony_ci struct snd_compr_runtime *runtime; 828c2ecf20Sopenharmony_ci enum snd_compr_direction dirn; 838c2ecf20Sopenharmony_ci int maj = imajor(inode); 848c2ecf20Sopenharmony_ci int ret; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci if ((f->f_flags & O_ACCMODE) == O_WRONLY) 878c2ecf20Sopenharmony_ci dirn = SND_COMPRESS_PLAYBACK; 888c2ecf20Sopenharmony_ci else if ((f->f_flags & O_ACCMODE) == O_RDONLY) 898c2ecf20Sopenharmony_ci dirn = SND_COMPRESS_CAPTURE; 908c2ecf20Sopenharmony_ci else 918c2ecf20Sopenharmony_ci return -EINVAL; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci if (maj == snd_major) 948c2ecf20Sopenharmony_ci compr = snd_lookup_minor_data(iminor(inode), 958c2ecf20Sopenharmony_ci SNDRV_DEVICE_TYPE_COMPRESS); 968c2ecf20Sopenharmony_ci else 978c2ecf20Sopenharmony_ci return -EBADFD; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci if (compr == NULL) { 1008c2ecf20Sopenharmony_ci pr_err("no device data!!!\n"); 1018c2ecf20Sopenharmony_ci return -ENODEV; 1028c2ecf20Sopenharmony_ci } 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci if (dirn != compr->direction) { 1058c2ecf20Sopenharmony_ci pr_err("this device doesn't support this direction\n"); 1068c2ecf20Sopenharmony_ci snd_card_unref(compr->card); 1078c2ecf20Sopenharmony_ci return -EINVAL; 1088c2ecf20Sopenharmony_ci } 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci data = kzalloc(sizeof(*data), GFP_KERNEL); 1118c2ecf20Sopenharmony_ci if (!data) { 1128c2ecf20Sopenharmony_ci snd_card_unref(compr->card); 1138c2ecf20Sopenharmony_ci return -ENOMEM; 1148c2ecf20Sopenharmony_ci } 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci INIT_DELAYED_WORK(&data->stream.error_work, error_delayed_work); 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci data->stream.ops = compr->ops; 1198c2ecf20Sopenharmony_ci data->stream.direction = dirn; 1208c2ecf20Sopenharmony_ci data->stream.private_data = compr->private_data; 1218c2ecf20Sopenharmony_ci data->stream.device = compr; 1228c2ecf20Sopenharmony_ci runtime = kzalloc(sizeof(*runtime), GFP_KERNEL); 1238c2ecf20Sopenharmony_ci if (!runtime) { 1248c2ecf20Sopenharmony_ci kfree(data); 1258c2ecf20Sopenharmony_ci snd_card_unref(compr->card); 1268c2ecf20Sopenharmony_ci return -ENOMEM; 1278c2ecf20Sopenharmony_ci } 1288c2ecf20Sopenharmony_ci runtime->state = SNDRV_PCM_STATE_OPEN; 1298c2ecf20Sopenharmony_ci init_waitqueue_head(&runtime->sleep); 1308c2ecf20Sopenharmony_ci data->stream.runtime = runtime; 1318c2ecf20Sopenharmony_ci f->private_data = (void *)data; 1328c2ecf20Sopenharmony_ci mutex_lock(&compr->lock); 1338c2ecf20Sopenharmony_ci ret = compr->ops->open(&data->stream); 1348c2ecf20Sopenharmony_ci mutex_unlock(&compr->lock); 1358c2ecf20Sopenharmony_ci if (ret) { 1368c2ecf20Sopenharmony_ci kfree(runtime); 1378c2ecf20Sopenharmony_ci kfree(data); 1388c2ecf20Sopenharmony_ci } 1398c2ecf20Sopenharmony_ci snd_card_unref(compr->card); 1408c2ecf20Sopenharmony_ci return ret; 1418c2ecf20Sopenharmony_ci} 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_cistatic int snd_compr_free(struct inode *inode, struct file *f) 1448c2ecf20Sopenharmony_ci{ 1458c2ecf20Sopenharmony_ci struct snd_compr_file *data = f->private_data; 1468c2ecf20Sopenharmony_ci struct snd_compr_runtime *runtime = data->stream.runtime; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&data->stream.error_work); 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci switch (runtime->state) { 1518c2ecf20Sopenharmony_ci case SNDRV_PCM_STATE_RUNNING: 1528c2ecf20Sopenharmony_ci case SNDRV_PCM_STATE_DRAINING: 1538c2ecf20Sopenharmony_ci case SNDRV_PCM_STATE_PAUSED: 1548c2ecf20Sopenharmony_ci data->stream.ops->trigger(&data->stream, SNDRV_PCM_TRIGGER_STOP); 1558c2ecf20Sopenharmony_ci break; 1568c2ecf20Sopenharmony_ci default: 1578c2ecf20Sopenharmony_ci break; 1588c2ecf20Sopenharmony_ci } 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci data->stream.ops->free(&data->stream); 1618c2ecf20Sopenharmony_ci if (!data->stream.runtime->dma_buffer_p) 1628c2ecf20Sopenharmony_ci kfree(data->stream.runtime->buffer); 1638c2ecf20Sopenharmony_ci kfree(data->stream.runtime); 1648c2ecf20Sopenharmony_ci kfree(data); 1658c2ecf20Sopenharmony_ci return 0; 1668c2ecf20Sopenharmony_ci} 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_cistatic int snd_compr_update_tstamp(struct snd_compr_stream *stream, 1698c2ecf20Sopenharmony_ci struct snd_compr_tstamp *tstamp) 1708c2ecf20Sopenharmony_ci{ 1718c2ecf20Sopenharmony_ci if (!stream->ops->pointer) 1728c2ecf20Sopenharmony_ci return -ENOTSUPP; 1738c2ecf20Sopenharmony_ci stream->ops->pointer(stream, tstamp); 1748c2ecf20Sopenharmony_ci pr_debug("dsp consumed till %d total %d bytes\n", 1758c2ecf20Sopenharmony_ci tstamp->byte_offset, tstamp->copied_total); 1768c2ecf20Sopenharmony_ci if (stream->direction == SND_COMPRESS_PLAYBACK) 1778c2ecf20Sopenharmony_ci stream->runtime->total_bytes_transferred = tstamp->copied_total; 1788c2ecf20Sopenharmony_ci else 1798c2ecf20Sopenharmony_ci stream->runtime->total_bytes_available = tstamp->copied_total; 1808c2ecf20Sopenharmony_ci return 0; 1818c2ecf20Sopenharmony_ci} 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_cistatic size_t snd_compr_calc_avail(struct snd_compr_stream *stream, 1848c2ecf20Sopenharmony_ci struct snd_compr_avail *avail) 1858c2ecf20Sopenharmony_ci{ 1868c2ecf20Sopenharmony_ci memset(avail, 0, sizeof(*avail)); 1878c2ecf20Sopenharmony_ci snd_compr_update_tstamp(stream, &avail->tstamp); 1888c2ecf20Sopenharmony_ci /* Still need to return avail even if tstamp can't be filled in */ 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci if (stream->runtime->total_bytes_available == 0 && 1918c2ecf20Sopenharmony_ci stream->runtime->state == SNDRV_PCM_STATE_SETUP && 1928c2ecf20Sopenharmony_ci stream->direction == SND_COMPRESS_PLAYBACK) { 1938c2ecf20Sopenharmony_ci pr_debug("detected init and someone forgot to do a write\n"); 1948c2ecf20Sopenharmony_ci return stream->runtime->buffer_size; 1958c2ecf20Sopenharmony_ci } 1968c2ecf20Sopenharmony_ci pr_debug("app wrote %lld, DSP consumed %lld\n", 1978c2ecf20Sopenharmony_ci stream->runtime->total_bytes_available, 1988c2ecf20Sopenharmony_ci stream->runtime->total_bytes_transferred); 1998c2ecf20Sopenharmony_ci if (stream->runtime->total_bytes_available == 2008c2ecf20Sopenharmony_ci stream->runtime->total_bytes_transferred) { 2018c2ecf20Sopenharmony_ci if (stream->direction == SND_COMPRESS_PLAYBACK) { 2028c2ecf20Sopenharmony_ci pr_debug("both pointers are same, returning full avail\n"); 2038c2ecf20Sopenharmony_ci return stream->runtime->buffer_size; 2048c2ecf20Sopenharmony_ci } else { 2058c2ecf20Sopenharmony_ci pr_debug("both pointers are same, returning no avail\n"); 2068c2ecf20Sopenharmony_ci return 0; 2078c2ecf20Sopenharmony_ci } 2088c2ecf20Sopenharmony_ci } 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci avail->avail = stream->runtime->total_bytes_available - 2118c2ecf20Sopenharmony_ci stream->runtime->total_bytes_transferred; 2128c2ecf20Sopenharmony_ci if (stream->direction == SND_COMPRESS_PLAYBACK) 2138c2ecf20Sopenharmony_ci avail->avail = stream->runtime->buffer_size - avail->avail; 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci pr_debug("ret avail as %lld\n", avail->avail); 2168c2ecf20Sopenharmony_ci return avail->avail; 2178c2ecf20Sopenharmony_ci} 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_cistatic inline size_t snd_compr_get_avail(struct snd_compr_stream *stream) 2208c2ecf20Sopenharmony_ci{ 2218c2ecf20Sopenharmony_ci struct snd_compr_avail avail; 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci return snd_compr_calc_avail(stream, &avail); 2248c2ecf20Sopenharmony_ci} 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_cistatic int 2278c2ecf20Sopenharmony_cisnd_compr_ioctl_avail(struct snd_compr_stream *stream, unsigned long arg) 2288c2ecf20Sopenharmony_ci{ 2298c2ecf20Sopenharmony_ci struct snd_compr_avail ioctl_avail; 2308c2ecf20Sopenharmony_ci size_t avail; 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci avail = snd_compr_calc_avail(stream, &ioctl_avail); 2338c2ecf20Sopenharmony_ci ioctl_avail.avail = avail; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci switch (stream->runtime->state) { 2368c2ecf20Sopenharmony_ci case SNDRV_PCM_STATE_OPEN: 2378c2ecf20Sopenharmony_ci return -EBADFD; 2388c2ecf20Sopenharmony_ci case SNDRV_PCM_STATE_XRUN: 2398c2ecf20Sopenharmony_ci return -EPIPE; 2408c2ecf20Sopenharmony_ci default: 2418c2ecf20Sopenharmony_ci break; 2428c2ecf20Sopenharmony_ci } 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci if (copy_to_user((__u64 __user *)arg, 2458c2ecf20Sopenharmony_ci &ioctl_avail, sizeof(ioctl_avail))) 2468c2ecf20Sopenharmony_ci return -EFAULT; 2478c2ecf20Sopenharmony_ci return 0; 2488c2ecf20Sopenharmony_ci} 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_cistatic int snd_compr_write_data(struct snd_compr_stream *stream, 2518c2ecf20Sopenharmony_ci const char __user *buf, size_t count) 2528c2ecf20Sopenharmony_ci{ 2538c2ecf20Sopenharmony_ci void *dstn; 2548c2ecf20Sopenharmony_ci size_t copy; 2558c2ecf20Sopenharmony_ci struct snd_compr_runtime *runtime = stream->runtime; 2568c2ecf20Sopenharmony_ci /* 64-bit Modulus */ 2578c2ecf20Sopenharmony_ci u64 app_pointer = div64_u64(runtime->total_bytes_available, 2588c2ecf20Sopenharmony_ci runtime->buffer_size); 2598c2ecf20Sopenharmony_ci app_pointer = runtime->total_bytes_available - 2608c2ecf20Sopenharmony_ci (app_pointer * runtime->buffer_size); 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci dstn = runtime->buffer + app_pointer; 2638c2ecf20Sopenharmony_ci pr_debug("copying %ld at %lld\n", 2648c2ecf20Sopenharmony_ci (unsigned long)count, app_pointer); 2658c2ecf20Sopenharmony_ci if (count < runtime->buffer_size - app_pointer) { 2668c2ecf20Sopenharmony_ci if (copy_from_user(dstn, buf, count)) 2678c2ecf20Sopenharmony_ci return -EFAULT; 2688c2ecf20Sopenharmony_ci } else { 2698c2ecf20Sopenharmony_ci copy = runtime->buffer_size - app_pointer; 2708c2ecf20Sopenharmony_ci if (copy_from_user(dstn, buf, copy)) 2718c2ecf20Sopenharmony_ci return -EFAULT; 2728c2ecf20Sopenharmony_ci if (copy_from_user(runtime->buffer, buf + copy, count - copy)) 2738c2ecf20Sopenharmony_ci return -EFAULT; 2748c2ecf20Sopenharmony_ci } 2758c2ecf20Sopenharmony_ci /* if DSP cares, let it know data has been written */ 2768c2ecf20Sopenharmony_ci if (stream->ops->ack) 2778c2ecf20Sopenharmony_ci stream->ops->ack(stream, count); 2788c2ecf20Sopenharmony_ci return count; 2798c2ecf20Sopenharmony_ci} 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_cistatic ssize_t snd_compr_write(struct file *f, const char __user *buf, 2828c2ecf20Sopenharmony_ci size_t count, loff_t *offset) 2838c2ecf20Sopenharmony_ci{ 2848c2ecf20Sopenharmony_ci struct snd_compr_file *data = f->private_data; 2858c2ecf20Sopenharmony_ci struct snd_compr_stream *stream; 2868c2ecf20Sopenharmony_ci size_t avail; 2878c2ecf20Sopenharmony_ci int retval; 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci if (snd_BUG_ON(!data)) 2908c2ecf20Sopenharmony_ci return -EFAULT; 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci stream = &data->stream; 2938c2ecf20Sopenharmony_ci mutex_lock(&stream->device->lock); 2948c2ecf20Sopenharmony_ci /* write is allowed when stream is running or has been steup */ 2958c2ecf20Sopenharmony_ci switch (stream->runtime->state) { 2968c2ecf20Sopenharmony_ci case SNDRV_PCM_STATE_SETUP: 2978c2ecf20Sopenharmony_ci case SNDRV_PCM_STATE_PREPARED: 2988c2ecf20Sopenharmony_ci case SNDRV_PCM_STATE_RUNNING: 2998c2ecf20Sopenharmony_ci break; 3008c2ecf20Sopenharmony_ci default: 3018c2ecf20Sopenharmony_ci mutex_unlock(&stream->device->lock); 3028c2ecf20Sopenharmony_ci return -EBADFD; 3038c2ecf20Sopenharmony_ci } 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci avail = snd_compr_get_avail(stream); 3068c2ecf20Sopenharmony_ci pr_debug("avail returned %ld\n", (unsigned long)avail); 3078c2ecf20Sopenharmony_ci /* calculate how much we can write to buffer */ 3088c2ecf20Sopenharmony_ci if (avail > count) 3098c2ecf20Sopenharmony_ci avail = count; 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci if (stream->ops->copy) { 3128c2ecf20Sopenharmony_ci char __user* cbuf = (char __user*)buf; 3138c2ecf20Sopenharmony_ci retval = stream->ops->copy(stream, cbuf, avail); 3148c2ecf20Sopenharmony_ci } else { 3158c2ecf20Sopenharmony_ci retval = snd_compr_write_data(stream, buf, avail); 3168c2ecf20Sopenharmony_ci } 3178c2ecf20Sopenharmony_ci if (retval > 0) 3188c2ecf20Sopenharmony_ci stream->runtime->total_bytes_available += retval; 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci /* while initiating the stream, write should be called before START 3218c2ecf20Sopenharmony_ci * call, so in setup move state */ 3228c2ecf20Sopenharmony_ci if (stream->runtime->state == SNDRV_PCM_STATE_SETUP) { 3238c2ecf20Sopenharmony_ci stream->runtime->state = SNDRV_PCM_STATE_PREPARED; 3248c2ecf20Sopenharmony_ci pr_debug("stream prepared, Houston we are good to go\n"); 3258c2ecf20Sopenharmony_ci } 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci mutex_unlock(&stream->device->lock); 3288c2ecf20Sopenharmony_ci return retval; 3298c2ecf20Sopenharmony_ci} 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_cistatic ssize_t snd_compr_read(struct file *f, char __user *buf, 3338c2ecf20Sopenharmony_ci size_t count, loff_t *offset) 3348c2ecf20Sopenharmony_ci{ 3358c2ecf20Sopenharmony_ci struct snd_compr_file *data = f->private_data; 3368c2ecf20Sopenharmony_ci struct snd_compr_stream *stream; 3378c2ecf20Sopenharmony_ci size_t avail; 3388c2ecf20Sopenharmony_ci int retval; 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci if (snd_BUG_ON(!data)) 3418c2ecf20Sopenharmony_ci return -EFAULT; 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci stream = &data->stream; 3448c2ecf20Sopenharmony_ci mutex_lock(&stream->device->lock); 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci /* read is allowed when stream is running, paused, draining and setup 3478c2ecf20Sopenharmony_ci * (yes setup is state which we transition to after stop, so if user 3488c2ecf20Sopenharmony_ci * wants to read data after stop we allow that) 3498c2ecf20Sopenharmony_ci */ 3508c2ecf20Sopenharmony_ci switch (stream->runtime->state) { 3518c2ecf20Sopenharmony_ci case SNDRV_PCM_STATE_OPEN: 3528c2ecf20Sopenharmony_ci case SNDRV_PCM_STATE_PREPARED: 3538c2ecf20Sopenharmony_ci case SNDRV_PCM_STATE_SUSPENDED: 3548c2ecf20Sopenharmony_ci case SNDRV_PCM_STATE_DISCONNECTED: 3558c2ecf20Sopenharmony_ci retval = -EBADFD; 3568c2ecf20Sopenharmony_ci goto out; 3578c2ecf20Sopenharmony_ci case SNDRV_PCM_STATE_XRUN: 3588c2ecf20Sopenharmony_ci retval = -EPIPE; 3598c2ecf20Sopenharmony_ci goto out; 3608c2ecf20Sopenharmony_ci } 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci avail = snd_compr_get_avail(stream); 3638c2ecf20Sopenharmony_ci pr_debug("avail returned %ld\n", (unsigned long)avail); 3648c2ecf20Sopenharmony_ci /* calculate how much we can read from buffer */ 3658c2ecf20Sopenharmony_ci if (avail > count) 3668c2ecf20Sopenharmony_ci avail = count; 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci if (stream->ops->copy) { 3698c2ecf20Sopenharmony_ci retval = stream->ops->copy(stream, buf, avail); 3708c2ecf20Sopenharmony_ci } else { 3718c2ecf20Sopenharmony_ci retval = -ENXIO; 3728c2ecf20Sopenharmony_ci goto out; 3738c2ecf20Sopenharmony_ci } 3748c2ecf20Sopenharmony_ci if (retval > 0) 3758c2ecf20Sopenharmony_ci stream->runtime->total_bytes_transferred += retval; 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ciout: 3788c2ecf20Sopenharmony_ci mutex_unlock(&stream->device->lock); 3798c2ecf20Sopenharmony_ci return retval; 3808c2ecf20Sopenharmony_ci} 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_cistatic int snd_compr_mmap(struct file *f, struct vm_area_struct *vma) 3838c2ecf20Sopenharmony_ci{ 3848c2ecf20Sopenharmony_ci return -ENXIO; 3858c2ecf20Sopenharmony_ci} 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_cistatic __poll_t snd_compr_get_poll(struct snd_compr_stream *stream) 3888c2ecf20Sopenharmony_ci{ 3898c2ecf20Sopenharmony_ci if (stream->direction == SND_COMPRESS_PLAYBACK) 3908c2ecf20Sopenharmony_ci return EPOLLOUT | EPOLLWRNORM; 3918c2ecf20Sopenharmony_ci else 3928c2ecf20Sopenharmony_ci return EPOLLIN | EPOLLRDNORM; 3938c2ecf20Sopenharmony_ci} 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_cistatic __poll_t snd_compr_poll(struct file *f, poll_table *wait) 3968c2ecf20Sopenharmony_ci{ 3978c2ecf20Sopenharmony_ci struct snd_compr_file *data = f->private_data; 3988c2ecf20Sopenharmony_ci struct snd_compr_stream *stream; 3998c2ecf20Sopenharmony_ci size_t avail; 4008c2ecf20Sopenharmony_ci __poll_t retval = 0; 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci if (snd_BUG_ON(!data)) 4038c2ecf20Sopenharmony_ci return EPOLLERR; 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci stream = &data->stream; 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci mutex_lock(&stream->device->lock); 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci switch (stream->runtime->state) { 4108c2ecf20Sopenharmony_ci case SNDRV_PCM_STATE_OPEN: 4118c2ecf20Sopenharmony_ci case SNDRV_PCM_STATE_XRUN: 4128c2ecf20Sopenharmony_ci retval = snd_compr_get_poll(stream) | EPOLLERR; 4138c2ecf20Sopenharmony_ci goto out; 4148c2ecf20Sopenharmony_ci default: 4158c2ecf20Sopenharmony_ci break; 4168c2ecf20Sopenharmony_ci } 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci poll_wait(f, &stream->runtime->sleep, wait); 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci avail = snd_compr_get_avail(stream); 4218c2ecf20Sopenharmony_ci pr_debug("avail is %ld\n", (unsigned long)avail); 4228c2ecf20Sopenharmony_ci /* check if we have at least one fragment to fill */ 4238c2ecf20Sopenharmony_ci switch (stream->runtime->state) { 4248c2ecf20Sopenharmony_ci case SNDRV_PCM_STATE_DRAINING: 4258c2ecf20Sopenharmony_ci /* stream has been woken up after drain is complete 4268c2ecf20Sopenharmony_ci * draining done so set stream state to stopped 4278c2ecf20Sopenharmony_ci */ 4288c2ecf20Sopenharmony_ci retval = snd_compr_get_poll(stream); 4298c2ecf20Sopenharmony_ci stream->runtime->state = SNDRV_PCM_STATE_SETUP; 4308c2ecf20Sopenharmony_ci break; 4318c2ecf20Sopenharmony_ci case SNDRV_PCM_STATE_RUNNING: 4328c2ecf20Sopenharmony_ci case SNDRV_PCM_STATE_PREPARED: 4338c2ecf20Sopenharmony_ci case SNDRV_PCM_STATE_PAUSED: 4348c2ecf20Sopenharmony_ci if (avail >= stream->runtime->fragment_size) 4358c2ecf20Sopenharmony_ci retval = snd_compr_get_poll(stream); 4368c2ecf20Sopenharmony_ci break; 4378c2ecf20Sopenharmony_ci default: 4388c2ecf20Sopenharmony_ci retval = snd_compr_get_poll(stream) | EPOLLERR; 4398c2ecf20Sopenharmony_ci break; 4408c2ecf20Sopenharmony_ci } 4418c2ecf20Sopenharmony_ciout: 4428c2ecf20Sopenharmony_ci mutex_unlock(&stream->device->lock); 4438c2ecf20Sopenharmony_ci return retval; 4448c2ecf20Sopenharmony_ci} 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_cistatic int 4478c2ecf20Sopenharmony_cisnd_compr_get_caps(struct snd_compr_stream *stream, unsigned long arg) 4488c2ecf20Sopenharmony_ci{ 4498c2ecf20Sopenharmony_ci int retval; 4508c2ecf20Sopenharmony_ci struct snd_compr_caps caps; 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci if (!stream->ops->get_caps) 4538c2ecf20Sopenharmony_ci return -ENXIO; 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci memset(&caps, 0, sizeof(caps)); 4568c2ecf20Sopenharmony_ci retval = stream->ops->get_caps(stream, &caps); 4578c2ecf20Sopenharmony_ci if (retval) 4588c2ecf20Sopenharmony_ci goto out; 4598c2ecf20Sopenharmony_ci if (copy_to_user((void __user *)arg, &caps, sizeof(caps))) 4608c2ecf20Sopenharmony_ci retval = -EFAULT; 4618c2ecf20Sopenharmony_ciout: 4628c2ecf20Sopenharmony_ci return retval; 4638c2ecf20Sopenharmony_ci} 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci#ifndef COMPR_CODEC_CAPS_OVERFLOW 4668c2ecf20Sopenharmony_cistatic int 4678c2ecf20Sopenharmony_cisnd_compr_get_codec_caps(struct snd_compr_stream *stream, unsigned long arg) 4688c2ecf20Sopenharmony_ci{ 4698c2ecf20Sopenharmony_ci int retval; 4708c2ecf20Sopenharmony_ci struct snd_compr_codec_caps *caps; 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci if (!stream->ops->get_codec_caps) 4738c2ecf20Sopenharmony_ci return -ENXIO; 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci caps = kzalloc(sizeof(*caps), GFP_KERNEL); 4768c2ecf20Sopenharmony_ci if (!caps) 4778c2ecf20Sopenharmony_ci return -ENOMEM; 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci retval = stream->ops->get_codec_caps(stream, caps); 4808c2ecf20Sopenharmony_ci if (retval) 4818c2ecf20Sopenharmony_ci goto out; 4828c2ecf20Sopenharmony_ci if (copy_to_user((void __user *)arg, caps, sizeof(*caps))) 4838c2ecf20Sopenharmony_ci retval = -EFAULT; 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ciout: 4868c2ecf20Sopenharmony_ci kfree(caps); 4878c2ecf20Sopenharmony_ci return retval; 4888c2ecf20Sopenharmony_ci} 4898c2ecf20Sopenharmony_ci#endif /* !COMPR_CODEC_CAPS_OVERFLOW */ 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ciint snd_compr_malloc_pages(struct snd_compr_stream *stream, size_t size) 4928c2ecf20Sopenharmony_ci{ 4938c2ecf20Sopenharmony_ci struct snd_dma_buffer *dmab; 4948c2ecf20Sopenharmony_ci int ret; 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci if (snd_BUG_ON(!(stream) || !(stream)->runtime)) 4978c2ecf20Sopenharmony_ci return -EINVAL; 4988c2ecf20Sopenharmony_ci dmab = kzalloc(sizeof(*dmab), GFP_KERNEL); 4998c2ecf20Sopenharmony_ci if (!dmab) 5008c2ecf20Sopenharmony_ci return -ENOMEM; 5018c2ecf20Sopenharmony_ci dmab->dev = stream->dma_buffer.dev; 5028c2ecf20Sopenharmony_ci ret = snd_dma_alloc_pages(dmab->dev.type, dmab->dev.dev, size, dmab); 5038c2ecf20Sopenharmony_ci if (ret < 0) { 5048c2ecf20Sopenharmony_ci kfree(dmab); 5058c2ecf20Sopenharmony_ci return ret; 5068c2ecf20Sopenharmony_ci } 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci snd_compr_set_runtime_buffer(stream, dmab); 5098c2ecf20Sopenharmony_ci stream->runtime->dma_bytes = size; 5108c2ecf20Sopenharmony_ci return 1; 5118c2ecf20Sopenharmony_ci} 5128c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_compr_malloc_pages); 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ciint snd_compr_free_pages(struct snd_compr_stream *stream) 5158c2ecf20Sopenharmony_ci{ 5168c2ecf20Sopenharmony_ci struct snd_compr_runtime *runtime; 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci if (snd_BUG_ON(!(stream) || !(stream)->runtime)) 5198c2ecf20Sopenharmony_ci return -EINVAL; 5208c2ecf20Sopenharmony_ci runtime = stream->runtime; 5218c2ecf20Sopenharmony_ci if (runtime->dma_area == NULL) 5228c2ecf20Sopenharmony_ci return 0; 5238c2ecf20Sopenharmony_ci if (runtime->dma_buffer_p != &stream->dma_buffer) { 5248c2ecf20Sopenharmony_ci /* It's a newly allocated buffer. Release it now. */ 5258c2ecf20Sopenharmony_ci snd_dma_free_pages(runtime->dma_buffer_p); 5268c2ecf20Sopenharmony_ci kfree(runtime->dma_buffer_p); 5278c2ecf20Sopenharmony_ci } 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci snd_compr_set_runtime_buffer(stream, NULL); 5308c2ecf20Sopenharmony_ci return 0; 5318c2ecf20Sopenharmony_ci} 5328c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_compr_free_pages); 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci/* revisit this with snd_pcm_preallocate_xxx */ 5358c2ecf20Sopenharmony_cistatic int snd_compr_allocate_buffer(struct snd_compr_stream *stream, 5368c2ecf20Sopenharmony_ci struct snd_compr_params *params) 5378c2ecf20Sopenharmony_ci{ 5388c2ecf20Sopenharmony_ci unsigned int buffer_size; 5398c2ecf20Sopenharmony_ci void *buffer = NULL; 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_ci buffer_size = params->buffer.fragment_size * params->buffer.fragments; 5428c2ecf20Sopenharmony_ci if (stream->ops->copy) { 5438c2ecf20Sopenharmony_ci buffer = NULL; 5448c2ecf20Sopenharmony_ci /* if copy is defined the driver will be required to copy 5458c2ecf20Sopenharmony_ci * the data from core 5468c2ecf20Sopenharmony_ci */ 5478c2ecf20Sopenharmony_ci } else { 5488c2ecf20Sopenharmony_ci if (stream->runtime->dma_buffer_p) { 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci if (buffer_size > stream->runtime->dma_buffer_p->bytes) 5518c2ecf20Sopenharmony_ci dev_err(&stream->device->dev, 5528c2ecf20Sopenharmony_ci "Not enough DMA buffer"); 5538c2ecf20Sopenharmony_ci else 5548c2ecf20Sopenharmony_ci buffer = stream->runtime->dma_buffer_p->area; 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci } else { 5578c2ecf20Sopenharmony_ci buffer = kmalloc(buffer_size, GFP_KERNEL); 5588c2ecf20Sopenharmony_ci } 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ci if (!buffer) 5618c2ecf20Sopenharmony_ci return -ENOMEM; 5628c2ecf20Sopenharmony_ci } 5638c2ecf20Sopenharmony_ci stream->runtime->fragment_size = params->buffer.fragment_size; 5648c2ecf20Sopenharmony_ci stream->runtime->fragments = params->buffer.fragments; 5658c2ecf20Sopenharmony_ci stream->runtime->buffer = buffer; 5668c2ecf20Sopenharmony_ci stream->runtime->buffer_size = buffer_size; 5678c2ecf20Sopenharmony_ci return 0; 5688c2ecf20Sopenharmony_ci} 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_cistatic int snd_compress_check_input(struct snd_compr_params *params) 5718c2ecf20Sopenharmony_ci{ 5728c2ecf20Sopenharmony_ci /* first let's check the buffer parameter's */ 5738c2ecf20Sopenharmony_ci if (params->buffer.fragment_size == 0 || 5748c2ecf20Sopenharmony_ci params->buffer.fragments > U32_MAX / params->buffer.fragment_size || 5758c2ecf20Sopenharmony_ci params->buffer.fragments == 0) 5768c2ecf20Sopenharmony_ci return -EINVAL; 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_ci /* now codec parameters */ 5798c2ecf20Sopenharmony_ci if (params->codec.id == 0 || params->codec.id > SND_AUDIOCODEC_MAX) 5808c2ecf20Sopenharmony_ci return -EINVAL; 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci if (params->codec.ch_in == 0 || params->codec.ch_out == 0) 5838c2ecf20Sopenharmony_ci return -EINVAL; 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ci return 0; 5868c2ecf20Sopenharmony_ci} 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_cistatic int 5898c2ecf20Sopenharmony_cisnd_compr_set_params(struct snd_compr_stream *stream, unsigned long arg) 5908c2ecf20Sopenharmony_ci{ 5918c2ecf20Sopenharmony_ci struct snd_compr_params *params; 5928c2ecf20Sopenharmony_ci int retval; 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_ci if (stream->runtime->state == SNDRV_PCM_STATE_OPEN) { 5958c2ecf20Sopenharmony_ci /* 5968c2ecf20Sopenharmony_ci * we should allow parameter change only when stream has been 5978c2ecf20Sopenharmony_ci * opened not in other cases 5988c2ecf20Sopenharmony_ci */ 5998c2ecf20Sopenharmony_ci params = memdup_user((void __user *)arg, sizeof(*params)); 6008c2ecf20Sopenharmony_ci if (IS_ERR(params)) 6018c2ecf20Sopenharmony_ci return PTR_ERR(params); 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_ci retval = snd_compress_check_input(params); 6048c2ecf20Sopenharmony_ci if (retval) 6058c2ecf20Sopenharmony_ci goto out; 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_ci retval = snd_compr_allocate_buffer(stream, params); 6088c2ecf20Sopenharmony_ci if (retval) { 6098c2ecf20Sopenharmony_ci retval = -ENOMEM; 6108c2ecf20Sopenharmony_ci goto out; 6118c2ecf20Sopenharmony_ci } 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_ci retval = stream->ops->set_params(stream, params); 6148c2ecf20Sopenharmony_ci if (retval) 6158c2ecf20Sopenharmony_ci goto out; 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_ci stream->metadata_set = false; 6188c2ecf20Sopenharmony_ci stream->next_track = false; 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_ci stream->runtime->state = SNDRV_PCM_STATE_SETUP; 6218c2ecf20Sopenharmony_ci } else { 6228c2ecf20Sopenharmony_ci return -EPERM; 6238c2ecf20Sopenharmony_ci } 6248c2ecf20Sopenharmony_ciout: 6258c2ecf20Sopenharmony_ci kfree(params); 6268c2ecf20Sopenharmony_ci return retval; 6278c2ecf20Sopenharmony_ci} 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_cistatic int 6308c2ecf20Sopenharmony_cisnd_compr_get_params(struct snd_compr_stream *stream, unsigned long arg) 6318c2ecf20Sopenharmony_ci{ 6328c2ecf20Sopenharmony_ci struct snd_codec *params; 6338c2ecf20Sopenharmony_ci int retval; 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_ci if (!stream->ops->get_params) 6368c2ecf20Sopenharmony_ci return -EBADFD; 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_ci params = kzalloc(sizeof(*params), GFP_KERNEL); 6398c2ecf20Sopenharmony_ci if (!params) 6408c2ecf20Sopenharmony_ci return -ENOMEM; 6418c2ecf20Sopenharmony_ci retval = stream->ops->get_params(stream, params); 6428c2ecf20Sopenharmony_ci if (retval) 6438c2ecf20Sopenharmony_ci goto out; 6448c2ecf20Sopenharmony_ci if (copy_to_user((char __user *)arg, params, sizeof(*params))) 6458c2ecf20Sopenharmony_ci retval = -EFAULT; 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_ciout: 6488c2ecf20Sopenharmony_ci kfree(params); 6498c2ecf20Sopenharmony_ci return retval; 6508c2ecf20Sopenharmony_ci} 6518c2ecf20Sopenharmony_ci 6528c2ecf20Sopenharmony_cistatic int 6538c2ecf20Sopenharmony_cisnd_compr_get_metadata(struct snd_compr_stream *stream, unsigned long arg) 6548c2ecf20Sopenharmony_ci{ 6558c2ecf20Sopenharmony_ci struct snd_compr_metadata metadata; 6568c2ecf20Sopenharmony_ci int retval; 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_ci if (!stream->ops->get_metadata) 6598c2ecf20Sopenharmony_ci return -ENXIO; 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_ci if (copy_from_user(&metadata, (void __user *)arg, sizeof(metadata))) 6628c2ecf20Sopenharmony_ci return -EFAULT; 6638c2ecf20Sopenharmony_ci 6648c2ecf20Sopenharmony_ci retval = stream->ops->get_metadata(stream, &metadata); 6658c2ecf20Sopenharmony_ci if (retval != 0) 6668c2ecf20Sopenharmony_ci return retval; 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_ci if (copy_to_user((void __user *)arg, &metadata, sizeof(metadata))) 6698c2ecf20Sopenharmony_ci return -EFAULT; 6708c2ecf20Sopenharmony_ci 6718c2ecf20Sopenharmony_ci return 0; 6728c2ecf20Sopenharmony_ci} 6738c2ecf20Sopenharmony_ci 6748c2ecf20Sopenharmony_cistatic int 6758c2ecf20Sopenharmony_cisnd_compr_set_metadata(struct snd_compr_stream *stream, unsigned long arg) 6768c2ecf20Sopenharmony_ci{ 6778c2ecf20Sopenharmony_ci struct snd_compr_metadata metadata; 6788c2ecf20Sopenharmony_ci int retval; 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_ci if (!stream->ops->set_metadata) 6818c2ecf20Sopenharmony_ci return -ENXIO; 6828c2ecf20Sopenharmony_ci /* 6838c2ecf20Sopenharmony_ci * we should allow parameter change only when stream has been 6848c2ecf20Sopenharmony_ci * opened not in other cases 6858c2ecf20Sopenharmony_ci */ 6868c2ecf20Sopenharmony_ci if (copy_from_user(&metadata, (void __user *)arg, sizeof(metadata))) 6878c2ecf20Sopenharmony_ci return -EFAULT; 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_ci retval = stream->ops->set_metadata(stream, &metadata); 6908c2ecf20Sopenharmony_ci stream->metadata_set = true; 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_ci return retval; 6938c2ecf20Sopenharmony_ci} 6948c2ecf20Sopenharmony_ci 6958c2ecf20Sopenharmony_cistatic inline int 6968c2ecf20Sopenharmony_cisnd_compr_tstamp(struct snd_compr_stream *stream, unsigned long arg) 6978c2ecf20Sopenharmony_ci{ 6988c2ecf20Sopenharmony_ci struct snd_compr_tstamp tstamp = {0}; 6998c2ecf20Sopenharmony_ci int ret; 7008c2ecf20Sopenharmony_ci 7018c2ecf20Sopenharmony_ci ret = snd_compr_update_tstamp(stream, &tstamp); 7028c2ecf20Sopenharmony_ci if (ret == 0) 7038c2ecf20Sopenharmony_ci ret = copy_to_user((struct snd_compr_tstamp __user *)arg, 7048c2ecf20Sopenharmony_ci &tstamp, sizeof(tstamp)) ? -EFAULT : 0; 7058c2ecf20Sopenharmony_ci return ret; 7068c2ecf20Sopenharmony_ci} 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_cistatic int snd_compr_pause(struct snd_compr_stream *stream) 7098c2ecf20Sopenharmony_ci{ 7108c2ecf20Sopenharmony_ci int retval; 7118c2ecf20Sopenharmony_ci 7128c2ecf20Sopenharmony_ci if (stream->runtime->state != SNDRV_PCM_STATE_RUNNING) 7138c2ecf20Sopenharmony_ci return -EPERM; 7148c2ecf20Sopenharmony_ci retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_PAUSE_PUSH); 7158c2ecf20Sopenharmony_ci if (!retval) 7168c2ecf20Sopenharmony_ci stream->runtime->state = SNDRV_PCM_STATE_PAUSED; 7178c2ecf20Sopenharmony_ci return retval; 7188c2ecf20Sopenharmony_ci} 7198c2ecf20Sopenharmony_ci 7208c2ecf20Sopenharmony_cistatic int snd_compr_resume(struct snd_compr_stream *stream) 7218c2ecf20Sopenharmony_ci{ 7228c2ecf20Sopenharmony_ci int retval; 7238c2ecf20Sopenharmony_ci 7248c2ecf20Sopenharmony_ci if (stream->runtime->state != SNDRV_PCM_STATE_PAUSED) 7258c2ecf20Sopenharmony_ci return -EPERM; 7268c2ecf20Sopenharmony_ci retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_PAUSE_RELEASE); 7278c2ecf20Sopenharmony_ci if (!retval) 7288c2ecf20Sopenharmony_ci stream->runtime->state = SNDRV_PCM_STATE_RUNNING; 7298c2ecf20Sopenharmony_ci return retval; 7308c2ecf20Sopenharmony_ci} 7318c2ecf20Sopenharmony_ci 7328c2ecf20Sopenharmony_cistatic int snd_compr_start(struct snd_compr_stream *stream) 7338c2ecf20Sopenharmony_ci{ 7348c2ecf20Sopenharmony_ci int retval; 7358c2ecf20Sopenharmony_ci 7368c2ecf20Sopenharmony_ci switch (stream->runtime->state) { 7378c2ecf20Sopenharmony_ci case SNDRV_PCM_STATE_SETUP: 7388c2ecf20Sopenharmony_ci if (stream->direction != SND_COMPRESS_CAPTURE) 7398c2ecf20Sopenharmony_ci return -EPERM; 7408c2ecf20Sopenharmony_ci break; 7418c2ecf20Sopenharmony_ci case SNDRV_PCM_STATE_PREPARED: 7428c2ecf20Sopenharmony_ci break; 7438c2ecf20Sopenharmony_ci default: 7448c2ecf20Sopenharmony_ci return -EPERM; 7458c2ecf20Sopenharmony_ci } 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_ci retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_START); 7488c2ecf20Sopenharmony_ci if (!retval) 7498c2ecf20Sopenharmony_ci stream->runtime->state = SNDRV_PCM_STATE_RUNNING; 7508c2ecf20Sopenharmony_ci return retval; 7518c2ecf20Sopenharmony_ci} 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_cistatic int snd_compr_stop(struct snd_compr_stream *stream) 7548c2ecf20Sopenharmony_ci{ 7558c2ecf20Sopenharmony_ci int retval; 7568c2ecf20Sopenharmony_ci 7578c2ecf20Sopenharmony_ci switch (stream->runtime->state) { 7588c2ecf20Sopenharmony_ci case SNDRV_PCM_STATE_OPEN: 7598c2ecf20Sopenharmony_ci case SNDRV_PCM_STATE_SETUP: 7608c2ecf20Sopenharmony_ci case SNDRV_PCM_STATE_PREPARED: 7618c2ecf20Sopenharmony_ci return -EPERM; 7628c2ecf20Sopenharmony_ci default: 7638c2ecf20Sopenharmony_ci break; 7648c2ecf20Sopenharmony_ci } 7658c2ecf20Sopenharmony_ci 7668c2ecf20Sopenharmony_ci retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_STOP); 7678c2ecf20Sopenharmony_ci if (!retval) { 7688c2ecf20Sopenharmony_ci /* clear flags and stop any drain wait */ 7698c2ecf20Sopenharmony_ci stream->partial_drain = false; 7708c2ecf20Sopenharmony_ci stream->metadata_set = false; 7718c2ecf20Sopenharmony_ci snd_compr_drain_notify(stream); 7728c2ecf20Sopenharmony_ci stream->runtime->total_bytes_available = 0; 7738c2ecf20Sopenharmony_ci stream->runtime->total_bytes_transferred = 0; 7748c2ecf20Sopenharmony_ci } 7758c2ecf20Sopenharmony_ci return retval; 7768c2ecf20Sopenharmony_ci} 7778c2ecf20Sopenharmony_ci 7788c2ecf20Sopenharmony_cistatic void error_delayed_work(struct work_struct *work) 7798c2ecf20Sopenharmony_ci{ 7808c2ecf20Sopenharmony_ci struct snd_compr_stream *stream; 7818c2ecf20Sopenharmony_ci 7828c2ecf20Sopenharmony_ci stream = container_of(work, struct snd_compr_stream, error_work.work); 7838c2ecf20Sopenharmony_ci 7848c2ecf20Sopenharmony_ci mutex_lock(&stream->device->lock); 7858c2ecf20Sopenharmony_ci 7868c2ecf20Sopenharmony_ci stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_STOP); 7878c2ecf20Sopenharmony_ci wake_up(&stream->runtime->sleep); 7888c2ecf20Sopenharmony_ci 7898c2ecf20Sopenharmony_ci mutex_unlock(&stream->device->lock); 7908c2ecf20Sopenharmony_ci} 7918c2ecf20Sopenharmony_ci 7928c2ecf20Sopenharmony_ci/* 7938c2ecf20Sopenharmony_ci * snd_compr_stop_error: Report a fatal error on a stream 7948c2ecf20Sopenharmony_ci * @stream: pointer to stream 7958c2ecf20Sopenharmony_ci * @state: state to transition the stream to 7968c2ecf20Sopenharmony_ci * 7978c2ecf20Sopenharmony_ci * Stop the stream and set its state. 7988c2ecf20Sopenharmony_ci * 7998c2ecf20Sopenharmony_ci * Should be called with compressed device lock held. 8008c2ecf20Sopenharmony_ci */ 8018c2ecf20Sopenharmony_ciint snd_compr_stop_error(struct snd_compr_stream *stream, 8028c2ecf20Sopenharmony_ci snd_pcm_state_t state) 8038c2ecf20Sopenharmony_ci{ 8048c2ecf20Sopenharmony_ci if (stream->runtime->state == state) 8058c2ecf20Sopenharmony_ci return 0; 8068c2ecf20Sopenharmony_ci 8078c2ecf20Sopenharmony_ci stream->runtime->state = state; 8088c2ecf20Sopenharmony_ci 8098c2ecf20Sopenharmony_ci pr_debug("Changing state to: %d\n", state); 8108c2ecf20Sopenharmony_ci 8118c2ecf20Sopenharmony_ci queue_delayed_work(system_power_efficient_wq, &stream->error_work, 0); 8128c2ecf20Sopenharmony_ci 8138c2ecf20Sopenharmony_ci return 0; 8148c2ecf20Sopenharmony_ci} 8158c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_compr_stop_error); 8168c2ecf20Sopenharmony_ci 8178c2ecf20Sopenharmony_cistatic int snd_compress_wait_for_drain(struct snd_compr_stream *stream) 8188c2ecf20Sopenharmony_ci{ 8198c2ecf20Sopenharmony_ci int ret; 8208c2ecf20Sopenharmony_ci 8218c2ecf20Sopenharmony_ci /* 8228c2ecf20Sopenharmony_ci * We are called with lock held. So drop the lock while we wait for 8238c2ecf20Sopenharmony_ci * drain complete notification from the driver 8248c2ecf20Sopenharmony_ci * 8258c2ecf20Sopenharmony_ci * It is expected that driver will notify the drain completion and then 8268c2ecf20Sopenharmony_ci * stream will be moved to SETUP state, even if draining resulted in an 8278c2ecf20Sopenharmony_ci * error. We can trigger next track after this. 8288c2ecf20Sopenharmony_ci */ 8298c2ecf20Sopenharmony_ci stream->runtime->state = SNDRV_PCM_STATE_DRAINING; 8308c2ecf20Sopenharmony_ci mutex_unlock(&stream->device->lock); 8318c2ecf20Sopenharmony_ci 8328c2ecf20Sopenharmony_ci /* we wait for drain to complete here, drain can return when 8338c2ecf20Sopenharmony_ci * interruption occurred, wait returned error or success. 8348c2ecf20Sopenharmony_ci * For the first two cases we don't do anything different here and 8358c2ecf20Sopenharmony_ci * return after waking up 8368c2ecf20Sopenharmony_ci */ 8378c2ecf20Sopenharmony_ci 8388c2ecf20Sopenharmony_ci ret = wait_event_interruptible(stream->runtime->sleep, 8398c2ecf20Sopenharmony_ci (stream->runtime->state != SNDRV_PCM_STATE_DRAINING)); 8408c2ecf20Sopenharmony_ci if (ret == -ERESTARTSYS) 8418c2ecf20Sopenharmony_ci pr_debug("wait aborted by a signal\n"); 8428c2ecf20Sopenharmony_ci else if (ret) 8438c2ecf20Sopenharmony_ci pr_debug("wait for drain failed with %d\n", ret); 8448c2ecf20Sopenharmony_ci 8458c2ecf20Sopenharmony_ci 8468c2ecf20Sopenharmony_ci wake_up(&stream->runtime->sleep); 8478c2ecf20Sopenharmony_ci mutex_lock(&stream->device->lock); 8488c2ecf20Sopenharmony_ci 8498c2ecf20Sopenharmony_ci return ret; 8508c2ecf20Sopenharmony_ci} 8518c2ecf20Sopenharmony_ci 8528c2ecf20Sopenharmony_cistatic int snd_compr_drain(struct snd_compr_stream *stream) 8538c2ecf20Sopenharmony_ci{ 8548c2ecf20Sopenharmony_ci int retval; 8558c2ecf20Sopenharmony_ci 8568c2ecf20Sopenharmony_ci switch (stream->runtime->state) { 8578c2ecf20Sopenharmony_ci case SNDRV_PCM_STATE_OPEN: 8588c2ecf20Sopenharmony_ci case SNDRV_PCM_STATE_SETUP: 8598c2ecf20Sopenharmony_ci case SNDRV_PCM_STATE_PREPARED: 8608c2ecf20Sopenharmony_ci case SNDRV_PCM_STATE_PAUSED: 8618c2ecf20Sopenharmony_ci return -EPERM; 8628c2ecf20Sopenharmony_ci case SNDRV_PCM_STATE_XRUN: 8638c2ecf20Sopenharmony_ci return -EPIPE; 8648c2ecf20Sopenharmony_ci default: 8658c2ecf20Sopenharmony_ci break; 8668c2ecf20Sopenharmony_ci } 8678c2ecf20Sopenharmony_ci 8688c2ecf20Sopenharmony_ci retval = stream->ops->trigger(stream, SND_COMPR_TRIGGER_DRAIN); 8698c2ecf20Sopenharmony_ci if (retval) { 8708c2ecf20Sopenharmony_ci pr_debug("SND_COMPR_TRIGGER_DRAIN failed %d\n", retval); 8718c2ecf20Sopenharmony_ci wake_up(&stream->runtime->sleep); 8728c2ecf20Sopenharmony_ci return retval; 8738c2ecf20Sopenharmony_ci } 8748c2ecf20Sopenharmony_ci 8758c2ecf20Sopenharmony_ci return snd_compress_wait_for_drain(stream); 8768c2ecf20Sopenharmony_ci} 8778c2ecf20Sopenharmony_ci 8788c2ecf20Sopenharmony_cistatic int snd_compr_next_track(struct snd_compr_stream *stream) 8798c2ecf20Sopenharmony_ci{ 8808c2ecf20Sopenharmony_ci int retval; 8818c2ecf20Sopenharmony_ci 8828c2ecf20Sopenharmony_ci /* only a running stream can transition to next track */ 8838c2ecf20Sopenharmony_ci if (stream->runtime->state != SNDRV_PCM_STATE_RUNNING) 8848c2ecf20Sopenharmony_ci return -EPERM; 8858c2ecf20Sopenharmony_ci 8868c2ecf20Sopenharmony_ci /* next track doesn't have any meaning for capture streams */ 8878c2ecf20Sopenharmony_ci if (stream->direction == SND_COMPRESS_CAPTURE) 8888c2ecf20Sopenharmony_ci return -EPERM; 8898c2ecf20Sopenharmony_ci 8908c2ecf20Sopenharmony_ci /* you can signal next track if this is intended to be a gapless stream 8918c2ecf20Sopenharmony_ci * and current track metadata is set 8928c2ecf20Sopenharmony_ci */ 8938c2ecf20Sopenharmony_ci if (stream->metadata_set == false) 8948c2ecf20Sopenharmony_ci return -EPERM; 8958c2ecf20Sopenharmony_ci 8968c2ecf20Sopenharmony_ci retval = stream->ops->trigger(stream, SND_COMPR_TRIGGER_NEXT_TRACK); 8978c2ecf20Sopenharmony_ci if (retval != 0) 8988c2ecf20Sopenharmony_ci return retval; 8998c2ecf20Sopenharmony_ci stream->metadata_set = false; 9008c2ecf20Sopenharmony_ci stream->next_track = true; 9018c2ecf20Sopenharmony_ci return 0; 9028c2ecf20Sopenharmony_ci} 9038c2ecf20Sopenharmony_ci 9048c2ecf20Sopenharmony_cistatic int snd_compr_partial_drain(struct snd_compr_stream *stream) 9058c2ecf20Sopenharmony_ci{ 9068c2ecf20Sopenharmony_ci int retval; 9078c2ecf20Sopenharmony_ci 9088c2ecf20Sopenharmony_ci switch (stream->runtime->state) { 9098c2ecf20Sopenharmony_ci case SNDRV_PCM_STATE_OPEN: 9108c2ecf20Sopenharmony_ci case SNDRV_PCM_STATE_SETUP: 9118c2ecf20Sopenharmony_ci case SNDRV_PCM_STATE_PREPARED: 9128c2ecf20Sopenharmony_ci case SNDRV_PCM_STATE_PAUSED: 9138c2ecf20Sopenharmony_ci return -EPERM; 9148c2ecf20Sopenharmony_ci case SNDRV_PCM_STATE_XRUN: 9158c2ecf20Sopenharmony_ci return -EPIPE; 9168c2ecf20Sopenharmony_ci default: 9178c2ecf20Sopenharmony_ci break; 9188c2ecf20Sopenharmony_ci } 9198c2ecf20Sopenharmony_ci 9208c2ecf20Sopenharmony_ci /* partial drain doesn't have any meaning for capture streams */ 9218c2ecf20Sopenharmony_ci if (stream->direction == SND_COMPRESS_CAPTURE) 9228c2ecf20Sopenharmony_ci return -EPERM; 9238c2ecf20Sopenharmony_ci 9248c2ecf20Sopenharmony_ci /* stream can be drained only when next track has been signalled */ 9258c2ecf20Sopenharmony_ci if (stream->next_track == false) 9268c2ecf20Sopenharmony_ci return -EPERM; 9278c2ecf20Sopenharmony_ci 9288c2ecf20Sopenharmony_ci stream->partial_drain = true; 9298c2ecf20Sopenharmony_ci retval = stream->ops->trigger(stream, SND_COMPR_TRIGGER_PARTIAL_DRAIN); 9308c2ecf20Sopenharmony_ci if (retval) { 9318c2ecf20Sopenharmony_ci pr_debug("Partial drain returned failure\n"); 9328c2ecf20Sopenharmony_ci wake_up(&stream->runtime->sleep); 9338c2ecf20Sopenharmony_ci return retval; 9348c2ecf20Sopenharmony_ci } 9358c2ecf20Sopenharmony_ci 9368c2ecf20Sopenharmony_ci stream->next_track = false; 9378c2ecf20Sopenharmony_ci return snd_compress_wait_for_drain(stream); 9388c2ecf20Sopenharmony_ci} 9398c2ecf20Sopenharmony_ci 9408c2ecf20Sopenharmony_cistatic long snd_compr_ioctl(struct file *f, unsigned int cmd, unsigned long arg) 9418c2ecf20Sopenharmony_ci{ 9428c2ecf20Sopenharmony_ci struct snd_compr_file *data = f->private_data; 9438c2ecf20Sopenharmony_ci struct snd_compr_stream *stream; 9448c2ecf20Sopenharmony_ci int retval = -ENOTTY; 9458c2ecf20Sopenharmony_ci 9468c2ecf20Sopenharmony_ci if (snd_BUG_ON(!data)) 9478c2ecf20Sopenharmony_ci return -EFAULT; 9488c2ecf20Sopenharmony_ci 9498c2ecf20Sopenharmony_ci stream = &data->stream; 9508c2ecf20Sopenharmony_ci 9518c2ecf20Sopenharmony_ci mutex_lock(&stream->device->lock); 9528c2ecf20Sopenharmony_ci switch (_IOC_NR(cmd)) { 9538c2ecf20Sopenharmony_ci case _IOC_NR(SNDRV_COMPRESS_IOCTL_VERSION): 9548c2ecf20Sopenharmony_ci retval = put_user(SNDRV_COMPRESS_VERSION, 9558c2ecf20Sopenharmony_ci (int __user *)arg) ? -EFAULT : 0; 9568c2ecf20Sopenharmony_ci break; 9578c2ecf20Sopenharmony_ci case _IOC_NR(SNDRV_COMPRESS_GET_CAPS): 9588c2ecf20Sopenharmony_ci retval = snd_compr_get_caps(stream, arg); 9598c2ecf20Sopenharmony_ci break; 9608c2ecf20Sopenharmony_ci#ifndef COMPR_CODEC_CAPS_OVERFLOW 9618c2ecf20Sopenharmony_ci case _IOC_NR(SNDRV_COMPRESS_GET_CODEC_CAPS): 9628c2ecf20Sopenharmony_ci retval = snd_compr_get_codec_caps(stream, arg); 9638c2ecf20Sopenharmony_ci break; 9648c2ecf20Sopenharmony_ci#endif 9658c2ecf20Sopenharmony_ci case _IOC_NR(SNDRV_COMPRESS_SET_PARAMS): 9668c2ecf20Sopenharmony_ci retval = snd_compr_set_params(stream, arg); 9678c2ecf20Sopenharmony_ci break; 9688c2ecf20Sopenharmony_ci case _IOC_NR(SNDRV_COMPRESS_GET_PARAMS): 9698c2ecf20Sopenharmony_ci retval = snd_compr_get_params(stream, arg); 9708c2ecf20Sopenharmony_ci break; 9718c2ecf20Sopenharmony_ci case _IOC_NR(SNDRV_COMPRESS_SET_METADATA): 9728c2ecf20Sopenharmony_ci retval = snd_compr_set_metadata(stream, arg); 9738c2ecf20Sopenharmony_ci break; 9748c2ecf20Sopenharmony_ci case _IOC_NR(SNDRV_COMPRESS_GET_METADATA): 9758c2ecf20Sopenharmony_ci retval = snd_compr_get_metadata(stream, arg); 9768c2ecf20Sopenharmony_ci break; 9778c2ecf20Sopenharmony_ci case _IOC_NR(SNDRV_COMPRESS_TSTAMP): 9788c2ecf20Sopenharmony_ci retval = snd_compr_tstamp(stream, arg); 9798c2ecf20Sopenharmony_ci break; 9808c2ecf20Sopenharmony_ci case _IOC_NR(SNDRV_COMPRESS_AVAIL): 9818c2ecf20Sopenharmony_ci retval = snd_compr_ioctl_avail(stream, arg); 9828c2ecf20Sopenharmony_ci break; 9838c2ecf20Sopenharmony_ci case _IOC_NR(SNDRV_COMPRESS_PAUSE): 9848c2ecf20Sopenharmony_ci retval = snd_compr_pause(stream); 9858c2ecf20Sopenharmony_ci break; 9868c2ecf20Sopenharmony_ci case _IOC_NR(SNDRV_COMPRESS_RESUME): 9878c2ecf20Sopenharmony_ci retval = snd_compr_resume(stream); 9888c2ecf20Sopenharmony_ci break; 9898c2ecf20Sopenharmony_ci case _IOC_NR(SNDRV_COMPRESS_START): 9908c2ecf20Sopenharmony_ci retval = snd_compr_start(stream); 9918c2ecf20Sopenharmony_ci break; 9928c2ecf20Sopenharmony_ci case _IOC_NR(SNDRV_COMPRESS_STOP): 9938c2ecf20Sopenharmony_ci retval = snd_compr_stop(stream); 9948c2ecf20Sopenharmony_ci break; 9958c2ecf20Sopenharmony_ci case _IOC_NR(SNDRV_COMPRESS_DRAIN): 9968c2ecf20Sopenharmony_ci retval = snd_compr_drain(stream); 9978c2ecf20Sopenharmony_ci break; 9988c2ecf20Sopenharmony_ci case _IOC_NR(SNDRV_COMPRESS_PARTIAL_DRAIN): 9998c2ecf20Sopenharmony_ci retval = snd_compr_partial_drain(stream); 10008c2ecf20Sopenharmony_ci break; 10018c2ecf20Sopenharmony_ci case _IOC_NR(SNDRV_COMPRESS_NEXT_TRACK): 10028c2ecf20Sopenharmony_ci retval = snd_compr_next_track(stream); 10038c2ecf20Sopenharmony_ci break; 10048c2ecf20Sopenharmony_ci 10058c2ecf20Sopenharmony_ci } 10068c2ecf20Sopenharmony_ci mutex_unlock(&stream->device->lock); 10078c2ecf20Sopenharmony_ci return retval; 10088c2ecf20Sopenharmony_ci} 10098c2ecf20Sopenharmony_ci 10108c2ecf20Sopenharmony_ci/* support of 32bit userspace on 64bit platforms */ 10118c2ecf20Sopenharmony_ci#ifdef CONFIG_COMPAT 10128c2ecf20Sopenharmony_cistatic long snd_compr_ioctl_compat(struct file *file, unsigned int cmd, 10138c2ecf20Sopenharmony_ci unsigned long arg) 10148c2ecf20Sopenharmony_ci{ 10158c2ecf20Sopenharmony_ci return snd_compr_ioctl(file, cmd, (unsigned long)compat_ptr(arg)); 10168c2ecf20Sopenharmony_ci} 10178c2ecf20Sopenharmony_ci#endif 10188c2ecf20Sopenharmony_ci 10198c2ecf20Sopenharmony_cistatic const struct file_operations snd_compr_file_ops = { 10208c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 10218c2ecf20Sopenharmony_ci .open = snd_compr_open, 10228c2ecf20Sopenharmony_ci .release = snd_compr_free, 10238c2ecf20Sopenharmony_ci .write = snd_compr_write, 10248c2ecf20Sopenharmony_ci .read = snd_compr_read, 10258c2ecf20Sopenharmony_ci .unlocked_ioctl = snd_compr_ioctl, 10268c2ecf20Sopenharmony_ci#ifdef CONFIG_COMPAT 10278c2ecf20Sopenharmony_ci .compat_ioctl = snd_compr_ioctl_compat, 10288c2ecf20Sopenharmony_ci#endif 10298c2ecf20Sopenharmony_ci .mmap = snd_compr_mmap, 10308c2ecf20Sopenharmony_ci .poll = snd_compr_poll, 10318c2ecf20Sopenharmony_ci}; 10328c2ecf20Sopenharmony_ci 10338c2ecf20Sopenharmony_cistatic int snd_compress_dev_register(struct snd_device *device) 10348c2ecf20Sopenharmony_ci{ 10358c2ecf20Sopenharmony_ci int ret; 10368c2ecf20Sopenharmony_ci struct snd_compr *compr; 10378c2ecf20Sopenharmony_ci 10388c2ecf20Sopenharmony_ci if (snd_BUG_ON(!device || !device->device_data)) 10398c2ecf20Sopenharmony_ci return -EBADFD; 10408c2ecf20Sopenharmony_ci compr = device->device_data; 10418c2ecf20Sopenharmony_ci 10428c2ecf20Sopenharmony_ci pr_debug("reg device %s, direction %d\n", compr->name, 10438c2ecf20Sopenharmony_ci compr->direction); 10448c2ecf20Sopenharmony_ci /* register compressed device */ 10458c2ecf20Sopenharmony_ci ret = snd_register_device(SNDRV_DEVICE_TYPE_COMPRESS, 10468c2ecf20Sopenharmony_ci compr->card, compr->device, 10478c2ecf20Sopenharmony_ci &snd_compr_file_ops, compr, &compr->dev); 10488c2ecf20Sopenharmony_ci if (ret < 0) { 10498c2ecf20Sopenharmony_ci pr_err("snd_register_device failed %d\n", ret); 10508c2ecf20Sopenharmony_ci return ret; 10518c2ecf20Sopenharmony_ci } 10528c2ecf20Sopenharmony_ci return ret; 10538c2ecf20Sopenharmony_ci 10548c2ecf20Sopenharmony_ci} 10558c2ecf20Sopenharmony_ci 10568c2ecf20Sopenharmony_cistatic int snd_compress_dev_disconnect(struct snd_device *device) 10578c2ecf20Sopenharmony_ci{ 10588c2ecf20Sopenharmony_ci struct snd_compr *compr; 10598c2ecf20Sopenharmony_ci 10608c2ecf20Sopenharmony_ci compr = device->device_data; 10618c2ecf20Sopenharmony_ci snd_unregister_device(&compr->dev); 10628c2ecf20Sopenharmony_ci return 0; 10638c2ecf20Sopenharmony_ci} 10648c2ecf20Sopenharmony_ci 10658c2ecf20Sopenharmony_ci#ifdef CONFIG_SND_VERBOSE_PROCFS 10668c2ecf20Sopenharmony_cistatic void snd_compress_proc_info_read(struct snd_info_entry *entry, 10678c2ecf20Sopenharmony_ci struct snd_info_buffer *buffer) 10688c2ecf20Sopenharmony_ci{ 10698c2ecf20Sopenharmony_ci struct snd_compr *compr = (struct snd_compr *)entry->private_data; 10708c2ecf20Sopenharmony_ci 10718c2ecf20Sopenharmony_ci snd_iprintf(buffer, "card: %d\n", compr->card->number); 10728c2ecf20Sopenharmony_ci snd_iprintf(buffer, "device: %d\n", compr->device); 10738c2ecf20Sopenharmony_ci snd_iprintf(buffer, "stream: %s\n", 10748c2ecf20Sopenharmony_ci compr->direction == SND_COMPRESS_PLAYBACK 10758c2ecf20Sopenharmony_ci ? "PLAYBACK" : "CAPTURE"); 10768c2ecf20Sopenharmony_ci snd_iprintf(buffer, "id: %s\n", compr->id); 10778c2ecf20Sopenharmony_ci} 10788c2ecf20Sopenharmony_ci 10798c2ecf20Sopenharmony_cistatic int snd_compress_proc_init(struct snd_compr *compr) 10808c2ecf20Sopenharmony_ci{ 10818c2ecf20Sopenharmony_ci struct snd_info_entry *entry; 10828c2ecf20Sopenharmony_ci char name[16]; 10838c2ecf20Sopenharmony_ci 10848c2ecf20Sopenharmony_ci sprintf(name, "compr%i", compr->device); 10858c2ecf20Sopenharmony_ci entry = snd_info_create_card_entry(compr->card, name, 10868c2ecf20Sopenharmony_ci compr->card->proc_root); 10878c2ecf20Sopenharmony_ci if (!entry) 10888c2ecf20Sopenharmony_ci return -ENOMEM; 10898c2ecf20Sopenharmony_ci entry->mode = S_IFDIR | 0555; 10908c2ecf20Sopenharmony_ci compr->proc_root = entry; 10918c2ecf20Sopenharmony_ci 10928c2ecf20Sopenharmony_ci entry = snd_info_create_card_entry(compr->card, "info", 10938c2ecf20Sopenharmony_ci compr->proc_root); 10948c2ecf20Sopenharmony_ci if (entry) 10958c2ecf20Sopenharmony_ci snd_info_set_text_ops(entry, compr, 10968c2ecf20Sopenharmony_ci snd_compress_proc_info_read); 10978c2ecf20Sopenharmony_ci compr->proc_info_entry = entry; 10988c2ecf20Sopenharmony_ci 10998c2ecf20Sopenharmony_ci return 0; 11008c2ecf20Sopenharmony_ci} 11018c2ecf20Sopenharmony_ci 11028c2ecf20Sopenharmony_cistatic void snd_compress_proc_done(struct snd_compr *compr) 11038c2ecf20Sopenharmony_ci{ 11048c2ecf20Sopenharmony_ci snd_info_free_entry(compr->proc_info_entry); 11058c2ecf20Sopenharmony_ci compr->proc_info_entry = NULL; 11068c2ecf20Sopenharmony_ci snd_info_free_entry(compr->proc_root); 11078c2ecf20Sopenharmony_ci compr->proc_root = NULL; 11088c2ecf20Sopenharmony_ci} 11098c2ecf20Sopenharmony_ci 11108c2ecf20Sopenharmony_cistatic inline void snd_compress_set_id(struct snd_compr *compr, const char *id) 11118c2ecf20Sopenharmony_ci{ 11128c2ecf20Sopenharmony_ci strlcpy(compr->id, id, sizeof(compr->id)); 11138c2ecf20Sopenharmony_ci} 11148c2ecf20Sopenharmony_ci#else 11158c2ecf20Sopenharmony_cistatic inline int snd_compress_proc_init(struct snd_compr *compr) 11168c2ecf20Sopenharmony_ci{ 11178c2ecf20Sopenharmony_ci return 0; 11188c2ecf20Sopenharmony_ci} 11198c2ecf20Sopenharmony_ci 11208c2ecf20Sopenharmony_cistatic inline void snd_compress_proc_done(struct snd_compr *compr) 11218c2ecf20Sopenharmony_ci{ 11228c2ecf20Sopenharmony_ci} 11238c2ecf20Sopenharmony_ci 11248c2ecf20Sopenharmony_cistatic inline void snd_compress_set_id(struct snd_compr *compr, const char *id) 11258c2ecf20Sopenharmony_ci{ 11268c2ecf20Sopenharmony_ci} 11278c2ecf20Sopenharmony_ci#endif 11288c2ecf20Sopenharmony_ci 11298c2ecf20Sopenharmony_cistatic int snd_compress_dev_free(struct snd_device *device) 11308c2ecf20Sopenharmony_ci{ 11318c2ecf20Sopenharmony_ci struct snd_compr *compr; 11328c2ecf20Sopenharmony_ci 11338c2ecf20Sopenharmony_ci compr = device->device_data; 11348c2ecf20Sopenharmony_ci snd_compress_proc_done(compr); 11358c2ecf20Sopenharmony_ci put_device(&compr->dev); 11368c2ecf20Sopenharmony_ci return 0; 11378c2ecf20Sopenharmony_ci} 11388c2ecf20Sopenharmony_ci 11398c2ecf20Sopenharmony_ci/* 11408c2ecf20Sopenharmony_ci * snd_compress_new: create new compress device 11418c2ecf20Sopenharmony_ci * @card: sound card pointer 11428c2ecf20Sopenharmony_ci * @device: device number 11438c2ecf20Sopenharmony_ci * @dirn: device direction, should be of type enum snd_compr_direction 11448c2ecf20Sopenharmony_ci * @compr: compress device pointer 11458c2ecf20Sopenharmony_ci */ 11468c2ecf20Sopenharmony_ciint snd_compress_new(struct snd_card *card, int device, 11478c2ecf20Sopenharmony_ci int dirn, const char *id, struct snd_compr *compr) 11488c2ecf20Sopenharmony_ci{ 11498c2ecf20Sopenharmony_ci static const struct snd_device_ops ops = { 11508c2ecf20Sopenharmony_ci .dev_free = snd_compress_dev_free, 11518c2ecf20Sopenharmony_ci .dev_register = snd_compress_dev_register, 11528c2ecf20Sopenharmony_ci .dev_disconnect = snd_compress_dev_disconnect, 11538c2ecf20Sopenharmony_ci }; 11548c2ecf20Sopenharmony_ci int ret; 11558c2ecf20Sopenharmony_ci 11568c2ecf20Sopenharmony_ci compr->card = card; 11578c2ecf20Sopenharmony_ci compr->device = device; 11588c2ecf20Sopenharmony_ci compr->direction = dirn; 11598c2ecf20Sopenharmony_ci 11608c2ecf20Sopenharmony_ci snd_compress_set_id(compr, id); 11618c2ecf20Sopenharmony_ci 11628c2ecf20Sopenharmony_ci snd_device_initialize(&compr->dev, card); 11638c2ecf20Sopenharmony_ci dev_set_name(&compr->dev, "comprC%iD%i", card->number, device); 11648c2ecf20Sopenharmony_ci 11658c2ecf20Sopenharmony_ci ret = snd_device_new(card, SNDRV_DEV_COMPRESS, compr, &ops); 11668c2ecf20Sopenharmony_ci if (ret == 0) 11678c2ecf20Sopenharmony_ci snd_compress_proc_init(compr); 11688c2ecf20Sopenharmony_ci 11698c2ecf20Sopenharmony_ci return ret; 11708c2ecf20Sopenharmony_ci} 11718c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_compress_new); 11728c2ecf20Sopenharmony_ci 11738c2ecf20Sopenharmony_cistatic int snd_compress_add_device(struct snd_compr *device) 11748c2ecf20Sopenharmony_ci{ 11758c2ecf20Sopenharmony_ci int ret; 11768c2ecf20Sopenharmony_ci 11778c2ecf20Sopenharmony_ci if (!device->card) 11788c2ecf20Sopenharmony_ci return -EINVAL; 11798c2ecf20Sopenharmony_ci 11808c2ecf20Sopenharmony_ci /* register the card */ 11818c2ecf20Sopenharmony_ci ret = snd_card_register(device->card); 11828c2ecf20Sopenharmony_ci if (ret) 11838c2ecf20Sopenharmony_ci goto out; 11848c2ecf20Sopenharmony_ci return 0; 11858c2ecf20Sopenharmony_ci 11868c2ecf20Sopenharmony_ciout: 11878c2ecf20Sopenharmony_ci pr_err("failed with %d\n", ret); 11888c2ecf20Sopenharmony_ci return ret; 11898c2ecf20Sopenharmony_ci 11908c2ecf20Sopenharmony_ci} 11918c2ecf20Sopenharmony_ci 11928c2ecf20Sopenharmony_cistatic int snd_compress_remove_device(struct snd_compr *device) 11938c2ecf20Sopenharmony_ci{ 11948c2ecf20Sopenharmony_ci return snd_card_free(device->card); 11958c2ecf20Sopenharmony_ci} 11968c2ecf20Sopenharmony_ci 11978c2ecf20Sopenharmony_ci/** 11988c2ecf20Sopenharmony_ci * snd_compress_register - register compressed device 11998c2ecf20Sopenharmony_ci * 12008c2ecf20Sopenharmony_ci * @device: compressed device to register 12018c2ecf20Sopenharmony_ci */ 12028c2ecf20Sopenharmony_ciint snd_compress_register(struct snd_compr *device) 12038c2ecf20Sopenharmony_ci{ 12048c2ecf20Sopenharmony_ci int retval; 12058c2ecf20Sopenharmony_ci 12068c2ecf20Sopenharmony_ci if (device->name == NULL || device->ops == NULL) 12078c2ecf20Sopenharmony_ci return -EINVAL; 12088c2ecf20Sopenharmony_ci 12098c2ecf20Sopenharmony_ci pr_debug("Registering compressed device %s\n", device->name); 12108c2ecf20Sopenharmony_ci if (snd_BUG_ON(!device->ops->open)) 12118c2ecf20Sopenharmony_ci return -EINVAL; 12128c2ecf20Sopenharmony_ci if (snd_BUG_ON(!device->ops->free)) 12138c2ecf20Sopenharmony_ci return -EINVAL; 12148c2ecf20Sopenharmony_ci if (snd_BUG_ON(!device->ops->set_params)) 12158c2ecf20Sopenharmony_ci return -EINVAL; 12168c2ecf20Sopenharmony_ci if (snd_BUG_ON(!device->ops->trigger)) 12178c2ecf20Sopenharmony_ci return -EINVAL; 12188c2ecf20Sopenharmony_ci 12198c2ecf20Sopenharmony_ci mutex_init(&device->lock); 12208c2ecf20Sopenharmony_ci 12218c2ecf20Sopenharmony_ci /* register a compressed card */ 12228c2ecf20Sopenharmony_ci mutex_lock(&device_mutex); 12238c2ecf20Sopenharmony_ci retval = snd_compress_add_device(device); 12248c2ecf20Sopenharmony_ci mutex_unlock(&device_mutex); 12258c2ecf20Sopenharmony_ci return retval; 12268c2ecf20Sopenharmony_ci} 12278c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_compress_register); 12288c2ecf20Sopenharmony_ci 12298c2ecf20Sopenharmony_ciint snd_compress_deregister(struct snd_compr *device) 12308c2ecf20Sopenharmony_ci{ 12318c2ecf20Sopenharmony_ci pr_debug("Removing compressed device %s\n", device->name); 12328c2ecf20Sopenharmony_ci mutex_lock(&device_mutex); 12338c2ecf20Sopenharmony_ci snd_compress_remove_device(device); 12348c2ecf20Sopenharmony_ci mutex_unlock(&device_mutex); 12358c2ecf20Sopenharmony_ci return 0; 12368c2ecf20Sopenharmony_ci} 12378c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_compress_deregister); 12388c2ecf20Sopenharmony_ci 12398c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("ALSA Compressed offload framework"); 12408c2ecf20Sopenharmony_ciMODULE_AUTHOR("Vinod Koul <vinod.koul@linux.intel.com>"); 12418c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 1242