xref: /openbmc/linux/sound/core/compress_offload.c (revision cdb1ee3f)
1b21c60a4SVinod Koul /*
2b21c60a4SVinod Koul  *  compress_core.c - compress offload core
3b21c60a4SVinod Koul  *
4b21c60a4SVinod Koul  *  Copyright (C) 2011 Intel Corporation
5b21c60a4SVinod Koul  *  Authors:	Vinod Koul <vinod.koul@linux.intel.com>
6b21c60a4SVinod Koul  *		Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
7b21c60a4SVinod Koul  *  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
8b21c60a4SVinod Koul  *
9b21c60a4SVinod Koul  *  This program is free software; you can redistribute it and/or modify
10b21c60a4SVinod Koul  *  it under the terms of the GNU General Public License as published by
11b21c60a4SVinod Koul  *  the Free Software Foundation; version 2 of the License.
12b21c60a4SVinod Koul  *
13b21c60a4SVinod Koul  *  This program is distributed in the hope that it will be useful, but
14b21c60a4SVinod Koul  *  WITHOUT ANY WARRANTY; without even the implied warranty of
15b21c60a4SVinod Koul  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16b21c60a4SVinod Koul  *  General Public License for more details.
17b21c60a4SVinod Koul  *
18b21c60a4SVinod Koul  *  You should have received a copy of the GNU General Public License along
19b21c60a4SVinod Koul  *  with this program; if not, write to the Free Software Foundation, Inc.,
20b21c60a4SVinod Koul  *  59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
21b21c60a4SVinod Koul  *
22b21c60a4SVinod Koul  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
23b21c60a4SVinod Koul  *
24b21c60a4SVinod Koul  */
25b21c60a4SVinod Koul #define FORMAT(fmt) "%s: %d: " fmt, __func__, __LINE__
26b21c60a4SVinod Koul #define pr_fmt(fmt) KBUILD_MODNAME ": " FORMAT(fmt)
27b21c60a4SVinod Koul 
28b21c60a4SVinod Koul #include <linux/file.h>
29b21c60a4SVinod Koul #include <linux/fs.h>
30b21c60a4SVinod Koul #include <linux/list.h>
31f0283b58SCharles Keepax #include <linux/math64.h>
32b21c60a4SVinod Koul #include <linux/mm.h>
33b21c60a4SVinod Koul #include <linux/mutex.h>
34b21c60a4SVinod Koul #include <linux/poll.h>
35b21c60a4SVinod Koul #include <linux/slab.h>
36b21c60a4SVinod Koul #include <linux/sched.h>
37f0283b58SCharles Keepax #include <linux/types.h>
38b21c60a4SVinod Koul #include <linux/uio.h>
39b21c60a4SVinod Koul #include <linux/uaccess.h>
40b21c60a4SVinod Koul #include <linux/module.h>
41c1036889SRavindra Lokhande #include <linux/compat.h>
42b21c60a4SVinod Koul #include <sound/core.h>
43b21c60a4SVinod Koul #include <sound/initval.h>
4431742724SRichard Fitzgerald #include <sound/info.h>
45b21c60a4SVinod Koul #include <sound/compress_params.h>
46b21c60a4SVinod Koul #include <sound/compress_offload.h>
47b21c60a4SVinod Koul #include <sound/compress_driver.h>
48b21c60a4SVinod Koul 
49462b3f16STakashi Iwai /* struct snd_compr_codec_caps overflows the ioctl bit size for some
50462b3f16STakashi Iwai  * architectures, so we need to disable the relevant ioctls.
51462b3f16STakashi Iwai  */
52462b3f16STakashi Iwai #if _IOC_SIZEBITS < 14
53462b3f16STakashi Iwai #define COMPR_CODEC_CAPS_OVERFLOW
54462b3f16STakashi Iwai #endif
55462b3f16STakashi Iwai 
56b21c60a4SVinod Koul /* TODO:
57b21c60a4SVinod Koul  * - add substream support for multiple devices in case of
58b21c60a4SVinod Koul  *	SND_DYNAMIC_MINORS is not used
59b21c60a4SVinod Koul  * - Multiple node representation
60b21c60a4SVinod Koul  *	driver should be able to register multiple nodes
61b21c60a4SVinod Koul  */
62b21c60a4SVinod Koul 
63b21c60a4SVinod Koul static DEFINE_MUTEX(device_mutex);
64b21c60a4SVinod Koul 
65b21c60a4SVinod Koul struct snd_compr_file {
66b21c60a4SVinod Koul 	unsigned long caps;
67b21c60a4SVinod Koul 	struct snd_compr_stream stream;
68b21c60a4SVinod Koul };
69b21c60a4SVinod Koul 
70b21c60a4SVinod Koul /*
71b21c60a4SVinod Koul  * a note on stream states used:
7241eb94fdSVinod Koul  * we use following states in the compressed core
73b21c60a4SVinod Koul  * SNDRV_PCM_STATE_OPEN: When stream has been opened.
74b21c60a4SVinod Koul  * SNDRV_PCM_STATE_SETUP: When stream has been initialized. This is done by
7541eb94fdSVinod Koul  *	calling SNDRV_COMPRESS_SET_PARAMS. Running streams will come to this
76b21c60a4SVinod Koul  *	state at stop by calling SNDRV_COMPRESS_STOP, or at end of drain.
77862bca5dSVinod Koul  * SNDRV_PCM_STATE_PREPARED: When a stream has been written to (for
78862bca5dSVinod Koul  *	playback only). User after setting up stream writes the data buffer
79862bca5dSVinod Koul  *	before starting the stream.
80b21c60a4SVinod Koul  * SNDRV_PCM_STATE_RUNNING: When stream has been started and is
81b21c60a4SVinod Koul  *	decoding/encoding and rendering/capturing data.
82b21c60a4SVinod Koul  * SNDRV_PCM_STATE_DRAINING: When stream is draining current data. This is done
83b21c60a4SVinod Koul  *	by calling SNDRV_COMPRESS_DRAIN.
84b21c60a4SVinod Koul  * SNDRV_PCM_STATE_PAUSED: When stream is paused. This is done by calling
85b21c60a4SVinod Koul  *	SNDRV_COMPRESS_PAUSE. It can be stopped or resumed by calling
86b21c60a4SVinod Koul  *	SNDRV_COMPRESS_STOP or SNDRV_COMPRESS_RESUME respectively.
87b21c60a4SVinod Koul  */
88b21c60a4SVinod Koul static int snd_compr_open(struct inode *inode, struct file *f)
89b21c60a4SVinod Koul {
90b21c60a4SVinod Koul 	struct snd_compr *compr;
91b21c60a4SVinod Koul 	struct snd_compr_file *data;
92b21c60a4SVinod Koul 	struct snd_compr_runtime *runtime;
93b21c60a4SVinod Koul 	enum snd_compr_direction dirn;
94b21c60a4SVinod Koul 	int maj = imajor(inode);
95b21c60a4SVinod Koul 	int ret;
96b21c60a4SVinod Koul 
9781cb3246SDan Carpenter 	if ((f->f_flags & O_ACCMODE) == O_WRONLY)
98b21c60a4SVinod Koul 		dirn = SND_COMPRESS_PLAYBACK;
9981cb3246SDan Carpenter 	else if ((f->f_flags & O_ACCMODE) == O_RDONLY)
100b21c60a4SVinod Koul 		dirn = SND_COMPRESS_CAPTURE;
10181cb3246SDan Carpenter 	else
102b21c60a4SVinod Koul 		return -EINVAL;
103b21c60a4SVinod Koul 
104b21c60a4SVinod Koul 	if (maj == snd_major)
105b21c60a4SVinod Koul 		compr = snd_lookup_minor_data(iminor(inode),
106b21c60a4SVinod Koul 					SNDRV_DEVICE_TYPE_COMPRESS);
107b21c60a4SVinod Koul 	else
108b21c60a4SVinod Koul 		return -EBADFD;
109b21c60a4SVinod Koul 
110b21c60a4SVinod Koul 	if (compr == NULL) {
111b21c60a4SVinod Koul 		pr_err("no device data!!!\n");
112b21c60a4SVinod Koul 		return -ENODEV;
113b21c60a4SVinod Koul 	}
114b21c60a4SVinod Koul 
115b21c60a4SVinod Koul 	if (dirn != compr->direction) {
116b21c60a4SVinod Koul 		pr_err("this device doesn't support this direction\n");
117a0830dbdSTakashi Iwai 		snd_card_unref(compr->card);
118b21c60a4SVinod Koul 		return -EINVAL;
119b21c60a4SVinod Koul 	}
120b21c60a4SVinod Koul 
121b21c60a4SVinod Koul 	data = kzalloc(sizeof(*data), GFP_KERNEL);
122a0830dbdSTakashi Iwai 	if (!data) {
123a0830dbdSTakashi Iwai 		snd_card_unref(compr->card);
124b21c60a4SVinod Koul 		return -ENOMEM;
125a0830dbdSTakashi Iwai 	}
126b21c60a4SVinod Koul 	data->stream.ops = compr->ops;
127b21c60a4SVinod Koul 	data->stream.direction = dirn;
128b21c60a4SVinod Koul 	data->stream.private_data = compr->private_data;
129b21c60a4SVinod Koul 	data->stream.device = compr;
130b21c60a4SVinod Koul 	runtime = kzalloc(sizeof(*runtime), GFP_KERNEL);
131b21c60a4SVinod Koul 	if (!runtime) {
132b21c60a4SVinod Koul 		kfree(data);
133a0830dbdSTakashi Iwai 		snd_card_unref(compr->card);
134b21c60a4SVinod Koul 		return -ENOMEM;
135b21c60a4SVinod Koul 	}
136b21c60a4SVinod Koul 	runtime->state = SNDRV_PCM_STATE_OPEN;
137b21c60a4SVinod Koul 	init_waitqueue_head(&runtime->sleep);
138b21c60a4SVinod Koul 	data->stream.runtime = runtime;
139b21c60a4SVinod Koul 	f->private_data = (void *)data;
140b21c60a4SVinod Koul 	mutex_lock(&compr->lock);
141b21c60a4SVinod Koul 	ret = compr->ops->open(&data->stream);
142b21c60a4SVinod Koul 	mutex_unlock(&compr->lock);
143b21c60a4SVinod Koul 	if (ret) {
144b21c60a4SVinod Koul 		kfree(runtime);
145b21c60a4SVinod Koul 		kfree(data);
146b21c60a4SVinod Koul 	}
147a0830dbdSTakashi Iwai 	snd_card_unref(compr->card);
148749d3223SCharles Keepax 	return ret;
149b21c60a4SVinod Koul }
150b21c60a4SVinod Koul 
151b21c60a4SVinod Koul static int snd_compr_free(struct inode *inode, struct file *f)
152b21c60a4SVinod Koul {
153b21c60a4SVinod Koul 	struct snd_compr_file *data = f->private_data;
154b26d19e4SLiam Girdwood 	struct snd_compr_runtime *runtime = data->stream.runtime;
155b26d19e4SLiam Girdwood 
156b26d19e4SLiam Girdwood 	switch (runtime->state) {
157b26d19e4SLiam Girdwood 	case SNDRV_PCM_STATE_RUNNING:
158b26d19e4SLiam Girdwood 	case SNDRV_PCM_STATE_DRAINING:
159b26d19e4SLiam Girdwood 	case SNDRV_PCM_STATE_PAUSED:
160b26d19e4SLiam Girdwood 		data->stream.ops->trigger(&data->stream, SNDRV_PCM_TRIGGER_STOP);
161b26d19e4SLiam Girdwood 		break;
162b26d19e4SLiam Girdwood 	default:
163b26d19e4SLiam Girdwood 		break;
164b26d19e4SLiam Girdwood 	}
165b26d19e4SLiam Girdwood 
166b21c60a4SVinod Koul 	data->stream.ops->free(&data->stream);
167b21c60a4SVinod Koul 	kfree(data->stream.runtime->buffer);
168b21c60a4SVinod Koul 	kfree(data->stream.runtime);
169b21c60a4SVinod Koul 	kfree(data);
170b21c60a4SVinod Koul 	return 0;
171b21c60a4SVinod Koul }
172b21c60a4SVinod Koul 
17317ac8e5cSRichard Fitzgerald static int snd_compr_update_tstamp(struct snd_compr_stream *stream,
174b21c60a4SVinod Koul 		struct snd_compr_tstamp *tstamp)
175b21c60a4SVinod Koul {
176b21c60a4SVinod Koul 	if (!stream->ops->pointer)
17717ac8e5cSRichard Fitzgerald 		return -ENOTSUPP;
178b21c60a4SVinod Koul 	stream->ops->pointer(stream, tstamp);
179b21c60a4SVinod Koul 	pr_debug("dsp consumed till %d total %d bytes\n",
180b21c60a4SVinod Koul 		tstamp->byte_offset, tstamp->copied_total);
1815b1f79f7SCharles Keepax 	if (stream->direction == SND_COMPRESS_PLAYBACK)
182b21c60a4SVinod Koul 		stream->runtime->total_bytes_transferred = tstamp->copied_total;
1835b1f79f7SCharles Keepax 	else
1845b1f79f7SCharles Keepax 		stream->runtime->total_bytes_available = tstamp->copied_total;
18517ac8e5cSRichard Fitzgerald 	return 0;
186b21c60a4SVinod Koul }
187b21c60a4SVinod Koul 
188b21c60a4SVinod Koul static size_t snd_compr_calc_avail(struct snd_compr_stream *stream,
189b21c60a4SVinod Koul 		struct snd_compr_avail *avail)
190b21c60a4SVinod Koul {
19117ac8e5cSRichard Fitzgerald 	memset(avail, 0, sizeof(*avail));
192b21c60a4SVinod Koul 	snd_compr_update_tstamp(stream, &avail->tstamp);
19317ac8e5cSRichard Fitzgerald 	/* Still need to return avail even if tstamp can't be filled in */
194b21c60a4SVinod Koul 
195b21c60a4SVinod Koul 	if (stream->runtime->total_bytes_available == 0 &&
1965b1f79f7SCharles Keepax 			stream->runtime->state == SNDRV_PCM_STATE_SETUP &&
1975b1f79f7SCharles Keepax 			stream->direction == SND_COMPRESS_PLAYBACK) {
198b21c60a4SVinod Koul 		pr_debug("detected init and someone forgot to do a write\n");
199b21c60a4SVinod Koul 		return stream->runtime->buffer_size;
200b21c60a4SVinod Koul 	}
201b21c60a4SVinod Koul 	pr_debug("app wrote %lld, DSP consumed %lld\n",
202b21c60a4SVinod Koul 			stream->runtime->total_bytes_available,
203b21c60a4SVinod Koul 			stream->runtime->total_bytes_transferred);
204b21c60a4SVinod Koul 	if (stream->runtime->total_bytes_available ==
205b21c60a4SVinod Koul 				stream->runtime->total_bytes_transferred) {
2065b1f79f7SCharles Keepax 		if (stream->direction == SND_COMPRESS_PLAYBACK) {
207b21c60a4SVinod Koul 			pr_debug("both pointers are same, returning full avail\n");
208b21c60a4SVinod Koul 			return stream->runtime->buffer_size;
2095b1f79f7SCharles Keepax 		} else {
2105b1f79f7SCharles Keepax 			pr_debug("both pointers are same, returning no avail\n");
2115b1f79f7SCharles Keepax 			return 0;
2125b1f79f7SCharles Keepax 		}
213b21c60a4SVinod Koul 	}
214b21c60a4SVinod Koul 
2155b1f79f7SCharles Keepax 	avail->avail = stream->runtime->total_bytes_available -
2165b1f79f7SCharles Keepax 			stream->runtime->total_bytes_transferred;
2175b1f79f7SCharles Keepax 	if (stream->direction == SND_COMPRESS_PLAYBACK)
2185b1f79f7SCharles Keepax 		avail->avail = stream->runtime->buffer_size - avail->avail;
2195b1f79f7SCharles Keepax 
2204c28e32dSCharles Keepax 	pr_debug("ret avail as %lld\n", avail->avail);
2214c28e32dSCharles Keepax 	return avail->avail;
222b21c60a4SVinod Koul }
223b21c60a4SVinod Koul 
224b21c60a4SVinod Koul static inline size_t snd_compr_get_avail(struct snd_compr_stream *stream)
225b21c60a4SVinod Koul {
226b21c60a4SVinod Koul 	struct snd_compr_avail avail;
227b21c60a4SVinod Koul 
228b21c60a4SVinod Koul 	return snd_compr_calc_avail(stream, &avail);
229b21c60a4SVinod Koul }
230b21c60a4SVinod Koul 
231b21c60a4SVinod Koul static int
232b21c60a4SVinod Koul snd_compr_ioctl_avail(struct snd_compr_stream *stream, unsigned long arg)
233b21c60a4SVinod Koul {
234b21c60a4SVinod Koul 	struct snd_compr_avail ioctl_avail;
235b21c60a4SVinod Koul 	size_t avail;
236b21c60a4SVinod Koul 
237b21c60a4SVinod Koul 	avail = snd_compr_calc_avail(stream, &ioctl_avail);
238b21c60a4SVinod Koul 	ioctl_avail.avail = avail;
239b21c60a4SVinod Koul 
240b21c60a4SVinod Koul 	if (copy_to_user((__u64 __user *)arg,
241b21c60a4SVinod Koul 				&ioctl_avail, sizeof(ioctl_avail)))
242b21c60a4SVinod Koul 		return -EFAULT;
243b21c60a4SVinod Koul 	return 0;
244b21c60a4SVinod Koul }
245b21c60a4SVinod Koul 
246b21c60a4SVinod Koul static int snd_compr_write_data(struct snd_compr_stream *stream,
247b21c60a4SVinod Koul 	       const char __user *buf, size_t count)
248b21c60a4SVinod Koul {
249b21c60a4SVinod Koul 	void *dstn;
250b21c60a4SVinod Koul 	size_t copy;
251b21c60a4SVinod Koul 	struct snd_compr_runtime *runtime = stream->runtime;
252f0283b58SCharles Keepax 	/* 64-bit Modulus */
253f0283b58SCharles Keepax 	u64 app_pointer = div64_u64(runtime->total_bytes_available,
254f0283b58SCharles Keepax 				    runtime->buffer_size);
255f0283b58SCharles Keepax 	app_pointer = runtime->total_bytes_available -
256f0283b58SCharles Keepax 		      (app_pointer * runtime->buffer_size);
257b21c60a4SVinod Koul 
258f0283b58SCharles Keepax 	dstn = runtime->buffer + app_pointer;
259b21c60a4SVinod Koul 	pr_debug("copying %ld at %lld\n",
260f0283b58SCharles Keepax 			(unsigned long)count, app_pointer);
261f0283b58SCharles Keepax 	if (count < runtime->buffer_size - app_pointer) {
262b21c60a4SVinod Koul 		if (copy_from_user(dstn, buf, count))
263b21c60a4SVinod Koul 			return -EFAULT;
264b21c60a4SVinod Koul 	} else {
265f0283b58SCharles Keepax 		copy = runtime->buffer_size - app_pointer;
266b21c60a4SVinod Koul 		if (copy_from_user(dstn, buf, copy))
267b21c60a4SVinod Koul 			return -EFAULT;
268b21c60a4SVinod Koul 		if (copy_from_user(runtime->buffer, buf + copy, count - copy))
269b21c60a4SVinod Koul 			return -EFAULT;
270b21c60a4SVinod Koul 	}
271b21c60a4SVinod Koul 	/* if DSP cares, let it know data has been written */
272b21c60a4SVinod Koul 	if (stream->ops->ack)
273b21c60a4SVinod Koul 		stream->ops->ack(stream, count);
274b21c60a4SVinod Koul 	return count;
275b21c60a4SVinod Koul }
276b21c60a4SVinod Koul 
277b21c60a4SVinod Koul static ssize_t snd_compr_write(struct file *f, const char __user *buf,
278b21c60a4SVinod Koul 		size_t count, loff_t *offset)
279b21c60a4SVinod Koul {
280b21c60a4SVinod Koul 	struct snd_compr_file *data = f->private_data;
281b21c60a4SVinod Koul 	struct snd_compr_stream *stream;
282b21c60a4SVinod Koul 	size_t avail;
283b21c60a4SVinod Koul 	int retval;
284b21c60a4SVinod Koul 
285b21c60a4SVinod Koul 	if (snd_BUG_ON(!data))
286b21c60a4SVinod Koul 		return -EFAULT;
287b21c60a4SVinod Koul 
288b21c60a4SVinod Koul 	stream = &data->stream;
289b21c60a4SVinod Koul 	mutex_lock(&stream->device->lock);
290b21c60a4SVinod Koul 	/* write is allowed when stream is running or has been steup */
291b21c60a4SVinod Koul 	if (stream->runtime->state != SNDRV_PCM_STATE_SETUP &&
29235383a24SEric Laurent 	    stream->runtime->state != SNDRV_PCM_STATE_PREPARED &&
293b21c60a4SVinod Koul 			stream->runtime->state != SNDRV_PCM_STATE_RUNNING) {
294b21c60a4SVinod Koul 		mutex_unlock(&stream->device->lock);
295b21c60a4SVinod Koul 		return -EBADFD;
296b21c60a4SVinod Koul 	}
297b21c60a4SVinod Koul 
298b21c60a4SVinod Koul 	avail = snd_compr_get_avail(stream);
299b21c60a4SVinod Koul 	pr_debug("avail returned %ld\n", (unsigned long)avail);
300b21c60a4SVinod Koul 	/* calculate how much we can write to buffer */
301b21c60a4SVinod Koul 	if (avail > count)
302b21c60a4SVinod Koul 		avail = count;
303b21c60a4SVinod Koul 
3044daf891cSCharles Keepax 	if (stream->ops->copy) {
3054daf891cSCharles Keepax 		char __user* cbuf = (char __user*)buf;
3064daf891cSCharles Keepax 		retval = stream->ops->copy(stream, cbuf, avail);
3074daf891cSCharles Keepax 	} else {
308b21c60a4SVinod Koul 		retval = snd_compr_write_data(stream, buf, avail);
3094daf891cSCharles Keepax 	}
310b21c60a4SVinod Koul 	if (retval > 0)
311b21c60a4SVinod Koul 		stream->runtime->total_bytes_available += retval;
312b21c60a4SVinod Koul 
313b21c60a4SVinod Koul 	/* while initiating the stream, write should be called before START
314b21c60a4SVinod Koul 	 * call, so in setup move state */
315b21c60a4SVinod Koul 	if (stream->runtime->state == SNDRV_PCM_STATE_SETUP) {
316b21c60a4SVinod Koul 		stream->runtime->state = SNDRV_PCM_STATE_PREPARED;
317b21c60a4SVinod Koul 		pr_debug("stream prepared, Houston we are good to go\n");
318b21c60a4SVinod Koul 	}
319b21c60a4SVinod Koul 
320b21c60a4SVinod Koul 	mutex_unlock(&stream->device->lock);
321b21c60a4SVinod Koul 	return retval;
322b21c60a4SVinod Koul }
323b21c60a4SVinod Koul 
324b21c60a4SVinod Koul 
325b21c60a4SVinod Koul static ssize_t snd_compr_read(struct file *f, char __user *buf,
326b21c60a4SVinod Koul 		size_t count, loff_t *offset)
327b21c60a4SVinod Koul {
32849bb6402SCharles Keepax 	struct snd_compr_file *data = f->private_data;
32949bb6402SCharles Keepax 	struct snd_compr_stream *stream;
33049bb6402SCharles Keepax 	size_t avail;
33149bb6402SCharles Keepax 	int retval;
33249bb6402SCharles Keepax 
33349bb6402SCharles Keepax 	if (snd_BUG_ON(!data))
33449bb6402SCharles Keepax 		return -EFAULT;
33549bb6402SCharles Keepax 
33649bb6402SCharles Keepax 	stream = &data->stream;
33749bb6402SCharles Keepax 	mutex_lock(&stream->device->lock);
33849bb6402SCharles Keepax 
33975481347SVinod Koul 	/* read is allowed when stream is running, paused, draining and setup
34075481347SVinod Koul 	 * (yes setup is state which we transition to after stop, so if user
34175481347SVinod Koul 	 * wants to read data after stop we allow that)
34275481347SVinod Koul 	 */
34375481347SVinod Koul 	switch (stream->runtime->state) {
34475481347SVinod Koul 	case SNDRV_PCM_STATE_OPEN:
34575481347SVinod Koul 	case SNDRV_PCM_STATE_PREPARED:
34675481347SVinod Koul 	case SNDRV_PCM_STATE_XRUN:
34775481347SVinod Koul 	case SNDRV_PCM_STATE_SUSPENDED:
34875481347SVinod Koul 	case SNDRV_PCM_STATE_DISCONNECTED:
34949bb6402SCharles Keepax 		retval = -EBADFD;
35049bb6402SCharles Keepax 		goto out;
35149bb6402SCharles Keepax 	}
35249bb6402SCharles Keepax 
35349bb6402SCharles Keepax 	avail = snd_compr_get_avail(stream);
35449bb6402SCharles Keepax 	pr_debug("avail returned %ld\n", (unsigned long)avail);
35549bb6402SCharles Keepax 	/* calculate how much we can read from buffer */
35649bb6402SCharles Keepax 	if (avail > count)
35749bb6402SCharles Keepax 		avail = count;
35849bb6402SCharles Keepax 
35949bb6402SCharles Keepax 	if (stream->ops->copy) {
36049bb6402SCharles Keepax 		retval = stream->ops->copy(stream, buf, avail);
36149bb6402SCharles Keepax 	} else {
36249bb6402SCharles Keepax 		retval = -ENXIO;
36349bb6402SCharles Keepax 		goto out;
36449bb6402SCharles Keepax 	}
36549bb6402SCharles Keepax 	if (retval > 0)
36649bb6402SCharles Keepax 		stream->runtime->total_bytes_transferred += retval;
36749bb6402SCharles Keepax 
36849bb6402SCharles Keepax out:
36949bb6402SCharles Keepax 	mutex_unlock(&stream->device->lock);
37049bb6402SCharles Keepax 	return retval;
371b21c60a4SVinod Koul }
372b21c60a4SVinod Koul 
373b21c60a4SVinod Koul static int snd_compr_mmap(struct file *f, struct vm_area_struct *vma)
374b21c60a4SVinod Koul {
375b21c60a4SVinod Koul 	return -ENXIO;
376b21c60a4SVinod Koul }
377b21c60a4SVinod Koul 
378b21c60a4SVinod Koul static inline int snd_compr_get_poll(struct snd_compr_stream *stream)
379b21c60a4SVinod Koul {
380b21c60a4SVinod Koul 	if (stream->direction == SND_COMPRESS_PLAYBACK)
381b21c60a4SVinod Koul 		return POLLOUT | POLLWRNORM;
382b21c60a4SVinod Koul 	else
383b21c60a4SVinod Koul 		return POLLIN | POLLRDNORM;
384b21c60a4SVinod Koul }
385b21c60a4SVinod Koul 
386b21c60a4SVinod Koul static unsigned int snd_compr_poll(struct file *f, poll_table *wait)
387b21c60a4SVinod Koul {
388b21c60a4SVinod Koul 	struct snd_compr_file *data = f->private_data;
389b21c60a4SVinod Koul 	struct snd_compr_stream *stream;
390b21c60a4SVinod Koul 	size_t avail;
391b21c60a4SVinod Koul 	int retval = 0;
392b21c60a4SVinod Koul 
393b21c60a4SVinod Koul 	if (snd_BUG_ON(!data))
394b21c60a4SVinod Koul 		return -EFAULT;
395b21c60a4SVinod Koul 	stream = &data->stream;
396b21c60a4SVinod Koul 	if (snd_BUG_ON(!stream))
397b21c60a4SVinod Koul 		return -EFAULT;
398b21c60a4SVinod Koul 
399b21c60a4SVinod Koul 	mutex_lock(&stream->device->lock);
400c15b149aSRichard Fitzgerald 	if (stream->runtime->state == SNDRV_PCM_STATE_OPEN) {
401b21c60a4SVinod Koul 		retval = -EBADFD;
402b21c60a4SVinod Koul 		goto out;
403b21c60a4SVinod Koul 	}
404b21c60a4SVinod Koul 	poll_wait(f, &stream->runtime->sleep, wait);
405b21c60a4SVinod Koul 
406b21c60a4SVinod Koul 	avail = snd_compr_get_avail(stream);
407b21c60a4SVinod Koul 	pr_debug("avail is %ld\n", (unsigned long)avail);
408b21c60a4SVinod Koul 	/* check if we have at least one fragment to fill */
409b21c60a4SVinod Koul 	switch (stream->runtime->state) {
410b21c60a4SVinod Koul 	case SNDRV_PCM_STATE_DRAINING:
411b21c60a4SVinod Koul 		/* stream has been woken up after drain is complete
412b21c60a4SVinod Koul 		 * draining done so set stream state to stopped
413b21c60a4SVinod Koul 		 */
414b21c60a4SVinod Koul 		retval = snd_compr_get_poll(stream);
415b21c60a4SVinod Koul 		stream->runtime->state = SNDRV_PCM_STATE_SETUP;
416b21c60a4SVinod Koul 		break;
417b21c60a4SVinod Koul 	case SNDRV_PCM_STATE_RUNNING:
418b21c60a4SVinod Koul 	case SNDRV_PCM_STATE_PREPARED:
419b21c60a4SVinod Koul 	case SNDRV_PCM_STATE_PAUSED:
420b21c60a4SVinod Koul 		if (avail >= stream->runtime->fragment_size)
421b21c60a4SVinod Koul 			retval = snd_compr_get_poll(stream);
422b21c60a4SVinod Koul 		break;
423b21c60a4SVinod Koul 	default:
424b21c60a4SVinod Koul 		if (stream->direction == SND_COMPRESS_PLAYBACK)
425b21c60a4SVinod Koul 			retval = POLLOUT | POLLWRNORM | POLLERR;
426b21c60a4SVinod Koul 		else
427b21c60a4SVinod Koul 			retval = POLLIN | POLLRDNORM | POLLERR;
428b21c60a4SVinod Koul 		break;
429b21c60a4SVinod Koul 	}
430b21c60a4SVinod Koul out:
431b21c60a4SVinod Koul 	mutex_unlock(&stream->device->lock);
432b21c60a4SVinod Koul 	return retval;
433b21c60a4SVinod Koul }
434b21c60a4SVinod Koul 
435b21c60a4SVinod Koul static int
436b21c60a4SVinod Koul snd_compr_get_caps(struct snd_compr_stream *stream, unsigned long arg)
437b21c60a4SVinod Koul {
438b21c60a4SVinod Koul 	int retval;
439b21c60a4SVinod Koul 	struct snd_compr_caps caps;
440b21c60a4SVinod Koul 
441b21c60a4SVinod Koul 	if (!stream->ops->get_caps)
442b21c60a4SVinod Koul 		return -ENXIO;
443b21c60a4SVinod Koul 
4441c62e9f2SDan Carpenter 	memset(&caps, 0, sizeof(caps));
445b21c60a4SVinod Koul 	retval = stream->ops->get_caps(stream, &caps);
446b21c60a4SVinod Koul 	if (retval)
447b21c60a4SVinod Koul 		goto out;
448b21c60a4SVinod Koul 	if (copy_to_user((void __user *)arg, &caps, sizeof(caps)))
449b21c60a4SVinod Koul 		retval = -EFAULT;
450b21c60a4SVinod Koul out:
451b21c60a4SVinod Koul 	return retval;
452b21c60a4SVinod Koul }
453b21c60a4SVinod Koul 
454462b3f16STakashi Iwai #ifndef COMPR_CODEC_CAPS_OVERFLOW
455b21c60a4SVinod Koul static int
456b21c60a4SVinod Koul snd_compr_get_codec_caps(struct snd_compr_stream *stream, unsigned long arg)
457b21c60a4SVinod Koul {
458b21c60a4SVinod Koul 	int retval;
459b21c60a4SVinod Koul 	struct snd_compr_codec_caps *caps;
460b21c60a4SVinod Koul 
461b21c60a4SVinod Koul 	if (!stream->ops->get_codec_caps)
462b21c60a4SVinod Koul 		return -ENXIO;
463b21c60a4SVinod Koul 
46447966e97STakashi Iwai 	caps = kzalloc(sizeof(*caps), GFP_KERNEL);
465b21c60a4SVinod Koul 	if (!caps)
466b21c60a4SVinod Koul 		return -ENOMEM;
467b21c60a4SVinod Koul 
468b21c60a4SVinod Koul 	retval = stream->ops->get_codec_caps(stream, caps);
469b21c60a4SVinod Koul 	if (retval)
470b21c60a4SVinod Koul 		goto out;
471b21c60a4SVinod Koul 	if (copy_to_user((void __user *)arg, caps, sizeof(*caps)))
472b21c60a4SVinod Koul 		retval = -EFAULT;
473b21c60a4SVinod Koul 
474b21c60a4SVinod Koul out:
475b21c60a4SVinod Koul 	kfree(caps);
476b21c60a4SVinod Koul 	return retval;
477b21c60a4SVinod Koul }
478462b3f16STakashi Iwai #endif /* !COMPR_CODEC_CAPS_OVERFLOW */
479b21c60a4SVinod Koul 
480b21c60a4SVinod Koul /* revisit this with snd_pcm_preallocate_xxx */
481b21c60a4SVinod Koul static int snd_compr_allocate_buffer(struct snd_compr_stream *stream,
482b21c60a4SVinod Koul 		struct snd_compr_params *params)
483b21c60a4SVinod Koul {
484b21c60a4SVinod Koul 	unsigned int buffer_size;
485b21c60a4SVinod Koul 	void *buffer;
486b21c60a4SVinod Koul 
487b21c60a4SVinod Koul 	buffer_size = params->buffer.fragment_size * params->buffer.fragments;
488b21c60a4SVinod Koul 	if (stream->ops->copy) {
489b21c60a4SVinod Koul 		buffer = NULL;
490b21c60a4SVinod Koul 		/* if copy is defined the driver will be required to copy
491b21c60a4SVinod Koul 		 * the data from core
492b21c60a4SVinod Koul 		 */
493b21c60a4SVinod Koul 	} else {
494b21c60a4SVinod Koul 		buffer = kmalloc(buffer_size, GFP_KERNEL);
495b21c60a4SVinod Koul 		if (!buffer)
496b21c60a4SVinod Koul 			return -ENOMEM;
497b21c60a4SVinod Koul 	}
498b21c60a4SVinod Koul 	stream->runtime->fragment_size = params->buffer.fragment_size;
499b21c60a4SVinod Koul 	stream->runtime->fragments = params->buffer.fragments;
500b21c60a4SVinod Koul 	stream->runtime->buffer = buffer;
501b21c60a4SVinod Koul 	stream->runtime->buffer_size = buffer_size;
502b21c60a4SVinod Koul 	return 0;
503b21c60a4SVinod Koul }
504b21c60a4SVinod Koul 
5054dc040a0SVinod Koul static int snd_compress_check_input(struct snd_compr_params *params)
5064dc040a0SVinod Koul {
5074dc040a0SVinod Koul 	/* first let's check the buffer parameter's */
5084dc040a0SVinod Koul 	if (params->buffer.fragment_size == 0 ||
5096217e5edSDan Carpenter 	    params->buffer.fragments > INT_MAX / params->buffer.fragment_size)
5104dc040a0SVinod Koul 		return -EINVAL;
5114dc040a0SVinod Koul 
512fb4a9779SVinod Koul 	/* now codec parameters */
513fb4a9779SVinod Koul 	if (params->codec.id == 0 || params->codec.id > SND_AUDIOCODEC_MAX)
514fb4a9779SVinod Koul 		return -EINVAL;
515fb4a9779SVinod Koul 
516fb4a9779SVinod Koul 	if (params->codec.ch_in == 0 || params->codec.ch_out == 0)
517fb4a9779SVinod Koul 		return -EINVAL;
518fb4a9779SVinod Koul 
5194dc040a0SVinod Koul 	return 0;
5204dc040a0SVinod Koul }
5214dc040a0SVinod Koul 
522b21c60a4SVinod Koul static int
523b21c60a4SVinod Koul snd_compr_set_params(struct snd_compr_stream *stream, unsigned long arg)
524b21c60a4SVinod Koul {
525b21c60a4SVinod Koul 	struct snd_compr_params *params;
526b21c60a4SVinod Koul 	int retval;
527b21c60a4SVinod Koul 
528b21c60a4SVinod Koul 	if (stream->runtime->state == SNDRV_PCM_STATE_OPEN) {
529b21c60a4SVinod Koul 		/*
530b21c60a4SVinod Koul 		 * we should allow parameter change only when stream has been
531b21c60a4SVinod Koul 		 * opened not in other cases
532b21c60a4SVinod Koul 		 */
533b21c60a4SVinod Koul 		params = kmalloc(sizeof(*params), GFP_KERNEL);
534b21c60a4SVinod Koul 		if (!params)
535b21c60a4SVinod Koul 			return -ENOMEM;
536769fab2aSJesper Juhl 		if (copy_from_user(params, (void __user *)arg, sizeof(*params))) {
537769fab2aSJesper Juhl 			retval = -EFAULT;
538769fab2aSJesper Juhl 			goto out;
539769fab2aSJesper Juhl 		}
5404dc040a0SVinod Koul 
5414dc040a0SVinod Koul 		retval = snd_compress_check_input(params);
5424dc040a0SVinod Koul 		if (retval)
5434dc040a0SVinod Koul 			goto out;
5444dc040a0SVinod Koul 
545b21c60a4SVinod Koul 		retval = snd_compr_allocate_buffer(stream, params);
546b21c60a4SVinod Koul 		if (retval) {
547769fab2aSJesper Juhl 			retval = -ENOMEM;
548769fab2aSJesper Juhl 			goto out;
549b21c60a4SVinod Koul 		}
5504dc040a0SVinod Koul 
551b21c60a4SVinod Koul 		retval = stream->ops->set_params(stream, params);
552b21c60a4SVinod Koul 		if (retval)
553b21c60a4SVinod Koul 			goto out;
55449bb6402SCharles Keepax 
5559727b490SJeeja KP 		stream->metadata_set = false;
5569727b490SJeeja KP 		stream->next_track = false;
55749bb6402SCharles Keepax 
55849bb6402SCharles Keepax 		if (stream->direction == SND_COMPRESS_PLAYBACK)
55949bb6402SCharles Keepax 			stream->runtime->state = SNDRV_PCM_STATE_SETUP;
56049bb6402SCharles Keepax 		else
56149bb6402SCharles Keepax 			stream->runtime->state = SNDRV_PCM_STATE_PREPARED;
562769fab2aSJesper Juhl 	} else {
563b21c60a4SVinod Koul 		return -EPERM;
564769fab2aSJesper Juhl 	}
565b21c60a4SVinod Koul out:
566b21c60a4SVinod Koul 	kfree(params);
567b21c60a4SVinod Koul 	return retval;
568b21c60a4SVinod Koul }
569b21c60a4SVinod Koul 
570b21c60a4SVinod Koul static int
571b21c60a4SVinod Koul snd_compr_get_params(struct snd_compr_stream *stream, unsigned long arg)
572b21c60a4SVinod Koul {
573b21c60a4SVinod Koul 	struct snd_codec *params;
574b21c60a4SVinod Koul 	int retval;
575b21c60a4SVinod Koul 
576b21c60a4SVinod Koul 	if (!stream->ops->get_params)
577b21c60a4SVinod Koul 		return -EBADFD;
578b21c60a4SVinod Koul 
57947966e97STakashi Iwai 	params = kzalloc(sizeof(*params), GFP_KERNEL);
580b21c60a4SVinod Koul 	if (!params)
581b21c60a4SVinod Koul 		return -ENOMEM;
582b21c60a4SVinod Koul 	retval = stream->ops->get_params(stream, params);
583b21c60a4SVinod Koul 	if (retval)
584b21c60a4SVinod Koul 		goto out;
585b21c60a4SVinod Koul 	if (copy_to_user((char __user *)arg, params, sizeof(*params)))
586b21c60a4SVinod Koul 		retval = -EFAULT;
587b21c60a4SVinod Koul 
588b21c60a4SVinod Koul out:
589b21c60a4SVinod Koul 	kfree(params);
590b21c60a4SVinod Koul 	return retval;
591b21c60a4SVinod Koul }
592b21c60a4SVinod Koul 
5939727b490SJeeja KP static int
5949727b490SJeeja KP snd_compr_get_metadata(struct snd_compr_stream *stream, unsigned long arg)
5959727b490SJeeja KP {
5969727b490SJeeja KP 	struct snd_compr_metadata metadata;
5979727b490SJeeja KP 	int retval;
5989727b490SJeeja KP 
5999727b490SJeeja KP 	if (!stream->ops->get_metadata)
6009727b490SJeeja KP 		return -ENXIO;
6019727b490SJeeja KP 
6029727b490SJeeja KP 	if (copy_from_user(&metadata, (void __user *)arg, sizeof(metadata)))
6039727b490SJeeja KP 		return -EFAULT;
6049727b490SJeeja KP 
6059727b490SJeeja KP 	retval = stream->ops->get_metadata(stream, &metadata);
6069727b490SJeeja KP 	if (retval != 0)
6079727b490SJeeja KP 		return retval;
6089727b490SJeeja KP 
6099727b490SJeeja KP 	if (copy_to_user((void __user *)arg, &metadata, sizeof(metadata)))
6109727b490SJeeja KP 		return -EFAULT;
6119727b490SJeeja KP 
6129727b490SJeeja KP 	return 0;
6139727b490SJeeja KP }
6149727b490SJeeja KP 
6159727b490SJeeja KP static int
6169727b490SJeeja KP snd_compr_set_metadata(struct snd_compr_stream *stream, unsigned long arg)
6179727b490SJeeja KP {
6189727b490SJeeja KP 	struct snd_compr_metadata metadata;
6199727b490SJeeja KP 	int retval;
6209727b490SJeeja KP 
6219727b490SJeeja KP 	if (!stream->ops->set_metadata)
6229727b490SJeeja KP 		return -ENXIO;
6239727b490SJeeja KP 	/*
6249727b490SJeeja KP 	* we should allow parameter change only when stream has been
6259727b490SJeeja KP 	* opened not in other cases
6269727b490SJeeja KP 	*/
6279727b490SJeeja KP 	if (copy_from_user(&metadata, (void __user *)arg, sizeof(metadata)))
6289727b490SJeeja KP 		return -EFAULT;
6299727b490SJeeja KP 
6309727b490SJeeja KP 	retval = stream->ops->set_metadata(stream, &metadata);
6319727b490SJeeja KP 	stream->metadata_set = true;
6329727b490SJeeja KP 
6339727b490SJeeja KP 	return retval;
6349727b490SJeeja KP }
6359727b490SJeeja KP 
636b21c60a4SVinod Koul static inline int
637b21c60a4SVinod Koul snd_compr_tstamp(struct snd_compr_stream *stream, unsigned long arg)
638b21c60a4SVinod Koul {
63917ac8e5cSRichard Fitzgerald 	struct snd_compr_tstamp tstamp = {0};
64017ac8e5cSRichard Fitzgerald 	int ret;
641b21c60a4SVinod Koul 
64217ac8e5cSRichard Fitzgerald 	ret = snd_compr_update_tstamp(stream, &tstamp);
64317ac8e5cSRichard Fitzgerald 	if (ret == 0)
64417ac8e5cSRichard Fitzgerald 		ret = copy_to_user((struct snd_compr_tstamp __user *)arg,
645b21c60a4SVinod Koul 			&tstamp, sizeof(tstamp)) ? -EFAULT : 0;
64617ac8e5cSRichard Fitzgerald 	return ret;
647b21c60a4SVinod Koul }
648b21c60a4SVinod Koul 
649b21c60a4SVinod Koul static int snd_compr_pause(struct snd_compr_stream *stream)
650b21c60a4SVinod Koul {
651b21c60a4SVinod Koul 	int retval;
652b21c60a4SVinod Koul 
653b21c60a4SVinod Koul 	if (stream->runtime->state != SNDRV_PCM_STATE_RUNNING)
654b21c60a4SVinod Koul 		return -EPERM;
655b21c60a4SVinod Koul 	retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_PAUSE_PUSH);
6566b18f793SVinod Koul 	if (!retval)
657b21c60a4SVinod Koul 		stream->runtime->state = SNDRV_PCM_STATE_PAUSED;
658b21c60a4SVinod Koul 	return retval;
659b21c60a4SVinod Koul }
660b21c60a4SVinod Koul 
661b21c60a4SVinod Koul static int snd_compr_resume(struct snd_compr_stream *stream)
662b21c60a4SVinod Koul {
663b21c60a4SVinod Koul 	int retval;
664b21c60a4SVinod Koul 
665b21c60a4SVinod Koul 	if (stream->runtime->state != SNDRV_PCM_STATE_PAUSED)
666b21c60a4SVinod Koul 		return -EPERM;
667b21c60a4SVinod Koul 	retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_PAUSE_RELEASE);
668b21c60a4SVinod Koul 	if (!retval)
669b21c60a4SVinod Koul 		stream->runtime->state = SNDRV_PCM_STATE_RUNNING;
670b21c60a4SVinod Koul 	return retval;
671b21c60a4SVinod Koul }
672b21c60a4SVinod Koul 
673b21c60a4SVinod Koul static int snd_compr_start(struct snd_compr_stream *stream)
674b21c60a4SVinod Koul {
675b21c60a4SVinod Koul 	int retval;
676b21c60a4SVinod Koul 
677b21c60a4SVinod Koul 	if (stream->runtime->state != SNDRV_PCM_STATE_PREPARED)
678b21c60a4SVinod Koul 		return -EPERM;
679b21c60a4SVinod Koul 	retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_START);
680b21c60a4SVinod Koul 	if (!retval)
681b21c60a4SVinod Koul 		stream->runtime->state = SNDRV_PCM_STATE_RUNNING;
682b21c60a4SVinod Koul 	return retval;
683b21c60a4SVinod Koul }
684b21c60a4SVinod Koul 
685b21c60a4SVinod Koul static int snd_compr_stop(struct snd_compr_stream *stream)
686b21c60a4SVinod Koul {
687b21c60a4SVinod Koul 	int retval;
688b21c60a4SVinod Koul 
689b21c60a4SVinod Koul 	if (stream->runtime->state == SNDRV_PCM_STATE_PREPARED ||
690b21c60a4SVinod Koul 			stream->runtime->state == SNDRV_PCM_STATE_SETUP)
691b21c60a4SVinod Koul 		return -EPERM;
692b21c60a4SVinod Koul 	retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_STOP);
693b21c60a4SVinod Koul 	if (!retval) {
694917f4b5cSVinod Koul 		snd_compr_drain_notify(stream);
6958b21460aSVinod Koul 		stream->runtime->total_bytes_available = 0;
6968b21460aSVinod Koul 		stream->runtime->total_bytes_transferred = 0;
697b21c60a4SVinod Koul 	}
698b21c60a4SVinod Koul 	return retval;
699b21c60a4SVinod Koul }
700b21c60a4SVinod Koul 
701917f4b5cSVinod Koul static int snd_compress_wait_for_drain(struct snd_compr_stream *stream)
702917f4b5cSVinod Koul {
703f44f2a54SVinod Koul 	int ret;
704f44f2a54SVinod Koul 
705917f4b5cSVinod Koul 	/*
706917f4b5cSVinod Koul 	 * We are called with lock held. So drop the lock while we wait for
707cdb1ee3fSVinod Koul 	 * drain complete notification from the driver
708917f4b5cSVinod Koul 	 *
709917f4b5cSVinod Koul 	 * It is expected that driver will notify the drain completion and then
710917f4b5cSVinod Koul 	 * stream will be moved to SETUP state, even if draining resulted in an
711917f4b5cSVinod Koul 	 * error. We can trigger next track after this.
712917f4b5cSVinod Koul 	 */
713917f4b5cSVinod Koul 	stream->runtime->state = SNDRV_PCM_STATE_DRAINING;
714917f4b5cSVinod Koul 	mutex_unlock(&stream->device->lock);
715917f4b5cSVinod Koul 
716f44f2a54SVinod Koul 	/* we wait for drain to complete here, drain can return when
717f44f2a54SVinod Koul 	 * interruption occurred, wait returned error or success.
718f44f2a54SVinod Koul 	 * For the first two cases we don't do anything different here and
719f44f2a54SVinod Koul 	 * return after waking up
720f44f2a54SVinod Koul 	 */
721f44f2a54SVinod Koul 
722f44f2a54SVinod Koul 	ret = wait_event_interruptible(stream->runtime->sleep,
723f44f2a54SVinod Koul 			(stream->runtime->state != SNDRV_PCM_STATE_DRAINING));
724f44f2a54SVinod Koul 	if (ret == -ERESTARTSYS)
725f44f2a54SVinod Koul 		pr_debug("wait aborted by a signal");
726f44f2a54SVinod Koul 	else if (ret)
727f44f2a54SVinod Koul 		pr_debug("wait for drain failed with %d\n", ret);
728f44f2a54SVinod Koul 
729917f4b5cSVinod Koul 
730917f4b5cSVinod Koul 	wake_up(&stream->runtime->sleep);
731917f4b5cSVinod Koul 	mutex_lock(&stream->device->lock);
732917f4b5cSVinod Koul 
733f44f2a54SVinod Koul 	return ret;
734917f4b5cSVinod Koul }
735917f4b5cSVinod Koul 
736b21c60a4SVinod Koul static int snd_compr_drain(struct snd_compr_stream *stream)
737b21c60a4SVinod Koul {
738b21c60a4SVinod Koul 	int retval;
739b21c60a4SVinod Koul 
740b21c60a4SVinod Koul 	if (stream->runtime->state == SNDRV_PCM_STATE_PREPARED ||
741b21c60a4SVinod Koul 			stream->runtime->state == SNDRV_PCM_STATE_SETUP)
742b21c60a4SVinod Koul 		return -EPERM;
743917f4b5cSVinod Koul 
744b21c60a4SVinod Koul 	retval = stream->ops->trigger(stream, SND_COMPR_TRIGGER_DRAIN);
745917f4b5cSVinod Koul 	if (retval) {
746f44f2a54SVinod Koul 		pr_debug("SND_COMPR_TRIGGER_DRAIN failed %d\n", retval);
747b21c60a4SVinod Koul 		wake_up(&stream->runtime->sleep);
748917f4b5cSVinod Koul 		return retval;
749b21c60a4SVinod Koul 	}
750917f4b5cSVinod Koul 
751f44f2a54SVinod Koul 	return snd_compress_wait_for_drain(stream);
752b21c60a4SVinod Koul }
753b21c60a4SVinod Koul 
7549727b490SJeeja KP static int snd_compr_next_track(struct snd_compr_stream *stream)
7559727b490SJeeja KP {
7569727b490SJeeja KP 	int retval;
7579727b490SJeeja KP 
7589727b490SJeeja KP 	/* only a running stream can transition to next track */
7599727b490SJeeja KP 	if (stream->runtime->state != SNDRV_PCM_STATE_RUNNING)
7609727b490SJeeja KP 		return -EPERM;
7619727b490SJeeja KP 
762cdb1ee3fSVinod Koul 	/* you can signal next track if this is intended to be a gapless stream
7639727b490SJeeja KP 	 * and current track metadata is set
7649727b490SJeeja KP 	 */
7659727b490SJeeja KP 	if (stream->metadata_set == false)
7669727b490SJeeja KP 		return -EPERM;
7679727b490SJeeja KP 
7689727b490SJeeja KP 	retval = stream->ops->trigger(stream, SND_COMPR_TRIGGER_NEXT_TRACK);
7699727b490SJeeja KP 	if (retval != 0)
7709727b490SJeeja KP 		return retval;
7719727b490SJeeja KP 	stream->metadata_set = false;
7729727b490SJeeja KP 	stream->next_track = true;
7739727b490SJeeja KP 	return 0;
7749727b490SJeeja KP }
7759727b490SJeeja KP 
7769727b490SJeeja KP static int snd_compr_partial_drain(struct snd_compr_stream *stream)
7779727b490SJeeja KP {
7789727b490SJeeja KP 	int retval;
7799727b490SJeeja KP 	if (stream->runtime->state == SNDRV_PCM_STATE_PREPARED ||
7809727b490SJeeja KP 			stream->runtime->state == SNDRV_PCM_STATE_SETUP)
7819727b490SJeeja KP 		return -EPERM;
7829727b490SJeeja KP 	/* stream can be drained only when next track has been signalled */
7839727b490SJeeja KP 	if (stream->next_track == false)
7849727b490SJeeja KP 		return -EPERM;
7859727b490SJeeja KP 
7869727b490SJeeja KP 	retval = stream->ops->trigger(stream, SND_COMPR_TRIGGER_PARTIAL_DRAIN);
787917f4b5cSVinod Koul 	if (retval) {
788f44f2a54SVinod Koul 		pr_debug("Partial drain returned failure\n");
789917f4b5cSVinod Koul 		wake_up(&stream->runtime->sleep);
790917f4b5cSVinod Koul 		return retval;
791917f4b5cSVinod Koul 	}
7929727b490SJeeja KP 
7939727b490SJeeja KP 	stream->next_track = false;
794917f4b5cSVinod Koul 	return snd_compress_wait_for_drain(stream);
7959727b490SJeeja KP }
7969727b490SJeeja KP 
797b21c60a4SVinod Koul static long snd_compr_ioctl(struct file *f, unsigned int cmd, unsigned long arg)
798b21c60a4SVinod Koul {
799b21c60a4SVinod Koul 	struct snd_compr_file *data = f->private_data;
800b21c60a4SVinod Koul 	struct snd_compr_stream *stream;
801b21c60a4SVinod Koul 	int retval = -ENOTTY;
802b21c60a4SVinod Koul 
803b21c60a4SVinod Koul 	if (snd_BUG_ON(!data))
804b21c60a4SVinod Koul 		return -EFAULT;
805b21c60a4SVinod Koul 	stream = &data->stream;
806b21c60a4SVinod Koul 	if (snd_BUG_ON(!stream))
807b21c60a4SVinod Koul 		return -EFAULT;
808b21c60a4SVinod Koul 	mutex_lock(&stream->device->lock);
809b21c60a4SVinod Koul 	switch (_IOC_NR(cmd)) {
810b21c60a4SVinod Koul 	case _IOC_NR(SNDRV_COMPRESS_IOCTL_VERSION):
811a8d30608SVinod Koul 		retval = put_user(SNDRV_COMPRESS_VERSION,
812b21c60a4SVinod Koul 				(int __user *)arg) ? -EFAULT : 0;
813b21c60a4SVinod Koul 		break;
814b21c60a4SVinod Koul 	case _IOC_NR(SNDRV_COMPRESS_GET_CAPS):
815b21c60a4SVinod Koul 		retval = snd_compr_get_caps(stream, arg);
816b21c60a4SVinod Koul 		break;
817462b3f16STakashi Iwai #ifndef COMPR_CODEC_CAPS_OVERFLOW
818b21c60a4SVinod Koul 	case _IOC_NR(SNDRV_COMPRESS_GET_CODEC_CAPS):
819b21c60a4SVinod Koul 		retval = snd_compr_get_codec_caps(stream, arg);
820b21c60a4SVinod Koul 		break;
821462b3f16STakashi Iwai #endif
822b21c60a4SVinod Koul 	case _IOC_NR(SNDRV_COMPRESS_SET_PARAMS):
823b21c60a4SVinod Koul 		retval = snd_compr_set_params(stream, arg);
824b21c60a4SVinod Koul 		break;
825b21c60a4SVinod Koul 	case _IOC_NR(SNDRV_COMPRESS_GET_PARAMS):
826b21c60a4SVinod Koul 		retval = snd_compr_get_params(stream, arg);
827b21c60a4SVinod Koul 		break;
8289727b490SJeeja KP 	case _IOC_NR(SNDRV_COMPRESS_SET_METADATA):
8299727b490SJeeja KP 		retval = snd_compr_set_metadata(stream, arg);
8309727b490SJeeja KP 		break;
8319727b490SJeeja KP 	case _IOC_NR(SNDRV_COMPRESS_GET_METADATA):
8329727b490SJeeja KP 		retval = snd_compr_get_metadata(stream, arg);
8339727b490SJeeja KP 		break;
834b21c60a4SVinod Koul 	case _IOC_NR(SNDRV_COMPRESS_TSTAMP):
835b21c60a4SVinod Koul 		retval = snd_compr_tstamp(stream, arg);
836b21c60a4SVinod Koul 		break;
837b21c60a4SVinod Koul 	case _IOC_NR(SNDRV_COMPRESS_AVAIL):
838b21c60a4SVinod Koul 		retval = snd_compr_ioctl_avail(stream, arg);
839b21c60a4SVinod Koul 		break;
840b21c60a4SVinod Koul 	case _IOC_NR(SNDRV_COMPRESS_PAUSE):
841b21c60a4SVinod Koul 		retval = snd_compr_pause(stream);
842b21c60a4SVinod Koul 		break;
843b21c60a4SVinod Koul 	case _IOC_NR(SNDRV_COMPRESS_RESUME):
844b21c60a4SVinod Koul 		retval = snd_compr_resume(stream);
845b21c60a4SVinod Koul 		break;
846b21c60a4SVinod Koul 	case _IOC_NR(SNDRV_COMPRESS_START):
847b21c60a4SVinod Koul 		retval = snd_compr_start(stream);
848b21c60a4SVinod Koul 		break;
849b21c60a4SVinod Koul 	case _IOC_NR(SNDRV_COMPRESS_STOP):
850b21c60a4SVinod Koul 		retval = snd_compr_stop(stream);
851b21c60a4SVinod Koul 		break;
852b21c60a4SVinod Koul 	case _IOC_NR(SNDRV_COMPRESS_DRAIN):
853b21c60a4SVinod Koul 		retval = snd_compr_drain(stream);
854b21c60a4SVinod Koul 		break;
8559727b490SJeeja KP 	case _IOC_NR(SNDRV_COMPRESS_PARTIAL_DRAIN):
8569727b490SJeeja KP 		retval = snd_compr_partial_drain(stream);
8579727b490SJeeja KP 		break;
8589727b490SJeeja KP 	case _IOC_NR(SNDRV_COMPRESS_NEXT_TRACK):
8599727b490SJeeja KP 		retval = snd_compr_next_track(stream);
8609727b490SJeeja KP 		break;
8619727b490SJeeja KP 
862b21c60a4SVinod Koul 	}
863b21c60a4SVinod Koul 	mutex_unlock(&stream->device->lock);
864b21c60a4SVinod Koul 	return retval;
865b21c60a4SVinod Koul }
866b21c60a4SVinod Koul 
867c1036889SRavindra Lokhande /* support of 32bit userspace on 64bit platforms */
868c1036889SRavindra Lokhande #ifdef CONFIG_COMPAT
869c1036889SRavindra Lokhande static long snd_compr_ioctl_compat(struct file *file, unsigned int cmd,
870c1036889SRavindra Lokhande 						unsigned long arg)
871c1036889SRavindra Lokhande {
872c1036889SRavindra Lokhande 	return snd_compr_ioctl(file, cmd, (unsigned long)compat_ptr(arg));
873c1036889SRavindra Lokhande }
874c1036889SRavindra Lokhande #endif
875c1036889SRavindra Lokhande 
876b21c60a4SVinod Koul static const struct file_operations snd_compr_file_ops = {
877b21c60a4SVinod Koul 		.owner =	THIS_MODULE,
878b21c60a4SVinod Koul 		.open =		snd_compr_open,
879b21c60a4SVinod Koul 		.release =	snd_compr_free,
880b21c60a4SVinod Koul 		.write =	snd_compr_write,
881b21c60a4SVinod Koul 		.read =		snd_compr_read,
882b21c60a4SVinod Koul 		.unlocked_ioctl = snd_compr_ioctl,
883c1036889SRavindra Lokhande #ifdef CONFIG_COMPAT
884c1036889SRavindra Lokhande 		.compat_ioctl = snd_compr_ioctl_compat,
885c1036889SRavindra Lokhande #endif
886b21c60a4SVinod Koul 		.mmap =		snd_compr_mmap,
887b21c60a4SVinod Koul 		.poll =		snd_compr_poll,
888b21c60a4SVinod Koul };
889b21c60a4SVinod Koul 
890b21c60a4SVinod Koul static int snd_compress_dev_register(struct snd_device *device)
891b21c60a4SVinod Koul {
892b21c60a4SVinod Koul 	int ret = -EINVAL;
893b21c60a4SVinod Koul 	char str[16];
894b21c60a4SVinod Koul 	struct snd_compr *compr;
895b21c60a4SVinod Koul 
896b21c60a4SVinod Koul 	if (snd_BUG_ON(!device || !device->device_data))
897b21c60a4SVinod Koul 		return -EBADFD;
898b21c60a4SVinod Koul 	compr = device->device_data;
899b21c60a4SVinod Koul 
900b21c60a4SVinod Koul 	pr_debug("reg %s for device %s, direction %d\n", str, compr->name,
901b21c60a4SVinod Koul 			compr->direction);
902b21c60a4SVinod Koul 	/* register compressed device */
90340a4b263STakashi Iwai 	ret = snd_register_device(SNDRV_DEVICE_TYPE_COMPRESS,
90404c5d5a4STakashi Iwai 				  compr->card, compr->device,
90540a4b263STakashi Iwai 				  &snd_compr_file_ops, compr, &compr->dev);
906b21c60a4SVinod Koul 	if (ret < 0) {
907b21c60a4SVinod Koul 		pr_err("snd_register_device failed\n %d", ret);
908b21c60a4SVinod Koul 		return ret;
909b21c60a4SVinod Koul 	}
910b21c60a4SVinod Koul 	return ret;
911b21c60a4SVinod Koul 
912b21c60a4SVinod Koul }
913b21c60a4SVinod Koul 
914b21c60a4SVinod Koul static int snd_compress_dev_disconnect(struct snd_device *device)
915b21c60a4SVinod Koul {
916b21c60a4SVinod Koul 	struct snd_compr *compr;
917b21c60a4SVinod Koul 
918b21c60a4SVinod Koul 	compr = device->device_data;
91940a4b263STakashi Iwai 	snd_unregister_device(&compr->dev);
920b21c60a4SVinod Koul 	return 0;
921b21c60a4SVinod Koul }
922b21c60a4SVinod Koul 
92331742724SRichard Fitzgerald #ifdef CONFIG_SND_VERBOSE_PROCFS
92431742724SRichard Fitzgerald static void snd_compress_proc_info_read(struct snd_info_entry *entry,
92531742724SRichard Fitzgerald 					struct snd_info_buffer *buffer)
92631742724SRichard Fitzgerald {
92731742724SRichard Fitzgerald 	struct snd_compr *compr = (struct snd_compr *)entry->private_data;
92831742724SRichard Fitzgerald 
92931742724SRichard Fitzgerald 	snd_iprintf(buffer, "card: %d\n", compr->card->number);
93031742724SRichard Fitzgerald 	snd_iprintf(buffer, "device: %d\n", compr->device);
93131742724SRichard Fitzgerald 	snd_iprintf(buffer, "stream: %s\n",
93231742724SRichard Fitzgerald 			compr->direction == SND_COMPRESS_PLAYBACK
93331742724SRichard Fitzgerald 				? "PLAYBACK" : "CAPTURE");
93431742724SRichard Fitzgerald 	snd_iprintf(buffer, "id: %s\n", compr->id);
93531742724SRichard Fitzgerald }
93631742724SRichard Fitzgerald 
93731742724SRichard Fitzgerald static int snd_compress_proc_init(struct snd_compr *compr)
93831742724SRichard Fitzgerald {
93931742724SRichard Fitzgerald 	struct snd_info_entry *entry;
94031742724SRichard Fitzgerald 	char name[16];
94131742724SRichard Fitzgerald 
94231742724SRichard Fitzgerald 	sprintf(name, "compr%i", compr->device);
94331742724SRichard Fitzgerald 	entry = snd_info_create_card_entry(compr->card, name,
94431742724SRichard Fitzgerald 					   compr->card->proc_root);
94531742724SRichard Fitzgerald 	if (!entry)
94631742724SRichard Fitzgerald 		return -ENOMEM;
94731742724SRichard Fitzgerald 	entry->mode = S_IFDIR | S_IRUGO | S_IXUGO;
94831742724SRichard Fitzgerald 	if (snd_info_register(entry) < 0) {
94931742724SRichard Fitzgerald 		snd_info_free_entry(entry);
95031742724SRichard Fitzgerald 		return -ENOMEM;
95131742724SRichard Fitzgerald 	}
95231742724SRichard Fitzgerald 	compr->proc_root = entry;
95331742724SRichard Fitzgerald 
95431742724SRichard Fitzgerald 	entry = snd_info_create_card_entry(compr->card, "info",
95531742724SRichard Fitzgerald 					   compr->proc_root);
95631742724SRichard Fitzgerald 	if (entry) {
95731742724SRichard Fitzgerald 		snd_info_set_text_ops(entry, compr,
95831742724SRichard Fitzgerald 				      snd_compress_proc_info_read);
95931742724SRichard Fitzgerald 		if (snd_info_register(entry) < 0) {
96031742724SRichard Fitzgerald 			snd_info_free_entry(entry);
96131742724SRichard Fitzgerald 			entry = NULL;
96231742724SRichard Fitzgerald 		}
96331742724SRichard Fitzgerald 	}
96431742724SRichard Fitzgerald 	compr->proc_info_entry = entry;
96531742724SRichard Fitzgerald 
96631742724SRichard Fitzgerald 	return 0;
96731742724SRichard Fitzgerald }
96831742724SRichard Fitzgerald 
96931742724SRichard Fitzgerald static void snd_compress_proc_done(struct snd_compr *compr)
97031742724SRichard Fitzgerald {
97131742724SRichard Fitzgerald 	snd_info_free_entry(compr->proc_info_entry);
97231742724SRichard Fitzgerald 	compr->proc_info_entry = NULL;
97331742724SRichard Fitzgerald 	snd_info_free_entry(compr->proc_root);
97431742724SRichard Fitzgerald 	compr->proc_root = NULL;
97531742724SRichard Fitzgerald }
976e5241a8cSRichard Fitzgerald 
977e5241a8cSRichard Fitzgerald static inline void snd_compress_set_id(struct snd_compr *compr, const char *id)
978e5241a8cSRichard Fitzgerald {
979e5241a8cSRichard Fitzgerald 	strlcpy(compr->id, id, sizeof(compr->id));
980e5241a8cSRichard Fitzgerald }
98131742724SRichard Fitzgerald #else
98231742724SRichard Fitzgerald static inline int snd_compress_proc_init(struct snd_compr *compr)
98331742724SRichard Fitzgerald {
98431742724SRichard Fitzgerald 	return 0;
98531742724SRichard Fitzgerald }
98631742724SRichard Fitzgerald 
98731742724SRichard Fitzgerald static inline void snd_compress_proc_done(struct snd_compr *compr)
98831742724SRichard Fitzgerald {
98931742724SRichard Fitzgerald }
990e5241a8cSRichard Fitzgerald 
991e5241a8cSRichard Fitzgerald static inline void snd_compress_set_id(struct snd_compr *compr, const char *id)
992e5241a8cSRichard Fitzgerald {
993e5241a8cSRichard Fitzgerald }
99431742724SRichard Fitzgerald #endif
99531742724SRichard Fitzgerald 
99604c5d5a4STakashi Iwai static int snd_compress_dev_free(struct snd_device *device)
99704c5d5a4STakashi Iwai {
99804c5d5a4STakashi Iwai 	struct snd_compr *compr;
99904c5d5a4STakashi Iwai 
100004c5d5a4STakashi Iwai 	compr = device->device_data;
100131742724SRichard Fitzgerald 	snd_compress_proc_done(compr);
100204c5d5a4STakashi Iwai 	put_device(&compr->dev);
100304c5d5a4STakashi Iwai 	return 0;
100404c5d5a4STakashi Iwai }
100504c5d5a4STakashi Iwai 
1006b21c60a4SVinod Koul /*
1007b21c60a4SVinod Koul  * snd_compress_new: create new compress device
1008b21c60a4SVinod Koul  * @card: sound card pointer
1009b21c60a4SVinod Koul  * @device: device number
1010b21c60a4SVinod Koul  * @dirn: device direction, should be of type enum snd_compr_direction
1011b21c60a4SVinod Koul  * @compr: compress device pointer
1012b21c60a4SVinod Koul  */
1013b21c60a4SVinod Koul int snd_compress_new(struct snd_card *card, int device,
1014e5241a8cSRichard Fitzgerald 			int dirn, const char *id, struct snd_compr *compr)
1015b21c60a4SVinod Koul {
1016b21c60a4SVinod Koul 	static struct snd_device_ops ops = {
101704c5d5a4STakashi Iwai 		.dev_free = snd_compress_dev_free,
1018b21c60a4SVinod Koul 		.dev_register = snd_compress_dev_register,
1019b21c60a4SVinod Koul 		.dev_disconnect = snd_compress_dev_disconnect,
1020b21c60a4SVinod Koul 	};
102131742724SRichard Fitzgerald 	int ret;
1022b21c60a4SVinod Koul 
1023b21c60a4SVinod Koul 	compr->card = card;
1024b21c60a4SVinod Koul 	compr->device = device;
1025b21c60a4SVinod Koul 	compr->direction = dirn;
102604c5d5a4STakashi Iwai 
1027e5241a8cSRichard Fitzgerald 	snd_compress_set_id(compr, id);
1028e5241a8cSRichard Fitzgerald 
102904c5d5a4STakashi Iwai 	snd_device_initialize(&compr->dev, card);
103004c5d5a4STakashi Iwai 	dev_set_name(&compr->dev, "comprC%iD%i", card->number, device);
103104c5d5a4STakashi Iwai 
103231742724SRichard Fitzgerald 	ret = snd_device_new(card, SNDRV_DEV_COMPRESS, compr, &ops);
103331742724SRichard Fitzgerald 	if (ret == 0)
103431742724SRichard Fitzgerald 		snd_compress_proc_init(compr);
103531742724SRichard Fitzgerald 
103631742724SRichard Fitzgerald 	return ret;
1037b21c60a4SVinod Koul }
1038b21c60a4SVinod Koul EXPORT_SYMBOL_GPL(snd_compress_new);
1039b21c60a4SVinod Koul 
1040b21c60a4SVinod Koul static int snd_compress_add_device(struct snd_compr *device)
1041b21c60a4SVinod Koul {
1042b21c60a4SVinod Koul 	int ret;
1043b21c60a4SVinod Koul 
1044b21c60a4SVinod Koul 	if (!device->card)
1045b21c60a4SVinod Koul 		return -EINVAL;
1046b21c60a4SVinod Koul 
1047b21c60a4SVinod Koul 	/* register the card */
1048b21c60a4SVinod Koul 	ret = snd_card_register(device->card);
1049b21c60a4SVinod Koul 	if (ret)
1050b21c60a4SVinod Koul 		goto out;
1051b21c60a4SVinod Koul 	return 0;
1052b21c60a4SVinod Koul 
1053b21c60a4SVinod Koul out:
1054b21c60a4SVinod Koul 	pr_err("failed with %d\n", ret);
1055b21c60a4SVinod Koul 	return ret;
1056b21c60a4SVinod Koul 
1057b21c60a4SVinod Koul }
1058b21c60a4SVinod Koul 
1059b21c60a4SVinod Koul static int snd_compress_remove_device(struct snd_compr *device)
1060b21c60a4SVinod Koul {
1061b21c60a4SVinod Koul 	return snd_card_free(device->card);
1062b21c60a4SVinod Koul }
1063b21c60a4SVinod Koul 
1064b21c60a4SVinod Koul /**
1065b21c60a4SVinod Koul  * snd_compress_register - register compressed device
1066b21c60a4SVinod Koul  *
1067b21c60a4SVinod Koul  * @device: compressed device to register
1068b21c60a4SVinod Koul  */
1069b21c60a4SVinod Koul int snd_compress_register(struct snd_compr *device)
1070b21c60a4SVinod Koul {
1071b21c60a4SVinod Koul 	int retval;
1072b21c60a4SVinod Koul 
107304c5d5a4STakashi Iwai 	if (device->name == NULL || device->ops == NULL)
1074b21c60a4SVinod Koul 		return -EINVAL;
1075b21c60a4SVinod Koul 
1076b21c60a4SVinod Koul 	pr_debug("Registering compressed device %s\n", device->name);
1077b21c60a4SVinod Koul 	if (snd_BUG_ON(!device->ops->open))
1078b21c60a4SVinod Koul 		return -EINVAL;
1079b21c60a4SVinod Koul 	if (snd_BUG_ON(!device->ops->free))
1080b21c60a4SVinod Koul 		return -EINVAL;
1081b21c60a4SVinod Koul 	if (snd_BUG_ON(!device->ops->set_params))
1082b21c60a4SVinod Koul 		return -EINVAL;
1083b21c60a4SVinod Koul 	if (snd_BUG_ON(!device->ops->trigger))
1084b21c60a4SVinod Koul 		return -EINVAL;
1085b21c60a4SVinod Koul 
1086b21c60a4SVinod Koul 	mutex_init(&device->lock);
1087b21c60a4SVinod Koul 
1088b21c60a4SVinod Koul 	/* register a compressed card */
1089b21c60a4SVinod Koul 	mutex_lock(&device_mutex);
1090b21c60a4SVinod Koul 	retval = snd_compress_add_device(device);
1091b21c60a4SVinod Koul 	mutex_unlock(&device_mutex);
1092b21c60a4SVinod Koul 	return retval;
1093b21c60a4SVinod Koul }
1094b21c60a4SVinod Koul EXPORT_SYMBOL_GPL(snd_compress_register);
1095b21c60a4SVinod Koul 
1096b21c60a4SVinod Koul int snd_compress_deregister(struct snd_compr *device)
1097b21c60a4SVinod Koul {
1098b21c60a4SVinod Koul 	pr_debug("Removing compressed device %s\n", device->name);
1099b21c60a4SVinod Koul 	mutex_lock(&device_mutex);
1100b21c60a4SVinod Koul 	snd_compress_remove_device(device);
1101b21c60a4SVinod Koul 	mutex_unlock(&device_mutex);
1102b21c60a4SVinod Koul 	return 0;
1103b21c60a4SVinod Koul }
1104b21c60a4SVinod Koul EXPORT_SYMBOL_GPL(snd_compress_deregister);
1105b21c60a4SVinod Koul 
1106b21c60a4SVinod Koul static int __init snd_compress_init(void)
1107b21c60a4SVinod Koul {
1108b21c60a4SVinod Koul 	return 0;
1109b21c60a4SVinod Koul }
1110b21c60a4SVinod Koul 
1111b21c60a4SVinod Koul static void __exit snd_compress_exit(void)
1112b21c60a4SVinod Koul {
1113b21c60a4SVinod Koul }
1114b21c60a4SVinod Koul 
1115b21c60a4SVinod Koul module_init(snd_compress_init);
1116b21c60a4SVinod Koul module_exit(snd_compress_exit);
1117b21c60a4SVinod Koul 
1118b21c60a4SVinod Koul MODULE_DESCRIPTION("ALSA Compressed offload framework");
1119b21c60a4SVinod Koul MODULE_AUTHOR("Vinod Koul <vinod.koul@linux.intel.com>");
1120b21c60a4SVinod Koul MODULE_LICENSE("GPL v2");
1121