xref: /openbmc/linux/sound/core/compress_offload.c (revision 4dc040a0)
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>
31b21c60a4SVinod Koul #include <linux/mm.h>
32b21c60a4SVinod Koul #include <linux/mutex.h>
33b21c60a4SVinod Koul #include <linux/poll.h>
34b21c60a4SVinod Koul #include <linux/slab.h>
35b21c60a4SVinod Koul #include <linux/sched.h>
36b21c60a4SVinod Koul #include <linux/uio.h>
37b21c60a4SVinod Koul #include <linux/uaccess.h>
38b21c60a4SVinod Koul #include <linux/module.h>
39b21c60a4SVinod Koul #include <sound/core.h>
40b21c60a4SVinod Koul #include <sound/initval.h>
41b21c60a4SVinod Koul #include <sound/compress_params.h>
42b21c60a4SVinod Koul #include <sound/compress_offload.h>
43b21c60a4SVinod Koul #include <sound/compress_driver.h>
44b21c60a4SVinod Koul 
45b21c60a4SVinod Koul /* TODO:
46b21c60a4SVinod Koul  * - add substream support for multiple devices in case of
47b21c60a4SVinod Koul  *	SND_DYNAMIC_MINORS is not used
48b21c60a4SVinod Koul  * - Multiple node representation
49b21c60a4SVinod Koul  *	driver should be able to register multiple nodes
50b21c60a4SVinod Koul  */
51b21c60a4SVinod Koul 
52b21c60a4SVinod Koul static DEFINE_MUTEX(device_mutex);
53b21c60a4SVinod Koul 
54b21c60a4SVinod Koul struct snd_compr_file {
55b21c60a4SVinod Koul 	unsigned long caps;
56b21c60a4SVinod Koul 	struct snd_compr_stream stream;
57b21c60a4SVinod Koul };
58b21c60a4SVinod Koul 
59b21c60a4SVinod Koul /*
60b21c60a4SVinod Koul  * a note on stream states used:
61b21c60a4SVinod Koul  * we use follwing states in the compressed core
62b21c60a4SVinod Koul  * SNDRV_PCM_STATE_OPEN: When stream has been opened.
63b21c60a4SVinod Koul  * SNDRV_PCM_STATE_SETUP: When stream has been initialized. This is done by
64b21c60a4SVinod Koul  *	calling SNDRV_COMPRESS_SET_PARAMS. running streams will come to this
65b21c60a4SVinod Koul  *	state at stop by calling SNDRV_COMPRESS_STOP, or at end of drain.
66b21c60a4SVinod Koul  * SNDRV_PCM_STATE_RUNNING: When stream has been started and is
67b21c60a4SVinod Koul  *	decoding/encoding and rendering/capturing data.
68b21c60a4SVinod Koul  * SNDRV_PCM_STATE_DRAINING: When stream is draining current data. This is done
69b21c60a4SVinod Koul  *	by calling SNDRV_COMPRESS_DRAIN.
70b21c60a4SVinod Koul  * SNDRV_PCM_STATE_PAUSED: When stream is paused. This is done by calling
71b21c60a4SVinod Koul  *	SNDRV_COMPRESS_PAUSE. It can be stopped or resumed by calling
72b21c60a4SVinod Koul  *	SNDRV_COMPRESS_STOP or SNDRV_COMPRESS_RESUME respectively.
73b21c60a4SVinod Koul  */
74b21c60a4SVinod Koul static int snd_compr_open(struct inode *inode, struct file *f)
75b21c60a4SVinod Koul {
76b21c60a4SVinod Koul 	struct snd_compr *compr;
77b21c60a4SVinod Koul 	struct snd_compr_file *data;
78b21c60a4SVinod Koul 	struct snd_compr_runtime *runtime;
79b21c60a4SVinod Koul 	enum snd_compr_direction dirn;
80b21c60a4SVinod Koul 	int maj = imajor(inode);
81b21c60a4SVinod Koul 	int ret;
82b21c60a4SVinod Koul 
8381cb3246SDan Carpenter 	if ((f->f_flags & O_ACCMODE) == O_WRONLY)
84b21c60a4SVinod Koul 		dirn = SND_COMPRESS_PLAYBACK;
8581cb3246SDan Carpenter 	else if ((f->f_flags & O_ACCMODE) == O_RDONLY)
86b21c60a4SVinod Koul 		dirn = SND_COMPRESS_CAPTURE;
8781cb3246SDan Carpenter 	else
88b21c60a4SVinod Koul 		return -EINVAL;
89b21c60a4SVinod Koul 
90b21c60a4SVinod Koul 	if (maj == snd_major)
91b21c60a4SVinod Koul 		compr = snd_lookup_minor_data(iminor(inode),
92b21c60a4SVinod Koul 					SNDRV_DEVICE_TYPE_COMPRESS);
93b21c60a4SVinod Koul 	else
94b21c60a4SVinod Koul 		return -EBADFD;
95b21c60a4SVinod Koul 
96b21c60a4SVinod Koul 	if (compr == NULL) {
97b21c60a4SVinod Koul 		pr_err("no device data!!!\n");
98b21c60a4SVinod Koul 		return -ENODEV;
99b21c60a4SVinod Koul 	}
100b21c60a4SVinod Koul 
101b21c60a4SVinod Koul 	if (dirn != compr->direction) {
102b21c60a4SVinod Koul 		pr_err("this device doesn't support this direction\n");
103b21c60a4SVinod Koul 		return -EINVAL;
104b21c60a4SVinod Koul 	}
105b21c60a4SVinod Koul 
106b21c60a4SVinod Koul 	data = kzalloc(sizeof(*data), GFP_KERNEL);
107b21c60a4SVinod Koul 	if (!data)
108b21c60a4SVinod Koul 		return -ENOMEM;
109b21c60a4SVinod Koul 	data->stream.ops = compr->ops;
110b21c60a4SVinod Koul 	data->stream.direction = dirn;
111b21c60a4SVinod Koul 	data->stream.private_data = compr->private_data;
112b21c60a4SVinod Koul 	data->stream.device = compr;
113b21c60a4SVinod Koul 	runtime = kzalloc(sizeof(*runtime), GFP_KERNEL);
114b21c60a4SVinod Koul 	if (!runtime) {
115b21c60a4SVinod Koul 		kfree(data);
116b21c60a4SVinod Koul 		return -ENOMEM;
117b21c60a4SVinod Koul 	}
118b21c60a4SVinod Koul 	runtime->state = SNDRV_PCM_STATE_OPEN;
119b21c60a4SVinod Koul 	init_waitqueue_head(&runtime->sleep);
120b21c60a4SVinod Koul 	data->stream.runtime = runtime;
121b21c60a4SVinod Koul 	f->private_data = (void *)data;
122b21c60a4SVinod Koul 	mutex_lock(&compr->lock);
123b21c60a4SVinod Koul 	ret = compr->ops->open(&data->stream);
124b21c60a4SVinod Koul 	mutex_unlock(&compr->lock);
125b21c60a4SVinod Koul 	if (ret) {
126b21c60a4SVinod Koul 		kfree(runtime);
127b21c60a4SVinod Koul 		kfree(data);
128b21c60a4SVinod Koul 	}
129b21c60a4SVinod Koul 	return ret;
130b21c60a4SVinod Koul }
131b21c60a4SVinod Koul 
132b21c60a4SVinod Koul static int snd_compr_free(struct inode *inode, struct file *f)
133b21c60a4SVinod Koul {
134b21c60a4SVinod Koul 	struct snd_compr_file *data = f->private_data;
135b21c60a4SVinod Koul 	data->stream.ops->free(&data->stream);
136b21c60a4SVinod Koul 	kfree(data->stream.runtime->buffer);
137b21c60a4SVinod Koul 	kfree(data->stream.runtime);
138b21c60a4SVinod Koul 	kfree(data);
139b21c60a4SVinod Koul 	return 0;
140b21c60a4SVinod Koul }
141b21c60a4SVinod Koul 
142b21c60a4SVinod Koul static void snd_compr_update_tstamp(struct snd_compr_stream *stream,
143b21c60a4SVinod Koul 		struct snd_compr_tstamp *tstamp)
144b21c60a4SVinod Koul {
145b21c60a4SVinod Koul 	if (!stream->ops->pointer)
146b21c60a4SVinod Koul 		return;
147b21c60a4SVinod Koul 	stream->ops->pointer(stream, tstamp);
148b21c60a4SVinod Koul 	pr_debug("dsp consumed till %d total %d bytes\n",
149b21c60a4SVinod Koul 		tstamp->byte_offset, tstamp->copied_total);
150b21c60a4SVinod Koul 	stream->runtime->hw_pointer = tstamp->byte_offset;
151b21c60a4SVinod Koul 	stream->runtime->total_bytes_transferred = tstamp->copied_total;
152b21c60a4SVinod Koul }
153b21c60a4SVinod Koul 
154b21c60a4SVinod Koul static size_t snd_compr_calc_avail(struct snd_compr_stream *stream,
155b21c60a4SVinod Koul 		struct snd_compr_avail *avail)
156b21c60a4SVinod Koul {
157b21c60a4SVinod Koul 	long avail_calc; /*this needs to be signed variable */
158b21c60a4SVinod Koul 
159b21c60a4SVinod Koul 	snd_compr_update_tstamp(stream, &avail->tstamp);
160b21c60a4SVinod Koul 
161b21c60a4SVinod Koul 	/* FIXME: This needs to be different for capture stream,
162b21c60a4SVinod Koul 	   available is # of compressed data, for playback it's
163b21c60a4SVinod Koul 	   remainder of buffer */
164b21c60a4SVinod Koul 
165b21c60a4SVinod Koul 	if (stream->runtime->total_bytes_available == 0 &&
166b21c60a4SVinod Koul 			stream->runtime->state == SNDRV_PCM_STATE_SETUP) {
167b21c60a4SVinod Koul 		pr_debug("detected init and someone forgot to do a write\n");
168b21c60a4SVinod Koul 		return stream->runtime->buffer_size;
169b21c60a4SVinod Koul 	}
170b21c60a4SVinod Koul 	pr_debug("app wrote %lld, DSP consumed %lld\n",
171b21c60a4SVinod Koul 			stream->runtime->total_bytes_available,
172b21c60a4SVinod Koul 			stream->runtime->total_bytes_transferred);
173b21c60a4SVinod Koul 	if (stream->runtime->total_bytes_available ==
174b21c60a4SVinod Koul 				stream->runtime->total_bytes_transferred) {
175b21c60a4SVinod Koul 		pr_debug("both pointers are same, returning full avail\n");
176b21c60a4SVinod Koul 		return stream->runtime->buffer_size;
177b21c60a4SVinod Koul 	}
178b21c60a4SVinod Koul 
179b21c60a4SVinod Koul 	/* FIXME: this routine isn't consistent, in one test we use
180b21c60a4SVinod Koul 	 * cumulative values and in the other byte offsets. Do we
181b21c60a4SVinod Koul 	 * really need the byte offsets if the cumulative values have
182b21c60a4SVinod Koul 	 * been updated? In the PCM interface app_ptr and hw_ptr are
183b21c60a4SVinod Koul 	 * already cumulative */
184b21c60a4SVinod Koul 
185b21c60a4SVinod Koul 	avail_calc = stream->runtime->buffer_size -
186b21c60a4SVinod Koul 		(stream->runtime->app_pointer - stream->runtime->hw_pointer);
187b21c60a4SVinod Koul 	pr_debug("calc avail as %ld, app_ptr %lld, hw+ptr %lld\n", avail_calc,
188b21c60a4SVinod Koul 			stream->runtime->app_pointer,
189b21c60a4SVinod Koul 			stream->runtime->hw_pointer);
190b21c60a4SVinod Koul 	if (avail_calc >= stream->runtime->buffer_size)
191b21c60a4SVinod Koul 		avail_calc -= stream->runtime->buffer_size;
192b21c60a4SVinod Koul 	pr_debug("ret avail as %ld\n", avail_calc);
193b21c60a4SVinod Koul 	avail->avail = avail_calc;
194b21c60a4SVinod Koul 	return avail_calc;
195b21c60a4SVinod Koul }
196b21c60a4SVinod Koul 
197b21c60a4SVinod Koul static inline size_t snd_compr_get_avail(struct snd_compr_stream *stream)
198b21c60a4SVinod Koul {
199b21c60a4SVinod Koul 	struct snd_compr_avail avail;
200b21c60a4SVinod Koul 
201b21c60a4SVinod Koul 	return snd_compr_calc_avail(stream, &avail);
202b21c60a4SVinod Koul }
203b21c60a4SVinod Koul 
204b21c60a4SVinod Koul static int
205b21c60a4SVinod Koul snd_compr_ioctl_avail(struct snd_compr_stream *stream, unsigned long arg)
206b21c60a4SVinod Koul {
207b21c60a4SVinod Koul 	struct snd_compr_avail ioctl_avail;
208b21c60a4SVinod Koul 	size_t avail;
209b21c60a4SVinod Koul 
210b21c60a4SVinod Koul 	avail = snd_compr_calc_avail(stream, &ioctl_avail);
211b21c60a4SVinod Koul 	ioctl_avail.avail = avail;
212b21c60a4SVinod Koul 
213b21c60a4SVinod Koul 	if (copy_to_user((__u64 __user *)arg,
214b21c60a4SVinod Koul 				&ioctl_avail, sizeof(ioctl_avail)))
215b21c60a4SVinod Koul 		return -EFAULT;
216b21c60a4SVinod Koul 	return 0;
217b21c60a4SVinod Koul }
218b21c60a4SVinod Koul 
219b21c60a4SVinod Koul static int snd_compr_write_data(struct snd_compr_stream *stream,
220b21c60a4SVinod Koul 	       const char __user *buf, size_t count)
221b21c60a4SVinod Koul {
222b21c60a4SVinod Koul 	void *dstn;
223b21c60a4SVinod Koul 	size_t copy;
224b21c60a4SVinod Koul 	struct snd_compr_runtime *runtime = stream->runtime;
225b21c60a4SVinod Koul 
226b21c60a4SVinod Koul 	dstn = runtime->buffer + runtime->app_pointer;
227b21c60a4SVinod Koul 	pr_debug("copying %ld at %lld\n",
228b21c60a4SVinod Koul 			(unsigned long)count, runtime->app_pointer);
229b21c60a4SVinod Koul 	if (count < runtime->buffer_size - runtime->app_pointer) {
230b21c60a4SVinod Koul 		if (copy_from_user(dstn, buf, count))
231b21c60a4SVinod Koul 			return -EFAULT;
232b21c60a4SVinod Koul 		runtime->app_pointer += count;
233b21c60a4SVinod Koul 	} else {
234b21c60a4SVinod Koul 		copy = runtime->buffer_size - runtime->app_pointer;
235b21c60a4SVinod Koul 		if (copy_from_user(dstn, buf, copy))
236b21c60a4SVinod Koul 			return -EFAULT;
237b21c60a4SVinod Koul 		if (copy_from_user(runtime->buffer, buf + copy, count - copy))
238b21c60a4SVinod Koul 			return -EFAULT;
239b21c60a4SVinod Koul 		runtime->app_pointer = count - copy;
240b21c60a4SVinod Koul 	}
241b21c60a4SVinod Koul 	/* if DSP cares, let it know data has been written */
242b21c60a4SVinod Koul 	if (stream->ops->ack)
243b21c60a4SVinod Koul 		stream->ops->ack(stream, count);
244b21c60a4SVinod Koul 	return count;
245b21c60a4SVinod Koul }
246b21c60a4SVinod Koul 
247b21c60a4SVinod Koul static ssize_t snd_compr_write(struct file *f, const char __user *buf,
248b21c60a4SVinod Koul 		size_t count, loff_t *offset)
249b21c60a4SVinod Koul {
250b21c60a4SVinod Koul 	struct snd_compr_file *data = f->private_data;
251b21c60a4SVinod Koul 	struct snd_compr_stream *stream;
252b21c60a4SVinod Koul 	size_t avail;
253b21c60a4SVinod Koul 	int retval;
254b21c60a4SVinod Koul 
255b21c60a4SVinod Koul 	if (snd_BUG_ON(!data))
256b21c60a4SVinod Koul 		return -EFAULT;
257b21c60a4SVinod Koul 
258b21c60a4SVinod Koul 	stream = &data->stream;
259b21c60a4SVinod Koul 	mutex_lock(&stream->device->lock);
260b21c60a4SVinod Koul 	/* write is allowed when stream is running or has been steup */
261b21c60a4SVinod Koul 	if (stream->runtime->state != SNDRV_PCM_STATE_SETUP &&
262b21c60a4SVinod Koul 			stream->runtime->state != SNDRV_PCM_STATE_RUNNING) {
263b21c60a4SVinod Koul 		mutex_unlock(&stream->device->lock);
264b21c60a4SVinod Koul 		return -EBADFD;
265b21c60a4SVinod Koul 	}
266b21c60a4SVinod Koul 
267b21c60a4SVinod Koul 	avail = snd_compr_get_avail(stream);
268b21c60a4SVinod Koul 	pr_debug("avail returned %ld\n", (unsigned long)avail);
269b21c60a4SVinod Koul 	/* calculate how much we can write to buffer */
270b21c60a4SVinod Koul 	if (avail > count)
271b21c60a4SVinod Koul 		avail = count;
272b21c60a4SVinod Koul 
273b21c60a4SVinod Koul 	if (stream->ops->copy)
274b21c60a4SVinod Koul 		retval = stream->ops->copy(stream, buf, avail);
275b21c60a4SVinod Koul 	else
276b21c60a4SVinod Koul 		retval = snd_compr_write_data(stream, buf, avail);
277b21c60a4SVinod Koul 	if (retval > 0)
278b21c60a4SVinod Koul 		stream->runtime->total_bytes_available += retval;
279b21c60a4SVinod Koul 
280b21c60a4SVinod Koul 	/* while initiating the stream, write should be called before START
281b21c60a4SVinod Koul 	 * call, so in setup move state */
282b21c60a4SVinod Koul 	if (stream->runtime->state == SNDRV_PCM_STATE_SETUP) {
283b21c60a4SVinod Koul 		stream->runtime->state = SNDRV_PCM_STATE_PREPARED;
284b21c60a4SVinod Koul 		pr_debug("stream prepared, Houston we are good to go\n");
285b21c60a4SVinod Koul 	}
286b21c60a4SVinod Koul 
287b21c60a4SVinod Koul 	mutex_unlock(&stream->device->lock);
288b21c60a4SVinod Koul 	return retval;
289b21c60a4SVinod Koul }
290b21c60a4SVinod Koul 
291b21c60a4SVinod Koul 
292b21c60a4SVinod Koul static ssize_t snd_compr_read(struct file *f, char __user *buf,
293b21c60a4SVinod Koul 		size_t count, loff_t *offset)
294b21c60a4SVinod Koul {
295b21c60a4SVinod Koul 	return -ENXIO;
296b21c60a4SVinod Koul }
297b21c60a4SVinod Koul 
298b21c60a4SVinod Koul static int snd_compr_mmap(struct file *f, struct vm_area_struct *vma)
299b21c60a4SVinod Koul {
300b21c60a4SVinod Koul 	return -ENXIO;
301b21c60a4SVinod Koul }
302b21c60a4SVinod Koul 
303b21c60a4SVinod Koul static inline int snd_compr_get_poll(struct snd_compr_stream *stream)
304b21c60a4SVinod Koul {
305b21c60a4SVinod Koul 	if (stream->direction == SND_COMPRESS_PLAYBACK)
306b21c60a4SVinod Koul 		return POLLOUT | POLLWRNORM;
307b21c60a4SVinod Koul 	else
308b21c60a4SVinod Koul 		return POLLIN | POLLRDNORM;
309b21c60a4SVinod Koul }
310b21c60a4SVinod Koul 
311b21c60a4SVinod Koul static unsigned int snd_compr_poll(struct file *f, poll_table *wait)
312b21c60a4SVinod Koul {
313b21c60a4SVinod Koul 	struct snd_compr_file *data = f->private_data;
314b21c60a4SVinod Koul 	struct snd_compr_stream *stream;
315b21c60a4SVinod Koul 	size_t avail;
316b21c60a4SVinod Koul 	int retval = 0;
317b21c60a4SVinod Koul 
318b21c60a4SVinod Koul 	if (snd_BUG_ON(!data))
319b21c60a4SVinod Koul 		return -EFAULT;
320b21c60a4SVinod Koul 	stream = &data->stream;
321b21c60a4SVinod Koul 	if (snd_BUG_ON(!stream))
322b21c60a4SVinod Koul 		return -EFAULT;
323b21c60a4SVinod Koul 
324b21c60a4SVinod Koul 	mutex_lock(&stream->device->lock);
325b21c60a4SVinod Koul 	if (stream->runtime->state == SNDRV_PCM_STATE_PAUSED ||
326b21c60a4SVinod Koul 			stream->runtime->state == SNDRV_PCM_STATE_OPEN) {
327b21c60a4SVinod Koul 		retval = -EBADFD;
328b21c60a4SVinod Koul 		goto out;
329b21c60a4SVinod Koul 	}
330b21c60a4SVinod Koul 	poll_wait(f, &stream->runtime->sleep, wait);
331b21c60a4SVinod Koul 
332b21c60a4SVinod Koul 	avail = snd_compr_get_avail(stream);
333b21c60a4SVinod Koul 	pr_debug("avail is %ld\n", (unsigned long)avail);
334b21c60a4SVinod Koul 	/* check if we have at least one fragment to fill */
335b21c60a4SVinod Koul 	switch (stream->runtime->state) {
336b21c60a4SVinod Koul 	case SNDRV_PCM_STATE_DRAINING:
337b21c60a4SVinod Koul 		/* stream has been woken up after drain is complete
338b21c60a4SVinod Koul 		 * draining done so set stream state to stopped
339b21c60a4SVinod Koul 		 */
340b21c60a4SVinod Koul 		retval = snd_compr_get_poll(stream);
341b21c60a4SVinod Koul 		stream->runtime->state = SNDRV_PCM_STATE_SETUP;
342b21c60a4SVinod Koul 		break;
343b21c60a4SVinod Koul 	case SNDRV_PCM_STATE_RUNNING:
344b21c60a4SVinod Koul 	case SNDRV_PCM_STATE_PREPARED:
345b21c60a4SVinod Koul 	case SNDRV_PCM_STATE_PAUSED:
346b21c60a4SVinod Koul 		if (avail >= stream->runtime->fragment_size)
347b21c60a4SVinod Koul 			retval = snd_compr_get_poll(stream);
348b21c60a4SVinod Koul 		break;
349b21c60a4SVinod Koul 	default:
350b21c60a4SVinod Koul 		if (stream->direction == SND_COMPRESS_PLAYBACK)
351b21c60a4SVinod Koul 			retval = POLLOUT | POLLWRNORM | POLLERR;
352b21c60a4SVinod Koul 		else
353b21c60a4SVinod Koul 			retval = POLLIN | POLLRDNORM | POLLERR;
354b21c60a4SVinod Koul 		break;
355b21c60a4SVinod Koul 	}
356b21c60a4SVinod Koul out:
357b21c60a4SVinod Koul 	mutex_unlock(&stream->device->lock);
358b21c60a4SVinod Koul 	return retval;
359b21c60a4SVinod Koul }
360b21c60a4SVinod Koul 
361b21c60a4SVinod Koul static int
362b21c60a4SVinod Koul snd_compr_get_caps(struct snd_compr_stream *stream, unsigned long arg)
363b21c60a4SVinod Koul {
364b21c60a4SVinod Koul 	int retval;
365b21c60a4SVinod Koul 	struct snd_compr_caps caps;
366b21c60a4SVinod Koul 
367b21c60a4SVinod Koul 	if (!stream->ops->get_caps)
368b21c60a4SVinod Koul 		return -ENXIO;
369b21c60a4SVinod Koul 
370b21c60a4SVinod Koul 	retval = stream->ops->get_caps(stream, &caps);
371b21c60a4SVinod Koul 	if (retval)
372b21c60a4SVinod Koul 		goto out;
373b21c60a4SVinod Koul 	if (copy_to_user((void __user *)arg, &caps, sizeof(caps)))
374b21c60a4SVinod Koul 		retval = -EFAULT;
375b21c60a4SVinod Koul out:
376b21c60a4SVinod Koul 	return retval;
377b21c60a4SVinod Koul }
378b21c60a4SVinod Koul 
379b21c60a4SVinod Koul static int
380b21c60a4SVinod Koul snd_compr_get_codec_caps(struct snd_compr_stream *stream, unsigned long arg)
381b21c60a4SVinod Koul {
382b21c60a4SVinod Koul 	int retval;
383b21c60a4SVinod Koul 	struct snd_compr_codec_caps *caps;
384b21c60a4SVinod Koul 
385b21c60a4SVinod Koul 	if (!stream->ops->get_codec_caps)
386b21c60a4SVinod Koul 		return -ENXIO;
387b21c60a4SVinod Koul 
388b21c60a4SVinod Koul 	caps = kmalloc(sizeof(*caps), GFP_KERNEL);
389b21c60a4SVinod Koul 	if (!caps)
390b21c60a4SVinod Koul 		return -ENOMEM;
391b21c60a4SVinod Koul 
392b21c60a4SVinod Koul 	retval = stream->ops->get_codec_caps(stream, caps);
393b21c60a4SVinod Koul 	if (retval)
394b21c60a4SVinod Koul 		goto out;
395b21c60a4SVinod Koul 	if (copy_to_user((void __user *)arg, caps, sizeof(*caps)))
396b21c60a4SVinod Koul 		retval = -EFAULT;
397b21c60a4SVinod Koul 
398b21c60a4SVinod Koul out:
399b21c60a4SVinod Koul 	kfree(caps);
400b21c60a4SVinod Koul 	return retval;
401b21c60a4SVinod Koul }
402b21c60a4SVinod Koul 
403b21c60a4SVinod Koul /* revisit this with snd_pcm_preallocate_xxx */
404b21c60a4SVinod Koul static int snd_compr_allocate_buffer(struct snd_compr_stream *stream,
405b21c60a4SVinod Koul 		struct snd_compr_params *params)
406b21c60a4SVinod Koul {
407b21c60a4SVinod Koul 	unsigned int buffer_size;
408b21c60a4SVinod Koul 	void *buffer;
409b21c60a4SVinod Koul 
410b21c60a4SVinod Koul 	buffer_size = params->buffer.fragment_size * params->buffer.fragments;
411b21c60a4SVinod Koul 	if (stream->ops->copy) {
412b21c60a4SVinod Koul 		buffer = NULL;
413b21c60a4SVinod Koul 		/* if copy is defined the driver will be required to copy
414b21c60a4SVinod Koul 		 * the data from core
415b21c60a4SVinod Koul 		 */
416b21c60a4SVinod Koul 	} else {
417b21c60a4SVinod Koul 		buffer = kmalloc(buffer_size, GFP_KERNEL);
418b21c60a4SVinod Koul 		if (!buffer)
419b21c60a4SVinod Koul 			return -ENOMEM;
420b21c60a4SVinod Koul 	}
421b21c60a4SVinod Koul 	stream->runtime->fragment_size = params->buffer.fragment_size;
422b21c60a4SVinod Koul 	stream->runtime->fragments = params->buffer.fragments;
423b21c60a4SVinod Koul 	stream->runtime->buffer = buffer;
424b21c60a4SVinod Koul 	stream->runtime->buffer_size = buffer_size;
425b21c60a4SVinod Koul 	return 0;
426b21c60a4SVinod Koul }
427b21c60a4SVinod Koul 
4284dc040a0SVinod Koul static int snd_compress_check_input(struct snd_compr_params *params)
4294dc040a0SVinod Koul {
4304dc040a0SVinod Koul 	/* first let's check the buffer parameter's */
4314dc040a0SVinod Koul 	if (params->buffer.fragment_size == 0 ||
4324dc040a0SVinod Koul 			params->buffer.fragments > SIZE_MAX / params->buffer.fragment_size)
4334dc040a0SVinod Koul 		return -EINVAL;
4344dc040a0SVinod Koul 
4354dc040a0SVinod Koul 	return 0;
4364dc040a0SVinod Koul }
4374dc040a0SVinod Koul 
438b21c60a4SVinod Koul static int
439b21c60a4SVinod Koul snd_compr_set_params(struct snd_compr_stream *stream, unsigned long arg)
440b21c60a4SVinod Koul {
441b21c60a4SVinod Koul 	struct snd_compr_params *params;
442b21c60a4SVinod Koul 	int retval;
443b21c60a4SVinod Koul 
444b21c60a4SVinod Koul 	if (stream->runtime->state == SNDRV_PCM_STATE_OPEN) {
445b21c60a4SVinod Koul 		/*
446b21c60a4SVinod Koul 		 * we should allow parameter change only when stream has been
447b21c60a4SVinod Koul 		 * opened not in other cases
448b21c60a4SVinod Koul 		 */
449b21c60a4SVinod Koul 		params = kmalloc(sizeof(*params), GFP_KERNEL);
450b21c60a4SVinod Koul 		if (!params)
451b21c60a4SVinod Koul 			return -ENOMEM;
452769fab2aSJesper Juhl 		if (copy_from_user(params, (void __user *)arg, sizeof(*params))) {
453769fab2aSJesper Juhl 			retval = -EFAULT;
454769fab2aSJesper Juhl 			goto out;
455769fab2aSJesper Juhl 		}
4564dc040a0SVinod Koul 
4574dc040a0SVinod Koul 		retval = snd_compress_check_input(params);
4584dc040a0SVinod Koul 		if (retval)
4594dc040a0SVinod Koul 			goto out;
4604dc040a0SVinod Koul 
461b21c60a4SVinod Koul 		retval = snd_compr_allocate_buffer(stream, params);
462b21c60a4SVinod Koul 		if (retval) {
463769fab2aSJesper Juhl 			retval = -ENOMEM;
464769fab2aSJesper Juhl 			goto out;
465b21c60a4SVinod Koul 		}
4664dc040a0SVinod Koul 
467b21c60a4SVinod Koul 		retval = stream->ops->set_params(stream, params);
468b21c60a4SVinod Koul 		if (retval)
469b21c60a4SVinod Koul 			goto out;
470b21c60a4SVinod Koul 		stream->runtime->state = SNDRV_PCM_STATE_SETUP;
471769fab2aSJesper Juhl 	} else {
472b21c60a4SVinod Koul 		return -EPERM;
473769fab2aSJesper Juhl 	}
474b21c60a4SVinod Koul out:
475b21c60a4SVinod Koul 	kfree(params);
476b21c60a4SVinod Koul 	return retval;
477b21c60a4SVinod Koul }
478b21c60a4SVinod Koul 
479b21c60a4SVinod Koul static int
480b21c60a4SVinod Koul snd_compr_get_params(struct snd_compr_stream *stream, unsigned long arg)
481b21c60a4SVinod Koul {
482b21c60a4SVinod Koul 	struct snd_codec *params;
483b21c60a4SVinod Koul 	int retval;
484b21c60a4SVinod Koul 
485b21c60a4SVinod Koul 	if (!stream->ops->get_params)
486b21c60a4SVinod Koul 		return -EBADFD;
487b21c60a4SVinod Koul 
488b21c60a4SVinod Koul 	params = kmalloc(sizeof(*params), GFP_KERNEL);
489b21c60a4SVinod Koul 	if (!params)
490b21c60a4SVinod Koul 		return -ENOMEM;
491b21c60a4SVinod Koul 	retval = stream->ops->get_params(stream, params);
492b21c60a4SVinod Koul 	if (retval)
493b21c60a4SVinod Koul 		goto out;
494b21c60a4SVinod Koul 	if (copy_to_user((char __user *)arg, params, sizeof(*params)))
495b21c60a4SVinod Koul 		retval = -EFAULT;
496b21c60a4SVinod Koul 
497b21c60a4SVinod Koul out:
498b21c60a4SVinod Koul 	kfree(params);
499b21c60a4SVinod Koul 	return retval;
500b21c60a4SVinod Koul }
501b21c60a4SVinod Koul 
502b21c60a4SVinod Koul static inline int
503b21c60a4SVinod Koul snd_compr_tstamp(struct snd_compr_stream *stream, unsigned long arg)
504b21c60a4SVinod Koul {
505b21c60a4SVinod Koul 	struct snd_compr_tstamp tstamp;
506b21c60a4SVinod Koul 
507b21c60a4SVinod Koul 	snd_compr_update_tstamp(stream, &tstamp);
508b21c60a4SVinod Koul 	return copy_to_user((struct snd_compr_tstamp __user *)arg,
509b21c60a4SVinod Koul 		&tstamp, sizeof(tstamp)) ? -EFAULT : 0;
510b21c60a4SVinod Koul }
511b21c60a4SVinod Koul 
512b21c60a4SVinod Koul static int snd_compr_pause(struct snd_compr_stream *stream)
513b21c60a4SVinod Koul {
514b21c60a4SVinod Koul 	int retval;
515b21c60a4SVinod Koul 
516b21c60a4SVinod Koul 	if (stream->runtime->state != SNDRV_PCM_STATE_RUNNING)
517b21c60a4SVinod Koul 		return -EPERM;
518b21c60a4SVinod Koul 	retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_PAUSE_PUSH);
5196b18f793SVinod Koul 	if (!retval)
520b21c60a4SVinod Koul 		stream->runtime->state = SNDRV_PCM_STATE_PAUSED;
521b21c60a4SVinod Koul 	return retval;
522b21c60a4SVinod Koul }
523b21c60a4SVinod Koul 
524b21c60a4SVinod Koul static int snd_compr_resume(struct snd_compr_stream *stream)
525b21c60a4SVinod Koul {
526b21c60a4SVinod Koul 	int retval;
527b21c60a4SVinod Koul 
528b21c60a4SVinod Koul 	if (stream->runtime->state != SNDRV_PCM_STATE_PAUSED)
529b21c60a4SVinod Koul 		return -EPERM;
530b21c60a4SVinod Koul 	retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_PAUSE_RELEASE);
531b21c60a4SVinod Koul 	if (!retval)
532b21c60a4SVinod Koul 		stream->runtime->state = SNDRV_PCM_STATE_RUNNING;
533b21c60a4SVinod Koul 	return retval;
534b21c60a4SVinod Koul }
535b21c60a4SVinod Koul 
536b21c60a4SVinod Koul static int snd_compr_start(struct snd_compr_stream *stream)
537b21c60a4SVinod Koul {
538b21c60a4SVinod Koul 	int retval;
539b21c60a4SVinod Koul 
540b21c60a4SVinod Koul 	if (stream->runtime->state != SNDRV_PCM_STATE_PREPARED)
541b21c60a4SVinod Koul 		return -EPERM;
542b21c60a4SVinod Koul 	retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_START);
543b21c60a4SVinod Koul 	if (!retval)
544b21c60a4SVinod Koul 		stream->runtime->state = SNDRV_PCM_STATE_RUNNING;
545b21c60a4SVinod Koul 	return retval;
546b21c60a4SVinod Koul }
547b21c60a4SVinod Koul 
548b21c60a4SVinod Koul static int snd_compr_stop(struct snd_compr_stream *stream)
549b21c60a4SVinod Koul {
550b21c60a4SVinod Koul 	int retval;
551b21c60a4SVinod Koul 
552b21c60a4SVinod Koul 	if (stream->runtime->state == SNDRV_PCM_STATE_PREPARED ||
553b21c60a4SVinod Koul 			stream->runtime->state == SNDRV_PCM_STATE_SETUP)
554b21c60a4SVinod Koul 		return -EPERM;
555b21c60a4SVinod Koul 	retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_STOP);
556b21c60a4SVinod Koul 	if (!retval) {
557b21c60a4SVinod Koul 		stream->runtime->state = SNDRV_PCM_STATE_SETUP;
558b21c60a4SVinod Koul 		wake_up(&stream->runtime->sleep);
5598b21460aSVinod Koul 		stream->runtime->hw_pointer = 0;
5608b21460aSVinod Koul 		stream->runtime->app_pointer = 0;
5618b21460aSVinod Koul 		stream->runtime->total_bytes_available = 0;
5628b21460aSVinod Koul 		stream->runtime->total_bytes_transferred = 0;
563b21c60a4SVinod Koul 	}
564b21c60a4SVinod Koul 	return retval;
565b21c60a4SVinod Koul }
566b21c60a4SVinod Koul 
567b21c60a4SVinod Koul static int snd_compr_drain(struct snd_compr_stream *stream)
568b21c60a4SVinod Koul {
569b21c60a4SVinod Koul 	int retval;
570b21c60a4SVinod Koul 
571b21c60a4SVinod Koul 	if (stream->runtime->state == SNDRV_PCM_STATE_PREPARED ||
572b21c60a4SVinod Koul 			stream->runtime->state == SNDRV_PCM_STATE_SETUP)
573b21c60a4SVinod Koul 		return -EPERM;
574b21c60a4SVinod Koul 	retval = stream->ops->trigger(stream, SND_COMPR_TRIGGER_DRAIN);
575b21c60a4SVinod Koul 	if (!retval) {
576b21c60a4SVinod Koul 		stream->runtime->state = SNDRV_PCM_STATE_DRAINING;
577b21c60a4SVinod Koul 		wake_up(&stream->runtime->sleep);
578b21c60a4SVinod Koul 	}
579b21c60a4SVinod Koul 	return retval;
580b21c60a4SVinod Koul }
581b21c60a4SVinod Koul 
582b21c60a4SVinod Koul static long snd_compr_ioctl(struct file *f, unsigned int cmd, unsigned long arg)
583b21c60a4SVinod Koul {
584b21c60a4SVinod Koul 	struct snd_compr_file *data = f->private_data;
585b21c60a4SVinod Koul 	struct snd_compr_stream *stream;
586b21c60a4SVinod Koul 	int retval = -ENOTTY;
587b21c60a4SVinod Koul 
588b21c60a4SVinod Koul 	if (snd_BUG_ON(!data))
589b21c60a4SVinod Koul 		return -EFAULT;
590b21c60a4SVinod Koul 	stream = &data->stream;
591b21c60a4SVinod Koul 	if (snd_BUG_ON(!stream))
592b21c60a4SVinod Koul 		return -EFAULT;
593b21c60a4SVinod Koul 	mutex_lock(&stream->device->lock);
594b21c60a4SVinod Koul 	switch (_IOC_NR(cmd)) {
595b21c60a4SVinod Koul 	case _IOC_NR(SNDRV_COMPRESS_IOCTL_VERSION):
596b21c60a4SVinod Koul 		put_user(SNDRV_COMPRESS_VERSION,
597b21c60a4SVinod Koul 				(int __user *)arg) ? -EFAULT : 0;
598b21c60a4SVinod Koul 		break;
599b21c60a4SVinod Koul 	case _IOC_NR(SNDRV_COMPRESS_GET_CAPS):
600b21c60a4SVinod Koul 		retval = snd_compr_get_caps(stream, arg);
601b21c60a4SVinod Koul 		break;
602b21c60a4SVinod Koul 	case _IOC_NR(SNDRV_COMPRESS_GET_CODEC_CAPS):
603b21c60a4SVinod Koul 		retval = snd_compr_get_codec_caps(stream, arg);
604b21c60a4SVinod Koul 		break;
605b21c60a4SVinod Koul 	case _IOC_NR(SNDRV_COMPRESS_SET_PARAMS):
606b21c60a4SVinod Koul 		retval = snd_compr_set_params(stream, arg);
607b21c60a4SVinod Koul 		break;
608b21c60a4SVinod Koul 	case _IOC_NR(SNDRV_COMPRESS_GET_PARAMS):
609b21c60a4SVinod Koul 		retval = snd_compr_get_params(stream, arg);
610b21c60a4SVinod Koul 		break;
611b21c60a4SVinod Koul 	case _IOC_NR(SNDRV_COMPRESS_TSTAMP):
612b21c60a4SVinod Koul 		retval = snd_compr_tstamp(stream, arg);
613b21c60a4SVinod Koul 		break;
614b21c60a4SVinod Koul 	case _IOC_NR(SNDRV_COMPRESS_AVAIL):
615b21c60a4SVinod Koul 		retval = snd_compr_ioctl_avail(stream, arg);
616b21c60a4SVinod Koul 		break;
617b21c60a4SVinod Koul 	case _IOC_NR(SNDRV_COMPRESS_PAUSE):
618b21c60a4SVinod Koul 		retval = snd_compr_pause(stream);
619b21c60a4SVinod Koul 		break;
620b21c60a4SVinod Koul 	case _IOC_NR(SNDRV_COMPRESS_RESUME):
621b21c60a4SVinod Koul 		retval = snd_compr_resume(stream);
622b21c60a4SVinod Koul 		break;
623b21c60a4SVinod Koul 	case _IOC_NR(SNDRV_COMPRESS_START):
624b21c60a4SVinod Koul 		retval = snd_compr_start(stream);
625b21c60a4SVinod Koul 		break;
626b21c60a4SVinod Koul 	case _IOC_NR(SNDRV_COMPRESS_STOP):
627b21c60a4SVinod Koul 		retval = snd_compr_stop(stream);
628b21c60a4SVinod Koul 		break;
629b21c60a4SVinod Koul 	case _IOC_NR(SNDRV_COMPRESS_DRAIN):
630b21c60a4SVinod Koul 		retval = snd_compr_drain(stream);
631b21c60a4SVinod Koul 		break;
632b21c60a4SVinod Koul 	}
633b21c60a4SVinod Koul 	mutex_unlock(&stream->device->lock);
634b21c60a4SVinod Koul 	return retval;
635b21c60a4SVinod Koul }
636b21c60a4SVinod Koul 
637b21c60a4SVinod Koul static const struct file_operations snd_compr_file_ops = {
638b21c60a4SVinod Koul 		.owner =	THIS_MODULE,
639b21c60a4SVinod Koul 		.open =		snd_compr_open,
640b21c60a4SVinod Koul 		.release =	snd_compr_free,
641b21c60a4SVinod Koul 		.write =	snd_compr_write,
642b21c60a4SVinod Koul 		.read =		snd_compr_read,
643b21c60a4SVinod Koul 		.unlocked_ioctl = snd_compr_ioctl,
644b21c60a4SVinod Koul 		.mmap =		snd_compr_mmap,
645b21c60a4SVinod Koul 		.poll =		snd_compr_poll,
646b21c60a4SVinod Koul };
647b21c60a4SVinod Koul 
648b21c60a4SVinod Koul static int snd_compress_dev_register(struct snd_device *device)
649b21c60a4SVinod Koul {
650b21c60a4SVinod Koul 	int ret = -EINVAL;
651b21c60a4SVinod Koul 	char str[16];
652b21c60a4SVinod Koul 	struct snd_compr *compr;
653b21c60a4SVinod Koul 
654b21c60a4SVinod Koul 	if (snd_BUG_ON(!device || !device->device_data))
655b21c60a4SVinod Koul 		return -EBADFD;
656b21c60a4SVinod Koul 	compr = device->device_data;
657b21c60a4SVinod Koul 
658b21c60a4SVinod Koul 	sprintf(str, "comprC%iD%i", compr->card->number, compr->device);
659b21c60a4SVinod Koul 	pr_debug("reg %s for device %s, direction %d\n", str, compr->name,
660b21c60a4SVinod Koul 			compr->direction);
661b21c60a4SVinod Koul 	/* register compressed device */
662b21c60a4SVinod Koul 	ret = snd_register_device(SNDRV_DEVICE_TYPE_COMPRESS, compr->card,
663b21c60a4SVinod Koul 			compr->device, &snd_compr_file_ops, compr, str);
664b21c60a4SVinod Koul 	if (ret < 0) {
665b21c60a4SVinod Koul 		pr_err("snd_register_device failed\n %d", ret);
666b21c60a4SVinod Koul 		return ret;
667b21c60a4SVinod Koul 	}
668b21c60a4SVinod Koul 	return ret;
669b21c60a4SVinod Koul 
670b21c60a4SVinod Koul }
671b21c60a4SVinod Koul 
672b21c60a4SVinod Koul static int snd_compress_dev_disconnect(struct snd_device *device)
673b21c60a4SVinod Koul {
674b21c60a4SVinod Koul 	struct snd_compr *compr;
675b21c60a4SVinod Koul 
676b21c60a4SVinod Koul 	compr = device->device_data;
677b21c60a4SVinod Koul 	snd_unregister_device(compr->direction, compr->card, compr->device);
678b21c60a4SVinod Koul 	return 0;
679b21c60a4SVinod Koul }
680b21c60a4SVinod Koul 
681b21c60a4SVinod Koul /*
682b21c60a4SVinod Koul  * snd_compress_new: create new compress device
683b21c60a4SVinod Koul  * @card: sound card pointer
684b21c60a4SVinod Koul  * @device: device number
685b21c60a4SVinod Koul  * @dirn: device direction, should be of type enum snd_compr_direction
686b21c60a4SVinod Koul  * @compr: compress device pointer
687b21c60a4SVinod Koul  */
688b21c60a4SVinod Koul int snd_compress_new(struct snd_card *card, int device,
689b21c60a4SVinod Koul 			int dirn, struct snd_compr *compr)
690b21c60a4SVinod Koul {
691b21c60a4SVinod Koul 	static struct snd_device_ops ops = {
692b21c60a4SVinod Koul 		.dev_free = NULL,
693b21c60a4SVinod Koul 		.dev_register = snd_compress_dev_register,
694b21c60a4SVinod Koul 		.dev_disconnect = snd_compress_dev_disconnect,
695b21c60a4SVinod Koul 	};
696b21c60a4SVinod Koul 
697b21c60a4SVinod Koul 	compr->card = card;
698b21c60a4SVinod Koul 	compr->device = device;
699b21c60a4SVinod Koul 	compr->direction = dirn;
700b21c60a4SVinod Koul 	return snd_device_new(card, SNDRV_DEV_COMPRESS, compr, &ops);
701b21c60a4SVinod Koul }
702b21c60a4SVinod Koul EXPORT_SYMBOL_GPL(snd_compress_new);
703b21c60a4SVinod Koul 
704b21c60a4SVinod Koul static int snd_compress_add_device(struct snd_compr *device)
705b21c60a4SVinod Koul {
706b21c60a4SVinod Koul 	int ret;
707b21c60a4SVinod Koul 
708b21c60a4SVinod Koul 	if (!device->card)
709b21c60a4SVinod Koul 		return -EINVAL;
710b21c60a4SVinod Koul 
711b21c60a4SVinod Koul 	/* register the card */
712b21c60a4SVinod Koul 	ret = snd_card_register(device->card);
713b21c60a4SVinod Koul 	if (ret)
714b21c60a4SVinod Koul 		goto out;
715b21c60a4SVinod Koul 	return 0;
716b21c60a4SVinod Koul 
717b21c60a4SVinod Koul out:
718b21c60a4SVinod Koul 	pr_err("failed with %d\n", ret);
719b21c60a4SVinod Koul 	return ret;
720b21c60a4SVinod Koul 
721b21c60a4SVinod Koul }
722b21c60a4SVinod Koul 
723b21c60a4SVinod Koul static int snd_compress_remove_device(struct snd_compr *device)
724b21c60a4SVinod Koul {
725b21c60a4SVinod Koul 	return snd_card_free(device->card);
726b21c60a4SVinod Koul }
727b21c60a4SVinod Koul 
728b21c60a4SVinod Koul /**
729b21c60a4SVinod Koul  * snd_compress_register - register compressed device
730b21c60a4SVinod Koul  *
731b21c60a4SVinod Koul  * @device: compressed device to register
732b21c60a4SVinod Koul  */
733b21c60a4SVinod Koul int snd_compress_register(struct snd_compr *device)
734b21c60a4SVinod Koul {
735b21c60a4SVinod Koul 	int retval;
736b21c60a4SVinod Koul 
737b21c60a4SVinod Koul 	if (device->name == NULL || device->dev == NULL || device->ops == NULL)
738b21c60a4SVinod Koul 		return -EINVAL;
739b21c60a4SVinod Koul 
740b21c60a4SVinod Koul 	pr_debug("Registering compressed device %s\n", device->name);
741b21c60a4SVinod Koul 	if (snd_BUG_ON(!device->ops->open))
742b21c60a4SVinod Koul 		return -EINVAL;
743b21c60a4SVinod Koul 	if (snd_BUG_ON(!device->ops->free))
744b21c60a4SVinod Koul 		return -EINVAL;
745b21c60a4SVinod Koul 	if (snd_BUG_ON(!device->ops->set_params))
746b21c60a4SVinod Koul 		return -EINVAL;
747b21c60a4SVinod Koul 	if (snd_BUG_ON(!device->ops->trigger))
748b21c60a4SVinod Koul 		return -EINVAL;
749b21c60a4SVinod Koul 
750b21c60a4SVinod Koul 	mutex_init(&device->lock);
751b21c60a4SVinod Koul 
752b21c60a4SVinod Koul 	/* register a compressed card */
753b21c60a4SVinod Koul 	mutex_lock(&device_mutex);
754b21c60a4SVinod Koul 	retval = snd_compress_add_device(device);
755b21c60a4SVinod Koul 	mutex_unlock(&device_mutex);
756b21c60a4SVinod Koul 	return retval;
757b21c60a4SVinod Koul }
758b21c60a4SVinod Koul EXPORT_SYMBOL_GPL(snd_compress_register);
759b21c60a4SVinod Koul 
760b21c60a4SVinod Koul int snd_compress_deregister(struct snd_compr *device)
761b21c60a4SVinod Koul {
762b21c60a4SVinod Koul 	pr_debug("Removing compressed device %s\n", device->name);
763b21c60a4SVinod Koul 	mutex_lock(&device_mutex);
764b21c60a4SVinod Koul 	snd_compress_remove_device(device);
765b21c60a4SVinod Koul 	mutex_unlock(&device_mutex);
766b21c60a4SVinod Koul 	return 0;
767b21c60a4SVinod Koul }
768b21c60a4SVinod Koul EXPORT_SYMBOL_GPL(snd_compress_deregister);
769b21c60a4SVinod Koul 
770b21c60a4SVinod Koul static int __init snd_compress_init(void)
771b21c60a4SVinod Koul {
772b21c60a4SVinod Koul 	return 0;
773b21c60a4SVinod Koul }
774b21c60a4SVinod Koul 
775b21c60a4SVinod Koul static void __exit snd_compress_exit(void)
776b21c60a4SVinod Koul {
777b21c60a4SVinod Koul }
778b21c60a4SVinod Koul 
779b21c60a4SVinod Koul module_init(snd_compress_init);
780b21c60a4SVinod Koul module_exit(snd_compress_exit);
781b21c60a4SVinod Koul 
782b21c60a4SVinod Koul MODULE_DESCRIPTION("ALSA Compressed offload framework");
783b21c60a4SVinod Koul MODULE_AUTHOR("Vinod Koul <vinod.koul@linux.intel.com>");
784b21c60a4SVinod Koul MODULE_LICENSE("GPL v2");
785