xref: /openbmc/linux/sound/core/compress_offload.c (revision 6217e5ed)
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>
41b21c60a4SVinod Koul #include <sound/core.h>
42b21c60a4SVinod Koul #include <sound/initval.h>
43b21c60a4SVinod Koul #include <sound/compress_params.h>
44b21c60a4SVinod Koul #include <sound/compress_offload.h>
45b21c60a4SVinod Koul #include <sound/compress_driver.h>
46b21c60a4SVinod Koul 
47b21c60a4SVinod Koul /* TODO:
48b21c60a4SVinod Koul  * - add substream support for multiple devices in case of
49b21c60a4SVinod Koul  *	SND_DYNAMIC_MINORS is not used
50b21c60a4SVinod Koul  * - Multiple node representation
51b21c60a4SVinod Koul  *	driver should be able to register multiple nodes
52b21c60a4SVinod Koul  */
53b21c60a4SVinod Koul 
54b21c60a4SVinod Koul static DEFINE_MUTEX(device_mutex);
55b21c60a4SVinod Koul 
56b21c60a4SVinod Koul struct snd_compr_file {
57b21c60a4SVinod Koul 	unsigned long caps;
58b21c60a4SVinod Koul 	struct snd_compr_stream stream;
59b21c60a4SVinod Koul };
60b21c60a4SVinod Koul 
61b21c60a4SVinod Koul /*
62b21c60a4SVinod Koul  * a note on stream states used:
63b21c60a4SVinod Koul  * we use follwing states in the compressed core
64b21c60a4SVinod Koul  * SNDRV_PCM_STATE_OPEN: When stream has been opened.
65b21c60a4SVinod Koul  * SNDRV_PCM_STATE_SETUP: When stream has been initialized. This is done by
66b21c60a4SVinod Koul  *	calling SNDRV_COMPRESS_SET_PARAMS. running streams will come to this
67b21c60a4SVinod Koul  *	state at stop by calling SNDRV_COMPRESS_STOP, or at end of drain.
68b21c60a4SVinod Koul  * SNDRV_PCM_STATE_RUNNING: When stream has been started and is
69b21c60a4SVinod Koul  *	decoding/encoding and rendering/capturing data.
70b21c60a4SVinod Koul  * SNDRV_PCM_STATE_DRAINING: When stream is draining current data. This is done
71b21c60a4SVinod Koul  *	by calling SNDRV_COMPRESS_DRAIN.
72b21c60a4SVinod Koul  * SNDRV_PCM_STATE_PAUSED: When stream is paused. This is done by calling
73b21c60a4SVinod Koul  *	SNDRV_COMPRESS_PAUSE. It can be stopped or resumed by calling
74b21c60a4SVinod Koul  *	SNDRV_COMPRESS_STOP or SNDRV_COMPRESS_RESUME respectively.
75b21c60a4SVinod Koul  */
76b21c60a4SVinod Koul static int snd_compr_open(struct inode *inode, struct file *f)
77b21c60a4SVinod Koul {
78b21c60a4SVinod Koul 	struct snd_compr *compr;
79b21c60a4SVinod Koul 	struct snd_compr_file *data;
80b21c60a4SVinod Koul 	struct snd_compr_runtime *runtime;
81b21c60a4SVinod Koul 	enum snd_compr_direction dirn;
82b21c60a4SVinod Koul 	int maj = imajor(inode);
83b21c60a4SVinod Koul 	int ret;
84b21c60a4SVinod Koul 
8581cb3246SDan Carpenter 	if ((f->f_flags & O_ACCMODE) == O_WRONLY)
86b21c60a4SVinod Koul 		dirn = SND_COMPRESS_PLAYBACK;
8781cb3246SDan Carpenter 	else if ((f->f_flags & O_ACCMODE) == O_RDONLY)
88b21c60a4SVinod Koul 		dirn = SND_COMPRESS_CAPTURE;
8981cb3246SDan Carpenter 	else
90b21c60a4SVinod Koul 		return -EINVAL;
91b21c60a4SVinod Koul 
92b21c60a4SVinod Koul 	if (maj == snd_major)
93b21c60a4SVinod Koul 		compr = snd_lookup_minor_data(iminor(inode),
94b21c60a4SVinod Koul 					SNDRV_DEVICE_TYPE_COMPRESS);
95b21c60a4SVinod Koul 	else
96b21c60a4SVinod Koul 		return -EBADFD;
97b21c60a4SVinod Koul 
98b21c60a4SVinod Koul 	if (compr == NULL) {
99b21c60a4SVinod Koul 		pr_err("no device data!!!\n");
100b21c60a4SVinod Koul 		return -ENODEV;
101b21c60a4SVinod Koul 	}
102b21c60a4SVinod Koul 
103b21c60a4SVinod Koul 	if (dirn != compr->direction) {
104b21c60a4SVinod Koul 		pr_err("this device doesn't support this direction\n");
105a0830dbdSTakashi Iwai 		snd_card_unref(compr->card);
106b21c60a4SVinod Koul 		return -EINVAL;
107b21c60a4SVinod Koul 	}
108b21c60a4SVinod Koul 
109b21c60a4SVinod Koul 	data = kzalloc(sizeof(*data), GFP_KERNEL);
110a0830dbdSTakashi Iwai 	if (!data) {
111a0830dbdSTakashi Iwai 		snd_card_unref(compr->card);
112b21c60a4SVinod Koul 		return -ENOMEM;
113a0830dbdSTakashi Iwai 	}
114b21c60a4SVinod Koul 	data->stream.ops = compr->ops;
115b21c60a4SVinod Koul 	data->stream.direction = dirn;
116b21c60a4SVinod Koul 	data->stream.private_data = compr->private_data;
117b21c60a4SVinod Koul 	data->stream.device = compr;
118b21c60a4SVinod Koul 	runtime = kzalloc(sizeof(*runtime), GFP_KERNEL);
119b21c60a4SVinod Koul 	if (!runtime) {
120b21c60a4SVinod Koul 		kfree(data);
121a0830dbdSTakashi Iwai 		snd_card_unref(compr->card);
122b21c60a4SVinod Koul 		return -ENOMEM;
123b21c60a4SVinod Koul 	}
124b21c60a4SVinod Koul 	runtime->state = SNDRV_PCM_STATE_OPEN;
125b21c60a4SVinod Koul 	init_waitqueue_head(&runtime->sleep);
126b21c60a4SVinod Koul 	data->stream.runtime = runtime;
127b21c60a4SVinod Koul 	f->private_data = (void *)data;
128b21c60a4SVinod Koul 	mutex_lock(&compr->lock);
129b21c60a4SVinod Koul 	ret = compr->ops->open(&data->stream);
130b21c60a4SVinod Koul 	mutex_unlock(&compr->lock);
131b21c60a4SVinod Koul 	if (ret) {
132b21c60a4SVinod Koul 		kfree(runtime);
133b21c60a4SVinod Koul 		kfree(data);
134b21c60a4SVinod Koul 	}
135a0830dbdSTakashi Iwai 	snd_card_unref(compr->card);
136749d3223SCharles Keepax 	return ret;
137b21c60a4SVinod Koul }
138b21c60a4SVinod Koul 
139b21c60a4SVinod Koul static int snd_compr_free(struct inode *inode, struct file *f)
140b21c60a4SVinod Koul {
141b21c60a4SVinod Koul 	struct snd_compr_file *data = f->private_data;
142b26d19e4SLiam Girdwood 	struct snd_compr_runtime *runtime = data->stream.runtime;
143b26d19e4SLiam Girdwood 
144b26d19e4SLiam Girdwood 	switch (runtime->state) {
145b26d19e4SLiam Girdwood 	case SNDRV_PCM_STATE_RUNNING:
146b26d19e4SLiam Girdwood 	case SNDRV_PCM_STATE_DRAINING:
147b26d19e4SLiam Girdwood 	case SNDRV_PCM_STATE_PAUSED:
148b26d19e4SLiam Girdwood 		data->stream.ops->trigger(&data->stream, SNDRV_PCM_TRIGGER_STOP);
149b26d19e4SLiam Girdwood 		break;
150b26d19e4SLiam Girdwood 	default:
151b26d19e4SLiam Girdwood 		break;
152b26d19e4SLiam Girdwood 	}
153b26d19e4SLiam Girdwood 
154b21c60a4SVinod Koul 	data->stream.ops->free(&data->stream);
155b21c60a4SVinod Koul 	kfree(data->stream.runtime->buffer);
156b21c60a4SVinod Koul 	kfree(data->stream.runtime);
157b21c60a4SVinod Koul 	kfree(data);
158b21c60a4SVinod Koul 	return 0;
159b21c60a4SVinod Koul }
160b21c60a4SVinod Koul 
16117ac8e5cSRichard Fitzgerald static int snd_compr_update_tstamp(struct snd_compr_stream *stream,
162b21c60a4SVinod Koul 		struct snd_compr_tstamp *tstamp)
163b21c60a4SVinod Koul {
164b21c60a4SVinod Koul 	if (!stream->ops->pointer)
16517ac8e5cSRichard Fitzgerald 		return -ENOTSUPP;
166b21c60a4SVinod Koul 	stream->ops->pointer(stream, tstamp);
167b21c60a4SVinod Koul 	pr_debug("dsp consumed till %d total %d bytes\n",
168b21c60a4SVinod Koul 		tstamp->byte_offset, tstamp->copied_total);
1695b1f79f7SCharles Keepax 	if (stream->direction == SND_COMPRESS_PLAYBACK)
170b21c60a4SVinod Koul 		stream->runtime->total_bytes_transferred = tstamp->copied_total;
1715b1f79f7SCharles Keepax 	else
1725b1f79f7SCharles Keepax 		stream->runtime->total_bytes_available = tstamp->copied_total;
17317ac8e5cSRichard Fitzgerald 	return 0;
174b21c60a4SVinod Koul }
175b21c60a4SVinod Koul 
176b21c60a4SVinod Koul static size_t snd_compr_calc_avail(struct snd_compr_stream *stream,
177b21c60a4SVinod Koul 		struct snd_compr_avail *avail)
178b21c60a4SVinod Koul {
17917ac8e5cSRichard Fitzgerald 	memset(avail, 0, sizeof(*avail));
180b21c60a4SVinod Koul 	snd_compr_update_tstamp(stream, &avail->tstamp);
18117ac8e5cSRichard Fitzgerald 	/* Still need to return avail even if tstamp can't be filled in */
182b21c60a4SVinod Koul 
183b21c60a4SVinod Koul 	if (stream->runtime->total_bytes_available == 0 &&
1845b1f79f7SCharles Keepax 			stream->runtime->state == SNDRV_PCM_STATE_SETUP &&
1855b1f79f7SCharles Keepax 			stream->direction == SND_COMPRESS_PLAYBACK) {
186b21c60a4SVinod Koul 		pr_debug("detected init and someone forgot to do a write\n");
187b21c60a4SVinod Koul 		return stream->runtime->buffer_size;
188b21c60a4SVinod Koul 	}
189b21c60a4SVinod Koul 	pr_debug("app wrote %lld, DSP consumed %lld\n",
190b21c60a4SVinod Koul 			stream->runtime->total_bytes_available,
191b21c60a4SVinod Koul 			stream->runtime->total_bytes_transferred);
192b21c60a4SVinod Koul 	if (stream->runtime->total_bytes_available ==
193b21c60a4SVinod Koul 				stream->runtime->total_bytes_transferred) {
1945b1f79f7SCharles Keepax 		if (stream->direction == SND_COMPRESS_PLAYBACK) {
195b21c60a4SVinod Koul 			pr_debug("both pointers are same, returning full avail\n");
196b21c60a4SVinod Koul 			return stream->runtime->buffer_size;
1975b1f79f7SCharles Keepax 		} else {
1985b1f79f7SCharles Keepax 			pr_debug("both pointers are same, returning no avail\n");
1995b1f79f7SCharles Keepax 			return 0;
2005b1f79f7SCharles Keepax 		}
201b21c60a4SVinod Koul 	}
202b21c60a4SVinod Koul 
2035b1f79f7SCharles Keepax 	avail->avail = stream->runtime->total_bytes_available -
2045b1f79f7SCharles Keepax 			stream->runtime->total_bytes_transferred;
2055b1f79f7SCharles Keepax 	if (stream->direction == SND_COMPRESS_PLAYBACK)
2065b1f79f7SCharles Keepax 		avail->avail = stream->runtime->buffer_size - avail->avail;
2075b1f79f7SCharles Keepax 
2084c28e32dSCharles Keepax 	pr_debug("ret avail as %lld\n", avail->avail);
2094c28e32dSCharles Keepax 	return avail->avail;
210b21c60a4SVinod Koul }
211b21c60a4SVinod Koul 
212b21c60a4SVinod Koul static inline size_t snd_compr_get_avail(struct snd_compr_stream *stream)
213b21c60a4SVinod Koul {
214b21c60a4SVinod Koul 	struct snd_compr_avail avail;
215b21c60a4SVinod Koul 
216b21c60a4SVinod Koul 	return snd_compr_calc_avail(stream, &avail);
217b21c60a4SVinod Koul }
218b21c60a4SVinod Koul 
219b21c60a4SVinod Koul static int
220b21c60a4SVinod Koul snd_compr_ioctl_avail(struct snd_compr_stream *stream, unsigned long arg)
221b21c60a4SVinod Koul {
222b21c60a4SVinod Koul 	struct snd_compr_avail ioctl_avail;
223b21c60a4SVinod Koul 	size_t avail;
224b21c60a4SVinod Koul 
225b21c60a4SVinod Koul 	avail = snd_compr_calc_avail(stream, &ioctl_avail);
226b21c60a4SVinod Koul 	ioctl_avail.avail = avail;
227b21c60a4SVinod Koul 
228b21c60a4SVinod Koul 	if (copy_to_user((__u64 __user *)arg,
229b21c60a4SVinod Koul 				&ioctl_avail, sizeof(ioctl_avail)))
230b21c60a4SVinod Koul 		return -EFAULT;
231b21c60a4SVinod Koul 	return 0;
232b21c60a4SVinod Koul }
233b21c60a4SVinod Koul 
234b21c60a4SVinod Koul static int snd_compr_write_data(struct snd_compr_stream *stream,
235b21c60a4SVinod Koul 	       const char __user *buf, size_t count)
236b21c60a4SVinod Koul {
237b21c60a4SVinod Koul 	void *dstn;
238b21c60a4SVinod Koul 	size_t copy;
239b21c60a4SVinod Koul 	struct snd_compr_runtime *runtime = stream->runtime;
240f0283b58SCharles Keepax 	/* 64-bit Modulus */
241f0283b58SCharles Keepax 	u64 app_pointer = div64_u64(runtime->total_bytes_available,
242f0283b58SCharles Keepax 				    runtime->buffer_size);
243f0283b58SCharles Keepax 	app_pointer = runtime->total_bytes_available -
244f0283b58SCharles Keepax 		      (app_pointer * runtime->buffer_size);
245b21c60a4SVinod Koul 
246f0283b58SCharles Keepax 	dstn = runtime->buffer + app_pointer;
247b21c60a4SVinod Koul 	pr_debug("copying %ld at %lld\n",
248f0283b58SCharles Keepax 			(unsigned long)count, app_pointer);
249f0283b58SCharles Keepax 	if (count < runtime->buffer_size - app_pointer) {
250b21c60a4SVinod Koul 		if (copy_from_user(dstn, buf, count))
251b21c60a4SVinod Koul 			return -EFAULT;
252b21c60a4SVinod Koul 	} else {
253f0283b58SCharles Keepax 		copy = runtime->buffer_size - app_pointer;
254b21c60a4SVinod Koul 		if (copy_from_user(dstn, buf, copy))
255b21c60a4SVinod Koul 			return -EFAULT;
256b21c60a4SVinod Koul 		if (copy_from_user(runtime->buffer, buf + copy, count - copy))
257b21c60a4SVinod Koul 			return -EFAULT;
258b21c60a4SVinod Koul 	}
259b21c60a4SVinod Koul 	/* if DSP cares, let it know data has been written */
260b21c60a4SVinod Koul 	if (stream->ops->ack)
261b21c60a4SVinod Koul 		stream->ops->ack(stream, count);
262b21c60a4SVinod Koul 	return count;
263b21c60a4SVinod Koul }
264b21c60a4SVinod Koul 
265b21c60a4SVinod Koul static ssize_t snd_compr_write(struct file *f, const char __user *buf,
266b21c60a4SVinod Koul 		size_t count, loff_t *offset)
267b21c60a4SVinod Koul {
268b21c60a4SVinod Koul 	struct snd_compr_file *data = f->private_data;
269b21c60a4SVinod Koul 	struct snd_compr_stream *stream;
270b21c60a4SVinod Koul 	size_t avail;
271b21c60a4SVinod Koul 	int retval;
272b21c60a4SVinod Koul 
273b21c60a4SVinod Koul 	if (snd_BUG_ON(!data))
274b21c60a4SVinod Koul 		return -EFAULT;
275b21c60a4SVinod Koul 
276b21c60a4SVinod Koul 	stream = &data->stream;
277b21c60a4SVinod Koul 	mutex_lock(&stream->device->lock);
278b21c60a4SVinod Koul 	/* write is allowed when stream is running or has been steup */
279b21c60a4SVinod Koul 	if (stream->runtime->state != SNDRV_PCM_STATE_SETUP &&
280b21c60a4SVinod Koul 			stream->runtime->state != SNDRV_PCM_STATE_RUNNING) {
281b21c60a4SVinod Koul 		mutex_unlock(&stream->device->lock);
282b21c60a4SVinod Koul 		return -EBADFD;
283b21c60a4SVinod Koul 	}
284b21c60a4SVinod Koul 
285b21c60a4SVinod Koul 	avail = snd_compr_get_avail(stream);
286b21c60a4SVinod Koul 	pr_debug("avail returned %ld\n", (unsigned long)avail);
287b21c60a4SVinod Koul 	/* calculate how much we can write to buffer */
288b21c60a4SVinod Koul 	if (avail > count)
289b21c60a4SVinod Koul 		avail = count;
290b21c60a4SVinod Koul 
2914daf891cSCharles Keepax 	if (stream->ops->copy) {
2924daf891cSCharles Keepax 		char __user* cbuf = (char __user*)buf;
2934daf891cSCharles Keepax 		retval = stream->ops->copy(stream, cbuf, avail);
2944daf891cSCharles Keepax 	} else {
295b21c60a4SVinod Koul 		retval = snd_compr_write_data(stream, buf, avail);
2964daf891cSCharles Keepax 	}
297b21c60a4SVinod Koul 	if (retval > 0)
298b21c60a4SVinod Koul 		stream->runtime->total_bytes_available += retval;
299b21c60a4SVinod Koul 
300b21c60a4SVinod Koul 	/* while initiating the stream, write should be called before START
301b21c60a4SVinod Koul 	 * call, so in setup move state */
302b21c60a4SVinod Koul 	if (stream->runtime->state == SNDRV_PCM_STATE_SETUP) {
303b21c60a4SVinod Koul 		stream->runtime->state = SNDRV_PCM_STATE_PREPARED;
304b21c60a4SVinod Koul 		pr_debug("stream prepared, Houston we are good to go\n");
305b21c60a4SVinod Koul 	}
306b21c60a4SVinod Koul 
307b21c60a4SVinod Koul 	mutex_unlock(&stream->device->lock);
308b21c60a4SVinod Koul 	return retval;
309b21c60a4SVinod Koul }
310b21c60a4SVinod Koul 
311b21c60a4SVinod Koul 
312b21c60a4SVinod Koul static ssize_t snd_compr_read(struct file *f, char __user *buf,
313b21c60a4SVinod Koul 		size_t count, loff_t *offset)
314b21c60a4SVinod Koul {
31549bb6402SCharles Keepax 	struct snd_compr_file *data = f->private_data;
31649bb6402SCharles Keepax 	struct snd_compr_stream *stream;
31749bb6402SCharles Keepax 	size_t avail;
31849bb6402SCharles Keepax 	int retval;
31949bb6402SCharles Keepax 
32049bb6402SCharles Keepax 	if (snd_BUG_ON(!data))
32149bb6402SCharles Keepax 		return -EFAULT;
32249bb6402SCharles Keepax 
32349bb6402SCharles Keepax 	stream = &data->stream;
32449bb6402SCharles Keepax 	mutex_lock(&stream->device->lock);
32549bb6402SCharles Keepax 
32675481347SVinod Koul 	/* read is allowed when stream is running, paused, draining and setup
32775481347SVinod Koul 	 * (yes setup is state which we transition to after stop, so if user
32875481347SVinod Koul 	 * wants to read data after stop we allow that)
32975481347SVinod Koul 	 */
33075481347SVinod Koul 	switch (stream->runtime->state) {
33175481347SVinod Koul 	case SNDRV_PCM_STATE_OPEN:
33275481347SVinod Koul 	case SNDRV_PCM_STATE_PREPARED:
33375481347SVinod Koul 	case SNDRV_PCM_STATE_XRUN:
33475481347SVinod Koul 	case SNDRV_PCM_STATE_SUSPENDED:
33575481347SVinod Koul 	case SNDRV_PCM_STATE_DISCONNECTED:
33649bb6402SCharles Keepax 		retval = -EBADFD;
33749bb6402SCharles Keepax 		goto out;
33849bb6402SCharles Keepax 	}
33949bb6402SCharles Keepax 
34049bb6402SCharles Keepax 	avail = snd_compr_get_avail(stream);
34149bb6402SCharles Keepax 	pr_debug("avail returned %ld\n", (unsigned long)avail);
34249bb6402SCharles Keepax 	/* calculate how much we can read from buffer */
34349bb6402SCharles Keepax 	if (avail > count)
34449bb6402SCharles Keepax 		avail = count;
34549bb6402SCharles Keepax 
34649bb6402SCharles Keepax 	if (stream->ops->copy) {
34749bb6402SCharles Keepax 		retval = stream->ops->copy(stream, buf, avail);
34849bb6402SCharles Keepax 	} else {
34949bb6402SCharles Keepax 		retval = -ENXIO;
35049bb6402SCharles Keepax 		goto out;
35149bb6402SCharles Keepax 	}
35249bb6402SCharles Keepax 	if (retval > 0)
35349bb6402SCharles Keepax 		stream->runtime->total_bytes_transferred += retval;
35449bb6402SCharles Keepax 
35549bb6402SCharles Keepax out:
35649bb6402SCharles Keepax 	mutex_unlock(&stream->device->lock);
35749bb6402SCharles Keepax 	return retval;
358b21c60a4SVinod Koul }
359b21c60a4SVinod Koul 
360b21c60a4SVinod Koul static int snd_compr_mmap(struct file *f, struct vm_area_struct *vma)
361b21c60a4SVinod Koul {
362b21c60a4SVinod Koul 	return -ENXIO;
363b21c60a4SVinod Koul }
364b21c60a4SVinod Koul 
365b21c60a4SVinod Koul static inline int snd_compr_get_poll(struct snd_compr_stream *stream)
366b21c60a4SVinod Koul {
367b21c60a4SVinod Koul 	if (stream->direction == SND_COMPRESS_PLAYBACK)
368b21c60a4SVinod Koul 		return POLLOUT | POLLWRNORM;
369b21c60a4SVinod Koul 	else
370b21c60a4SVinod Koul 		return POLLIN | POLLRDNORM;
371b21c60a4SVinod Koul }
372b21c60a4SVinod Koul 
373b21c60a4SVinod Koul static unsigned int snd_compr_poll(struct file *f, poll_table *wait)
374b21c60a4SVinod Koul {
375b21c60a4SVinod Koul 	struct snd_compr_file *data = f->private_data;
376b21c60a4SVinod Koul 	struct snd_compr_stream *stream;
377b21c60a4SVinod Koul 	size_t avail;
378b21c60a4SVinod Koul 	int retval = 0;
379b21c60a4SVinod Koul 
380b21c60a4SVinod Koul 	if (snd_BUG_ON(!data))
381b21c60a4SVinod Koul 		return -EFAULT;
382b21c60a4SVinod Koul 	stream = &data->stream;
383b21c60a4SVinod Koul 	if (snd_BUG_ON(!stream))
384b21c60a4SVinod Koul 		return -EFAULT;
385b21c60a4SVinod Koul 
386b21c60a4SVinod Koul 	mutex_lock(&stream->device->lock);
387c15b149aSRichard Fitzgerald 	if (stream->runtime->state == SNDRV_PCM_STATE_OPEN) {
388b21c60a4SVinod Koul 		retval = -EBADFD;
389b21c60a4SVinod Koul 		goto out;
390b21c60a4SVinod Koul 	}
391b21c60a4SVinod Koul 	poll_wait(f, &stream->runtime->sleep, wait);
392b21c60a4SVinod Koul 
393b21c60a4SVinod Koul 	avail = snd_compr_get_avail(stream);
394b21c60a4SVinod Koul 	pr_debug("avail is %ld\n", (unsigned long)avail);
395b21c60a4SVinod Koul 	/* check if we have at least one fragment to fill */
396b21c60a4SVinod Koul 	switch (stream->runtime->state) {
397b21c60a4SVinod Koul 	case SNDRV_PCM_STATE_DRAINING:
398b21c60a4SVinod Koul 		/* stream has been woken up after drain is complete
399b21c60a4SVinod Koul 		 * draining done so set stream state to stopped
400b21c60a4SVinod Koul 		 */
401b21c60a4SVinod Koul 		retval = snd_compr_get_poll(stream);
402b21c60a4SVinod Koul 		stream->runtime->state = SNDRV_PCM_STATE_SETUP;
403b21c60a4SVinod Koul 		break;
404b21c60a4SVinod Koul 	case SNDRV_PCM_STATE_RUNNING:
405b21c60a4SVinod Koul 	case SNDRV_PCM_STATE_PREPARED:
406b21c60a4SVinod Koul 	case SNDRV_PCM_STATE_PAUSED:
407b21c60a4SVinod Koul 		if (avail >= stream->runtime->fragment_size)
408b21c60a4SVinod Koul 			retval = snd_compr_get_poll(stream);
409b21c60a4SVinod Koul 		break;
410b21c60a4SVinod Koul 	default:
411b21c60a4SVinod Koul 		if (stream->direction == SND_COMPRESS_PLAYBACK)
412b21c60a4SVinod Koul 			retval = POLLOUT | POLLWRNORM | POLLERR;
413b21c60a4SVinod Koul 		else
414b21c60a4SVinod Koul 			retval = POLLIN | POLLRDNORM | POLLERR;
415b21c60a4SVinod Koul 		break;
416b21c60a4SVinod Koul 	}
417b21c60a4SVinod Koul out:
418b21c60a4SVinod Koul 	mutex_unlock(&stream->device->lock);
419b21c60a4SVinod Koul 	return retval;
420b21c60a4SVinod Koul }
421b21c60a4SVinod Koul 
422b21c60a4SVinod Koul static int
423b21c60a4SVinod Koul snd_compr_get_caps(struct snd_compr_stream *stream, unsigned long arg)
424b21c60a4SVinod Koul {
425b21c60a4SVinod Koul 	int retval;
426b21c60a4SVinod Koul 	struct snd_compr_caps caps;
427b21c60a4SVinod Koul 
428b21c60a4SVinod Koul 	if (!stream->ops->get_caps)
429b21c60a4SVinod Koul 		return -ENXIO;
430b21c60a4SVinod Koul 
4311c62e9f2SDan Carpenter 	memset(&caps, 0, sizeof(caps));
432b21c60a4SVinod Koul 	retval = stream->ops->get_caps(stream, &caps);
433b21c60a4SVinod Koul 	if (retval)
434b21c60a4SVinod Koul 		goto out;
435b21c60a4SVinod Koul 	if (copy_to_user((void __user *)arg, &caps, sizeof(caps)))
436b21c60a4SVinod Koul 		retval = -EFAULT;
437b21c60a4SVinod Koul out:
438b21c60a4SVinod Koul 	return retval;
439b21c60a4SVinod Koul }
440b21c60a4SVinod Koul 
441b21c60a4SVinod Koul static int
442b21c60a4SVinod Koul snd_compr_get_codec_caps(struct snd_compr_stream *stream, unsigned long arg)
443b21c60a4SVinod Koul {
444b21c60a4SVinod Koul 	int retval;
445b21c60a4SVinod Koul 	struct snd_compr_codec_caps *caps;
446b21c60a4SVinod Koul 
447b21c60a4SVinod Koul 	if (!stream->ops->get_codec_caps)
448b21c60a4SVinod Koul 		return -ENXIO;
449b21c60a4SVinod Koul 
45047966e97STakashi Iwai 	caps = kzalloc(sizeof(*caps), GFP_KERNEL);
451b21c60a4SVinod Koul 	if (!caps)
452b21c60a4SVinod Koul 		return -ENOMEM;
453b21c60a4SVinod Koul 
454b21c60a4SVinod Koul 	retval = stream->ops->get_codec_caps(stream, caps);
455b21c60a4SVinod Koul 	if (retval)
456b21c60a4SVinod Koul 		goto out;
457b21c60a4SVinod Koul 	if (copy_to_user((void __user *)arg, caps, sizeof(*caps)))
458b21c60a4SVinod Koul 		retval = -EFAULT;
459b21c60a4SVinod Koul 
460b21c60a4SVinod Koul out:
461b21c60a4SVinod Koul 	kfree(caps);
462b21c60a4SVinod Koul 	return retval;
463b21c60a4SVinod Koul }
464b21c60a4SVinod Koul 
465b21c60a4SVinod Koul /* revisit this with snd_pcm_preallocate_xxx */
466b21c60a4SVinod Koul static int snd_compr_allocate_buffer(struct snd_compr_stream *stream,
467b21c60a4SVinod Koul 		struct snd_compr_params *params)
468b21c60a4SVinod Koul {
469b21c60a4SVinod Koul 	unsigned int buffer_size;
470b21c60a4SVinod Koul 	void *buffer;
471b21c60a4SVinod Koul 
472b21c60a4SVinod Koul 	buffer_size = params->buffer.fragment_size * params->buffer.fragments;
473b21c60a4SVinod Koul 	if (stream->ops->copy) {
474b21c60a4SVinod Koul 		buffer = NULL;
475b21c60a4SVinod Koul 		/* if copy is defined the driver will be required to copy
476b21c60a4SVinod Koul 		 * the data from core
477b21c60a4SVinod Koul 		 */
478b21c60a4SVinod Koul 	} else {
479b21c60a4SVinod Koul 		buffer = kmalloc(buffer_size, GFP_KERNEL);
480b21c60a4SVinod Koul 		if (!buffer)
481b21c60a4SVinod Koul 			return -ENOMEM;
482b21c60a4SVinod Koul 	}
483b21c60a4SVinod Koul 	stream->runtime->fragment_size = params->buffer.fragment_size;
484b21c60a4SVinod Koul 	stream->runtime->fragments = params->buffer.fragments;
485b21c60a4SVinod Koul 	stream->runtime->buffer = buffer;
486b21c60a4SVinod Koul 	stream->runtime->buffer_size = buffer_size;
487b21c60a4SVinod Koul 	return 0;
488b21c60a4SVinod Koul }
489b21c60a4SVinod Koul 
4904dc040a0SVinod Koul static int snd_compress_check_input(struct snd_compr_params *params)
4914dc040a0SVinod Koul {
4924dc040a0SVinod Koul 	/* first let's check the buffer parameter's */
4934dc040a0SVinod Koul 	if (params->buffer.fragment_size == 0 ||
4946217e5edSDan Carpenter 	    params->buffer.fragments > INT_MAX / params->buffer.fragment_size)
4954dc040a0SVinod Koul 		return -EINVAL;
4964dc040a0SVinod Koul 
497fb4a9779SVinod Koul 	/* now codec parameters */
498fb4a9779SVinod Koul 	if (params->codec.id == 0 || params->codec.id > SND_AUDIOCODEC_MAX)
499fb4a9779SVinod Koul 		return -EINVAL;
500fb4a9779SVinod Koul 
501fb4a9779SVinod Koul 	if (params->codec.ch_in == 0 || params->codec.ch_out == 0)
502fb4a9779SVinod Koul 		return -EINVAL;
503fb4a9779SVinod Koul 
5044dc040a0SVinod Koul 	return 0;
5054dc040a0SVinod Koul }
5064dc040a0SVinod Koul 
507b21c60a4SVinod Koul static int
508b21c60a4SVinod Koul snd_compr_set_params(struct snd_compr_stream *stream, unsigned long arg)
509b21c60a4SVinod Koul {
510b21c60a4SVinod Koul 	struct snd_compr_params *params;
511b21c60a4SVinod Koul 	int retval;
512b21c60a4SVinod Koul 
513b21c60a4SVinod Koul 	if (stream->runtime->state == SNDRV_PCM_STATE_OPEN) {
514b21c60a4SVinod Koul 		/*
515b21c60a4SVinod Koul 		 * we should allow parameter change only when stream has been
516b21c60a4SVinod Koul 		 * opened not in other cases
517b21c60a4SVinod Koul 		 */
518b21c60a4SVinod Koul 		params = kmalloc(sizeof(*params), GFP_KERNEL);
519b21c60a4SVinod Koul 		if (!params)
520b21c60a4SVinod Koul 			return -ENOMEM;
521769fab2aSJesper Juhl 		if (copy_from_user(params, (void __user *)arg, sizeof(*params))) {
522769fab2aSJesper Juhl 			retval = -EFAULT;
523769fab2aSJesper Juhl 			goto out;
524769fab2aSJesper Juhl 		}
5254dc040a0SVinod Koul 
5264dc040a0SVinod Koul 		retval = snd_compress_check_input(params);
5274dc040a0SVinod Koul 		if (retval)
5284dc040a0SVinod Koul 			goto out;
5294dc040a0SVinod Koul 
530b21c60a4SVinod Koul 		retval = snd_compr_allocate_buffer(stream, params);
531b21c60a4SVinod Koul 		if (retval) {
532769fab2aSJesper Juhl 			retval = -ENOMEM;
533769fab2aSJesper Juhl 			goto out;
534b21c60a4SVinod Koul 		}
5354dc040a0SVinod Koul 
536b21c60a4SVinod Koul 		retval = stream->ops->set_params(stream, params);
537b21c60a4SVinod Koul 		if (retval)
538b21c60a4SVinod Koul 			goto out;
53949bb6402SCharles Keepax 
5409727b490SJeeja KP 		stream->metadata_set = false;
5419727b490SJeeja KP 		stream->next_track = false;
54249bb6402SCharles Keepax 
54349bb6402SCharles Keepax 		if (stream->direction == SND_COMPRESS_PLAYBACK)
54449bb6402SCharles Keepax 			stream->runtime->state = SNDRV_PCM_STATE_SETUP;
54549bb6402SCharles Keepax 		else
54649bb6402SCharles Keepax 			stream->runtime->state = SNDRV_PCM_STATE_PREPARED;
547769fab2aSJesper Juhl 	} else {
548b21c60a4SVinod Koul 		return -EPERM;
549769fab2aSJesper Juhl 	}
550b21c60a4SVinod Koul out:
551b21c60a4SVinod Koul 	kfree(params);
552b21c60a4SVinod Koul 	return retval;
553b21c60a4SVinod Koul }
554b21c60a4SVinod Koul 
555b21c60a4SVinod Koul static int
556b21c60a4SVinod Koul snd_compr_get_params(struct snd_compr_stream *stream, unsigned long arg)
557b21c60a4SVinod Koul {
558b21c60a4SVinod Koul 	struct snd_codec *params;
559b21c60a4SVinod Koul 	int retval;
560b21c60a4SVinod Koul 
561b21c60a4SVinod Koul 	if (!stream->ops->get_params)
562b21c60a4SVinod Koul 		return -EBADFD;
563b21c60a4SVinod Koul 
56447966e97STakashi Iwai 	params = kzalloc(sizeof(*params), GFP_KERNEL);
565b21c60a4SVinod Koul 	if (!params)
566b21c60a4SVinod Koul 		return -ENOMEM;
567b21c60a4SVinod Koul 	retval = stream->ops->get_params(stream, params);
568b21c60a4SVinod Koul 	if (retval)
569b21c60a4SVinod Koul 		goto out;
570b21c60a4SVinod Koul 	if (copy_to_user((char __user *)arg, params, sizeof(*params)))
571b21c60a4SVinod Koul 		retval = -EFAULT;
572b21c60a4SVinod Koul 
573b21c60a4SVinod Koul out:
574b21c60a4SVinod Koul 	kfree(params);
575b21c60a4SVinod Koul 	return retval;
576b21c60a4SVinod Koul }
577b21c60a4SVinod Koul 
5789727b490SJeeja KP static int
5799727b490SJeeja KP snd_compr_get_metadata(struct snd_compr_stream *stream, unsigned long arg)
5809727b490SJeeja KP {
5819727b490SJeeja KP 	struct snd_compr_metadata metadata;
5829727b490SJeeja KP 	int retval;
5839727b490SJeeja KP 
5849727b490SJeeja KP 	if (!stream->ops->get_metadata)
5859727b490SJeeja KP 		return -ENXIO;
5869727b490SJeeja KP 
5879727b490SJeeja KP 	if (copy_from_user(&metadata, (void __user *)arg, sizeof(metadata)))
5889727b490SJeeja KP 		return -EFAULT;
5899727b490SJeeja KP 
5909727b490SJeeja KP 	retval = stream->ops->get_metadata(stream, &metadata);
5919727b490SJeeja KP 	if (retval != 0)
5929727b490SJeeja KP 		return retval;
5939727b490SJeeja KP 
5949727b490SJeeja KP 	if (copy_to_user((void __user *)arg, &metadata, sizeof(metadata)))
5959727b490SJeeja KP 		return -EFAULT;
5969727b490SJeeja KP 
5979727b490SJeeja KP 	return 0;
5989727b490SJeeja KP }
5999727b490SJeeja KP 
6009727b490SJeeja KP static int
6019727b490SJeeja KP snd_compr_set_metadata(struct snd_compr_stream *stream, unsigned long arg)
6029727b490SJeeja KP {
6039727b490SJeeja KP 	struct snd_compr_metadata metadata;
6049727b490SJeeja KP 	int retval;
6059727b490SJeeja KP 
6069727b490SJeeja KP 	if (!stream->ops->set_metadata)
6079727b490SJeeja KP 		return -ENXIO;
6089727b490SJeeja KP 	/*
6099727b490SJeeja KP 	* we should allow parameter change only when stream has been
6109727b490SJeeja KP 	* opened not in other cases
6119727b490SJeeja KP 	*/
6129727b490SJeeja KP 	if (copy_from_user(&metadata, (void __user *)arg, sizeof(metadata)))
6139727b490SJeeja KP 		return -EFAULT;
6149727b490SJeeja KP 
6159727b490SJeeja KP 	retval = stream->ops->set_metadata(stream, &metadata);
6169727b490SJeeja KP 	stream->metadata_set = true;
6179727b490SJeeja KP 
6189727b490SJeeja KP 	return retval;
6199727b490SJeeja KP }
6209727b490SJeeja KP 
621b21c60a4SVinod Koul static inline int
622b21c60a4SVinod Koul snd_compr_tstamp(struct snd_compr_stream *stream, unsigned long arg)
623b21c60a4SVinod Koul {
62417ac8e5cSRichard Fitzgerald 	struct snd_compr_tstamp tstamp = {0};
62517ac8e5cSRichard Fitzgerald 	int ret;
626b21c60a4SVinod Koul 
62717ac8e5cSRichard Fitzgerald 	ret = snd_compr_update_tstamp(stream, &tstamp);
62817ac8e5cSRichard Fitzgerald 	if (ret == 0)
62917ac8e5cSRichard Fitzgerald 		ret = copy_to_user((struct snd_compr_tstamp __user *)arg,
630b21c60a4SVinod Koul 			&tstamp, sizeof(tstamp)) ? -EFAULT : 0;
63117ac8e5cSRichard Fitzgerald 	return ret;
632b21c60a4SVinod Koul }
633b21c60a4SVinod Koul 
634b21c60a4SVinod Koul static int snd_compr_pause(struct snd_compr_stream *stream)
635b21c60a4SVinod Koul {
636b21c60a4SVinod Koul 	int retval;
637b21c60a4SVinod Koul 
638b21c60a4SVinod Koul 	if (stream->runtime->state != SNDRV_PCM_STATE_RUNNING)
639b21c60a4SVinod Koul 		return -EPERM;
640b21c60a4SVinod Koul 	retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_PAUSE_PUSH);
6416b18f793SVinod Koul 	if (!retval)
642b21c60a4SVinod Koul 		stream->runtime->state = SNDRV_PCM_STATE_PAUSED;
643b21c60a4SVinod Koul 	return retval;
644b21c60a4SVinod Koul }
645b21c60a4SVinod Koul 
646b21c60a4SVinod Koul static int snd_compr_resume(struct snd_compr_stream *stream)
647b21c60a4SVinod Koul {
648b21c60a4SVinod Koul 	int retval;
649b21c60a4SVinod Koul 
650b21c60a4SVinod Koul 	if (stream->runtime->state != SNDRV_PCM_STATE_PAUSED)
651b21c60a4SVinod Koul 		return -EPERM;
652b21c60a4SVinod Koul 	retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_PAUSE_RELEASE);
653b21c60a4SVinod Koul 	if (!retval)
654b21c60a4SVinod Koul 		stream->runtime->state = SNDRV_PCM_STATE_RUNNING;
655b21c60a4SVinod Koul 	return retval;
656b21c60a4SVinod Koul }
657b21c60a4SVinod Koul 
658b21c60a4SVinod Koul static int snd_compr_start(struct snd_compr_stream *stream)
659b21c60a4SVinod Koul {
660b21c60a4SVinod Koul 	int retval;
661b21c60a4SVinod Koul 
662b21c60a4SVinod Koul 	if (stream->runtime->state != SNDRV_PCM_STATE_PREPARED)
663b21c60a4SVinod Koul 		return -EPERM;
664b21c60a4SVinod Koul 	retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_START);
665b21c60a4SVinod Koul 	if (!retval)
666b21c60a4SVinod Koul 		stream->runtime->state = SNDRV_PCM_STATE_RUNNING;
667b21c60a4SVinod Koul 	return retval;
668b21c60a4SVinod Koul }
669b21c60a4SVinod Koul 
670b21c60a4SVinod Koul static int snd_compr_stop(struct snd_compr_stream *stream)
671b21c60a4SVinod Koul {
672b21c60a4SVinod Koul 	int retval;
673b21c60a4SVinod Koul 
674b21c60a4SVinod Koul 	if (stream->runtime->state == SNDRV_PCM_STATE_PREPARED ||
675b21c60a4SVinod Koul 			stream->runtime->state == SNDRV_PCM_STATE_SETUP)
676b21c60a4SVinod Koul 		return -EPERM;
677b21c60a4SVinod Koul 	retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_STOP);
678b21c60a4SVinod Koul 	if (!retval) {
679917f4b5cSVinod Koul 		snd_compr_drain_notify(stream);
6808b21460aSVinod Koul 		stream->runtime->total_bytes_available = 0;
6818b21460aSVinod Koul 		stream->runtime->total_bytes_transferred = 0;
682b21c60a4SVinod Koul 	}
683b21c60a4SVinod Koul 	return retval;
684b21c60a4SVinod Koul }
685b21c60a4SVinod Koul 
686917f4b5cSVinod Koul static int snd_compress_wait_for_drain(struct snd_compr_stream *stream)
687917f4b5cSVinod Koul {
688f44f2a54SVinod Koul 	int ret;
689f44f2a54SVinod Koul 
690917f4b5cSVinod Koul 	/*
691917f4b5cSVinod Koul 	 * We are called with lock held. So drop the lock while we wait for
692917f4b5cSVinod Koul 	 * drain complete notfication from the driver
693917f4b5cSVinod Koul 	 *
694917f4b5cSVinod Koul 	 * It is expected that driver will notify the drain completion and then
695917f4b5cSVinod Koul 	 * stream will be moved to SETUP state, even if draining resulted in an
696917f4b5cSVinod Koul 	 * error. We can trigger next track after this.
697917f4b5cSVinod Koul 	 */
698917f4b5cSVinod Koul 	stream->runtime->state = SNDRV_PCM_STATE_DRAINING;
699917f4b5cSVinod Koul 	mutex_unlock(&stream->device->lock);
700917f4b5cSVinod Koul 
701f44f2a54SVinod Koul 	/* we wait for drain to complete here, drain can return when
702f44f2a54SVinod Koul 	 * interruption occurred, wait returned error or success.
703f44f2a54SVinod Koul 	 * For the first two cases we don't do anything different here and
704f44f2a54SVinod Koul 	 * return after waking up
705f44f2a54SVinod Koul 	 */
706f44f2a54SVinod Koul 
707f44f2a54SVinod Koul 	ret = wait_event_interruptible(stream->runtime->sleep,
708f44f2a54SVinod Koul 			(stream->runtime->state != SNDRV_PCM_STATE_DRAINING));
709f44f2a54SVinod Koul 	if (ret == -ERESTARTSYS)
710f44f2a54SVinod Koul 		pr_debug("wait aborted by a signal");
711f44f2a54SVinod Koul 	else if (ret)
712f44f2a54SVinod Koul 		pr_debug("wait for drain failed with %d\n", ret);
713f44f2a54SVinod Koul 
714917f4b5cSVinod Koul 
715917f4b5cSVinod Koul 	wake_up(&stream->runtime->sleep);
716917f4b5cSVinod Koul 	mutex_lock(&stream->device->lock);
717917f4b5cSVinod Koul 
718f44f2a54SVinod Koul 	return ret;
719917f4b5cSVinod Koul }
720917f4b5cSVinod Koul 
721b21c60a4SVinod Koul static int snd_compr_drain(struct snd_compr_stream *stream)
722b21c60a4SVinod Koul {
723b21c60a4SVinod Koul 	int retval;
724b21c60a4SVinod Koul 
725b21c60a4SVinod Koul 	if (stream->runtime->state == SNDRV_PCM_STATE_PREPARED ||
726b21c60a4SVinod Koul 			stream->runtime->state == SNDRV_PCM_STATE_SETUP)
727b21c60a4SVinod Koul 		return -EPERM;
728917f4b5cSVinod Koul 
729b21c60a4SVinod Koul 	retval = stream->ops->trigger(stream, SND_COMPR_TRIGGER_DRAIN);
730917f4b5cSVinod Koul 	if (retval) {
731f44f2a54SVinod Koul 		pr_debug("SND_COMPR_TRIGGER_DRAIN failed %d\n", retval);
732b21c60a4SVinod Koul 		wake_up(&stream->runtime->sleep);
733917f4b5cSVinod Koul 		return retval;
734b21c60a4SVinod Koul 	}
735917f4b5cSVinod Koul 
736f44f2a54SVinod Koul 	return snd_compress_wait_for_drain(stream);
737b21c60a4SVinod Koul }
738b21c60a4SVinod Koul 
7399727b490SJeeja KP static int snd_compr_next_track(struct snd_compr_stream *stream)
7409727b490SJeeja KP {
7419727b490SJeeja KP 	int retval;
7429727b490SJeeja KP 
7439727b490SJeeja KP 	/* only a running stream can transition to next track */
7449727b490SJeeja KP 	if (stream->runtime->state != SNDRV_PCM_STATE_RUNNING)
7459727b490SJeeja KP 		return -EPERM;
7469727b490SJeeja KP 
7479727b490SJeeja KP 	/* you can signal next track isf this is intended to be a gapless stream
7489727b490SJeeja KP 	 * and current track metadata is set
7499727b490SJeeja KP 	 */
7509727b490SJeeja KP 	if (stream->metadata_set == false)
7519727b490SJeeja KP 		return -EPERM;
7529727b490SJeeja KP 
7539727b490SJeeja KP 	retval = stream->ops->trigger(stream, SND_COMPR_TRIGGER_NEXT_TRACK);
7549727b490SJeeja KP 	if (retval != 0)
7559727b490SJeeja KP 		return retval;
7569727b490SJeeja KP 	stream->metadata_set = false;
7579727b490SJeeja KP 	stream->next_track = true;
7589727b490SJeeja KP 	return 0;
7599727b490SJeeja KP }
7609727b490SJeeja KP 
7619727b490SJeeja KP static int snd_compr_partial_drain(struct snd_compr_stream *stream)
7629727b490SJeeja KP {
7639727b490SJeeja KP 	int retval;
7649727b490SJeeja KP 	if (stream->runtime->state == SNDRV_PCM_STATE_PREPARED ||
7659727b490SJeeja KP 			stream->runtime->state == SNDRV_PCM_STATE_SETUP)
7669727b490SJeeja KP 		return -EPERM;
7679727b490SJeeja KP 	/* stream can be drained only when next track has been signalled */
7689727b490SJeeja KP 	if (stream->next_track == false)
7699727b490SJeeja KP 		return -EPERM;
7709727b490SJeeja KP 
7719727b490SJeeja KP 	retval = stream->ops->trigger(stream, SND_COMPR_TRIGGER_PARTIAL_DRAIN);
772917f4b5cSVinod Koul 	if (retval) {
773f44f2a54SVinod Koul 		pr_debug("Partial drain returned failure\n");
774917f4b5cSVinod Koul 		wake_up(&stream->runtime->sleep);
775917f4b5cSVinod Koul 		return retval;
776917f4b5cSVinod Koul 	}
7779727b490SJeeja KP 
7789727b490SJeeja KP 	stream->next_track = false;
779917f4b5cSVinod Koul 	return snd_compress_wait_for_drain(stream);
7809727b490SJeeja KP }
7819727b490SJeeja KP 
782b21c60a4SVinod Koul static long snd_compr_ioctl(struct file *f, unsigned int cmd, unsigned long arg)
783b21c60a4SVinod Koul {
784b21c60a4SVinod Koul 	struct snd_compr_file *data = f->private_data;
785b21c60a4SVinod Koul 	struct snd_compr_stream *stream;
786b21c60a4SVinod Koul 	int retval = -ENOTTY;
787b21c60a4SVinod Koul 
788b21c60a4SVinod Koul 	if (snd_BUG_ON(!data))
789b21c60a4SVinod Koul 		return -EFAULT;
790b21c60a4SVinod Koul 	stream = &data->stream;
791b21c60a4SVinod Koul 	if (snd_BUG_ON(!stream))
792b21c60a4SVinod Koul 		return -EFAULT;
793b21c60a4SVinod Koul 	mutex_lock(&stream->device->lock);
794b21c60a4SVinod Koul 	switch (_IOC_NR(cmd)) {
795b21c60a4SVinod Koul 	case _IOC_NR(SNDRV_COMPRESS_IOCTL_VERSION):
796a8d30608SVinod Koul 		retval = put_user(SNDRV_COMPRESS_VERSION,
797b21c60a4SVinod Koul 				(int __user *)arg) ? -EFAULT : 0;
798b21c60a4SVinod Koul 		break;
799b21c60a4SVinod Koul 	case _IOC_NR(SNDRV_COMPRESS_GET_CAPS):
800b21c60a4SVinod Koul 		retval = snd_compr_get_caps(stream, arg);
801b21c60a4SVinod Koul 		break;
802b21c60a4SVinod Koul 	case _IOC_NR(SNDRV_COMPRESS_GET_CODEC_CAPS):
803b21c60a4SVinod Koul 		retval = snd_compr_get_codec_caps(stream, arg);
804b21c60a4SVinod Koul 		break;
805b21c60a4SVinod Koul 	case _IOC_NR(SNDRV_COMPRESS_SET_PARAMS):
806b21c60a4SVinod Koul 		retval = snd_compr_set_params(stream, arg);
807b21c60a4SVinod Koul 		break;
808b21c60a4SVinod Koul 	case _IOC_NR(SNDRV_COMPRESS_GET_PARAMS):
809b21c60a4SVinod Koul 		retval = snd_compr_get_params(stream, arg);
810b21c60a4SVinod Koul 		break;
8119727b490SJeeja KP 	case _IOC_NR(SNDRV_COMPRESS_SET_METADATA):
8129727b490SJeeja KP 		retval = snd_compr_set_metadata(stream, arg);
8139727b490SJeeja KP 		break;
8149727b490SJeeja KP 	case _IOC_NR(SNDRV_COMPRESS_GET_METADATA):
8159727b490SJeeja KP 		retval = snd_compr_get_metadata(stream, arg);
8169727b490SJeeja KP 		break;
817b21c60a4SVinod Koul 	case _IOC_NR(SNDRV_COMPRESS_TSTAMP):
818b21c60a4SVinod Koul 		retval = snd_compr_tstamp(stream, arg);
819b21c60a4SVinod Koul 		break;
820b21c60a4SVinod Koul 	case _IOC_NR(SNDRV_COMPRESS_AVAIL):
821b21c60a4SVinod Koul 		retval = snd_compr_ioctl_avail(stream, arg);
822b21c60a4SVinod Koul 		break;
823b21c60a4SVinod Koul 	case _IOC_NR(SNDRV_COMPRESS_PAUSE):
824b21c60a4SVinod Koul 		retval = snd_compr_pause(stream);
825b21c60a4SVinod Koul 		break;
826b21c60a4SVinod Koul 	case _IOC_NR(SNDRV_COMPRESS_RESUME):
827b21c60a4SVinod Koul 		retval = snd_compr_resume(stream);
828b21c60a4SVinod Koul 		break;
829b21c60a4SVinod Koul 	case _IOC_NR(SNDRV_COMPRESS_START):
830b21c60a4SVinod Koul 		retval = snd_compr_start(stream);
831b21c60a4SVinod Koul 		break;
832b21c60a4SVinod Koul 	case _IOC_NR(SNDRV_COMPRESS_STOP):
833b21c60a4SVinod Koul 		retval = snd_compr_stop(stream);
834b21c60a4SVinod Koul 		break;
835b21c60a4SVinod Koul 	case _IOC_NR(SNDRV_COMPRESS_DRAIN):
836b21c60a4SVinod Koul 		retval = snd_compr_drain(stream);
837b21c60a4SVinod Koul 		break;
8389727b490SJeeja KP 	case _IOC_NR(SNDRV_COMPRESS_PARTIAL_DRAIN):
8399727b490SJeeja KP 		retval = snd_compr_partial_drain(stream);
8409727b490SJeeja KP 		break;
8419727b490SJeeja KP 	case _IOC_NR(SNDRV_COMPRESS_NEXT_TRACK):
8429727b490SJeeja KP 		retval = snd_compr_next_track(stream);
8439727b490SJeeja KP 		break;
8449727b490SJeeja KP 
845b21c60a4SVinod Koul 	}
846b21c60a4SVinod Koul 	mutex_unlock(&stream->device->lock);
847b21c60a4SVinod Koul 	return retval;
848b21c60a4SVinod Koul }
849b21c60a4SVinod Koul 
850b21c60a4SVinod Koul static const struct file_operations snd_compr_file_ops = {
851b21c60a4SVinod Koul 		.owner =	THIS_MODULE,
852b21c60a4SVinod Koul 		.open =		snd_compr_open,
853b21c60a4SVinod Koul 		.release =	snd_compr_free,
854b21c60a4SVinod Koul 		.write =	snd_compr_write,
855b21c60a4SVinod Koul 		.read =		snd_compr_read,
856b21c60a4SVinod Koul 		.unlocked_ioctl = snd_compr_ioctl,
857b21c60a4SVinod Koul 		.mmap =		snd_compr_mmap,
858b21c60a4SVinod Koul 		.poll =		snd_compr_poll,
859b21c60a4SVinod Koul };
860b21c60a4SVinod Koul 
861b21c60a4SVinod Koul static int snd_compress_dev_register(struct snd_device *device)
862b21c60a4SVinod Koul {
863b21c60a4SVinod Koul 	int ret = -EINVAL;
864b21c60a4SVinod Koul 	char str[16];
865b21c60a4SVinod Koul 	struct snd_compr *compr;
866b21c60a4SVinod Koul 
867b21c60a4SVinod Koul 	if (snd_BUG_ON(!device || !device->device_data))
868b21c60a4SVinod Koul 		return -EBADFD;
869b21c60a4SVinod Koul 	compr = device->device_data;
870b21c60a4SVinod Koul 
871b21c60a4SVinod Koul 	sprintf(str, "comprC%iD%i", compr->card->number, compr->device);
872b21c60a4SVinod Koul 	pr_debug("reg %s for device %s, direction %d\n", str, compr->name,
873b21c60a4SVinod Koul 			compr->direction);
874b21c60a4SVinod Koul 	/* register compressed device */
875b21c60a4SVinod Koul 	ret = snd_register_device(SNDRV_DEVICE_TYPE_COMPRESS, compr->card,
876b21c60a4SVinod Koul 			compr->device, &snd_compr_file_ops, compr, str);
877b21c60a4SVinod Koul 	if (ret < 0) {
878b21c60a4SVinod Koul 		pr_err("snd_register_device failed\n %d", ret);
879b21c60a4SVinod Koul 		return ret;
880b21c60a4SVinod Koul 	}
881b21c60a4SVinod Koul 	return ret;
882b21c60a4SVinod Koul 
883b21c60a4SVinod Koul }
884b21c60a4SVinod Koul 
885b21c60a4SVinod Koul static int snd_compress_dev_disconnect(struct snd_device *device)
886b21c60a4SVinod Koul {
887b21c60a4SVinod Koul 	struct snd_compr *compr;
888b21c60a4SVinod Koul 
889b21c60a4SVinod Koul 	compr = device->device_data;
8904028b6c4SLiam Girdwood 	snd_unregister_device(SNDRV_DEVICE_TYPE_COMPRESS, compr->card,
8914028b6c4SLiam Girdwood 		compr->device);
892b21c60a4SVinod Koul 	return 0;
893b21c60a4SVinod Koul }
894b21c60a4SVinod Koul 
895b21c60a4SVinod Koul /*
896b21c60a4SVinod Koul  * snd_compress_new: create new compress device
897b21c60a4SVinod Koul  * @card: sound card pointer
898b21c60a4SVinod Koul  * @device: device number
899b21c60a4SVinod Koul  * @dirn: device direction, should be of type enum snd_compr_direction
900b21c60a4SVinod Koul  * @compr: compress device pointer
901b21c60a4SVinod Koul  */
902b21c60a4SVinod Koul int snd_compress_new(struct snd_card *card, int device,
903b21c60a4SVinod Koul 			int dirn, struct snd_compr *compr)
904b21c60a4SVinod Koul {
905b21c60a4SVinod Koul 	static struct snd_device_ops ops = {
906b21c60a4SVinod Koul 		.dev_free = NULL,
907b21c60a4SVinod Koul 		.dev_register = snd_compress_dev_register,
908b21c60a4SVinod Koul 		.dev_disconnect = snd_compress_dev_disconnect,
909b21c60a4SVinod Koul 	};
910b21c60a4SVinod Koul 
911b21c60a4SVinod Koul 	compr->card = card;
912b21c60a4SVinod Koul 	compr->device = device;
913b21c60a4SVinod Koul 	compr->direction = dirn;
914b21c60a4SVinod Koul 	return snd_device_new(card, SNDRV_DEV_COMPRESS, compr, &ops);
915b21c60a4SVinod Koul }
916b21c60a4SVinod Koul EXPORT_SYMBOL_GPL(snd_compress_new);
917b21c60a4SVinod Koul 
918b21c60a4SVinod Koul static int snd_compress_add_device(struct snd_compr *device)
919b21c60a4SVinod Koul {
920b21c60a4SVinod Koul 	int ret;
921b21c60a4SVinod Koul 
922b21c60a4SVinod Koul 	if (!device->card)
923b21c60a4SVinod Koul 		return -EINVAL;
924b21c60a4SVinod Koul 
925b21c60a4SVinod Koul 	/* register the card */
926b21c60a4SVinod Koul 	ret = snd_card_register(device->card);
927b21c60a4SVinod Koul 	if (ret)
928b21c60a4SVinod Koul 		goto out;
929b21c60a4SVinod Koul 	return 0;
930b21c60a4SVinod Koul 
931b21c60a4SVinod Koul out:
932b21c60a4SVinod Koul 	pr_err("failed with %d\n", ret);
933b21c60a4SVinod Koul 	return ret;
934b21c60a4SVinod Koul 
935b21c60a4SVinod Koul }
936b21c60a4SVinod Koul 
937b21c60a4SVinod Koul static int snd_compress_remove_device(struct snd_compr *device)
938b21c60a4SVinod Koul {
939b21c60a4SVinod Koul 	return snd_card_free(device->card);
940b21c60a4SVinod Koul }
941b21c60a4SVinod Koul 
942b21c60a4SVinod Koul /**
943b21c60a4SVinod Koul  * snd_compress_register - register compressed device
944b21c60a4SVinod Koul  *
945b21c60a4SVinod Koul  * @device: compressed device to register
946b21c60a4SVinod Koul  */
947b21c60a4SVinod Koul int snd_compress_register(struct snd_compr *device)
948b21c60a4SVinod Koul {
949b21c60a4SVinod Koul 	int retval;
950b21c60a4SVinod Koul 
951b21c60a4SVinod Koul 	if (device->name == NULL || device->dev == NULL || device->ops == NULL)
952b21c60a4SVinod Koul 		return -EINVAL;
953b21c60a4SVinod Koul 
954b21c60a4SVinod Koul 	pr_debug("Registering compressed device %s\n", device->name);
955b21c60a4SVinod Koul 	if (snd_BUG_ON(!device->ops->open))
956b21c60a4SVinod Koul 		return -EINVAL;
957b21c60a4SVinod Koul 	if (snd_BUG_ON(!device->ops->free))
958b21c60a4SVinod Koul 		return -EINVAL;
959b21c60a4SVinod Koul 	if (snd_BUG_ON(!device->ops->set_params))
960b21c60a4SVinod Koul 		return -EINVAL;
961b21c60a4SVinod Koul 	if (snd_BUG_ON(!device->ops->trigger))
962b21c60a4SVinod Koul 		return -EINVAL;
963b21c60a4SVinod Koul 
964b21c60a4SVinod Koul 	mutex_init(&device->lock);
965b21c60a4SVinod Koul 
966b21c60a4SVinod Koul 	/* register a compressed card */
967b21c60a4SVinod Koul 	mutex_lock(&device_mutex);
968b21c60a4SVinod Koul 	retval = snd_compress_add_device(device);
969b21c60a4SVinod Koul 	mutex_unlock(&device_mutex);
970b21c60a4SVinod Koul 	return retval;
971b21c60a4SVinod Koul }
972b21c60a4SVinod Koul EXPORT_SYMBOL_GPL(snd_compress_register);
973b21c60a4SVinod Koul 
974b21c60a4SVinod Koul int snd_compress_deregister(struct snd_compr *device)
975b21c60a4SVinod Koul {
976b21c60a4SVinod Koul 	pr_debug("Removing compressed device %s\n", device->name);
977b21c60a4SVinod Koul 	mutex_lock(&device_mutex);
978b21c60a4SVinod Koul 	snd_compress_remove_device(device);
979b21c60a4SVinod Koul 	mutex_unlock(&device_mutex);
980b21c60a4SVinod Koul 	return 0;
981b21c60a4SVinod Koul }
982b21c60a4SVinod Koul EXPORT_SYMBOL_GPL(snd_compress_deregister);
983b21c60a4SVinod Koul 
984b21c60a4SVinod Koul static int __init snd_compress_init(void)
985b21c60a4SVinod Koul {
986b21c60a4SVinod Koul 	return 0;
987b21c60a4SVinod Koul }
988b21c60a4SVinod Koul 
989b21c60a4SVinod Koul static void __exit snd_compress_exit(void)
990b21c60a4SVinod Koul {
991b21c60a4SVinod Koul }
992b21c60a4SVinod Koul 
993b21c60a4SVinod Koul module_init(snd_compress_init);
994b21c60a4SVinod Koul module_exit(snd_compress_exit);
995b21c60a4SVinod Koul 
996b21c60a4SVinod Koul MODULE_DESCRIPTION("ALSA Compressed offload framework");
997b21c60a4SVinod Koul MODULE_AUTHOR("Vinod Koul <vinod.koul@linux.intel.com>");
998b21c60a4SVinod Koul MODULE_LICENSE("GPL v2");
999