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