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