xref: /openbmc/linux/sound/core/compress_offload.c (revision b53a41ee)
1873e65bcSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2b21c60a4SVinod Koul /*
3b21c60a4SVinod Koul  *  compress_core.c - compress offload core
4b21c60a4SVinod Koul  *
5b21c60a4SVinod Koul  *  Copyright (C) 2011 Intel Corporation
6b21c60a4SVinod Koul  *  Authors:	Vinod Koul <vinod.koul@linux.intel.com>
7b21c60a4SVinod Koul  *		Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
8b21c60a4SVinod Koul  *  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
9b21c60a4SVinod Koul  *
10b21c60a4SVinod Koul  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
11b21c60a4SVinod Koul  */
12b21c60a4SVinod Koul #define FORMAT(fmt) "%s: %d: " fmt, __func__, __LINE__
13b21c60a4SVinod Koul #define pr_fmt(fmt) KBUILD_MODNAME ": " FORMAT(fmt)
14b21c60a4SVinod Koul 
15b21c60a4SVinod Koul #include <linux/file.h>
16b21c60a4SVinod Koul #include <linux/fs.h>
17b21c60a4SVinod Koul #include <linux/list.h>
18f0283b58SCharles Keepax #include <linux/math64.h>
19b21c60a4SVinod Koul #include <linux/mm.h>
20b21c60a4SVinod Koul #include <linux/mutex.h>
21b21c60a4SVinod Koul #include <linux/poll.h>
22b21c60a4SVinod Koul #include <linux/slab.h>
23b21c60a4SVinod Koul #include <linux/sched.h>
24f0283b58SCharles Keepax #include <linux/types.h>
25b21c60a4SVinod Koul #include <linux/uio.h>
26b21c60a4SVinod Koul #include <linux/uaccess.h>
27b21c60a4SVinod Koul #include <linux/module.h>
28c1036889SRavindra Lokhande #include <linux/compat.h>
29b21c60a4SVinod Koul #include <sound/core.h>
30b21c60a4SVinod Koul #include <sound/initval.h>
3131742724SRichard Fitzgerald #include <sound/info.h>
32b21c60a4SVinod Koul #include <sound/compress_params.h>
33b21c60a4SVinod Koul #include <sound/compress_offload.h>
34b21c60a4SVinod Koul #include <sound/compress_driver.h>
35b21c60a4SVinod Koul 
36462b3f16STakashi Iwai /* struct snd_compr_codec_caps overflows the ioctl bit size for some
37462b3f16STakashi Iwai  * architectures, so we need to disable the relevant ioctls.
38462b3f16STakashi Iwai  */
39462b3f16STakashi Iwai #if _IOC_SIZEBITS < 14
40462b3f16STakashi Iwai #define COMPR_CODEC_CAPS_OVERFLOW
41462b3f16STakashi Iwai #endif
42462b3f16STakashi Iwai 
43b21c60a4SVinod Koul /* TODO:
44b21c60a4SVinod Koul  * - add substream support for multiple devices in case of
45b21c60a4SVinod Koul  *	SND_DYNAMIC_MINORS is not used
46b21c60a4SVinod Koul  * - Multiple node representation
47b21c60a4SVinod Koul  *	driver should be able to register multiple nodes
48b21c60a4SVinod Koul  */
49b21c60a4SVinod Koul 
50b21c60a4SVinod Koul struct snd_compr_file {
51b21c60a4SVinod Koul 	unsigned long caps;
52b21c60a4SVinod Koul 	struct snd_compr_stream stream;
53b21c60a4SVinod Koul };
54b21c60a4SVinod Koul 
55a4f2d87cSCharles Keepax static void error_delayed_work(struct work_struct *work);
56a4f2d87cSCharles Keepax 
57b21c60a4SVinod Koul /*
58b21c60a4SVinod Koul  * a note on stream states used:
5941eb94fdSVinod Koul  * we use following states in the compressed core
60b21c60a4SVinod Koul  * SNDRV_PCM_STATE_OPEN: When stream has been opened.
61b21c60a4SVinod Koul  * SNDRV_PCM_STATE_SETUP: When stream has been initialized. This is done by
6241eb94fdSVinod Koul  *	calling SNDRV_COMPRESS_SET_PARAMS. Running streams will come to this
63b21c60a4SVinod Koul  *	state at stop by calling SNDRV_COMPRESS_STOP, or at end of drain.
64862bca5dSVinod Koul  * SNDRV_PCM_STATE_PREPARED: When a stream has been written to (for
65862bca5dSVinod Koul  *	playback only). User after setting up stream writes the data buffer
66862bca5dSVinod Koul  *	before starting the stream.
67b21c60a4SVinod Koul  * SNDRV_PCM_STATE_RUNNING: When stream has been started and is
68b21c60a4SVinod Koul  *	decoding/encoding and rendering/capturing data.
69b21c60a4SVinod Koul  * SNDRV_PCM_STATE_DRAINING: When stream is draining current data. This is done
70b21c60a4SVinod Koul  *	by calling SNDRV_COMPRESS_DRAIN.
71b21c60a4SVinod Koul  * SNDRV_PCM_STATE_PAUSED: When stream is paused. This is done by calling
72b21c60a4SVinod Koul  *	SNDRV_COMPRESS_PAUSE. It can be stopped or resumed by calling
73b21c60a4SVinod Koul  *	SNDRV_COMPRESS_STOP or SNDRV_COMPRESS_RESUME respectively.
74b21c60a4SVinod Koul  */
snd_compr_open(struct inode * inode,struct file * f)75b21c60a4SVinod Koul static int snd_compr_open(struct inode *inode, struct file *f)
76b21c60a4SVinod Koul {
77b21c60a4SVinod Koul 	struct snd_compr *compr;
78b21c60a4SVinod Koul 	struct snd_compr_file *data;
79b21c60a4SVinod Koul 	struct snd_compr_runtime *runtime;
80b21c60a4SVinod Koul 	enum snd_compr_direction dirn;
81b21c60a4SVinod Koul 	int maj = imajor(inode);
82b21c60a4SVinod Koul 	int ret;
83b21c60a4SVinod Koul 
8481cb3246SDan Carpenter 	if ((f->f_flags & O_ACCMODE) == O_WRONLY)
85b21c60a4SVinod Koul 		dirn = SND_COMPRESS_PLAYBACK;
8681cb3246SDan Carpenter 	else if ((f->f_flags & O_ACCMODE) == O_RDONLY)
87b21c60a4SVinod Koul 		dirn = SND_COMPRESS_CAPTURE;
8881cb3246SDan Carpenter 	else
89b21c60a4SVinod Koul 		return -EINVAL;
90b21c60a4SVinod Koul 
91b21c60a4SVinod Koul 	if (maj == snd_major)
92b21c60a4SVinod Koul 		compr = snd_lookup_minor_data(iminor(inode),
93b21c60a4SVinod Koul 					SNDRV_DEVICE_TYPE_COMPRESS);
94b21c60a4SVinod Koul 	else
95b21c60a4SVinod Koul 		return -EBADFD;
96b21c60a4SVinod Koul 
97b21c60a4SVinod Koul 	if (compr == NULL) {
98b21c60a4SVinod Koul 		pr_err("no device data!!!\n");
99b21c60a4SVinod Koul 		return -ENODEV;
100b21c60a4SVinod Koul 	}
101b21c60a4SVinod Koul 
102b21c60a4SVinod Koul 	if (dirn != compr->direction) {
103b21c60a4SVinod Koul 		pr_err("this device doesn't support this direction\n");
104a0830dbdSTakashi Iwai 		snd_card_unref(compr->card);
105b21c60a4SVinod Koul 		return -EINVAL;
106b21c60a4SVinod Koul 	}
107b21c60a4SVinod Koul 
108b21c60a4SVinod Koul 	data = kzalloc(sizeof(*data), GFP_KERNEL);
109a0830dbdSTakashi Iwai 	if (!data) {
110a0830dbdSTakashi Iwai 		snd_card_unref(compr->card);
111b21c60a4SVinod Koul 		return -ENOMEM;
112a0830dbdSTakashi Iwai 	}
113a4f2d87cSCharles Keepax 
114a4f2d87cSCharles Keepax 	INIT_DELAYED_WORK(&data->stream.error_work, error_delayed_work);
115a4f2d87cSCharles Keepax 
116b21c60a4SVinod Koul 	data->stream.ops = compr->ops;
117b21c60a4SVinod Koul 	data->stream.direction = dirn;
118b21c60a4SVinod Koul 	data->stream.private_data = compr->private_data;
119b21c60a4SVinod Koul 	data->stream.device = compr;
120b21c60a4SVinod Koul 	runtime = kzalloc(sizeof(*runtime), GFP_KERNEL);
121b21c60a4SVinod Koul 	if (!runtime) {
122b21c60a4SVinod Koul 		kfree(data);
123a0830dbdSTakashi Iwai 		snd_card_unref(compr->card);
124b21c60a4SVinod Koul 		return -ENOMEM;
125b21c60a4SVinod Koul 	}
126b21c60a4SVinod Koul 	runtime->state = SNDRV_PCM_STATE_OPEN;
127b21c60a4SVinod Koul 	init_waitqueue_head(&runtime->sleep);
128b21c60a4SVinod Koul 	data->stream.runtime = runtime;
129b21c60a4SVinod Koul 	f->private_data = (void *)data;
130b21c60a4SVinod Koul 	mutex_lock(&compr->lock);
131b21c60a4SVinod Koul 	ret = compr->ops->open(&data->stream);
132b21c60a4SVinod Koul 	mutex_unlock(&compr->lock);
133b21c60a4SVinod Koul 	if (ret) {
134b21c60a4SVinod Koul 		kfree(runtime);
135b21c60a4SVinod Koul 		kfree(data);
136b21c60a4SVinod Koul 	}
137a0830dbdSTakashi Iwai 	snd_card_unref(compr->card);
138749d3223SCharles Keepax 	return ret;
139b21c60a4SVinod Koul }
140b21c60a4SVinod Koul 
snd_compr_free(struct inode * inode,struct file * f)141b21c60a4SVinod Koul static int snd_compr_free(struct inode *inode, struct file *f)
142b21c60a4SVinod Koul {
143b21c60a4SVinod Koul 	struct snd_compr_file *data = f->private_data;
144b26d19e4SLiam Girdwood 	struct snd_compr_runtime *runtime = data->stream.runtime;
145b26d19e4SLiam Girdwood 
146a4f2d87cSCharles Keepax 	cancel_delayed_work_sync(&data->stream.error_work);
147a4f2d87cSCharles Keepax 
148b26d19e4SLiam Girdwood 	switch (runtime->state) {
149b26d19e4SLiam Girdwood 	case SNDRV_PCM_STATE_RUNNING:
150b26d19e4SLiam Girdwood 	case SNDRV_PCM_STATE_DRAINING:
151b26d19e4SLiam Girdwood 	case SNDRV_PCM_STATE_PAUSED:
152b26d19e4SLiam Girdwood 		data->stream.ops->trigger(&data->stream, SNDRV_PCM_TRIGGER_STOP);
153b26d19e4SLiam Girdwood 		break;
154b26d19e4SLiam Girdwood 	default:
155b26d19e4SLiam Girdwood 		break;
156b26d19e4SLiam Girdwood 	}
157b26d19e4SLiam Girdwood 
158b21c60a4SVinod Koul 	data->stream.ops->free(&data->stream);
159d00f749bSSrinivas Kandagatla 	if (!data->stream.runtime->dma_buffer_p)
160b21c60a4SVinod Koul 		kfree(data->stream.runtime->buffer);
161b21c60a4SVinod Koul 	kfree(data->stream.runtime);
162b21c60a4SVinod Koul 	kfree(data);
163b21c60a4SVinod Koul 	return 0;
164b21c60a4SVinod Koul }
165b21c60a4SVinod Koul 
snd_compr_update_tstamp(struct snd_compr_stream * stream,struct snd_compr_tstamp * tstamp)16617ac8e5cSRichard Fitzgerald static int snd_compr_update_tstamp(struct snd_compr_stream *stream,
167b21c60a4SVinod Koul 		struct snd_compr_tstamp *tstamp)
168b21c60a4SVinod Koul {
169b21c60a4SVinod Koul 	if (!stream->ops->pointer)
17017ac8e5cSRichard Fitzgerald 		return -ENOTSUPP;
171b21c60a4SVinod Koul 	stream->ops->pointer(stream, tstamp);
172b21c60a4SVinod Koul 	pr_debug("dsp consumed till %d total %d bytes\n",
173b21c60a4SVinod Koul 		tstamp->byte_offset, tstamp->copied_total);
1745b1f79f7SCharles Keepax 	if (stream->direction == SND_COMPRESS_PLAYBACK)
175b21c60a4SVinod Koul 		stream->runtime->total_bytes_transferred = tstamp->copied_total;
1765b1f79f7SCharles Keepax 	else
1775b1f79f7SCharles Keepax 		stream->runtime->total_bytes_available = tstamp->copied_total;
17817ac8e5cSRichard Fitzgerald 	return 0;
179b21c60a4SVinod Koul }
180b21c60a4SVinod Koul 
snd_compr_calc_avail(struct snd_compr_stream * stream,struct snd_compr_avail * avail)181b21c60a4SVinod Koul static size_t snd_compr_calc_avail(struct snd_compr_stream *stream,
182b21c60a4SVinod Koul 		struct snd_compr_avail *avail)
183b21c60a4SVinod Koul {
18417ac8e5cSRichard Fitzgerald 	memset(avail, 0, sizeof(*avail));
185b21c60a4SVinod Koul 	snd_compr_update_tstamp(stream, &avail->tstamp);
18617ac8e5cSRichard Fitzgerald 	/* Still need to return avail even if tstamp can't be filled in */
187b21c60a4SVinod Koul 
188b21c60a4SVinod Koul 	if (stream->runtime->total_bytes_available == 0 &&
1895b1f79f7SCharles Keepax 			stream->runtime->state == SNDRV_PCM_STATE_SETUP &&
1905b1f79f7SCharles Keepax 			stream->direction == SND_COMPRESS_PLAYBACK) {
191b21c60a4SVinod Koul 		pr_debug("detected init and someone forgot to do a write\n");
192b21c60a4SVinod Koul 		return stream->runtime->buffer_size;
193b21c60a4SVinod Koul 	}
194b21c60a4SVinod Koul 	pr_debug("app wrote %lld, DSP consumed %lld\n",
195b21c60a4SVinod Koul 			stream->runtime->total_bytes_available,
196b21c60a4SVinod Koul 			stream->runtime->total_bytes_transferred);
197b21c60a4SVinod Koul 	if (stream->runtime->total_bytes_available ==
198b21c60a4SVinod Koul 				stream->runtime->total_bytes_transferred) {
1995b1f79f7SCharles Keepax 		if (stream->direction == SND_COMPRESS_PLAYBACK) {
200b21c60a4SVinod Koul 			pr_debug("both pointers are same, returning full avail\n");
201b21c60a4SVinod Koul 			return stream->runtime->buffer_size;
2025b1f79f7SCharles Keepax 		} else {
2035b1f79f7SCharles Keepax 			pr_debug("both pointers are same, returning no avail\n");
2045b1f79f7SCharles Keepax 			return 0;
2055b1f79f7SCharles Keepax 		}
206b21c60a4SVinod Koul 	}
207b21c60a4SVinod Koul 
2085b1f79f7SCharles Keepax 	avail->avail = stream->runtime->total_bytes_available -
2095b1f79f7SCharles Keepax 			stream->runtime->total_bytes_transferred;
2105b1f79f7SCharles Keepax 	if (stream->direction == SND_COMPRESS_PLAYBACK)
2115b1f79f7SCharles Keepax 		avail->avail = stream->runtime->buffer_size - avail->avail;
2125b1f79f7SCharles Keepax 
2134c28e32dSCharles Keepax 	pr_debug("ret avail as %lld\n", avail->avail);
2144c28e32dSCharles Keepax 	return avail->avail;
215b21c60a4SVinod Koul }
216b21c60a4SVinod Koul 
snd_compr_get_avail(struct snd_compr_stream * stream)217b21c60a4SVinod Koul static inline size_t snd_compr_get_avail(struct snd_compr_stream *stream)
218b21c60a4SVinod Koul {
219b21c60a4SVinod Koul 	struct snd_compr_avail avail;
220b21c60a4SVinod Koul 
221b21c60a4SVinod Koul 	return snd_compr_calc_avail(stream, &avail);
222b21c60a4SVinod Koul }
223b21c60a4SVinod Koul 
224b21c60a4SVinod Koul static int
snd_compr_ioctl_avail(struct snd_compr_stream * stream,unsigned long arg)225b21c60a4SVinod Koul snd_compr_ioctl_avail(struct snd_compr_stream *stream, unsigned long arg)
226b21c60a4SVinod Koul {
227b21c60a4SVinod Koul 	struct snd_compr_avail ioctl_avail;
228b21c60a4SVinod Koul 	size_t avail;
229b21c60a4SVinod Koul 
230b21c60a4SVinod Koul 	avail = snd_compr_calc_avail(stream, &ioctl_avail);
231b21c60a4SVinod Koul 	ioctl_avail.avail = avail;
232b21c60a4SVinod Koul 
233a4f2d87cSCharles Keepax 	switch (stream->runtime->state) {
234a4f2d87cSCharles Keepax 	case SNDRV_PCM_STATE_OPEN:
235a4f2d87cSCharles Keepax 		return -EBADFD;
236a4f2d87cSCharles Keepax 	case SNDRV_PCM_STATE_XRUN:
237a4f2d87cSCharles Keepax 		return -EPIPE;
238a4f2d87cSCharles Keepax 	default:
239a4f2d87cSCharles Keepax 		break;
240a4f2d87cSCharles Keepax 	}
241a4f2d87cSCharles Keepax 
242b21c60a4SVinod Koul 	if (copy_to_user((__u64 __user *)arg,
243b21c60a4SVinod Koul 				&ioctl_avail, sizeof(ioctl_avail)))
244b21c60a4SVinod Koul 		return -EFAULT;
245b21c60a4SVinod Koul 	return 0;
246b21c60a4SVinod Koul }
247b21c60a4SVinod Koul 
snd_compr_write_data(struct snd_compr_stream * stream,const char __user * buf,size_t count)248b21c60a4SVinod Koul static int snd_compr_write_data(struct snd_compr_stream *stream,
249b21c60a4SVinod Koul 	       const char __user *buf, size_t count)
250b21c60a4SVinod Koul {
251b21c60a4SVinod Koul 	void *dstn;
252b21c60a4SVinod Koul 	size_t copy;
253b21c60a4SVinod Koul 	struct snd_compr_runtime *runtime = stream->runtime;
254f0283b58SCharles Keepax 	/* 64-bit Modulus */
255f0283b58SCharles Keepax 	u64 app_pointer = div64_u64(runtime->total_bytes_available,
256f0283b58SCharles Keepax 				    runtime->buffer_size);
257f0283b58SCharles Keepax 	app_pointer = runtime->total_bytes_available -
258f0283b58SCharles Keepax 		      (app_pointer * runtime->buffer_size);
259b21c60a4SVinod Koul 
260f0283b58SCharles Keepax 	dstn = runtime->buffer + app_pointer;
261b21c60a4SVinod Koul 	pr_debug("copying %ld at %lld\n",
262f0283b58SCharles Keepax 			(unsigned long)count, app_pointer);
263f0283b58SCharles Keepax 	if (count < runtime->buffer_size - app_pointer) {
264b21c60a4SVinod Koul 		if (copy_from_user(dstn, buf, count))
265b21c60a4SVinod Koul 			return -EFAULT;
266b21c60a4SVinod Koul 	} else {
267f0283b58SCharles Keepax 		copy = runtime->buffer_size - app_pointer;
268b21c60a4SVinod Koul 		if (copy_from_user(dstn, buf, copy))
269b21c60a4SVinod Koul 			return -EFAULT;
270b21c60a4SVinod Koul 		if (copy_from_user(runtime->buffer, buf + copy, count - copy))
271b21c60a4SVinod Koul 			return -EFAULT;
272b21c60a4SVinod Koul 	}
273b21c60a4SVinod Koul 	/* if DSP cares, let it know data has been written */
274b21c60a4SVinod Koul 	if (stream->ops->ack)
275b21c60a4SVinod Koul 		stream->ops->ack(stream, count);
276b21c60a4SVinod Koul 	return count;
277b21c60a4SVinod Koul }
278b21c60a4SVinod Koul 
snd_compr_write(struct file * f,const char __user * buf,size_t count,loff_t * offset)279b21c60a4SVinod Koul static ssize_t snd_compr_write(struct file *f, const char __user *buf,
280b21c60a4SVinod Koul 		size_t count, loff_t *offset)
281b21c60a4SVinod Koul {
282b21c60a4SVinod Koul 	struct snd_compr_file *data = f->private_data;
283b21c60a4SVinod Koul 	struct snd_compr_stream *stream;
284b21c60a4SVinod Koul 	size_t avail;
285b21c60a4SVinod Koul 	int retval;
286b21c60a4SVinod Koul 
287b21c60a4SVinod Koul 	if (snd_BUG_ON(!data))
288b21c60a4SVinod Koul 		return -EFAULT;
289b21c60a4SVinod Koul 
290b21c60a4SVinod Koul 	stream = &data->stream;
291b21c60a4SVinod Koul 	mutex_lock(&stream->device->lock);
292b21c60a4SVinod Koul 	/* write is allowed when stream is running or has been steup */
293875f6fffSCharles Keepax 	switch (stream->runtime->state) {
294875f6fffSCharles Keepax 	case SNDRV_PCM_STATE_SETUP:
295875f6fffSCharles Keepax 	case SNDRV_PCM_STATE_PREPARED:
296875f6fffSCharles Keepax 	case SNDRV_PCM_STATE_RUNNING:
297875f6fffSCharles Keepax 		break;
298875f6fffSCharles Keepax 	default:
299b21c60a4SVinod Koul 		mutex_unlock(&stream->device->lock);
300b21c60a4SVinod Koul 		return -EBADFD;
301b21c60a4SVinod Koul 	}
302b21c60a4SVinod Koul 
303b21c60a4SVinod Koul 	avail = snd_compr_get_avail(stream);
304b21c60a4SVinod Koul 	pr_debug("avail returned %ld\n", (unsigned long)avail);
305b21c60a4SVinod Koul 	/* calculate how much we can write to buffer */
306b21c60a4SVinod Koul 	if (avail > count)
307b21c60a4SVinod Koul 		avail = count;
308b21c60a4SVinod Koul 
3094daf891cSCharles Keepax 	if (stream->ops->copy) {
3104daf891cSCharles Keepax 		char __user* cbuf = (char __user*)buf;
3114daf891cSCharles Keepax 		retval = stream->ops->copy(stream, cbuf, avail);
3124daf891cSCharles Keepax 	} else {
313b21c60a4SVinod Koul 		retval = snd_compr_write_data(stream, buf, avail);
3144daf891cSCharles Keepax 	}
315b21c60a4SVinod Koul 	if (retval > 0)
316b21c60a4SVinod Koul 		stream->runtime->total_bytes_available += retval;
317b21c60a4SVinod Koul 
318b21c60a4SVinod Koul 	/* while initiating the stream, write should be called before START
319b21c60a4SVinod Koul 	 * call, so in setup move state */
320b21c60a4SVinod Koul 	if (stream->runtime->state == SNDRV_PCM_STATE_SETUP) {
321b21c60a4SVinod Koul 		stream->runtime->state = SNDRV_PCM_STATE_PREPARED;
322b21c60a4SVinod Koul 		pr_debug("stream prepared, Houston we are good to go\n");
323b21c60a4SVinod Koul 	}
324b21c60a4SVinod Koul 
325b21c60a4SVinod Koul 	mutex_unlock(&stream->device->lock);
326b21c60a4SVinod Koul 	return retval;
327b21c60a4SVinod Koul }
328b21c60a4SVinod Koul 
329b21c60a4SVinod Koul 
snd_compr_read(struct file * f,char __user * buf,size_t count,loff_t * offset)330b21c60a4SVinod Koul static ssize_t snd_compr_read(struct file *f, char __user *buf,
331b21c60a4SVinod Koul 		size_t count, loff_t *offset)
332b21c60a4SVinod Koul {
33349bb6402SCharles Keepax 	struct snd_compr_file *data = f->private_data;
33449bb6402SCharles Keepax 	struct snd_compr_stream *stream;
33549bb6402SCharles Keepax 	size_t avail;
33649bb6402SCharles Keepax 	int retval;
33749bb6402SCharles Keepax 
33849bb6402SCharles Keepax 	if (snd_BUG_ON(!data))
33949bb6402SCharles Keepax 		return -EFAULT;
34049bb6402SCharles Keepax 
34149bb6402SCharles Keepax 	stream = &data->stream;
34249bb6402SCharles Keepax 	mutex_lock(&stream->device->lock);
34349bb6402SCharles Keepax 
34475481347SVinod Koul 	/* read is allowed when stream is running, paused, draining and setup
34575481347SVinod Koul 	 * (yes setup is state which we transition to after stop, so if user
34675481347SVinod Koul 	 * wants to read data after stop we allow that)
34775481347SVinod Koul 	 */
34875481347SVinod Koul 	switch (stream->runtime->state) {
34975481347SVinod Koul 	case SNDRV_PCM_STATE_OPEN:
35075481347SVinod Koul 	case SNDRV_PCM_STATE_PREPARED:
35175481347SVinod Koul 	case SNDRV_PCM_STATE_SUSPENDED:
35275481347SVinod Koul 	case SNDRV_PCM_STATE_DISCONNECTED:
35349bb6402SCharles Keepax 		retval = -EBADFD;
35449bb6402SCharles Keepax 		goto out;
355a4f2d87cSCharles Keepax 	case SNDRV_PCM_STATE_XRUN:
356a4f2d87cSCharles Keepax 		retval = -EPIPE;
357a4f2d87cSCharles Keepax 		goto out;
35849bb6402SCharles Keepax 	}
35949bb6402SCharles Keepax 
36049bb6402SCharles Keepax 	avail = snd_compr_get_avail(stream);
36149bb6402SCharles Keepax 	pr_debug("avail returned %ld\n", (unsigned long)avail);
36249bb6402SCharles Keepax 	/* calculate how much we can read from buffer */
36349bb6402SCharles Keepax 	if (avail > count)
36449bb6402SCharles Keepax 		avail = count;
36549bb6402SCharles Keepax 
36649bb6402SCharles Keepax 	if (stream->ops->copy) {
36749bb6402SCharles Keepax 		retval = stream->ops->copy(stream, buf, avail);
36849bb6402SCharles Keepax 	} else {
36949bb6402SCharles Keepax 		retval = -ENXIO;
37049bb6402SCharles Keepax 		goto out;
37149bb6402SCharles Keepax 	}
37249bb6402SCharles Keepax 	if (retval > 0)
37349bb6402SCharles Keepax 		stream->runtime->total_bytes_transferred += retval;
37449bb6402SCharles Keepax 
37549bb6402SCharles Keepax out:
37649bb6402SCharles Keepax 	mutex_unlock(&stream->device->lock);
37749bb6402SCharles Keepax 	return retval;
378b21c60a4SVinod Koul }
379b21c60a4SVinod Koul 
snd_compr_mmap(struct file * f,struct vm_area_struct * vma)380b21c60a4SVinod Koul static int snd_compr_mmap(struct file *f, struct vm_area_struct *vma)
381b21c60a4SVinod Koul {
382b21c60a4SVinod Koul 	return -ENXIO;
383b21c60a4SVinod Koul }
384b21c60a4SVinod Koul 
snd_compr_get_poll(struct snd_compr_stream * stream)385680ef72aSAl Viro static __poll_t snd_compr_get_poll(struct snd_compr_stream *stream)
386b21c60a4SVinod Koul {
387b21c60a4SVinod Koul 	if (stream->direction == SND_COMPRESS_PLAYBACK)
388a9a08845SLinus Torvalds 		return EPOLLOUT | EPOLLWRNORM;
389b21c60a4SVinod Koul 	else
390a9a08845SLinus Torvalds 		return EPOLLIN | EPOLLRDNORM;
391b21c60a4SVinod Koul }
392b21c60a4SVinod Koul 
snd_compr_poll(struct file * f,poll_table * wait)393680ef72aSAl Viro static __poll_t snd_compr_poll(struct file *f, poll_table *wait)
394b21c60a4SVinod Koul {
395b21c60a4SVinod Koul 	struct snd_compr_file *data = f->private_data;
396b21c60a4SVinod Koul 	struct snd_compr_stream *stream;
397b21c60a4SVinod Koul 	size_t avail;
398680ef72aSAl Viro 	__poll_t retval = 0;
399b21c60a4SVinod Koul 
400b21c60a4SVinod Koul 	if (snd_BUG_ON(!data))
401a9a08845SLinus Torvalds 		return EPOLLERR;
4025bd05390SCharles Keepax 
403b21c60a4SVinod Koul 	stream = &data->stream;
404b21c60a4SVinod Koul 
405b21c60a4SVinod Koul 	mutex_lock(&stream->device->lock);
406a4f2d87cSCharles Keepax 
407a4f2d87cSCharles Keepax 	switch (stream->runtime->state) {
408a4f2d87cSCharles Keepax 	case SNDRV_PCM_STATE_OPEN:
409a4f2d87cSCharles Keepax 	case SNDRV_PCM_STATE_XRUN:
410a9a08845SLinus Torvalds 		retval = snd_compr_get_poll(stream) | EPOLLERR;
411b21c60a4SVinod Koul 		goto out;
412a4f2d87cSCharles Keepax 	default:
413a4f2d87cSCharles Keepax 		break;
414b21c60a4SVinod Koul 	}
415a4f2d87cSCharles Keepax 
416b21c60a4SVinod Koul 	poll_wait(f, &stream->runtime->sleep, wait);
417b21c60a4SVinod Koul 
418b21c60a4SVinod Koul 	avail = snd_compr_get_avail(stream);
419b21c60a4SVinod Koul 	pr_debug("avail is %ld\n", (unsigned long)avail);
420b21c60a4SVinod Koul 	/* check if we have at least one fragment to fill */
421b21c60a4SVinod Koul 	switch (stream->runtime->state) {
422b21c60a4SVinod Koul 	case SNDRV_PCM_STATE_DRAINING:
423b21c60a4SVinod Koul 		/* stream has been woken up after drain is complete
424b21c60a4SVinod Koul 		 * draining done so set stream state to stopped
425b21c60a4SVinod Koul 		 */
426b21c60a4SVinod Koul 		retval = snd_compr_get_poll(stream);
427b21c60a4SVinod Koul 		stream->runtime->state = SNDRV_PCM_STATE_SETUP;
428b21c60a4SVinod Koul 		break;
429b21c60a4SVinod Koul 	case SNDRV_PCM_STATE_RUNNING:
430b21c60a4SVinod Koul 	case SNDRV_PCM_STATE_PREPARED:
431b21c60a4SVinod Koul 	case SNDRV_PCM_STATE_PAUSED:
432b21c60a4SVinod Koul 		if (avail >= stream->runtime->fragment_size)
433b21c60a4SVinod Koul 			retval = snd_compr_get_poll(stream);
434b21c60a4SVinod Koul 		break;
435b21c60a4SVinod Koul 	default:
436a9a08845SLinus Torvalds 		retval = snd_compr_get_poll(stream) | EPOLLERR;
437b21c60a4SVinod Koul 		break;
438b21c60a4SVinod Koul 	}
439b21c60a4SVinod Koul out:
440b21c60a4SVinod Koul 	mutex_unlock(&stream->device->lock);
441b21c60a4SVinod Koul 	return retval;
442b21c60a4SVinod Koul }
443b21c60a4SVinod Koul 
444b21c60a4SVinod Koul static int
snd_compr_get_caps(struct snd_compr_stream * stream,unsigned long arg)445b21c60a4SVinod Koul snd_compr_get_caps(struct snd_compr_stream *stream, unsigned long arg)
446b21c60a4SVinod Koul {
447b21c60a4SVinod Koul 	int retval;
448b21c60a4SVinod Koul 	struct snd_compr_caps caps;
449b21c60a4SVinod Koul 
450b21c60a4SVinod Koul 	if (!stream->ops->get_caps)
451b21c60a4SVinod Koul 		return -ENXIO;
452b21c60a4SVinod Koul 
4531c62e9f2SDan Carpenter 	memset(&caps, 0, sizeof(caps));
454b21c60a4SVinod Koul 	retval = stream->ops->get_caps(stream, &caps);
455b21c60a4SVinod Koul 	if (retval)
456b21c60a4SVinod Koul 		goto out;
457b21c60a4SVinod Koul 	if (copy_to_user((void __user *)arg, &caps, sizeof(caps)))
458b21c60a4SVinod Koul 		retval = -EFAULT;
459b21c60a4SVinod Koul out:
460b21c60a4SVinod Koul 	return retval;
461b21c60a4SVinod Koul }
462b21c60a4SVinod Koul 
463462b3f16STakashi Iwai #ifndef COMPR_CODEC_CAPS_OVERFLOW
464b21c60a4SVinod Koul static int
snd_compr_get_codec_caps(struct snd_compr_stream * stream,unsigned long arg)465b21c60a4SVinod Koul snd_compr_get_codec_caps(struct snd_compr_stream *stream, unsigned long arg)
466b21c60a4SVinod Koul {
467b21c60a4SVinod Koul 	int retval;
468b21c60a4SVinod Koul 	struct snd_compr_codec_caps *caps;
469b21c60a4SVinod Koul 
470b21c60a4SVinod Koul 	if (!stream->ops->get_codec_caps)
471b21c60a4SVinod Koul 		return -ENXIO;
472b21c60a4SVinod Koul 
47347966e97STakashi Iwai 	caps = kzalloc(sizeof(*caps), GFP_KERNEL);
474b21c60a4SVinod Koul 	if (!caps)
475b21c60a4SVinod Koul 		return -ENOMEM;
476b21c60a4SVinod Koul 
477b21c60a4SVinod Koul 	retval = stream->ops->get_codec_caps(stream, caps);
478b21c60a4SVinod Koul 	if (retval)
479b21c60a4SVinod Koul 		goto out;
480b21c60a4SVinod Koul 	if (copy_to_user((void __user *)arg, caps, sizeof(*caps)))
481b21c60a4SVinod Koul 		retval = -EFAULT;
482b21c60a4SVinod Koul 
483b21c60a4SVinod Koul out:
484b21c60a4SVinod Koul 	kfree(caps);
485b21c60a4SVinod Koul 	return retval;
486b21c60a4SVinod Koul }
487462b3f16STakashi Iwai #endif /* !COMPR_CODEC_CAPS_OVERFLOW */
488b21c60a4SVinod Koul 
snd_compr_malloc_pages(struct snd_compr_stream * stream,size_t size)489b9759ef2SCezary Rojewski int snd_compr_malloc_pages(struct snd_compr_stream *stream, size_t size)
490b9759ef2SCezary Rojewski {
491b9759ef2SCezary Rojewski 	struct snd_dma_buffer *dmab;
492b9759ef2SCezary Rojewski 	int ret;
493b9759ef2SCezary Rojewski 
494b9759ef2SCezary Rojewski 	if (snd_BUG_ON(!(stream) || !(stream)->runtime))
495b9759ef2SCezary Rojewski 		return -EINVAL;
496b9759ef2SCezary Rojewski 	dmab = kzalloc(sizeof(*dmab), GFP_KERNEL);
497b9759ef2SCezary Rojewski 	if (!dmab)
498b9759ef2SCezary Rojewski 		return -ENOMEM;
499b9759ef2SCezary Rojewski 	dmab->dev = stream->dma_buffer.dev;
500b9759ef2SCezary Rojewski 	ret = snd_dma_alloc_pages(dmab->dev.type, dmab->dev.dev, size, dmab);
501b9759ef2SCezary Rojewski 	if (ret < 0) {
502b9759ef2SCezary Rojewski 		kfree(dmab);
503b9759ef2SCezary Rojewski 		return ret;
504b9759ef2SCezary Rojewski 	}
505b9759ef2SCezary Rojewski 
506b9759ef2SCezary Rojewski 	snd_compr_set_runtime_buffer(stream, dmab);
507b9759ef2SCezary Rojewski 	stream->runtime->dma_bytes = size;
508b9759ef2SCezary Rojewski 	return 1;
509b9759ef2SCezary Rojewski }
510b9759ef2SCezary Rojewski EXPORT_SYMBOL(snd_compr_malloc_pages);
511b9759ef2SCezary Rojewski 
snd_compr_free_pages(struct snd_compr_stream * stream)512b9759ef2SCezary Rojewski int snd_compr_free_pages(struct snd_compr_stream *stream)
513b9759ef2SCezary Rojewski {
514cd91fd9fSPierre-Louis Bossart 	struct snd_compr_runtime *runtime;
515b9759ef2SCezary Rojewski 
516b9759ef2SCezary Rojewski 	if (snd_BUG_ON(!(stream) || !(stream)->runtime))
517b9759ef2SCezary Rojewski 		return -EINVAL;
518cd91fd9fSPierre-Louis Bossart 	runtime = stream->runtime;
519b9759ef2SCezary Rojewski 	if (runtime->dma_area == NULL)
520b9759ef2SCezary Rojewski 		return 0;
521b9759ef2SCezary Rojewski 	if (runtime->dma_buffer_p != &stream->dma_buffer) {
522b9759ef2SCezary Rojewski 		/* It's a newly allocated buffer. Release it now. */
523b9759ef2SCezary Rojewski 		snd_dma_free_pages(runtime->dma_buffer_p);
524b9759ef2SCezary Rojewski 		kfree(runtime->dma_buffer_p);
525b9759ef2SCezary Rojewski 	}
526b9759ef2SCezary Rojewski 
527b9759ef2SCezary Rojewski 	snd_compr_set_runtime_buffer(stream, NULL);
528b9759ef2SCezary Rojewski 	return 0;
529b9759ef2SCezary Rojewski }
530b9759ef2SCezary Rojewski EXPORT_SYMBOL(snd_compr_free_pages);
531b9759ef2SCezary Rojewski 
532b21c60a4SVinod Koul /* revisit this with snd_pcm_preallocate_xxx */
snd_compr_allocate_buffer(struct snd_compr_stream * stream,struct snd_compr_params * params)533b21c60a4SVinod Koul static int snd_compr_allocate_buffer(struct snd_compr_stream *stream,
534b21c60a4SVinod Koul 		struct snd_compr_params *params)
535b21c60a4SVinod Koul {
536b21c60a4SVinod Koul 	unsigned int buffer_size;
537d00f749bSSrinivas Kandagatla 	void *buffer = NULL;
538b21c60a4SVinod Koul 
539b21c60a4SVinod Koul 	buffer_size = params->buffer.fragment_size * params->buffer.fragments;
540b21c60a4SVinod Koul 	if (stream->ops->copy) {
541b21c60a4SVinod Koul 		buffer = NULL;
542b21c60a4SVinod Koul 		/* if copy is defined the driver will be required to copy
543b21c60a4SVinod Koul 		 * the data from core
544b21c60a4SVinod Koul 		 */
545b21c60a4SVinod Koul 	} else {
546d00f749bSSrinivas Kandagatla 		if (stream->runtime->dma_buffer_p) {
547d00f749bSSrinivas Kandagatla 
548d00f749bSSrinivas Kandagatla 			if (buffer_size > stream->runtime->dma_buffer_p->bytes)
549*b53a41eeSTakashi Iwai 				dev_err(stream->device->dev,
550d00f749bSSrinivas Kandagatla 						"Not enough DMA buffer");
551d00f749bSSrinivas Kandagatla 			else
552d00f749bSSrinivas Kandagatla 				buffer = stream->runtime->dma_buffer_p->area;
553d00f749bSSrinivas Kandagatla 
554d00f749bSSrinivas Kandagatla 		} else {
555b21c60a4SVinod Koul 			buffer = kmalloc(buffer_size, GFP_KERNEL);
556d00f749bSSrinivas Kandagatla 		}
557d00f749bSSrinivas Kandagatla 
558b21c60a4SVinod Koul 		if (!buffer)
559b21c60a4SVinod Koul 			return -ENOMEM;
560b21c60a4SVinod Koul 	}
561b21c60a4SVinod Koul 	stream->runtime->fragment_size = params->buffer.fragment_size;
562b21c60a4SVinod Koul 	stream->runtime->fragments = params->buffer.fragments;
563b21c60a4SVinod Koul 	stream->runtime->buffer = buffer;
564b21c60a4SVinod Koul 	stream->runtime->buffer_size = buffer_size;
565b21c60a4SVinod Koul 	return 0;
566b21c60a4SVinod Koul }
567b21c60a4SVinod Koul 
snd_compress_check_input(struct snd_compr_params * params)5684dc040a0SVinod Koul static int snd_compress_check_input(struct snd_compr_params *params)
5694dc040a0SVinod Koul {
5704dc040a0SVinod Koul 	/* first let's check the buffer parameter's */
5714dc040a0SVinod Koul 	if (params->buffer.fragment_size == 0 ||
572d3645b05SXiaojun Sang 	    params->buffer.fragments > U32_MAX / params->buffer.fragment_size ||
573678e2b44SDan Carpenter 	    params->buffer.fragments == 0)
5744dc040a0SVinod Koul 		return -EINVAL;
5754dc040a0SVinod Koul 
576fb4a9779SVinod Koul 	/* now codec parameters */
577fb4a9779SVinod Koul 	if (params->codec.id == 0 || params->codec.id > SND_AUDIOCODEC_MAX)
578fb4a9779SVinod Koul 		return -EINVAL;
579fb4a9779SVinod Koul 
580fb4a9779SVinod Koul 	if (params->codec.ch_in == 0 || params->codec.ch_out == 0)
581fb4a9779SVinod Koul 		return -EINVAL;
582fb4a9779SVinod Koul 
5834dc040a0SVinod Koul 	return 0;
5844dc040a0SVinod Koul }
5854dc040a0SVinod Koul 
586b21c60a4SVinod Koul static int
snd_compr_set_params(struct snd_compr_stream * stream,unsigned long arg)587b21c60a4SVinod Koul snd_compr_set_params(struct snd_compr_stream *stream, unsigned long arg)
588b21c60a4SVinod Koul {
589b21c60a4SVinod Koul 	struct snd_compr_params *params;
590b21c60a4SVinod Koul 	int retval;
591b21c60a4SVinod Koul 
5927ea9ee00SSrinivas Kandagatla 	if (stream->runtime->state == SNDRV_PCM_STATE_OPEN || stream->next_track) {
593b21c60a4SVinod Koul 		/*
594b21c60a4SVinod Koul 		 * we should allow parameter change only when stream has been
595b21c60a4SVinod Koul 		 * opened not in other cases
596b21c60a4SVinod Koul 		 */
597c2f14ba7SMarkus Elfring 		params = memdup_user((void __user *)arg, sizeof(*params));
598c2f14ba7SMarkus Elfring 		if (IS_ERR(params))
599c2f14ba7SMarkus Elfring 			return PTR_ERR(params);
6004dc040a0SVinod Koul 
6014dc040a0SVinod Koul 		retval = snd_compress_check_input(params);
6024dc040a0SVinod Koul 		if (retval)
6034dc040a0SVinod Koul 			goto out;
6044dc040a0SVinod Koul 
605b21c60a4SVinod Koul 		retval = snd_compr_allocate_buffer(stream, params);
606b21c60a4SVinod Koul 		if (retval) {
607769fab2aSJesper Juhl 			retval = -ENOMEM;
608769fab2aSJesper Juhl 			goto out;
609b21c60a4SVinod Koul 		}
6104dc040a0SVinod Koul 
611b21c60a4SVinod Koul 		retval = stream->ops->set_params(stream, params);
612b21c60a4SVinod Koul 		if (retval)
613b21c60a4SVinod Koul 			goto out;
61449bb6402SCharles Keepax 
6157ea9ee00SSrinivas Kandagatla 		if (stream->next_track)
6167ea9ee00SSrinivas Kandagatla 			goto out;
6177ea9ee00SSrinivas Kandagatla 
6189727b490SJeeja KP 		stream->metadata_set = false;
6199727b490SJeeja KP 		stream->next_track = false;
62049bb6402SCharles Keepax 
62149bb6402SCharles Keepax 		stream->runtime->state = SNDRV_PCM_STATE_SETUP;
622769fab2aSJesper Juhl 	} else {
623b21c60a4SVinod Koul 		return -EPERM;
624769fab2aSJesper Juhl 	}
625b21c60a4SVinod Koul out:
626b21c60a4SVinod Koul 	kfree(params);
627b21c60a4SVinod Koul 	return retval;
628b21c60a4SVinod Koul }
629b21c60a4SVinod Koul 
630b21c60a4SVinod Koul static int
snd_compr_get_params(struct snd_compr_stream * stream,unsigned long arg)631b21c60a4SVinod Koul snd_compr_get_params(struct snd_compr_stream *stream, unsigned long arg)
632b21c60a4SVinod Koul {
633b21c60a4SVinod Koul 	struct snd_codec *params;
634b21c60a4SVinod Koul 	int retval;
635b21c60a4SVinod Koul 
636b21c60a4SVinod Koul 	if (!stream->ops->get_params)
637b21c60a4SVinod Koul 		return -EBADFD;
638b21c60a4SVinod Koul 
63947966e97STakashi Iwai 	params = kzalloc(sizeof(*params), GFP_KERNEL);
640b21c60a4SVinod Koul 	if (!params)
641b21c60a4SVinod Koul 		return -ENOMEM;
642b21c60a4SVinod Koul 	retval = stream->ops->get_params(stream, params);
643b21c60a4SVinod Koul 	if (retval)
644b21c60a4SVinod Koul 		goto out;
645b21c60a4SVinod Koul 	if (copy_to_user((char __user *)arg, params, sizeof(*params)))
646b21c60a4SVinod Koul 		retval = -EFAULT;
647b21c60a4SVinod Koul 
648b21c60a4SVinod Koul out:
649b21c60a4SVinod Koul 	kfree(params);
650b21c60a4SVinod Koul 	return retval;
651b21c60a4SVinod Koul }
652b21c60a4SVinod Koul 
6539727b490SJeeja KP static int
snd_compr_get_metadata(struct snd_compr_stream * stream,unsigned long arg)6549727b490SJeeja KP snd_compr_get_metadata(struct snd_compr_stream *stream, unsigned long arg)
6559727b490SJeeja KP {
6569727b490SJeeja KP 	struct snd_compr_metadata metadata;
6579727b490SJeeja KP 	int retval;
6589727b490SJeeja KP 
6599727b490SJeeja KP 	if (!stream->ops->get_metadata)
6609727b490SJeeja KP 		return -ENXIO;
6619727b490SJeeja KP 
6629727b490SJeeja KP 	if (copy_from_user(&metadata, (void __user *)arg, sizeof(metadata)))
6639727b490SJeeja KP 		return -EFAULT;
6649727b490SJeeja KP 
6659727b490SJeeja KP 	retval = stream->ops->get_metadata(stream, &metadata);
6669727b490SJeeja KP 	if (retval != 0)
6679727b490SJeeja KP 		return retval;
6689727b490SJeeja KP 
6699727b490SJeeja KP 	if (copy_to_user((void __user *)arg, &metadata, sizeof(metadata)))
6709727b490SJeeja KP 		return -EFAULT;
6719727b490SJeeja KP 
6729727b490SJeeja KP 	return 0;
6739727b490SJeeja KP }
6749727b490SJeeja KP 
6759727b490SJeeja KP static int
snd_compr_set_metadata(struct snd_compr_stream * stream,unsigned long arg)6769727b490SJeeja KP snd_compr_set_metadata(struct snd_compr_stream *stream, unsigned long arg)
6779727b490SJeeja KP {
6789727b490SJeeja KP 	struct snd_compr_metadata metadata;
6799727b490SJeeja KP 	int retval;
6809727b490SJeeja KP 
6819727b490SJeeja KP 	if (!stream->ops->set_metadata)
6829727b490SJeeja KP 		return -ENXIO;
6839727b490SJeeja KP 	/*
6849727b490SJeeja KP 	* we should allow parameter change only when stream has been
6859727b490SJeeja KP 	* opened not in other cases
6869727b490SJeeja KP 	*/
6879727b490SJeeja KP 	if (copy_from_user(&metadata, (void __user *)arg, sizeof(metadata)))
6889727b490SJeeja KP 		return -EFAULT;
6899727b490SJeeja KP 
6909727b490SJeeja KP 	retval = stream->ops->set_metadata(stream, &metadata);
6919727b490SJeeja KP 	stream->metadata_set = true;
6929727b490SJeeja KP 
6939727b490SJeeja KP 	return retval;
6949727b490SJeeja KP }
6959727b490SJeeja KP 
696b21c60a4SVinod Koul static inline int
snd_compr_tstamp(struct snd_compr_stream * stream,unsigned long arg)697b21c60a4SVinod Koul snd_compr_tstamp(struct snd_compr_stream *stream, unsigned long arg)
698b21c60a4SVinod Koul {
69917ac8e5cSRichard Fitzgerald 	struct snd_compr_tstamp tstamp = {0};
70017ac8e5cSRichard Fitzgerald 	int ret;
701b21c60a4SVinod Koul 
70217ac8e5cSRichard Fitzgerald 	ret = snd_compr_update_tstamp(stream, &tstamp);
70317ac8e5cSRichard Fitzgerald 	if (ret == 0)
70417ac8e5cSRichard Fitzgerald 		ret = copy_to_user((struct snd_compr_tstamp __user *)arg,
705b21c60a4SVinod Koul 			&tstamp, sizeof(tstamp)) ? -EFAULT : 0;
70617ac8e5cSRichard Fitzgerald 	return ret;
707b21c60a4SVinod Koul }
708b21c60a4SVinod Koul 
snd_compr_pause(struct snd_compr_stream * stream)709b21c60a4SVinod Koul static int snd_compr_pause(struct snd_compr_stream *stream)
710b21c60a4SVinod Koul {
711b21c60a4SVinod Koul 	int retval;
712b21c60a4SVinod Koul 
7139be9f2d3SGyeongtaek Lee 	switch (stream->runtime->state) {
7149be9f2d3SGyeongtaek Lee 	case SNDRV_PCM_STATE_RUNNING:
715b21c60a4SVinod Koul 		retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_PAUSE_PUSH);
7166b18f793SVinod Koul 		if (!retval)
717b21c60a4SVinod Koul 			stream->runtime->state = SNDRV_PCM_STATE_PAUSED;
7189be9f2d3SGyeongtaek Lee 		break;
7199be9f2d3SGyeongtaek Lee 	case SNDRV_PCM_STATE_DRAINING:
7209be9f2d3SGyeongtaek Lee 		if (!stream->device->use_pause_in_draining)
7219be9f2d3SGyeongtaek Lee 			return -EPERM;
7229be9f2d3SGyeongtaek Lee 		retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_PAUSE_PUSH);
7239be9f2d3SGyeongtaek Lee 		if (!retval)
7249be9f2d3SGyeongtaek Lee 			stream->pause_in_draining = true;
7259be9f2d3SGyeongtaek Lee 		break;
7269be9f2d3SGyeongtaek Lee 	default:
7279be9f2d3SGyeongtaek Lee 		return -EPERM;
7289be9f2d3SGyeongtaek Lee 	}
729b21c60a4SVinod Koul 	return retval;
730b21c60a4SVinod Koul }
731b21c60a4SVinod Koul 
snd_compr_resume(struct snd_compr_stream * stream)732b21c60a4SVinod Koul static int snd_compr_resume(struct snd_compr_stream *stream)
733b21c60a4SVinod Koul {
734b21c60a4SVinod Koul 	int retval;
735b21c60a4SVinod Koul 
7369be9f2d3SGyeongtaek Lee 	switch (stream->runtime->state) {
7379be9f2d3SGyeongtaek Lee 	case SNDRV_PCM_STATE_PAUSED:
738b21c60a4SVinod Koul 		retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_PAUSE_RELEASE);
739b21c60a4SVinod Koul 		if (!retval)
740b21c60a4SVinod Koul 			stream->runtime->state = SNDRV_PCM_STATE_RUNNING;
7419be9f2d3SGyeongtaek Lee 		break;
7429be9f2d3SGyeongtaek Lee 	case SNDRV_PCM_STATE_DRAINING:
7439be9f2d3SGyeongtaek Lee 		if (!stream->pause_in_draining)
7449be9f2d3SGyeongtaek Lee 			return -EPERM;
7459be9f2d3SGyeongtaek Lee 		retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_PAUSE_RELEASE);
7469be9f2d3SGyeongtaek Lee 		if (!retval)
7479be9f2d3SGyeongtaek Lee 			stream->pause_in_draining = false;
7489be9f2d3SGyeongtaek Lee 		break;
7499be9f2d3SGyeongtaek Lee 	default:
7509be9f2d3SGyeongtaek Lee 		return -EPERM;
7519be9f2d3SGyeongtaek Lee 	}
752b21c60a4SVinod Koul 	return retval;
753b21c60a4SVinod Koul }
754b21c60a4SVinod Koul 
snd_compr_start(struct snd_compr_stream * stream)755b21c60a4SVinod Koul static int snd_compr_start(struct snd_compr_stream *stream)
756b21c60a4SVinod Koul {
757b21c60a4SVinod Koul 	int retval;
758b21c60a4SVinod Koul 
7594475f8c4SCharles Keepax 	switch (stream->runtime->state) {
7604475f8c4SCharles Keepax 	case SNDRV_PCM_STATE_SETUP:
7614475f8c4SCharles Keepax 		if (stream->direction != SND_COMPRESS_CAPTURE)
762b21c60a4SVinod Koul 			return -EPERM;
7634475f8c4SCharles Keepax 		break;
7644475f8c4SCharles Keepax 	case SNDRV_PCM_STATE_PREPARED:
7654475f8c4SCharles Keepax 		break;
7664475f8c4SCharles Keepax 	default:
7674475f8c4SCharles Keepax 		return -EPERM;
7684475f8c4SCharles Keepax 	}
7694475f8c4SCharles Keepax 
770b21c60a4SVinod Koul 	retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_START);
771b21c60a4SVinod Koul 	if (!retval)
772b21c60a4SVinod Koul 		stream->runtime->state = SNDRV_PCM_STATE_RUNNING;
773b21c60a4SVinod Koul 	return retval;
774b21c60a4SVinod Koul }
775b21c60a4SVinod Koul 
snd_compr_stop(struct snd_compr_stream * stream)776b21c60a4SVinod Koul static int snd_compr_stop(struct snd_compr_stream *stream)
777b21c60a4SVinod Koul {
778b21c60a4SVinod Koul 	int retval;
779b21c60a4SVinod Koul 
78026c3f154SCharles Keepax 	switch (stream->runtime->state) {
78126c3f154SCharles Keepax 	case SNDRV_PCM_STATE_OPEN:
78226c3f154SCharles Keepax 	case SNDRV_PCM_STATE_SETUP:
78326c3f154SCharles Keepax 	case SNDRV_PCM_STATE_PREPARED:
784b21c60a4SVinod Koul 		return -EPERM;
78526c3f154SCharles Keepax 	default:
78626c3f154SCharles Keepax 		break;
78726c3f154SCharles Keepax 	}
78826c3f154SCharles Keepax 
789b21c60a4SVinod Koul 	retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_STOP);
790b21c60a4SVinod Koul 	if (!retval) {
791f79a732aSVinod Koul 		/* clear flags and stop any drain wait */
792f79a732aSVinod Koul 		stream->partial_drain = false;
793f79a732aSVinod Koul 		stream->metadata_set = false;
7949be9f2d3SGyeongtaek Lee 		stream->pause_in_draining = false;
795917f4b5cSVinod Koul 		snd_compr_drain_notify(stream);
7968b21460aSVinod Koul 		stream->runtime->total_bytes_available = 0;
7978b21460aSVinod Koul 		stream->runtime->total_bytes_transferred = 0;
798b21c60a4SVinod Koul 	}
799b21c60a4SVinod Koul 	return retval;
800b21c60a4SVinod Koul }
801b21c60a4SVinod Koul 
error_delayed_work(struct work_struct * work)802a4f2d87cSCharles Keepax static void error_delayed_work(struct work_struct *work)
803a4f2d87cSCharles Keepax {
804a4f2d87cSCharles Keepax 	struct snd_compr_stream *stream;
805a4f2d87cSCharles Keepax 
806a4f2d87cSCharles Keepax 	stream = container_of(work, struct snd_compr_stream, error_work.work);
807a4f2d87cSCharles Keepax 
808a4f2d87cSCharles Keepax 	mutex_lock(&stream->device->lock);
809a4f2d87cSCharles Keepax 
810a4f2d87cSCharles Keepax 	stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_STOP);
811a4f2d87cSCharles Keepax 	wake_up(&stream->runtime->sleep);
812a4f2d87cSCharles Keepax 
813a4f2d87cSCharles Keepax 	mutex_unlock(&stream->device->lock);
814a4f2d87cSCharles Keepax }
815a4f2d87cSCharles Keepax 
816539311aaSTakashi Iwai /**
817a4f2d87cSCharles Keepax  * snd_compr_stop_error: Report a fatal error on a stream
818a4f2d87cSCharles Keepax  * @stream: pointer to stream
819a4f2d87cSCharles Keepax  * @state: state to transition the stream to
820a4f2d87cSCharles Keepax  *
821a4f2d87cSCharles Keepax  * Stop the stream and set its state.
822a4f2d87cSCharles Keepax  *
823a4f2d87cSCharles Keepax  * Should be called with compressed device lock held.
824b05d834eSTakashi Iwai  *
825b05d834eSTakashi Iwai  * Return: zero if successful, or a negative error code
826a4f2d87cSCharles Keepax  */
snd_compr_stop_error(struct snd_compr_stream * stream,snd_pcm_state_t state)827a4f2d87cSCharles Keepax int snd_compr_stop_error(struct snd_compr_stream *stream,
828a4f2d87cSCharles Keepax 			 snd_pcm_state_t state)
829a4f2d87cSCharles Keepax {
830a4f2d87cSCharles Keepax 	if (stream->runtime->state == state)
831a4f2d87cSCharles Keepax 		return 0;
832a4f2d87cSCharles Keepax 
833a4f2d87cSCharles Keepax 	stream->runtime->state = state;
834a4f2d87cSCharles Keepax 
835a4f2d87cSCharles Keepax 	pr_debug("Changing state to: %d\n", state);
836a4f2d87cSCharles Keepax 
837a4f2d87cSCharles Keepax 	queue_delayed_work(system_power_efficient_wq, &stream->error_work, 0);
838a4f2d87cSCharles Keepax 
839a4f2d87cSCharles Keepax 	return 0;
840a4f2d87cSCharles Keepax }
841a4f2d87cSCharles Keepax EXPORT_SYMBOL_GPL(snd_compr_stop_error);
842a4f2d87cSCharles Keepax 
snd_compress_wait_for_drain(struct snd_compr_stream * stream)843917f4b5cSVinod Koul static int snd_compress_wait_for_drain(struct snd_compr_stream *stream)
844917f4b5cSVinod Koul {
845f44f2a54SVinod Koul 	int ret;
846f44f2a54SVinod Koul 
847917f4b5cSVinod Koul 	/*
848917f4b5cSVinod Koul 	 * We are called with lock held. So drop the lock while we wait for
849cdb1ee3fSVinod Koul 	 * drain complete notification from the driver
850917f4b5cSVinod Koul 	 *
851917f4b5cSVinod Koul 	 * It is expected that driver will notify the drain completion and then
852917f4b5cSVinod Koul 	 * stream will be moved to SETUP state, even if draining resulted in an
853917f4b5cSVinod Koul 	 * error. We can trigger next track after this.
854917f4b5cSVinod Koul 	 */
855917f4b5cSVinod Koul 	stream->runtime->state = SNDRV_PCM_STATE_DRAINING;
856917f4b5cSVinod Koul 	mutex_unlock(&stream->device->lock);
857917f4b5cSVinod Koul 
858f44f2a54SVinod Koul 	/* we wait for drain to complete here, drain can return when
859f44f2a54SVinod Koul 	 * interruption occurred, wait returned error or success.
860f44f2a54SVinod Koul 	 * For the first two cases we don't do anything different here and
861f44f2a54SVinod Koul 	 * return after waking up
862f44f2a54SVinod Koul 	 */
863f44f2a54SVinod Koul 
864f44f2a54SVinod Koul 	ret = wait_event_interruptible(stream->runtime->sleep,
865f44f2a54SVinod Koul 			(stream->runtime->state != SNDRV_PCM_STATE_DRAINING));
866f44f2a54SVinod Koul 	if (ret == -ERESTARTSYS)
867c5a905d3SColin Ian King 		pr_debug("wait aborted by a signal\n");
868f44f2a54SVinod Koul 	else if (ret)
869f44f2a54SVinod Koul 		pr_debug("wait for drain failed with %d\n", ret);
870f44f2a54SVinod Koul 
871917f4b5cSVinod Koul 
872917f4b5cSVinod Koul 	wake_up(&stream->runtime->sleep);
873917f4b5cSVinod Koul 	mutex_lock(&stream->device->lock);
874917f4b5cSVinod Koul 
875f44f2a54SVinod Koul 	return ret;
876917f4b5cSVinod Koul }
877917f4b5cSVinod Koul 
snd_compr_drain(struct snd_compr_stream * stream)878b21c60a4SVinod Koul static int snd_compr_drain(struct snd_compr_stream *stream)
879b21c60a4SVinod Koul {
880b21c60a4SVinod Koul 	int retval;
881b21c60a4SVinod Koul 
88226c3f154SCharles Keepax 	switch (stream->runtime->state) {
88326c3f154SCharles Keepax 	case SNDRV_PCM_STATE_OPEN:
88426c3f154SCharles Keepax 	case SNDRV_PCM_STATE_SETUP:
88526c3f154SCharles Keepax 	case SNDRV_PCM_STATE_PREPARED:
8863b817994SCharles Keepax 	case SNDRV_PCM_STATE_PAUSED:
887b21c60a4SVinod Koul 		return -EPERM;
8883b817994SCharles Keepax 	case SNDRV_PCM_STATE_XRUN:
8893b817994SCharles Keepax 		return -EPIPE;
89026c3f154SCharles Keepax 	default:
89126c3f154SCharles Keepax 		break;
89226c3f154SCharles Keepax 	}
893917f4b5cSVinod Koul 
894b21c60a4SVinod Koul 	retval = stream->ops->trigger(stream, SND_COMPR_TRIGGER_DRAIN);
895917f4b5cSVinod Koul 	if (retval) {
896f44f2a54SVinod Koul 		pr_debug("SND_COMPR_TRIGGER_DRAIN failed %d\n", retval);
897b21c60a4SVinod Koul 		wake_up(&stream->runtime->sleep);
898917f4b5cSVinod Koul 		return retval;
899b21c60a4SVinod Koul 	}
900917f4b5cSVinod Koul 
901f44f2a54SVinod Koul 	return snd_compress_wait_for_drain(stream);
902b21c60a4SVinod Koul }
903b21c60a4SVinod Koul 
snd_compr_next_track(struct snd_compr_stream * stream)9049727b490SJeeja KP static int snd_compr_next_track(struct snd_compr_stream *stream)
9059727b490SJeeja KP {
9069727b490SJeeja KP 	int retval;
9079727b490SJeeja KP 
9089727b490SJeeja KP 	/* only a running stream can transition to next track */
9099727b490SJeeja KP 	if (stream->runtime->state != SNDRV_PCM_STATE_RUNNING)
9109727b490SJeeja KP 		return -EPERM;
9119727b490SJeeja KP 
912a70ab8a8SCharles Keepax 	/* next track doesn't have any meaning for capture streams */
913a70ab8a8SCharles Keepax 	if (stream->direction == SND_COMPRESS_CAPTURE)
914a70ab8a8SCharles Keepax 		return -EPERM;
915a70ab8a8SCharles Keepax 
916cdb1ee3fSVinod Koul 	/* you can signal next track if this is intended to be a gapless stream
9179727b490SJeeja KP 	 * and current track metadata is set
9189727b490SJeeja KP 	 */
9199727b490SJeeja KP 	if (stream->metadata_set == false)
9209727b490SJeeja KP 		return -EPERM;
9219727b490SJeeja KP 
9229727b490SJeeja KP 	retval = stream->ops->trigger(stream, SND_COMPR_TRIGGER_NEXT_TRACK);
9239727b490SJeeja KP 	if (retval != 0)
9249727b490SJeeja KP 		return retval;
9259727b490SJeeja KP 	stream->metadata_set = false;
9269727b490SJeeja KP 	stream->next_track = true;
9279727b490SJeeja KP 	return 0;
9289727b490SJeeja KP }
9299727b490SJeeja KP 
snd_compr_partial_drain(struct snd_compr_stream * stream)9309727b490SJeeja KP static int snd_compr_partial_drain(struct snd_compr_stream *stream)
9319727b490SJeeja KP {
9329727b490SJeeja KP 	int retval;
93326c3f154SCharles Keepax 
93426c3f154SCharles Keepax 	switch (stream->runtime->state) {
93526c3f154SCharles Keepax 	case SNDRV_PCM_STATE_OPEN:
93626c3f154SCharles Keepax 	case SNDRV_PCM_STATE_SETUP:
93726c3f154SCharles Keepax 	case SNDRV_PCM_STATE_PREPARED:
9383b817994SCharles Keepax 	case SNDRV_PCM_STATE_PAUSED:
9399727b490SJeeja KP 		return -EPERM;
9403b817994SCharles Keepax 	case SNDRV_PCM_STATE_XRUN:
9413b817994SCharles Keepax 		return -EPIPE;
94226c3f154SCharles Keepax 	default:
94326c3f154SCharles Keepax 		break;
94426c3f154SCharles Keepax 	}
94526c3f154SCharles Keepax 
946a70ab8a8SCharles Keepax 	/* partial drain doesn't have any meaning for capture streams */
947a70ab8a8SCharles Keepax 	if (stream->direction == SND_COMPRESS_CAPTURE)
948a70ab8a8SCharles Keepax 		return -EPERM;
949a70ab8a8SCharles Keepax 
9509727b490SJeeja KP 	/* stream can be drained only when next track has been signalled */
9519727b490SJeeja KP 	if (stream->next_track == false)
9529727b490SJeeja KP 		return -EPERM;
9539727b490SJeeja KP 
954f79a732aSVinod Koul 	stream->partial_drain = true;
9559727b490SJeeja KP 	retval = stream->ops->trigger(stream, SND_COMPR_TRIGGER_PARTIAL_DRAIN);
956917f4b5cSVinod Koul 	if (retval) {
957f44f2a54SVinod Koul 		pr_debug("Partial drain returned failure\n");
958917f4b5cSVinod Koul 		wake_up(&stream->runtime->sleep);
959917f4b5cSVinod Koul 		return retval;
960917f4b5cSVinod Koul 	}
9619727b490SJeeja KP 
9629727b490SJeeja KP 	stream->next_track = false;
963917f4b5cSVinod Koul 	return snd_compress_wait_for_drain(stream);
9649727b490SJeeja KP }
9659727b490SJeeja KP 
snd_compr_ioctl(struct file * f,unsigned int cmd,unsigned long arg)966b21c60a4SVinod Koul static long snd_compr_ioctl(struct file *f, unsigned int cmd, unsigned long arg)
967b21c60a4SVinod Koul {
968b21c60a4SVinod Koul 	struct snd_compr_file *data = f->private_data;
969b21c60a4SVinod Koul 	struct snd_compr_stream *stream;
970b21c60a4SVinod Koul 	int retval = -ENOTTY;
971b21c60a4SVinod Koul 
972b21c60a4SVinod Koul 	if (snd_BUG_ON(!data))
973b21c60a4SVinod Koul 		return -EFAULT;
9745bd05390SCharles Keepax 
975b21c60a4SVinod Koul 	stream = &data->stream;
9765bd05390SCharles Keepax 
977b21c60a4SVinod Koul 	mutex_lock(&stream->device->lock);
978b21c60a4SVinod Koul 	switch (_IOC_NR(cmd)) {
979b21c60a4SVinod Koul 	case _IOC_NR(SNDRV_COMPRESS_IOCTL_VERSION):
980a8d30608SVinod Koul 		retval = put_user(SNDRV_COMPRESS_VERSION,
981b21c60a4SVinod Koul 				(int __user *)arg) ? -EFAULT : 0;
982b21c60a4SVinod Koul 		break;
983b21c60a4SVinod Koul 	case _IOC_NR(SNDRV_COMPRESS_GET_CAPS):
984b21c60a4SVinod Koul 		retval = snd_compr_get_caps(stream, arg);
985b21c60a4SVinod Koul 		break;
986462b3f16STakashi Iwai #ifndef COMPR_CODEC_CAPS_OVERFLOW
987b21c60a4SVinod Koul 	case _IOC_NR(SNDRV_COMPRESS_GET_CODEC_CAPS):
988b21c60a4SVinod Koul 		retval = snd_compr_get_codec_caps(stream, arg);
989b21c60a4SVinod Koul 		break;
990462b3f16STakashi Iwai #endif
991b21c60a4SVinod Koul 	case _IOC_NR(SNDRV_COMPRESS_SET_PARAMS):
992b21c60a4SVinod Koul 		retval = snd_compr_set_params(stream, arg);
993b21c60a4SVinod Koul 		break;
994b21c60a4SVinod Koul 	case _IOC_NR(SNDRV_COMPRESS_GET_PARAMS):
995b21c60a4SVinod Koul 		retval = snd_compr_get_params(stream, arg);
996b21c60a4SVinod Koul 		break;
9979727b490SJeeja KP 	case _IOC_NR(SNDRV_COMPRESS_SET_METADATA):
9989727b490SJeeja KP 		retval = snd_compr_set_metadata(stream, arg);
9999727b490SJeeja KP 		break;
10009727b490SJeeja KP 	case _IOC_NR(SNDRV_COMPRESS_GET_METADATA):
10019727b490SJeeja KP 		retval = snd_compr_get_metadata(stream, arg);
10029727b490SJeeja KP 		break;
1003b21c60a4SVinod Koul 	case _IOC_NR(SNDRV_COMPRESS_TSTAMP):
1004b21c60a4SVinod Koul 		retval = snd_compr_tstamp(stream, arg);
1005b21c60a4SVinod Koul 		break;
1006b21c60a4SVinod Koul 	case _IOC_NR(SNDRV_COMPRESS_AVAIL):
1007b21c60a4SVinod Koul 		retval = snd_compr_ioctl_avail(stream, arg);
1008b21c60a4SVinod Koul 		break;
1009b21c60a4SVinod Koul 	case _IOC_NR(SNDRV_COMPRESS_PAUSE):
1010b21c60a4SVinod Koul 		retval = snd_compr_pause(stream);
1011b21c60a4SVinod Koul 		break;
1012b21c60a4SVinod Koul 	case _IOC_NR(SNDRV_COMPRESS_RESUME):
1013b21c60a4SVinod Koul 		retval = snd_compr_resume(stream);
1014b21c60a4SVinod Koul 		break;
1015b21c60a4SVinod Koul 	case _IOC_NR(SNDRV_COMPRESS_START):
1016b21c60a4SVinod Koul 		retval = snd_compr_start(stream);
1017b21c60a4SVinod Koul 		break;
1018b21c60a4SVinod Koul 	case _IOC_NR(SNDRV_COMPRESS_STOP):
1019b21c60a4SVinod Koul 		retval = snd_compr_stop(stream);
1020b21c60a4SVinod Koul 		break;
1021b21c60a4SVinod Koul 	case _IOC_NR(SNDRV_COMPRESS_DRAIN):
1022b21c60a4SVinod Koul 		retval = snd_compr_drain(stream);
1023b21c60a4SVinod Koul 		break;
10249727b490SJeeja KP 	case _IOC_NR(SNDRV_COMPRESS_PARTIAL_DRAIN):
10259727b490SJeeja KP 		retval = snd_compr_partial_drain(stream);
10269727b490SJeeja KP 		break;
10279727b490SJeeja KP 	case _IOC_NR(SNDRV_COMPRESS_NEXT_TRACK):
10289727b490SJeeja KP 		retval = snd_compr_next_track(stream);
10299727b490SJeeja KP 		break;
10309727b490SJeeja KP 
1031b21c60a4SVinod Koul 	}
1032b21c60a4SVinod Koul 	mutex_unlock(&stream->device->lock);
1033b21c60a4SVinod Koul 	return retval;
1034b21c60a4SVinod Koul }
1035b21c60a4SVinod Koul 
1036c1036889SRavindra Lokhande /* support of 32bit userspace on 64bit platforms */
1037c1036889SRavindra Lokhande #ifdef CONFIG_COMPAT
snd_compr_ioctl_compat(struct file * file,unsigned int cmd,unsigned long arg)1038c1036889SRavindra Lokhande static long snd_compr_ioctl_compat(struct file *file, unsigned int cmd,
1039c1036889SRavindra Lokhande 						unsigned long arg)
1040c1036889SRavindra Lokhande {
1041c1036889SRavindra Lokhande 	return snd_compr_ioctl(file, cmd, (unsigned long)compat_ptr(arg));
1042c1036889SRavindra Lokhande }
1043c1036889SRavindra Lokhande #endif
1044c1036889SRavindra Lokhande 
1045b21c60a4SVinod Koul static const struct file_operations snd_compr_file_ops = {
1046b21c60a4SVinod Koul 		.owner =	THIS_MODULE,
1047b21c60a4SVinod Koul 		.open =		snd_compr_open,
1048b21c60a4SVinod Koul 		.release =	snd_compr_free,
1049b21c60a4SVinod Koul 		.write =	snd_compr_write,
1050b21c60a4SVinod Koul 		.read =		snd_compr_read,
1051b21c60a4SVinod Koul 		.unlocked_ioctl = snd_compr_ioctl,
1052c1036889SRavindra Lokhande #ifdef CONFIG_COMPAT
1053c1036889SRavindra Lokhande 		.compat_ioctl = snd_compr_ioctl_compat,
1054c1036889SRavindra Lokhande #endif
1055b21c60a4SVinod Koul 		.mmap =		snd_compr_mmap,
1056b21c60a4SVinod Koul 		.poll =		snd_compr_poll,
1057b21c60a4SVinod Koul };
1058b21c60a4SVinod Koul 
snd_compress_dev_register(struct snd_device * device)1059b21c60a4SVinod Koul static int snd_compress_dev_register(struct snd_device *device)
1060b21c60a4SVinod Koul {
10619725ce39SPierre-Louis Bossart 	int ret;
1062b21c60a4SVinod Koul 	struct snd_compr *compr;
1063b21c60a4SVinod Koul 
1064b21c60a4SVinod Koul 	if (snd_BUG_ON(!device || !device->device_data))
1065b21c60a4SVinod Koul 		return -EBADFD;
1066b21c60a4SVinod Koul 	compr = device->device_data;
1067b21c60a4SVinod Koul 
1068a931b9ceSGuneshwor Singh 	pr_debug("reg device %s, direction %d\n", compr->name,
1069b21c60a4SVinod Koul 			compr->direction);
1070b21c60a4SVinod Koul 	/* register compressed device */
107140a4b263STakashi Iwai 	ret = snd_register_device(SNDRV_DEVICE_TYPE_COMPRESS,
107204c5d5a4STakashi Iwai 				  compr->card, compr->device,
1073*b53a41eeSTakashi Iwai 				  &snd_compr_file_ops, compr, compr->dev);
1074b21c60a4SVinod Koul 	if (ret < 0) {
1075c5a905d3SColin Ian King 		pr_err("snd_register_device failed %d\n", ret);
1076b21c60a4SVinod Koul 		return ret;
1077b21c60a4SVinod Koul 	}
1078b21c60a4SVinod Koul 	return ret;
1079b21c60a4SVinod Koul 
1080b21c60a4SVinod Koul }
1081b21c60a4SVinod Koul 
snd_compress_dev_disconnect(struct snd_device * device)1082b21c60a4SVinod Koul static int snd_compress_dev_disconnect(struct snd_device *device)
1083b21c60a4SVinod Koul {
1084b21c60a4SVinod Koul 	struct snd_compr *compr;
1085b21c60a4SVinod Koul 
1086b21c60a4SVinod Koul 	compr = device->device_data;
1087*b53a41eeSTakashi Iwai 	snd_unregister_device(compr->dev);
1088b21c60a4SVinod Koul 	return 0;
1089b21c60a4SVinod Koul }
1090b21c60a4SVinod Koul 
109131742724SRichard Fitzgerald #ifdef CONFIG_SND_VERBOSE_PROCFS
snd_compress_proc_info_read(struct snd_info_entry * entry,struct snd_info_buffer * buffer)109231742724SRichard Fitzgerald static void snd_compress_proc_info_read(struct snd_info_entry *entry,
109331742724SRichard Fitzgerald 					struct snd_info_buffer *buffer)
109431742724SRichard Fitzgerald {
109531742724SRichard Fitzgerald 	struct snd_compr *compr = (struct snd_compr *)entry->private_data;
109631742724SRichard Fitzgerald 
109731742724SRichard Fitzgerald 	snd_iprintf(buffer, "card: %d\n", compr->card->number);
109831742724SRichard Fitzgerald 	snd_iprintf(buffer, "device: %d\n", compr->device);
109931742724SRichard Fitzgerald 	snd_iprintf(buffer, "stream: %s\n",
110031742724SRichard Fitzgerald 			compr->direction == SND_COMPRESS_PLAYBACK
110131742724SRichard Fitzgerald 				? "PLAYBACK" : "CAPTURE");
110231742724SRichard Fitzgerald 	snd_iprintf(buffer, "id: %s\n", compr->id);
110331742724SRichard Fitzgerald }
110431742724SRichard Fitzgerald 
snd_compress_proc_init(struct snd_compr * compr)110531742724SRichard Fitzgerald static int snd_compress_proc_init(struct snd_compr *compr)
110631742724SRichard Fitzgerald {
110731742724SRichard Fitzgerald 	struct snd_info_entry *entry;
110831742724SRichard Fitzgerald 	char name[16];
110931742724SRichard Fitzgerald 
111031742724SRichard Fitzgerald 	sprintf(name, "compr%i", compr->device);
111131742724SRichard Fitzgerald 	entry = snd_info_create_card_entry(compr->card, name,
111231742724SRichard Fitzgerald 					   compr->card->proc_root);
111331742724SRichard Fitzgerald 	if (!entry)
111431742724SRichard Fitzgerald 		return -ENOMEM;
11156a73cf46SJoe Perches 	entry->mode = S_IFDIR | 0555;
111631742724SRichard Fitzgerald 	compr->proc_root = entry;
111731742724SRichard Fitzgerald 
111831742724SRichard Fitzgerald 	entry = snd_info_create_card_entry(compr->card, "info",
111931742724SRichard Fitzgerald 					   compr->proc_root);
11204a471d7cSTakashi Iwai 	if (entry)
112131742724SRichard Fitzgerald 		snd_info_set_text_ops(entry, compr,
112231742724SRichard Fitzgerald 				      snd_compress_proc_info_read);
112331742724SRichard Fitzgerald 	compr->proc_info_entry = entry;
112431742724SRichard Fitzgerald 
112531742724SRichard Fitzgerald 	return 0;
112631742724SRichard Fitzgerald }
112731742724SRichard Fitzgerald 
snd_compress_proc_done(struct snd_compr * compr)112831742724SRichard Fitzgerald static void snd_compress_proc_done(struct snd_compr *compr)
112931742724SRichard Fitzgerald {
113031742724SRichard Fitzgerald 	snd_info_free_entry(compr->proc_info_entry);
113131742724SRichard Fitzgerald 	compr->proc_info_entry = NULL;
113231742724SRichard Fitzgerald 	snd_info_free_entry(compr->proc_root);
113331742724SRichard Fitzgerald 	compr->proc_root = NULL;
113431742724SRichard Fitzgerald }
1135e5241a8cSRichard Fitzgerald 
snd_compress_set_id(struct snd_compr * compr,const char * id)1136e5241a8cSRichard Fitzgerald static inline void snd_compress_set_id(struct snd_compr *compr, const char *id)
1137e5241a8cSRichard Fitzgerald {
113875b1a8f9SJoe Perches 	strscpy(compr->id, id, sizeof(compr->id));
1139e5241a8cSRichard Fitzgerald }
114031742724SRichard Fitzgerald #else
snd_compress_proc_init(struct snd_compr * compr)114131742724SRichard Fitzgerald static inline int snd_compress_proc_init(struct snd_compr *compr)
114231742724SRichard Fitzgerald {
114331742724SRichard Fitzgerald 	return 0;
114431742724SRichard Fitzgerald }
114531742724SRichard Fitzgerald 
snd_compress_proc_done(struct snd_compr * compr)114631742724SRichard Fitzgerald static inline void snd_compress_proc_done(struct snd_compr *compr)
114731742724SRichard Fitzgerald {
114831742724SRichard Fitzgerald }
1149e5241a8cSRichard Fitzgerald 
snd_compress_set_id(struct snd_compr * compr,const char * id)1150e5241a8cSRichard Fitzgerald static inline void snd_compress_set_id(struct snd_compr *compr, const char *id)
1151e5241a8cSRichard Fitzgerald {
1152e5241a8cSRichard Fitzgerald }
115331742724SRichard Fitzgerald #endif
115431742724SRichard Fitzgerald 
snd_compress_dev_free(struct snd_device * device)115504c5d5a4STakashi Iwai static int snd_compress_dev_free(struct snd_device *device)
115604c5d5a4STakashi Iwai {
115704c5d5a4STakashi Iwai 	struct snd_compr *compr;
115804c5d5a4STakashi Iwai 
115904c5d5a4STakashi Iwai 	compr = device->device_data;
116031742724SRichard Fitzgerald 	snd_compress_proc_done(compr);
1161*b53a41eeSTakashi Iwai 	put_device(compr->dev);
116204c5d5a4STakashi Iwai 	return 0;
116304c5d5a4STakashi Iwai }
116404c5d5a4STakashi Iwai 
1165539311aaSTakashi Iwai /**
1166b21c60a4SVinod Koul  * snd_compress_new: create new compress device
1167b21c60a4SVinod Koul  * @card: sound card pointer
1168b21c60a4SVinod Koul  * @device: device number
1169b21c60a4SVinod Koul  * @dirn: device direction, should be of type enum snd_compr_direction
1170539311aaSTakashi Iwai  * @id: ID string
1171b21c60a4SVinod Koul  * @compr: compress device pointer
1172b05d834eSTakashi Iwai  *
1173b05d834eSTakashi Iwai  * Return: zero if successful, or a negative error code
1174b21c60a4SVinod Koul  */
snd_compress_new(struct snd_card * card,int device,int dirn,const char * id,struct snd_compr * compr)1175b21c60a4SVinod Koul int snd_compress_new(struct snd_card *card, int device,
1176e5241a8cSRichard Fitzgerald 			int dirn, const char *id, struct snd_compr *compr)
1177b21c60a4SVinod Koul {
1178f15ee210STakashi Iwai 	static const struct snd_device_ops ops = {
117904c5d5a4STakashi Iwai 		.dev_free = snd_compress_dev_free,
1180b21c60a4SVinod Koul 		.dev_register = snd_compress_dev_register,
1181b21c60a4SVinod Koul 		.dev_disconnect = snd_compress_dev_disconnect,
1182b21c60a4SVinod Koul 	};
118331742724SRichard Fitzgerald 	int ret;
1184b21c60a4SVinod Koul 
1185b21c60a4SVinod Koul 	compr->card = card;
1186b21c60a4SVinod Koul 	compr->device = device;
1187b21c60a4SVinod Koul 	compr->direction = dirn;
11882c4e3154STakashi Iwai 	mutex_init(&compr->lock);
118904c5d5a4STakashi Iwai 
1190e5241a8cSRichard Fitzgerald 	snd_compress_set_id(compr, id);
1191e5241a8cSRichard Fitzgerald 
1192*b53a41eeSTakashi Iwai 	ret = snd_device_alloc(&compr->dev, card);
1193*b53a41eeSTakashi Iwai 	if (ret)
1194*b53a41eeSTakashi Iwai 		return ret;
1195*b53a41eeSTakashi Iwai 	dev_set_name(compr->dev, "comprC%iD%i", card->number, device);
119604c5d5a4STakashi Iwai 
119731742724SRichard Fitzgerald 	ret = snd_device_new(card, SNDRV_DEV_COMPRESS, compr, &ops);
119831742724SRichard Fitzgerald 	if (ret == 0)
119931742724SRichard Fitzgerald 		snd_compress_proc_init(compr);
1200*b53a41eeSTakashi Iwai 	else
1201*b53a41eeSTakashi Iwai 		put_device(compr->dev);
120231742724SRichard Fitzgerald 
120331742724SRichard Fitzgerald 	return ret;
1204b21c60a4SVinod Koul }
1205b21c60a4SVinod Koul EXPORT_SYMBOL_GPL(snd_compress_new);
1206b21c60a4SVinod Koul 
1207b21c60a4SVinod Koul MODULE_DESCRIPTION("ALSA Compressed offload framework");
1208b21c60a4SVinod Koul MODULE_AUTHOR("Vinod Koul <vinod.koul@linux.intel.com>");
1209b21c60a4SVinod Koul MODULE_LICENSE("GPL v2");
1210