xref: /openbmc/linux/sound/core/compress_offload.c (revision a70ab8a8)
1873e65bcSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2b21c60a4SVinod Koul /*
3b21c60a4SVinod Koul  *  compress_core.c - compress offload core
4b21c60a4SVinod Koul  *
5b21c60a4SVinod Koul  *  Copyright (C) 2011 Intel Corporation
6b21c60a4SVinod Koul  *  Authors:	Vinod Koul <vinod.koul@linux.intel.com>
7b21c60a4SVinod Koul  *		Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
8b21c60a4SVinod Koul  *  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
9b21c60a4SVinod Koul  *
10b21c60a4SVinod Koul  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
11b21c60a4SVinod Koul  */
12b21c60a4SVinod Koul #define FORMAT(fmt) "%s: %d: " fmt, __func__, __LINE__
13b21c60a4SVinod Koul #define pr_fmt(fmt) KBUILD_MODNAME ": " FORMAT(fmt)
14b21c60a4SVinod Koul 
15b21c60a4SVinod Koul #include <linux/file.h>
16b21c60a4SVinod Koul #include <linux/fs.h>
17b21c60a4SVinod Koul #include <linux/list.h>
18f0283b58SCharles Keepax #include <linux/math64.h>
19b21c60a4SVinod Koul #include <linux/mm.h>
20b21c60a4SVinod Koul #include <linux/mutex.h>
21b21c60a4SVinod Koul #include <linux/poll.h>
22b21c60a4SVinod Koul #include <linux/slab.h>
23b21c60a4SVinod Koul #include <linux/sched.h>
24f0283b58SCharles Keepax #include <linux/types.h>
25b21c60a4SVinod Koul #include <linux/uio.h>
26b21c60a4SVinod Koul #include <linux/uaccess.h>
27b21c60a4SVinod Koul #include <linux/module.h>
28c1036889SRavindra Lokhande #include <linux/compat.h>
29b21c60a4SVinod Koul #include <sound/core.h>
30b21c60a4SVinod Koul #include <sound/initval.h>
3131742724SRichard Fitzgerald #include <sound/info.h>
32b21c60a4SVinod Koul #include <sound/compress_params.h>
33b21c60a4SVinod Koul #include <sound/compress_offload.h>
34b21c60a4SVinod Koul #include <sound/compress_driver.h>
35b21c60a4SVinod Koul 
36462b3f16STakashi Iwai /* struct snd_compr_codec_caps overflows the ioctl bit size for some
37462b3f16STakashi Iwai  * architectures, so we need to disable the relevant ioctls.
38462b3f16STakashi Iwai  */
39462b3f16STakashi Iwai #if _IOC_SIZEBITS < 14
40462b3f16STakashi Iwai #define COMPR_CODEC_CAPS_OVERFLOW
41462b3f16STakashi Iwai #endif
42462b3f16STakashi Iwai 
43b21c60a4SVinod Koul /* TODO:
44b21c60a4SVinod Koul  * - add substream support for multiple devices in case of
45b21c60a4SVinod Koul  *	SND_DYNAMIC_MINORS is not used
46b21c60a4SVinod Koul  * - Multiple node representation
47b21c60a4SVinod Koul  *	driver should be able to register multiple nodes
48b21c60a4SVinod Koul  */
49b21c60a4SVinod Koul 
50b21c60a4SVinod Koul static DEFINE_MUTEX(device_mutex);
51b21c60a4SVinod Koul 
52b21c60a4SVinod Koul struct snd_compr_file {
53b21c60a4SVinod Koul 	unsigned long caps;
54b21c60a4SVinod Koul 	struct snd_compr_stream stream;
55b21c60a4SVinod Koul };
56b21c60a4SVinod Koul 
57a4f2d87cSCharles Keepax static void error_delayed_work(struct work_struct *work);
58a4f2d87cSCharles Keepax 
59b21c60a4SVinod Koul /*
60b21c60a4SVinod Koul  * a note on stream states used:
6141eb94fdSVinod Koul  * we use following 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
6441eb94fdSVinod 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.
66862bca5dSVinod Koul  * SNDRV_PCM_STATE_PREPARED: When a stream has been written to (for
67862bca5dSVinod Koul  *	playback only). User after setting up stream writes the data buffer
68862bca5dSVinod Koul  *	before starting the stream.
69b21c60a4SVinod Koul  * SNDRV_PCM_STATE_RUNNING: When stream has been started and is
70b21c60a4SVinod Koul  *	decoding/encoding and rendering/capturing data.
71b21c60a4SVinod Koul  * SNDRV_PCM_STATE_DRAINING: When stream is draining current data. This is done
72b21c60a4SVinod Koul  *	by calling SNDRV_COMPRESS_DRAIN.
73b21c60a4SVinod Koul  * SNDRV_PCM_STATE_PAUSED: When stream is paused. This is done by calling
74b21c60a4SVinod Koul  *	SNDRV_COMPRESS_PAUSE. It can be stopped or resumed by calling
75b21c60a4SVinod Koul  *	SNDRV_COMPRESS_STOP or SNDRV_COMPRESS_RESUME respectively.
76b21c60a4SVinod Koul  */
77b21c60a4SVinod Koul static int snd_compr_open(struct inode *inode, struct file *f)
78b21c60a4SVinod Koul {
79b21c60a4SVinod Koul 	struct snd_compr *compr;
80b21c60a4SVinod Koul 	struct snd_compr_file *data;
81b21c60a4SVinod Koul 	struct snd_compr_runtime *runtime;
82b21c60a4SVinod Koul 	enum snd_compr_direction dirn;
83b21c60a4SVinod Koul 	int maj = imajor(inode);
84b21c60a4SVinod Koul 	int ret;
85b21c60a4SVinod Koul 
8681cb3246SDan Carpenter 	if ((f->f_flags & O_ACCMODE) == O_WRONLY)
87b21c60a4SVinod Koul 		dirn = SND_COMPRESS_PLAYBACK;
8881cb3246SDan Carpenter 	else if ((f->f_flags & O_ACCMODE) == O_RDONLY)
89b21c60a4SVinod Koul 		dirn = SND_COMPRESS_CAPTURE;
9081cb3246SDan Carpenter 	else
91b21c60a4SVinod Koul 		return -EINVAL;
92b21c60a4SVinod Koul 
93b21c60a4SVinod Koul 	if (maj == snd_major)
94b21c60a4SVinod Koul 		compr = snd_lookup_minor_data(iminor(inode),
95b21c60a4SVinod Koul 					SNDRV_DEVICE_TYPE_COMPRESS);
96b21c60a4SVinod Koul 	else
97b21c60a4SVinod Koul 		return -EBADFD;
98b21c60a4SVinod Koul 
99b21c60a4SVinod Koul 	if (compr == NULL) {
100b21c60a4SVinod Koul 		pr_err("no device data!!!\n");
101b21c60a4SVinod Koul 		return -ENODEV;
102b21c60a4SVinod Koul 	}
103b21c60a4SVinod Koul 
104b21c60a4SVinod Koul 	if (dirn != compr->direction) {
105b21c60a4SVinod Koul 		pr_err("this device doesn't support this direction\n");
106a0830dbdSTakashi Iwai 		snd_card_unref(compr->card);
107b21c60a4SVinod Koul 		return -EINVAL;
108b21c60a4SVinod Koul 	}
109b21c60a4SVinod Koul 
110b21c60a4SVinod Koul 	data = kzalloc(sizeof(*data), GFP_KERNEL);
111a0830dbdSTakashi Iwai 	if (!data) {
112a0830dbdSTakashi Iwai 		snd_card_unref(compr->card);
113b21c60a4SVinod Koul 		return -ENOMEM;
114a0830dbdSTakashi Iwai 	}
115a4f2d87cSCharles Keepax 
116a4f2d87cSCharles Keepax 	INIT_DELAYED_WORK(&data->stream.error_work, error_delayed_work);
117a4f2d87cSCharles Keepax 
118b21c60a4SVinod Koul 	data->stream.ops = compr->ops;
119b21c60a4SVinod Koul 	data->stream.direction = dirn;
120b21c60a4SVinod Koul 	data->stream.private_data = compr->private_data;
121b21c60a4SVinod Koul 	data->stream.device = compr;
122b21c60a4SVinod Koul 	runtime = kzalloc(sizeof(*runtime), GFP_KERNEL);
123b21c60a4SVinod Koul 	if (!runtime) {
124b21c60a4SVinod Koul 		kfree(data);
125a0830dbdSTakashi Iwai 		snd_card_unref(compr->card);
126b21c60a4SVinod Koul 		return -ENOMEM;
127b21c60a4SVinod Koul 	}
128b21c60a4SVinod Koul 	runtime->state = SNDRV_PCM_STATE_OPEN;
129b21c60a4SVinod Koul 	init_waitqueue_head(&runtime->sleep);
130b21c60a4SVinod Koul 	data->stream.runtime = runtime;
131b21c60a4SVinod Koul 	f->private_data = (void *)data;
132b21c60a4SVinod Koul 	mutex_lock(&compr->lock);
133b21c60a4SVinod Koul 	ret = compr->ops->open(&data->stream);
134b21c60a4SVinod Koul 	mutex_unlock(&compr->lock);
135b21c60a4SVinod Koul 	if (ret) {
136b21c60a4SVinod Koul 		kfree(runtime);
137b21c60a4SVinod Koul 		kfree(data);
138b21c60a4SVinod Koul 	}
139a0830dbdSTakashi Iwai 	snd_card_unref(compr->card);
140749d3223SCharles Keepax 	return ret;
141b21c60a4SVinod Koul }
142b21c60a4SVinod Koul 
143b21c60a4SVinod Koul static int snd_compr_free(struct inode *inode, struct file *f)
144b21c60a4SVinod Koul {
145b21c60a4SVinod Koul 	struct snd_compr_file *data = f->private_data;
146b26d19e4SLiam Girdwood 	struct snd_compr_runtime *runtime = data->stream.runtime;
147b26d19e4SLiam Girdwood 
148a4f2d87cSCharles Keepax 	cancel_delayed_work_sync(&data->stream.error_work);
149a4f2d87cSCharles Keepax 
150b26d19e4SLiam Girdwood 	switch (runtime->state) {
151b26d19e4SLiam Girdwood 	case SNDRV_PCM_STATE_RUNNING:
152b26d19e4SLiam Girdwood 	case SNDRV_PCM_STATE_DRAINING:
153b26d19e4SLiam Girdwood 	case SNDRV_PCM_STATE_PAUSED:
154b26d19e4SLiam Girdwood 		data->stream.ops->trigger(&data->stream, SNDRV_PCM_TRIGGER_STOP);
155b26d19e4SLiam Girdwood 		break;
156b26d19e4SLiam Girdwood 	default:
157b26d19e4SLiam Girdwood 		break;
158b26d19e4SLiam Girdwood 	}
159b26d19e4SLiam Girdwood 
160b21c60a4SVinod Koul 	data->stream.ops->free(&data->stream);
161d00f749bSSrinivas Kandagatla 	if (!data->stream.runtime->dma_buffer_p)
162b21c60a4SVinod Koul 		kfree(data->stream.runtime->buffer);
163b21c60a4SVinod Koul 	kfree(data->stream.runtime);
164b21c60a4SVinod Koul 	kfree(data);
165b21c60a4SVinod Koul 	return 0;
166b21c60a4SVinod Koul }
167b21c60a4SVinod Koul 
16817ac8e5cSRichard Fitzgerald static int snd_compr_update_tstamp(struct snd_compr_stream *stream,
169b21c60a4SVinod Koul 		struct snd_compr_tstamp *tstamp)
170b21c60a4SVinod Koul {
171b21c60a4SVinod Koul 	if (!stream->ops->pointer)
17217ac8e5cSRichard Fitzgerald 		return -ENOTSUPP;
173b21c60a4SVinod Koul 	stream->ops->pointer(stream, tstamp);
174b21c60a4SVinod Koul 	pr_debug("dsp consumed till %d total %d bytes\n",
175b21c60a4SVinod Koul 		tstamp->byte_offset, tstamp->copied_total);
1765b1f79f7SCharles Keepax 	if (stream->direction == SND_COMPRESS_PLAYBACK)
177b21c60a4SVinod Koul 		stream->runtime->total_bytes_transferred = tstamp->copied_total;
1785b1f79f7SCharles Keepax 	else
1795b1f79f7SCharles Keepax 		stream->runtime->total_bytes_available = tstamp->copied_total;
18017ac8e5cSRichard Fitzgerald 	return 0;
181b21c60a4SVinod Koul }
182b21c60a4SVinod Koul 
183b21c60a4SVinod Koul static size_t snd_compr_calc_avail(struct snd_compr_stream *stream,
184b21c60a4SVinod Koul 		struct snd_compr_avail *avail)
185b21c60a4SVinod Koul {
18617ac8e5cSRichard Fitzgerald 	memset(avail, 0, sizeof(*avail));
187b21c60a4SVinod Koul 	snd_compr_update_tstamp(stream, &avail->tstamp);
18817ac8e5cSRichard Fitzgerald 	/* Still need to return avail even if tstamp can't be filled in */
189b21c60a4SVinod Koul 
190b21c60a4SVinod Koul 	if (stream->runtime->total_bytes_available == 0 &&
1915b1f79f7SCharles Keepax 			stream->runtime->state == SNDRV_PCM_STATE_SETUP &&
1925b1f79f7SCharles Keepax 			stream->direction == SND_COMPRESS_PLAYBACK) {
193b21c60a4SVinod Koul 		pr_debug("detected init and someone forgot to do a write\n");
194b21c60a4SVinod Koul 		return stream->runtime->buffer_size;
195b21c60a4SVinod Koul 	}
196b21c60a4SVinod Koul 	pr_debug("app wrote %lld, DSP consumed %lld\n",
197b21c60a4SVinod Koul 			stream->runtime->total_bytes_available,
198b21c60a4SVinod Koul 			stream->runtime->total_bytes_transferred);
199b21c60a4SVinod Koul 	if (stream->runtime->total_bytes_available ==
200b21c60a4SVinod Koul 				stream->runtime->total_bytes_transferred) {
2015b1f79f7SCharles Keepax 		if (stream->direction == SND_COMPRESS_PLAYBACK) {
202b21c60a4SVinod Koul 			pr_debug("both pointers are same, returning full avail\n");
203b21c60a4SVinod Koul 			return stream->runtime->buffer_size;
2045b1f79f7SCharles Keepax 		} else {
2055b1f79f7SCharles Keepax 			pr_debug("both pointers are same, returning no avail\n");
2065b1f79f7SCharles Keepax 			return 0;
2075b1f79f7SCharles Keepax 		}
208b21c60a4SVinod Koul 	}
209b21c60a4SVinod Koul 
2105b1f79f7SCharles Keepax 	avail->avail = stream->runtime->total_bytes_available -
2115b1f79f7SCharles Keepax 			stream->runtime->total_bytes_transferred;
2125b1f79f7SCharles Keepax 	if (stream->direction == SND_COMPRESS_PLAYBACK)
2135b1f79f7SCharles Keepax 		avail->avail = stream->runtime->buffer_size - avail->avail;
2145b1f79f7SCharles Keepax 
2154c28e32dSCharles Keepax 	pr_debug("ret avail as %lld\n", avail->avail);
2164c28e32dSCharles Keepax 	return avail->avail;
217b21c60a4SVinod Koul }
218b21c60a4SVinod Koul 
219b21c60a4SVinod Koul static inline size_t snd_compr_get_avail(struct snd_compr_stream *stream)
220b21c60a4SVinod Koul {
221b21c60a4SVinod Koul 	struct snd_compr_avail avail;
222b21c60a4SVinod Koul 
223b21c60a4SVinod Koul 	return snd_compr_calc_avail(stream, &avail);
224b21c60a4SVinod Koul }
225b21c60a4SVinod Koul 
226b21c60a4SVinod Koul static int
227b21c60a4SVinod Koul snd_compr_ioctl_avail(struct snd_compr_stream *stream, unsigned long arg)
228b21c60a4SVinod Koul {
229b21c60a4SVinod Koul 	struct snd_compr_avail ioctl_avail;
230b21c60a4SVinod Koul 	size_t avail;
231b21c60a4SVinod Koul 
232b21c60a4SVinod Koul 	avail = snd_compr_calc_avail(stream, &ioctl_avail);
233b21c60a4SVinod Koul 	ioctl_avail.avail = avail;
234b21c60a4SVinod Koul 
235a4f2d87cSCharles Keepax 	switch (stream->runtime->state) {
236a4f2d87cSCharles Keepax 	case SNDRV_PCM_STATE_OPEN:
237a4f2d87cSCharles Keepax 		return -EBADFD;
238a4f2d87cSCharles Keepax 	case SNDRV_PCM_STATE_XRUN:
239a4f2d87cSCharles Keepax 		return -EPIPE;
240a4f2d87cSCharles Keepax 	default:
241a4f2d87cSCharles Keepax 		break;
242a4f2d87cSCharles Keepax 	}
243a4f2d87cSCharles Keepax 
244b21c60a4SVinod Koul 	if (copy_to_user((__u64 __user *)arg,
245b21c60a4SVinod Koul 				&ioctl_avail, sizeof(ioctl_avail)))
246b21c60a4SVinod Koul 		return -EFAULT;
247b21c60a4SVinod Koul 	return 0;
248b21c60a4SVinod Koul }
249b21c60a4SVinod Koul 
250b21c60a4SVinod Koul static int snd_compr_write_data(struct snd_compr_stream *stream,
251b21c60a4SVinod Koul 	       const char __user *buf, size_t count)
252b21c60a4SVinod Koul {
253b21c60a4SVinod Koul 	void *dstn;
254b21c60a4SVinod Koul 	size_t copy;
255b21c60a4SVinod Koul 	struct snd_compr_runtime *runtime = stream->runtime;
256f0283b58SCharles Keepax 	/* 64-bit Modulus */
257f0283b58SCharles Keepax 	u64 app_pointer = div64_u64(runtime->total_bytes_available,
258f0283b58SCharles Keepax 				    runtime->buffer_size);
259f0283b58SCharles Keepax 	app_pointer = runtime->total_bytes_available -
260f0283b58SCharles Keepax 		      (app_pointer * runtime->buffer_size);
261b21c60a4SVinod Koul 
262f0283b58SCharles Keepax 	dstn = runtime->buffer + app_pointer;
263b21c60a4SVinod Koul 	pr_debug("copying %ld at %lld\n",
264f0283b58SCharles Keepax 			(unsigned long)count, app_pointer);
265f0283b58SCharles Keepax 	if (count < runtime->buffer_size - app_pointer) {
266b21c60a4SVinod Koul 		if (copy_from_user(dstn, buf, count))
267b21c60a4SVinod Koul 			return -EFAULT;
268b21c60a4SVinod Koul 	} else {
269f0283b58SCharles Keepax 		copy = runtime->buffer_size - app_pointer;
270b21c60a4SVinod Koul 		if (copy_from_user(dstn, buf, copy))
271b21c60a4SVinod Koul 			return -EFAULT;
272b21c60a4SVinod Koul 		if (copy_from_user(runtime->buffer, buf + copy, count - copy))
273b21c60a4SVinod Koul 			return -EFAULT;
274b21c60a4SVinod Koul 	}
275b21c60a4SVinod Koul 	/* if DSP cares, let it know data has been written */
276b21c60a4SVinod Koul 	if (stream->ops->ack)
277b21c60a4SVinod Koul 		stream->ops->ack(stream, count);
278b21c60a4SVinod Koul 	return count;
279b21c60a4SVinod Koul }
280b21c60a4SVinod Koul 
281b21c60a4SVinod Koul static ssize_t snd_compr_write(struct file *f, const char __user *buf,
282b21c60a4SVinod Koul 		size_t count, loff_t *offset)
283b21c60a4SVinod Koul {
284b21c60a4SVinod Koul 	struct snd_compr_file *data = f->private_data;
285b21c60a4SVinod Koul 	struct snd_compr_stream *stream;
286b21c60a4SVinod Koul 	size_t avail;
287b21c60a4SVinod Koul 	int retval;
288b21c60a4SVinod Koul 
289b21c60a4SVinod Koul 	if (snd_BUG_ON(!data))
290b21c60a4SVinod Koul 		return -EFAULT;
291b21c60a4SVinod Koul 
292b21c60a4SVinod Koul 	stream = &data->stream;
293b21c60a4SVinod Koul 	mutex_lock(&stream->device->lock);
294b21c60a4SVinod Koul 	/* write is allowed when stream is running or has been steup */
295875f6fffSCharles Keepax 	switch (stream->runtime->state) {
296875f6fffSCharles Keepax 	case SNDRV_PCM_STATE_SETUP:
297875f6fffSCharles Keepax 	case SNDRV_PCM_STATE_PREPARED:
298875f6fffSCharles Keepax 	case SNDRV_PCM_STATE_RUNNING:
299875f6fffSCharles Keepax 		break;
300875f6fffSCharles Keepax 	default:
301b21c60a4SVinod Koul 		mutex_unlock(&stream->device->lock);
302b21c60a4SVinod Koul 		return -EBADFD;
303b21c60a4SVinod Koul 	}
304b21c60a4SVinod Koul 
305b21c60a4SVinod Koul 	avail = snd_compr_get_avail(stream);
306b21c60a4SVinod Koul 	pr_debug("avail returned %ld\n", (unsigned long)avail);
307b21c60a4SVinod Koul 	/* calculate how much we can write to buffer */
308b21c60a4SVinod Koul 	if (avail > count)
309b21c60a4SVinod Koul 		avail = count;
310b21c60a4SVinod Koul 
3114daf891cSCharles Keepax 	if (stream->ops->copy) {
3124daf891cSCharles Keepax 		char __user* cbuf = (char __user*)buf;
3134daf891cSCharles Keepax 		retval = stream->ops->copy(stream, cbuf, avail);
3144daf891cSCharles Keepax 	} else {
315b21c60a4SVinod Koul 		retval = snd_compr_write_data(stream, buf, avail);
3164daf891cSCharles Keepax 	}
317b21c60a4SVinod Koul 	if (retval > 0)
318b21c60a4SVinod Koul 		stream->runtime->total_bytes_available += retval;
319b21c60a4SVinod Koul 
320b21c60a4SVinod Koul 	/* while initiating the stream, write should be called before START
321b21c60a4SVinod Koul 	 * call, so in setup move state */
322b21c60a4SVinod Koul 	if (stream->runtime->state == SNDRV_PCM_STATE_SETUP) {
323b21c60a4SVinod Koul 		stream->runtime->state = SNDRV_PCM_STATE_PREPARED;
324b21c60a4SVinod Koul 		pr_debug("stream prepared, Houston we are good to go\n");
325b21c60a4SVinod Koul 	}
326b21c60a4SVinod Koul 
327b21c60a4SVinod Koul 	mutex_unlock(&stream->device->lock);
328b21c60a4SVinod Koul 	return retval;
329b21c60a4SVinod Koul }
330b21c60a4SVinod Koul 
331b21c60a4SVinod Koul 
332b21c60a4SVinod Koul static ssize_t snd_compr_read(struct file *f, char __user *buf,
333b21c60a4SVinod Koul 		size_t count, loff_t *offset)
334b21c60a4SVinod Koul {
33549bb6402SCharles Keepax 	struct snd_compr_file *data = f->private_data;
33649bb6402SCharles Keepax 	struct snd_compr_stream *stream;
33749bb6402SCharles Keepax 	size_t avail;
33849bb6402SCharles Keepax 	int retval;
33949bb6402SCharles Keepax 
34049bb6402SCharles Keepax 	if (snd_BUG_ON(!data))
34149bb6402SCharles Keepax 		return -EFAULT;
34249bb6402SCharles Keepax 
34349bb6402SCharles Keepax 	stream = &data->stream;
34449bb6402SCharles Keepax 	mutex_lock(&stream->device->lock);
34549bb6402SCharles Keepax 
34675481347SVinod Koul 	/* read is allowed when stream is running, paused, draining and setup
34775481347SVinod Koul 	 * (yes setup is state which we transition to after stop, so if user
34875481347SVinod Koul 	 * wants to read data after stop we allow that)
34975481347SVinod Koul 	 */
35075481347SVinod Koul 	switch (stream->runtime->state) {
35175481347SVinod Koul 	case SNDRV_PCM_STATE_OPEN:
35275481347SVinod Koul 	case SNDRV_PCM_STATE_PREPARED:
35375481347SVinod Koul 	case SNDRV_PCM_STATE_SUSPENDED:
35475481347SVinod Koul 	case SNDRV_PCM_STATE_DISCONNECTED:
35549bb6402SCharles Keepax 		retval = -EBADFD;
35649bb6402SCharles Keepax 		goto out;
357a4f2d87cSCharles Keepax 	case SNDRV_PCM_STATE_XRUN:
358a4f2d87cSCharles Keepax 		retval = -EPIPE;
359a4f2d87cSCharles Keepax 		goto out;
36049bb6402SCharles Keepax 	}
36149bb6402SCharles Keepax 
36249bb6402SCharles Keepax 	avail = snd_compr_get_avail(stream);
36349bb6402SCharles Keepax 	pr_debug("avail returned %ld\n", (unsigned long)avail);
36449bb6402SCharles Keepax 	/* calculate how much we can read from buffer */
36549bb6402SCharles Keepax 	if (avail > count)
36649bb6402SCharles Keepax 		avail = count;
36749bb6402SCharles Keepax 
36849bb6402SCharles Keepax 	if (stream->ops->copy) {
36949bb6402SCharles Keepax 		retval = stream->ops->copy(stream, buf, avail);
37049bb6402SCharles Keepax 	} else {
37149bb6402SCharles Keepax 		retval = -ENXIO;
37249bb6402SCharles Keepax 		goto out;
37349bb6402SCharles Keepax 	}
37449bb6402SCharles Keepax 	if (retval > 0)
37549bb6402SCharles Keepax 		stream->runtime->total_bytes_transferred += retval;
37649bb6402SCharles Keepax 
37749bb6402SCharles Keepax out:
37849bb6402SCharles Keepax 	mutex_unlock(&stream->device->lock);
37949bb6402SCharles Keepax 	return retval;
380b21c60a4SVinod Koul }
381b21c60a4SVinod Koul 
382b21c60a4SVinod Koul static int snd_compr_mmap(struct file *f, struct vm_area_struct *vma)
383b21c60a4SVinod Koul {
384b21c60a4SVinod Koul 	return -ENXIO;
385b21c60a4SVinod Koul }
386b21c60a4SVinod Koul 
387680ef72aSAl Viro static __poll_t snd_compr_get_poll(struct snd_compr_stream *stream)
388b21c60a4SVinod Koul {
389b21c60a4SVinod Koul 	if (stream->direction == SND_COMPRESS_PLAYBACK)
390a9a08845SLinus Torvalds 		return EPOLLOUT | EPOLLWRNORM;
391b21c60a4SVinod Koul 	else
392a9a08845SLinus Torvalds 		return EPOLLIN | EPOLLRDNORM;
393b21c60a4SVinod Koul }
394b21c60a4SVinod Koul 
395680ef72aSAl Viro static __poll_t snd_compr_poll(struct file *f, poll_table *wait)
396b21c60a4SVinod Koul {
397b21c60a4SVinod Koul 	struct snd_compr_file *data = f->private_data;
398b21c60a4SVinod Koul 	struct snd_compr_stream *stream;
399b21c60a4SVinod Koul 	size_t avail;
400680ef72aSAl Viro 	__poll_t retval = 0;
401b21c60a4SVinod Koul 
402b21c60a4SVinod Koul 	if (snd_BUG_ON(!data))
403a9a08845SLinus Torvalds 		return EPOLLERR;
4045bd05390SCharles Keepax 
405b21c60a4SVinod Koul 	stream = &data->stream;
406b21c60a4SVinod Koul 
407b21c60a4SVinod Koul 	mutex_lock(&stream->device->lock);
408a4f2d87cSCharles Keepax 
409a4f2d87cSCharles Keepax 	switch (stream->runtime->state) {
410a4f2d87cSCharles Keepax 	case SNDRV_PCM_STATE_OPEN:
411a4f2d87cSCharles Keepax 	case SNDRV_PCM_STATE_XRUN:
412a9a08845SLinus Torvalds 		retval = snd_compr_get_poll(stream) | EPOLLERR;
413b21c60a4SVinod Koul 		goto out;
414a4f2d87cSCharles Keepax 	default:
415a4f2d87cSCharles Keepax 		break;
416b21c60a4SVinod Koul 	}
417a4f2d87cSCharles Keepax 
418b21c60a4SVinod Koul 	poll_wait(f, &stream->runtime->sleep, wait);
419b21c60a4SVinod Koul 
420b21c60a4SVinod Koul 	avail = snd_compr_get_avail(stream);
421b21c60a4SVinod Koul 	pr_debug("avail is %ld\n", (unsigned long)avail);
422b21c60a4SVinod Koul 	/* check if we have at least one fragment to fill */
423b21c60a4SVinod Koul 	switch (stream->runtime->state) {
424b21c60a4SVinod Koul 	case SNDRV_PCM_STATE_DRAINING:
425b21c60a4SVinod Koul 		/* stream has been woken up after drain is complete
426b21c60a4SVinod Koul 		 * draining done so set stream state to stopped
427b21c60a4SVinod Koul 		 */
428b21c60a4SVinod Koul 		retval = snd_compr_get_poll(stream);
429b21c60a4SVinod Koul 		stream->runtime->state = SNDRV_PCM_STATE_SETUP;
430b21c60a4SVinod Koul 		break;
431b21c60a4SVinod Koul 	case SNDRV_PCM_STATE_RUNNING:
432b21c60a4SVinod Koul 	case SNDRV_PCM_STATE_PREPARED:
433b21c60a4SVinod Koul 	case SNDRV_PCM_STATE_PAUSED:
434b21c60a4SVinod Koul 		if (avail >= stream->runtime->fragment_size)
435b21c60a4SVinod Koul 			retval = snd_compr_get_poll(stream);
436b21c60a4SVinod Koul 		break;
437b21c60a4SVinod Koul 	default:
438a9a08845SLinus Torvalds 		retval = snd_compr_get_poll(stream) | EPOLLERR;
439b21c60a4SVinod Koul 		break;
440b21c60a4SVinod Koul 	}
441b21c60a4SVinod Koul out:
442b21c60a4SVinod Koul 	mutex_unlock(&stream->device->lock);
443b21c60a4SVinod Koul 	return retval;
444b21c60a4SVinod Koul }
445b21c60a4SVinod Koul 
446b21c60a4SVinod Koul static int
447b21c60a4SVinod Koul snd_compr_get_caps(struct snd_compr_stream *stream, unsigned long arg)
448b21c60a4SVinod Koul {
449b21c60a4SVinod Koul 	int retval;
450b21c60a4SVinod Koul 	struct snd_compr_caps caps;
451b21c60a4SVinod Koul 
452b21c60a4SVinod Koul 	if (!stream->ops->get_caps)
453b21c60a4SVinod Koul 		return -ENXIO;
454b21c60a4SVinod Koul 
4551c62e9f2SDan Carpenter 	memset(&caps, 0, sizeof(caps));
456b21c60a4SVinod Koul 	retval = stream->ops->get_caps(stream, &caps);
457b21c60a4SVinod Koul 	if (retval)
458b21c60a4SVinod Koul 		goto out;
459b21c60a4SVinod Koul 	if (copy_to_user((void __user *)arg, &caps, sizeof(caps)))
460b21c60a4SVinod Koul 		retval = -EFAULT;
461b21c60a4SVinod Koul out:
462b21c60a4SVinod Koul 	return retval;
463b21c60a4SVinod Koul }
464b21c60a4SVinod Koul 
465462b3f16STakashi Iwai #ifndef COMPR_CODEC_CAPS_OVERFLOW
466b21c60a4SVinod Koul static int
467b21c60a4SVinod Koul snd_compr_get_codec_caps(struct snd_compr_stream *stream, unsigned long arg)
468b21c60a4SVinod Koul {
469b21c60a4SVinod Koul 	int retval;
470b21c60a4SVinod Koul 	struct snd_compr_codec_caps *caps;
471b21c60a4SVinod Koul 
472b21c60a4SVinod Koul 	if (!stream->ops->get_codec_caps)
473b21c60a4SVinod Koul 		return -ENXIO;
474b21c60a4SVinod Koul 
47547966e97STakashi Iwai 	caps = kzalloc(sizeof(*caps), GFP_KERNEL);
476b21c60a4SVinod Koul 	if (!caps)
477b21c60a4SVinod Koul 		return -ENOMEM;
478b21c60a4SVinod Koul 
479b21c60a4SVinod Koul 	retval = stream->ops->get_codec_caps(stream, caps);
480b21c60a4SVinod Koul 	if (retval)
481b21c60a4SVinod Koul 		goto out;
482b21c60a4SVinod Koul 	if (copy_to_user((void __user *)arg, caps, sizeof(*caps)))
483b21c60a4SVinod Koul 		retval = -EFAULT;
484b21c60a4SVinod Koul 
485b21c60a4SVinod Koul out:
486b21c60a4SVinod Koul 	kfree(caps);
487b21c60a4SVinod Koul 	return retval;
488b21c60a4SVinod Koul }
489462b3f16STakashi Iwai #endif /* !COMPR_CODEC_CAPS_OVERFLOW */
490b21c60a4SVinod Koul 
491b21c60a4SVinod Koul /* revisit this with snd_pcm_preallocate_xxx */
492b21c60a4SVinod Koul static int snd_compr_allocate_buffer(struct snd_compr_stream *stream,
493b21c60a4SVinod Koul 		struct snd_compr_params *params)
494b21c60a4SVinod Koul {
495b21c60a4SVinod Koul 	unsigned int buffer_size;
496d00f749bSSrinivas Kandagatla 	void *buffer = NULL;
497b21c60a4SVinod Koul 
498b21c60a4SVinod Koul 	buffer_size = params->buffer.fragment_size * params->buffer.fragments;
499b21c60a4SVinod Koul 	if (stream->ops->copy) {
500b21c60a4SVinod Koul 		buffer = NULL;
501b21c60a4SVinod Koul 		/* if copy is defined the driver will be required to copy
502b21c60a4SVinod Koul 		 * the data from core
503b21c60a4SVinod Koul 		 */
504b21c60a4SVinod Koul 	} else {
505d00f749bSSrinivas Kandagatla 		if (stream->runtime->dma_buffer_p) {
506d00f749bSSrinivas Kandagatla 
507d00f749bSSrinivas Kandagatla 			if (buffer_size > stream->runtime->dma_buffer_p->bytes)
508d00f749bSSrinivas Kandagatla 				dev_err(&stream->device->dev,
509d00f749bSSrinivas Kandagatla 						"Not enough DMA buffer");
510d00f749bSSrinivas Kandagatla 			else
511d00f749bSSrinivas Kandagatla 				buffer = stream->runtime->dma_buffer_p->area;
512d00f749bSSrinivas Kandagatla 
513d00f749bSSrinivas Kandagatla 		} else {
514b21c60a4SVinod Koul 			buffer = kmalloc(buffer_size, GFP_KERNEL);
515d00f749bSSrinivas Kandagatla 		}
516d00f749bSSrinivas Kandagatla 
517b21c60a4SVinod Koul 		if (!buffer)
518b21c60a4SVinod Koul 			return -ENOMEM;
519b21c60a4SVinod Koul 	}
520b21c60a4SVinod Koul 	stream->runtime->fragment_size = params->buffer.fragment_size;
521b21c60a4SVinod Koul 	stream->runtime->fragments = params->buffer.fragments;
522b21c60a4SVinod Koul 	stream->runtime->buffer = buffer;
523b21c60a4SVinod Koul 	stream->runtime->buffer_size = buffer_size;
524b21c60a4SVinod Koul 	return 0;
525b21c60a4SVinod Koul }
526b21c60a4SVinod Koul 
5274dc040a0SVinod Koul static int snd_compress_check_input(struct snd_compr_params *params)
5284dc040a0SVinod Koul {
5294dc040a0SVinod Koul 	/* first let's check the buffer parameter's */
5304dc040a0SVinod Koul 	if (params->buffer.fragment_size == 0 ||
531678e2b44SDan Carpenter 	    params->buffer.fragments > INT_MAX / params->buffer.fragment_size ||
532678e2b44SDan Carpenter 	    params->buffer.fragments == 0)
5334dc040a0SVinod Koul 		return -EINVAL;
5344dc040a0SVinod Koul 
535fb4a9779SVinod Koul 	/* now codec parameters */
536fb4a9779SVinod Koul 	if (params->codec.id == 0 || params->codec.id > SND_AUDIOCODEC_MAX)
537fb4a9779SVinod Koul 		return -EINVAL;
538fb4a9779SVinod Koul 
539fb4a9779SVinod Koul 	if (params->codec.ch_in == 0 || params->codec.ch_out == 0)
540fb4a9779SVinod Koul 		return -EINVAL;
541fb4a9779SVinod Koul 
5424dc040a0SVinod Koul 	return 0;
5434dc040a0SVinod Koul }
5444dc040a0SVinod Koul 
545b21c60a4SVinod Koul static int
546b21c60a4SVinod Koul snd_compr_set_params(struct snd_compr_stream *stream, unsigned long arg)
547b21c60a4SVinod Koul {
548b21c60a4SVinod Koul 	struct snd_compr_params *params;
549b21c60a4SVinod Koul 	int retval;
550b21c60a4SVinod Koul 
551b21c60a4SVinod Koul 	if (stream->runtime->state == SNDRV_PCM_STATE_OPEN) {
552b21c60a4SVinod Koul 		/*
553b21c60a4SVinod Koul 		 * we should allow parameter change only when stream has been
554b21c60a4SVinod Koul 		 * opened not in other cases
555b21c60a4SVinod Koul 		 */
556c2f14ba7SMarkus Elfring 		params = memdup_user((void __user *)arg, sizeof(*params));
557c2f14ba7SMarkus Elfring 		if (IS_ERR(params))
558c2f14ba7SMarkus Elfring 			return PTR_ERR(params);
5594dc040a0SVinod Koul 
5604dc040a0SVinod Koul 		retval = snd_compress_check_input(params);
5614dc040a0SVinod Koul 		if (retval)
5624dc040a0SVinod Koul 			goto out;
5634dc040a0SVinod Koul 
564b21c60a4SVinod Koul 		retval = snd_compr_allocate_buffer(stream, params);
565b21c60a4SVinod Koul 		if (retval) {
566769fab2aSJesper Juhl 			retval = -ENOMEM;
567769fab2aSJesper Juhl 			goto out;
568b21c60a4SVinod Koul 		}
5694dc040a0SVinod Koul 
570b21c60a4SVinod Koul 		retval = stream->ops->set_params(stream, params);
571b21c60a4SVinod Koul 		if (retval)
572b21c60a4SVinod Koul 			goto out;
57349bb6402SCharles Keepax 
5749727b490SJeeja KP 		stream->metadata_set = false;
5759727b490SJeeja KP 		stream->next_track = false;
57649bb6402SCharles Keepax 
57749bb6402SCharles Keepax 		stream->runtime->state = SNDRV_PCM_STATE_SETUP;
578769fab2aSJesper Juhl 	} else {
579b21c60a4SVinod Koul 		return -EPERM;
580769fab2aSJesper Juhl 	}
581b21c60a4SVinod Koul out:
582b21c60a4SVinod Koul 	kfree(params);
583b21c60a4SVinod Koul 	return retval;
584b21c60a4SVinod Koul }
585b21c60a4SVinod Koul 
586b21c60a4SVinod Koul static int
587b21c60a4SVinod Koul snd_compr_get_params(struct snd_compr_stream *stream, unsigned long arg)
588b21c60a4SVinod Koul {
589b21c60a4SVinod Koul 	struct snd_codec *params;
590b21c60a4SVinod Koul 	int retval;
591b21c60a4SVinod Koul 
592b21c60a4SVinod Koul 	if (!stream->ops->get_params)
593b21c60a4SVinod Koul 		return -EBADFD;
594b21c60a4SVinod Koul 
59547966e97STakashi Iwai 	params = kzalloc(sizeof(*params), GFP_KERNEL);
596b21c60a4SVinod Koul 	if (!params)
597b21c60a4SVinod Koul 		return -ENOMEM;
598b21c60a4SVinod Koul 	retval = stream->ops->get_params(stream, params);
599b21c60a4SVinod Koul 	if (retval)
600b21c60a4SVinod Koul 		goto out;
601b21c60a4SVinod Koul 	if (copy_to_user((char __user *)arg, params, sizeof(*params)))
602b21c60a4SVinod Koul 		retval = -EFAULT;
603b21c60a4SVinod Koul 
604b21c60a4SVinod Koul out:
605b21c60a4SVinod Koul 	kfree(params);
606b21c60a4SVinod Koul 	return retval;
607b21c60a4SVinod Koul }
608b21c60a4SVinod Koul 
6099727b490SJeeja KP static int
6109727b490SJeeja KP snd_compr_get_metadata(struct snd_compr_stream *stream, unsigned long arg)
6119727b490SJeeja KP {
6129727b490SJeeja KP 	struct snd_compr_metadata metadata;
6139727b490SJeeja KP 	int retval;
6149727b490SJeeja KP 
6159727b490SJeeja KP 	if (!stream->ops->get_metadata)
6169727b490SJeeja KP 		return -ENXIO;
6179727b490SJeeja KP 
6189727b490SJeeja KP 	if (copy_from_user(&metadata, (void __user *)arg, sizeof(metadata)))
6199727b490SJeeja KP 		return -EFAULT;
6209727b490SJeeja KP 
6219727b490SJeeja KP 	retval = stream->ops->get_metadata(stream, &metadata);
6229727b490SJeeja KP 	if (retval != 0)
6239727b490SJeeja KP 		return retval;
6249727b490SJeeja KP 
6259727b490SJeeja KP 	if (copy_to_user((void __user *)arg, &metadata, sizeof(metadata)))
6269727b490SJeeja KP 		return -EFAULT;
6279727b490SJeeja KP 
6289727b490SJeeja KP 	return 0;
6299727b490SJeeja KP }
6309727b490SJeeja KP 
6319727b490SJeeja KP static int
6329727b490SJeeja KP snd_compr_set_metadata(struct snd_compr_stream *stream, unsigned long arg)
6339727b490SJeeja KP {
6349727b490SJeeja KP 	struct snd_compr_metadata metadata;
6359727b490SJeeja KP 	int retval;
6369727b490SJeeja KP 
6379727b490SJeeja KP 	if (!stream->ops->set_metadata)
6389727b490SJeeja KP 		return -ENXIO;
6399727b490SJeeja KP 	/*
6409727b490SJeeja KP 	* we should allow parameter change only when stream has been
6419727b490SJeeja KP 	* opened not in other cases
6429727b490SJeeja KP 	*/
6439727b490SJeeja KP 	if (copy_from_user(&metadata, (void __user *)arg, sizeof(metadata)))
6449727b490SJeeja KP 		return -EFAULT;
6459727b490SJeeja KP 
6469727b490SJeeja KP 	retval = stream->ops->set_metadata(stream, &metadata);
6479727b490SJeeja KP 	stream->metadata_set = true;
6489727b490SJeeja KP 
6499727b490SJeeja KP 	return retval;
6509727b490SJeeja KP }
6519727b490SJeeja KP 
652b21c60a4SVinod Koul static inline int
653b21c60a4SVinod Koul snd_compr_tstamp(struct snd_compr_stream *stream, unsigned long arg)
654b21c60a4SVinod Koul {
65517ac8e5cSRichard Fitzgerald 	struct snd_compr_tstamp tstamp = {0};
65617ac8e5cSRichard Fitzgerald 	int ret;
657b21c60a4SVinod Koul 
65817ac8e5cSRichard Fitzgerald 	ret = snd_compr_update_tstamp(stream, &tstamp);
65917ac8e5cSRichard Fitzgerald 	if (ret == 0)
66017ac8e5cSRichard Fitzgerald 		ret = copy_to_user((struct snd_compr_tstamp __user *)arg,
661b21c60a4SVinod Koul 			&tstamp, sizeof(tstamp)) ? -EFAULT : 0;
66217ac8e5cSRichard Fitzgerald 	return ret;
663b21c60a4SVinod Koul }
664b21c60a4SVinod Koul 
665b21c60a4SVinod Koul static int snd_compr_pause(struct snd_compr_stream *stream)
666b21c60a4SVinod Koul {
667b21c60a4SVinod Koul 	int retval;
668b21c60a4SVinod Koul 
669b21c60a4SVinod Koul 	if (stream->runtime->state != SNDRV_PCM_STATE_RUNNING)
670b21c60a4SVinod Koul 		return -EPERM;
671b21c60a4SVinod Koul 	retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_PAUSE_PUSH);
6726b18f793SVinod Koul 	if (!retval)
673b21c60a4SVinod Koul 		stream->runtime->state = SNDRV_PCM_STATE_PAUSED;
674b21c60a4SVinod Koul 	return retval;
675b21c60a4SVinod Koul }
676b21c60a4SVinod Koul 
677b21c60a4SVinod Koul static int snd_compr_resume(struct snd_compr_stream *stream)
678b21c60a4SVinod Koul {
679b21c60a4SVinod Koul 	int retval;
680b21c60a4SVinod Koul 
681b21c60a4SVinod Koul 	if (stream->runtime->state != SNDRV_PCM_STATE_PAUSED)
682b21c60a4SVinod Koul 		return -EPERM;
683b21c60a4SVinod Koul 	retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_PAUSE_RELEASE);
684b21c60a4SVinod Koul 	if (!retval)
685b21c60a4SVinod Koul 		stream->runtime->state = SNDRV_PCM_STATE_RUNNING;
686b21c60a4SVinod Koul 	return retval;
687b21c60a4SVinod Koul }
688b21c60a4SVinod Koul 
689b21c60a4SVinod Koul static int snd_compr_start(struct snd_compr_stream *stream)
690b21c60a4SVinod Koul {
691b21c60a4SVinod Koul 	int retval;
692b21c60a4SVinod Koul 
6934475f8c4SCharles Keepax 	switch (stream->runtime->state) {
6944475f8c4SCharles Keepax 	case SNDRV_PCM_STATE_SETUP:
6954475f8c4SCharles Keepax 		if (stream->direction != SND_COMPRESS_CAPTURE)
696b21c60a4SVinod Koul 			return -EPERM;
6974475f8c4SCharles Keepax 		break;
6984475f8c4SCharles Keepax 	case SNDRV_PCM_STATE_PREPARED:
6994475f8c4SCharles Keepax 		break;
7004475f8c4SCharles Keepax 	default:
7014475f8c4SCharles Keepax 		return -EPERM;
7024475f8c4SCharles Keepax 	}
7034475f8c4SCharles Keepax 
704b21c60a4SVinod Koul 	retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_START);
705b21c60a4SVinod Koul 	if (!retval)
706b21c60a4SVinod Koul 		stream->runtime->state = SNDRV_PCM_STATE_RUNNING;
707b21c60a4SVinod Koul 	return retval;
708b21c60a4SVinod Koul }
709b21c60a4SVinod Koul 
710b21c60a4SVinod Koul static int snd_compr_stop(struct snd_compr_stream *stream)
711b21c60a4SVinod Koul {
712b21c60a4SVinod Koul 	int retval;
713b21c60a4SVinod Koul 
71426c3f154SCharles Keepax 	switch (stream->runtime->state) {
71526c3f154SCharles Keepax 	case SNDRV_PCM_STATE_OPEN:
71626c3f154SCharles Keepax 	case SNDRV_PCM_STATE_SETUP:
71726c3f154SCharles Keepax 	case SNDRV_PCM_STATE_PREPARED:
718b21c60a4SVinod Koul 		return -EPERM;
71926c3f154SCharles Keepax 	default:
72026c3f154SCharles Keepax 		break;
72126c3f154SCharles Keepax 	}
72226c3f154SCharles Keepax 
723b21c60a4SVinod Koul 	retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_STOP);
724b21c60a4SVinod Koul 	if (!retval) {
725917f4b5cSVinod Koul 		snd_compr_drain_notify(stream);
7268b21460aSVinod Koul 		stream->runtime->total_bytes_available = 0;
7278b21460aSVinod Koul 		stream->runtime->total_bytes_transferred = 0;
728b21c60a4SVinod Koul 	}
729b21c60a4SVinod Koul 	return retval;
730b21c60a4SVinod Koul }
731b21c60a4SVinod Koul 
732a4f2d87cSCharles Keepax static void error_delayed_work(struct work_struct *work)
733a4f2d87cSCharles Keepax {
734a4f2d87cSCharles Keepax 	struct snd_compr_stream *stream;
735a4f2d87cSCharles Keepax 
736a4f2d87cSCharles Keepax 	stream = container_of(work, struct snd_compr_stream, error_work.work);
737a4f2d87cSCharles Keepax 
738a4f2d87cSCharles Keepax 	mutex_lock(&stream->device->lock);
739a4f2d87cSCharles Keepax 
740a4f2d87cSCharles Keepax 	stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_STOP);
741a4f2d87cSCharles Keepax 	wake_up(&stream->runtime->sleep);
742a4f2d87cSCharles Keepax 
743a4f2d87cSCharles Keepax 	mutex_unlock(&stream->device->lock);
744a4f2d87cSCharles Keepax }
745a4f2d87cSCharles Keepax 
746a4f2d87cSCharles Keepax /*
747a4f2d87cSCharles Keepax  * snd_compr_stop_error: Report a fatal error on a stream
748a4f2d87cSCharles Keepax  * @stream: pointer to stream
749a4f2d87cSCharles Keepax  * @state: state to transition the stream to
750a4f2d87cSCharles Keepax  *
751a4f2d87cSCharles Keepax  * Stop the stream and set its state.
752a4f2d87cSCharles Keepax  *
753a4f2d87cSCharles Keepax  * Should be called with compressed device lock held.
754a4f2d87cSCharles Keepax  */
755a4f2d87cSCharles Keepax int snd_compr_stop_error(struct snd_compr_stream *stream,
756a4f2d87cSCharles Keepax 			 snd_pcm_state_t state)
757a4f2d87cSCharles Keepax {
758a4f2d87cSCharles Keepax 	if (stream->runtime->state == state)
759a4f2d87cSCharles Keepax 		return 0;
760a4f2d87cSCharles Keepax 
761a4f2d87cSCharles Keepax 	stream->runtime->state = state;
762a4f2d87cSCharles Keepax 
763a4f2d87cSCharles Keepax 	pr_debug("Changing state to: %d\n", state);
764a4f2d87cSCharles Keepax 
765a4f2d87cSCharles Keepax 	queue_delayed_work(system_power_efficient_wq, &stream->error_work, 0);
766a4f2d87cSCharles Keepax 
767a4f2d87cSCharles Keepax 	return 0;
768a4f2d87cSCharles Keepax }
769a4f2d87cSCharles Keepax EXPORT_SYMBOL_GPL(snd_compr_stop_error);
770a4f2d87cSCharles Keepax 
771917f4b5cSVinod Koul static int snd_compress_wait_for_drain(struct snd_compr_stream *stream)
772917f4b5cSVinod Koul {
773f44f2a54SVinod Koul 	int ret;
774f44f2a54SVinod Koul 
775917f4b5cSVinod Koul 	/*
776917f4b5cSVinod Koul 	 * We are called with lock held. So drop the lock while we wait for
777cdb1ee3fSVinod Koul 	 * drain complete notification from the driver
778917f4b5cSVinod Koul 	 *
779917f4b5cSVinod Koul 	 * It is expected that driver will notify the drain completion and then
780917f4b5cSVinod Koul 	 * stream will be moved to SETUP state, even if draining resulted in an
781917f4b5cSVinod Koul 	 * error. We can trigger next track after this.
782917f4b5cSVinod Koul 	 */
783917f4b5cSVinod Koul 	stream->runtime->state = SNDRV_PCM_STATE_DRAINING;
784917f4b5cSVinod Koul 	mutex_unlock(&stream->device->lock);
785917f4b5cSVinod Koul 
786f44f2a54SVinod Koul 	/* we wait for drain to complete here, drain can return when
787f44f2a54SVinod Koul 	 * interruption occurred, wait returned error or success.
788f44f2a54SVinod Koul 	 * For the first two cases we don't do anything different here and
789f44f2a54SVinod Koul 	 * return after waking up
790f44f2a54SVinod Koul 	 */
791f44f2a54SVinod Koul 
792f44f2a54SVinod Koul 	ret = wait_event_interruptible(stream->runtime->sleep,
793f44f2a54SVinod Koul 			(stream->runtime->state != SNDRV_PCM_STATE_DRAINING));
794f44f2a54SVinod Koul 	if (ret == -ERESTARTSYS)
795c5a905d3SColin Ian King 		pr_debug("wait aborted by a signal\n");
796f44f2a54SVinod Koul 	else if (ret)
797f44f2a54SVinod Koul 		pr_debug("wait for drain failed with %d\n", ret);
798f44f2a54SVinod Koul 
799917f4b5cSVinod Koul 
800917f4b5cSVinod Koul 	wake_up(&stream->runtime->sleep);
801917f4b5cSVinod Koul 	mutex_lock(&stream->device->lock);
802917f4b5cSVinod Koul 
803f44f2a54SVinod Koul 	return ret;
804917f4b5cSVinod Koul }
805917f4b5cSVinod Koul 
806b21c60a4SVinod Koul static int snd_compr_drain(struct snd_compr_stream *stream)
807b21c60a4SVinod Koul {
808b21c60a4SVinod Koul 	int retval;
809b21c60a4SVinod Koul 
81026c3f154SCharles Keepax 	switch (stream->runtime->state) {
81126c3f154SCharles Keepax 	case SNDRV_PCM_STATE_OPEN:
81226c3f154SCharles Keepax 	case SNDRV_PCM_STATE_SETUP:
81326c3f154SCharles Keepax 	case SNDRV_PCM_STATE_PREPARED:
814b21c60a4SVinod Koul 		return -EPERM;
81526c3f154SCharles Keepax 	default:
81626c3f154SCharles Keepax 		break;
81726c3f154SCharles Keepax 	}
818917f4b5cSVinod Koul 
819b21c60a4SVinod Koul 	retval = stream->ops->trigger(stream, SND_COMPR_TRIGGER_DRAIN);
820917f4b5cSVinod Koul 	if (retval) {
821f44f2a54SVinod Koul 		pr_debug("SND_COMPR_TRIGGER_DRAIN failed %d\n", retval);
822b21c60a4SVinod Koul 		wake_up(&stream->runtime->sleep);
823917f4b5cSVinod Koul 		return retval;
824b21c60a4SVinod Koul 	}
825917f4b5cSVinod Koul 
826f44f2a54SVinod Koul 	return snd_compress_wait_for_drain(stream);
827b21c60a4SVinod Koul }
828b21c60a4SVinod Koul 
8299727b490SJeeja KP static int snd_compr_next_track(struct snd_compr_stream *stream)
8309727b490SJeeja KP {
8319727b490SJeeja KP 	int retval;
8329727b490SJeeja KP 
8339727b490SJeeja KP 	/* only a running stream can transition to next track */
8349727b490SJeeja KP 	if (stream->runtime->state != SNDRV_PCM_STATE_RUNNING)
8359727b490SJeeja KP 		return -EPERM;
8369727b490SJeeja KP 
837a70ab8a8SCharles Keepax 	/* next track doesn't have any meaning for capture streams */
838a70ab8a8SCharles Keepax 	if (stream->direction == SND_COMPRESS_CAPTURE)
839a70ab8a8SCharles Keepax 		return -EPERM;
840a70ab8a8SCharles Keepax 
841cdb1ee3fSVinod Koul 	/* you can signal next track if this is intended to be a gapless stream
8429727b490SJeeja KP 	 * and current track metadata is set
8439727b490SJeeja KP 	 */
8449727b490SJeeja KP 	if (stream->metadata_set == false)
8459727b490SJeeja KP 		return -EPERM;
8469727b490SJeeja KP 
8479727b490SJeeja KP 	retval = stream->ops->trigger(stream, SND_COMPR_TRIGGER_NEXT_TRACK);
8489727b490SJeeja KP 	if (retval != 0)
8499727b490SJeeja KP 		return retval;
8509727b490SJeeja KP 	stream->metadata_set = false;
8519727b490SJeeja KP 	stream->next_track = true;
8529727b490SJeeja KP 	return 0;
8539727b490SJeeja KP }
8549727b490SJeeja KP 
8559727b490SJeeja KP static int snd_compr_partial_drain(struct snd_compr_stream *stream)
8569727b490SJeeja KP {
8579727b490SJeeja KP 	int retval;
85826c3f154SCharles Keepax 
85926c3f154SCharles Keepax 	switch (stream->runtime->state) {
86026c3f154SCharles Keepax 	case SNDRV_PCM_STATE_OPEN:
86126c3f154SCharles Keepax 	case SNDRV_PCM_STATE_SETUP:
86226c3f154SCharles Keepax 	case SNDRV_PCM_STATE_PREPARED:
8639727b490SJeeja KP 		return -EPERM;
86426c3f154SCharles Keepax 	default:
86526c3f154SCharles Keepax 		break;
86626c3f154SCharles Keepax 	}
86726c3f154SCharles Keepax 
868a70ab8a8SCharles Keepax 	/* partial drain doesn't have any meaning for capture streams */
869a70ab8a8SCharles Keepax 	if (stream->direction == SND_COMPRESS_CAPTURE)
870a70ab8a8SCharles Keepax 		return -EPERM;
871a70ab8a8SCharles Keepax 
8729727b490SJeeja KP 	/* stream can be drained only when next track has been signalled */
8739727b490SJeeja KP 	if (stream->next_track == false)
8749727b490SJeeja KP 		return -EPERM;
8759727b490SJeeja KP 
8769727b490SJeeja KP 	retval = stream->ops->trigger(stream, SND_COMPR_TRIGGER_PARTIAL_DRAIN);
877917f4b5cSVinod Koul 	if (retval) {
878f44f2a54SVinod Koul 		pr_debug("Partial drain returned failure\n");
879917f4b5cSVinod Koul 		wake_up(&stream->runtime->sleep);
880917f4b5cSVinod Koul 		return retval;
881917f4b5cSVinod Koul 	}
8829727b490SJeeja KP 
8839727b490SJeeja KP 	stream->next_track = false;
884917f4b5cSVinod Koul 	return snd_compress_wait_for_drain(stream);
8859727b490SJeeja KP }
8869727b490SJeeja KP 
887b21c60a4SVinod Koul static long snd_compr_ioctl(struct file *f, unsigned int cmd, unsigned long arg)
888b21c60a4SVinod Koul {
889b21c60a4SVinod Koul 	struct snd_compr_file *data = f->private_data;
890b21c60a4SVinod Koul 	struct snd_compr_stream *stream;
891b21c60a4SVinod Koul 	int retval = -ENOTTY;
892b21c60a4SVinod Koul 
893b21c60a4SVinod Koul 	if (snd_BUG_ON(!data))
894b21c60a4SVinod Koul 		return -EFAULT;
8955bd05390SCharles Keepax 
896b21c60a4SVinod Koul 	stream = &data->stream;
8975bd05390SCharles Keepax 
898b21c60a4SVinod Koul 	mutex_lock(&stream->device->lock);
899b21c60a4SVinod Koul 	switch (_IOC_NR(cmd)) {
900b21c60a4SVinod Koul 	case _IOC_NR(SNDRV_COMPRESS_IOCTL_VERSION):
901a8d30608SVinod Koul 		retval = put_user(SNDRV_COMPRESS_VERSION,
902b21c60a4SVinod Koul 				(int __user *)arg) ? -EFAULT : 0;
903b21c60a4SVinod Koul 		break;
904b21c60a4SVinod Koul 	case _IOC_NR(SNDRV_COMPRESS_GET_CAPS):
905b21c60a4SVinod Koul 		retval = snd_compr_get_caps(stream, arg);
906b21c60a4SVinod Koul 		break;
907462b3f16STakashi Iwai #ifndef COMPR_CODEC_CAPS_OVERFLOW
908b21c60a4SVinod Koul 	case _IOC_NR(SNDRV_COMPRESS_GET_CODEC_CAPS):
909b21c60a4SVinod Koul 		retval = snd_compr_get_codec_caps(stream, arg);
910b21c60a4SVinod Koul 		break;
911462b3f16STakashi Iwai #endif
912b21c60a4SVinod Koul 	case _IOC_NR(SNDRV_COMPRESS_SET_PARAMS):
913b21c60a4SVinod Koul 		retval = snd_compr_set_params(stream, arg);
914b21c60a4SVinod Koul 		break;
915b21c60a4SVinod Koul 	case _IOC_NR(SNDRV_COMPRESS_GET_PARAMS):
916b21c60a4SVinod Koul 		retval = snd_compr_get_params(stream, arg);
917b21c60a4SVinod Koul 		break;
9189727b490SJeeja KP 	case _IOC_NR(SNDRV_COMPRESS_SET_METADATA):
9199727b490SJeeja KP 		retval = snd_compr_set_metadata(stream, arg);
9209727b490SJeeja KP 		break;
9219727b490SJeeja KP 	case _IOC_NR(SNDRV_COMPRESS_GET_METADATA):
9229727b490SJeeja KP 		retval = snd_compr_get_metadata(stream, arg);
9239727b490SJeeja KP 		break;
924b21c60a4SVinod Koul 	case _IOC_NR(SNDRV_COMPRESS_TSTAMP):
925b21c60a4SVinod Koul 		retval = snd_compr_tstamp(stream, arg);
926b21c60a4SVinod Koul 		break;
927b21c60a4SVinod Koul 	case _IOC_NR(SNDRV_COMPRESS_AVAIL):
928b21c60a4SVinod Koul 		retval = snd_compr_ioctl_avail(stream, arg);
929b21c60a4SVinod Koul 		break;
930b21c60a4SVinod Koul 	case _IOC_NR(SNDRV_COMPRESS_PAUSE):
931b21c60a4SVinod Koul 		retval = snd_compr_pause(stream);
932b21c60a4SVinod Koul 		break;
933b21c60a4SVinod Koul 	case _IOC_NR(SNDRV_COMPRESS_RESUME):
934b21c60a4SVinod Koul 		retval = snd_compr_resume(stream);
935b21c60a4SVinod Koul 		break;
936b21c60a4SVinod Koul 	case _IOC_NR(SNDRV_COMPRESS_START):
937b21c60a4SVinod Koul 		retval = snd_compr_start(stream);
938b21c60a4SVinod Koul 		break;
939b21c60a4SVinod Koul 	case _IOC_NR(SNDRV_COMPRESS_STOP):
940b21c60a4SVinod Koul 		retval = snd_compr_stop(stream);
941b21c60a4SVinod Koul 		break;
942b21c60a4SVinod Koul 	case _IOC_NR(SNDRV_COMPRESS_DRAIN):
943b21c60a4SVinod Koul 		retval = snd_compr_drain(stream);
944b21c60a4SVinod Koul 		break;
9459727b490SJeeja KP 	case _IOC_NR(SNDRV_COMPRESS_PARTIAL_DRAIN):
9469727b490SJeeja KP 		retval = snd_compr_partial_drain(stream);
9479727b490SJeeja KP 		break;
9489727b490SJeeja KP 	case _IOC_NR(SNDRV_COMPRESS_NEXT_TRACK):
9499727b490SJeeja KP 		retval = snd_compr_next_track(stream);
9509727b490SJeeja KP 		break;
9519727b490SJeeja KP 
952b21c60a4SVinod Koul 	}
953b21c60a4SVinod Koul 	mutex_unlock(&stream->device->lock);
954b21c60a4SVinod Koul 	return retval;
955b21c60a4SVinod Koul }
956b21c60a4SVinod Koul 
957c1036889SRavindra Lokhande /* support of 32bit userspace on 64bit platforms */
958c1036889SRavindra Lokhande #ifdef CONFIG_COMPAT
959c1036889SRavindra Lokhande static long snd_compr_ioctl_compat(struct file *file, unsigned int cmd,
960c1036889SRavindra Lokhande 						unsigned long arg)
961c1036889SRavindra Lokhande {
962c1036889SRavindra Lokhande 	return snd_compr_ioctl(file, cmd, (unsigned long)compat_ptr(arg));
963c1036889SRavindra Lokhande }
964c1036889SRavindra Lokhande #endif
965c1036889SRavindra Lokhande 
966b21c60a4SVinod Koul static const struct file_operations snd_compr_file_ops = {
967b21c60a4SVinod Koul 		.owner =	THIS_MODULE,
968b21c60a4SVinod Koul 		.open =		snd_compr_open,
969b21c60a4SVinod Koul 		.release =	snd_compr_free,
970b21c60a4SVinod Koul 		.write =	snd_compr_write,
971b21c60a4SVinod Koul 		.read =		snd_compr_read,
972b21c60a4SVinod Koul 		.unlocked_ioctl = snd_compr_ioctl,
973c1036889SRavindra Lokhande #ifdef CONFIG_COMPAT
974c1036889SRavindra Lokhande 		.compat_ioctl = snd_compr_ioctl_compat,
975c1036889SRavindra Lokhande #endif
976b21c60a4SVinod Koul 		.mmap =		snd_compr_mmap,
977b21c60a4SVinod Koul 		.poll =		snd_compr_poll,
978b21c60a4SVinod Koul };
979b21c60a4SVinod Koul 
980b21c60a4SVinod Koul static int snd_compress_dev_register(struct snd_device *device)
981b21c60a4SVinod Koul {
982b21c60a4SVinod Koul 	int ret = -EINVAL;
983b21c60a4SVinod Koul 	struct snd_compr *compr;
984b21c60a4SVinod Koul 
985b21c60a4SVinod Koul 	if (snd_BUG_ON(!device || !device->device_data))
986b21c60a4SVinod Koul 		return -EBADFD;
987b21c60a4SVinod Koul 	compr = device->device_data;
988b21c60a4SVinod Koul 
989a931b9ceSGuneshwor Singh 	pr_debug("reg device %s, direction %d\n", compr->name,
990b21c60a4SVinod Koul 			compr->direction);
991b21c60a4SVinod Koul 	/* register compressed device */
99240a4b263STakashi Iwai 	ret = snd_register_device(SNDRV_DEVICE_TYPE_COMPRESS,
99304c5d5a4STakashi Iwai 				  compr->card, compr->device,
99440a4b263STakashi Iwai 				  &snd_compr_file_ops, compr, &compr->dev);
995b21c60a4SVinod Koul 	if (ret < 0) {
996c5a905d3SColin Ian King 		pr_err("snd_register_device failed %d\n", ret);
997b21c60a4SVinod Koul 		return ret;
998b21c60a4SVinod Koul 	}
999b21c60a4SVinod Koul 	return ret;
1000b21c60a4SVinod Koul 
1001b21c60a4SVinod Koul }
1002b21c60a4SVinod Koul 
1003b21c60a4SVinod Koul static int snd_compress_dev_disconnect(struct snd_device *device)
1004b21c60a4SVinod Koul {
1005b21c60a4SVinod Koul 	struct snd_compr *compr;
1006b21c60a4SVinod Koul 
1007b21c60a4SVinod Koul 	compr = device->device_data;
100840a4b263STakashi Iwai 	snd_unregister_device(&compr->dev);
1009b21c60a4SVinod Koul 	return 0;
1010b21c60a4SVinod Koul }
1011b21c60a4SVinod Koul 
101231742724SRichard Fitzgerald #ifdef CONFIG_SND_VERBOSE_PROCFS
101331742724SRichard Fitzgerald static void snd_compress_proc_info_read(struct snd_info_entry *entry,
101431742724SRichard Fitzgerald 					struct snd_info_buffer *buffer)
101531742724SRichard Fitzgerald {
101631742724SRichard Fitzgerald 	struct snd_compr *compr = (struct snd_compr *)entry->private_data;
101731742724SRichard Fitzgerald 
101831742724SRichard Fitzgerald 	snd_iprintf(buffer, "card: %d\n", compr->card->number);
101931742724SRichard Fitzgerald 	snd_iprintf(buffer, "device: %d\n", compr->device);
102031742724SRichard Fitzgerald 	snd_iprintf(buffer, "stream: %s\n",
102131742724SRichard Fitzgerald 			compr->direction == SND_COMPRESS_PLAYBACK
102231742724SRichard Fitzgerald 				? "PLAYBACK" : "CAPTURE");
102331742724SRichard Fitzgerald 	snd_iprintf(buffer, "id: %s\n", compr->id);
102431742724SRichard Fitzgerald }
102531742724SRichard Fitzgerald 
102631742724SRichard Fitzgerald static int snd_compress_proc_init(struct snd_compr *compr)
102731742724SRichard Fitzgerald {
102831742724SRichard Fitzgerald 	struct snd_info_entry *entry;
102931742724SRichard Fitzgerald 	char name[16];
103031742724SRichard Fitzgerald 
103131742724SRichard Fitzgerald 	sprintf(name, "compr%i", compr->device);
103231742724SRichard Fitzgerald 	entry = snd_info_create_card_entry(compr->card, name,
103331742724SRichard Fitzgerald 					   compr->card->proc_root);
103431742724SRichard Fitzgerald 	if (!entry)
103531742724SRichard Fitzgerald 		return -ENOMEM;
10366a73cf46SJoe Perches 	entry->mode = S_IFDIR | 0555;
103731742724SRichard Fitzgerald 	compr->proc_root = entry;
103831742724SRichard Fitzgerald 
103931742724SRichard Fitzgerald 	entry = snd_info_create_card_entry(compr->card, "info",
104031742724SRichard Fitzgerald 					   compr->proc_root);
10414a471d7cSTakashi Iwai 	if (entry)
104231742724SRichard Fitzgerald 		snd_info_set_text_ops(entry, compr,
104331742724SRichard Fitzgerald 				      snd_compress_proc_info_read);
104431742724SRichard Fitzgerald 	compr->proc_info_entry = entry;
104531742724SRichard Fitzgerald 
104631742724SRichard Fitzgerald 	return 0;
104731742724SRichard Fitzgerald }
104831742724SRichard Fitzgerald 
104931742724SRichard Fitzgerald static void snd_compress_proc_done(struct snd_compr *compr)
105031742724SRichard Fitzgerald {
105131742724SRichard Fitzgerald 	snd_info_free_entry(compr->proc_info_entry);
105231742724SRichard Fitzgerald 	compr->proc_info_entry = NULL;
105331742724SRichard Fitzgerald 	snd_info_free_entry(compr->proc_root);
105431742724SRichard Fitzgerald 	compr->proc_root = NULL;
105531742724SRichard Fitzgerald }
1056e5241a8cSRichard Fitzgerald 
1057e5241a8cSRichard Fitzgerald static inline void snd_compress_set_id(struct snd_compr *compr, const char *id)
1058e5241a8cSRichard Fitzgerald {
1059e5241a8cSRichard Fitzgerald 	strlcpy(compr->id, id, sizeof(compr->id));
1060e5241a8cSRichard Fitzgerald }
106131742724SRichard Fitzgerald #else
106231742724SRichard Fitzgerald static inline int snd_compress_proc_init(struct snd_compr *compr)
106331742724SRichard Fitzgerald {
106431742724SRichard Fitzgerald 	return 0;
106531742724SRichard Fitzgerald }
106631742724SRichard Fitzgerald 
106731742724SRichard Fitzgerald static inline void snd_compress_proc_done(struct snd_compr *compr)
106831742724SRichard Fitzgerald {
106931742724SRichard Fitzgerald }
1070e5241a8cSRichard Fitzgerald 
1071e5241a8cSRichard Fitzgerald static inline void snd_compress_set_id(struct snd_compr *compr, const char *id)
1072e5241a8cSRichard Fitzgerald {
1073e5241a8cSRichard Fitzgerald }
107431742724SRichard Fitzgerald #endif
107531742724SRichard Fitzgerald 
107604c5d5a4STakashi Iwai static int snd_compress_dev_free(struct snd_device *device)
107704c5d5a4STakashi Iwai {
107804c5d5a4STakashi Iwai 	struct snd_compr *compr;
107904c5d5a4STakashi Iwai 
108004c5d5a4STakashi Iwai 	compr = device->device_data;
108131742724SRichard Fitzgerald 	snd_compress_proc_done(compr);
108204c5d5a4STakashi Iwai 	put_device(&compr->dev);
108304c5d5a4STakashi Iwai 	return 0;
108404c5d5a4STakashi Iwai }
108504c5d5a4STakashi Iwai 
1086b21c60a4SVinod Koul /*
1087b21c60a4SVinod Koul  * snd_compress_new: create new compress device
1088b21c60a4SVinod Koul  * @card: sound card pointer
1089b21c60a4SVinod Koul  * @device: device number
1090b21c60a4SVinod Koul  * @dirn: device direction, should be of type enum snd_compr_direction
1091b21c60a4SVinod Koul  * @compr: compress device pointer
1092b21c60a4SVinod Koul  */
1093b21c60a4SVinod Koul int snd_compress_new(struct snd_card *card, int device,
1094e5241a8cSRichard Fitzgerald 			int dirn, const char *id, struct snd_compr *compr)
1095b21c60a4SVinod Koul {
1096b21c60a4SVinod Koul 	static struct snd_device_ops ops = {
109704c5d5a4STakashi Iwai 		.dev_free = snd_compress_dev_free,
1098b21c60a4SVinod Koul 		.dev_register = snd_compress_dev_register,
1099b21c60a4SVinod Koul 		.dev_disconnect = snd_compress_dev_disconnect,
1100b21c60a4SVinod Koul 	};
110131742724SRichard Fitzgerald 	int ret;
1102b21c60a4SVinod Koul 
1103b21c60a4SVinod Koul 	compr->card = card;
1104b21c60a4SVinod Koul 	compr->device = device;
1105b21c60a4SVinod Koul 	compr->direction = dirn;
110604c5d5a4STakashi Iwai 
1107e5241a8cSRichard Fitzgerald 	snd_compress_set_id(compr, id);
1108e5241a8cSRichard Fitzgerald 
110904c5d5a4STakashi Iwai 	snd_device_initialize(&compr->dev, card);
111004c5d5a4STakashi Iwai 	dev_set_name(&compr->dev, "comprC%iD%i", card->number, device);
111104c5d5a4STakashi Iwai 
111231742724SRichard Fitzgerald 	ret = snd_device_new(card, SNDRV_DEV_COMPRESS, compr, &ops);
111331742724SRichard Fitzgerald 	if (ret == 0)
111431742724SRichard Fitzgerald 		snd_compress_proc_init(compr);
111531742724SRichard Fitzgerald 
111631742724SRichard Fitzgerald 	return ret;
1117b21c60a4SVinod Koul }
1118b21c60a4SVinod Koul EXPORT_SYMBOL_GPL(snd_compress_new);
1119b21c60a4SVinod Koul 
1120b21c60a4SVinod Koul static int snd_compress_add_device(struct snd_compr *device)
1121b21c60a4SVinod Koul {
1122b21c60a4SVinod Koul 	int ret;
1123b21c60a4SVinod Koul 
1124b21c60a4SVinod Koul 	if (!device->card)
1125b21c60a4SVinod Koul 		return -EINVAL;
1126b21c60a4SVinod Koul 
1127b21c60a4SVinod Koul 	/* register the card */
1128b21c60a4SVinod Koul 	ret = snd_card_register(device->card);
1129b21c60a4SVinod Koul 	if (ret)
1130b21c60a4SVinod Koul 		goto out;
1131b21c60a4SVinod Koul 	return 0;
1132b21c60a4SVinod Koul 
1133b21c60a4SVinod Koul out:
1134b21c60a4SVinod Koul 	pr_err("failed with %d\n", ret);
1135b21c60a4SVinod Koul 	return ret;
1136b21c60a4SVinod Koul 
1137b21c60a4SVinod Koul }
1138b21c60a4SVinod Koul 
1139b21c60a4SVinod Koul static int snd_compress_remove_device(struct snd_compr *device)
1140b21c60a4SVinod Koul {
1141b21c60a4SVinod Koul 	return snd_card_free(device->card);
1142b21c60a4SVinod Koul }
1143b21c60a4SVinod Koul 
1144b21c60a4SVinod Koul /**
1145b21c60a4SVinod Koul  * snd_compress_register - register compressed device
1146b21c60a4SVinod Koul  *
1147b21c60a4SVinod Koul  * @device: compressed device to register
1148b21c60a4SVinod Koul  */
1149b21c60a4SVinod Koul int snd_compress_register(struct snd_compr *device)
1150b21c60a4SVinod Koul {
1151b21c60a4SVinod Koul 	int retval;
1152b21c60a4SVinod Koul 
115304c5d5a4STakashi Iwai 	if (device->name == NULL || device->ops == NULL)
1154b21c60a4SVinod Koul 		return -EINVAL;
1155b21c60a4SVinod Koul 
1156b21c60a4SVinod Koul 	pr_debug("Registering compressed device %s\n", device->name);
1157b21c60a4SVinod Koul 	if (snd_BUG_ON(!device->ops->open))
1158b21c60a4SVinod Koul 		return -EINVAL;
1159b21c60a4SVinod Koul 	if (snd_BUG_ON(!device->ops->free))
1160b21c60a4SVinod Koul 		return -EINVAL;
1161b21c60a4SVinod Koul 	if (snd_BUG_ON(!device->ops->set_params))
1162b21c60a4SVinod Koul 		return -EINVAL;
1163b21c60a4SVinod Koul 	if (snd_BUG_ON(!device->ops->trigger))
1164b21c60a4SVinod Koul 		return -EINVAL;
1165b21c60a4SVinod Koul 
1166b21c60a4SVinod Koul 	mutex_init(&device->lock);
1167b21c60a4SVinod Koul 
1168b21c60a4SVinod Koul 	/* register a compressed card */
1169b21c60a4SVinod Koul 	mutex_lock(&device_mutex);
1170b21c60a4SVinod Koul 	retval = snd_compress_add_device(device);
1171b21c60a4SVinod Koul 	mutex_unlock(&device_mutex);
1172b21c60a4SVinod Koul 	return retval;
1173b21c60a4SVinod Koul }
1174b21c60a4SVinod Koul EXPORT_SYMBOL_GPL(snd_compress_register);
1175b21c60a4SVinod Koul 
1176b21c60a4SVinod Koul int snd_compress_deregister(struct snd_compr *device)
1177b21c60a4SVinod Koul {
1178b21c60a4SVinod Koul 	pr_debug("Removing compressed device %s\n", device->name);
1179b21c60a4SVinod Koul 	mutex_lock(&device_mutex);
1180b21c60a4SVinod Koul 	snd_compress_remove_device(device);
1181b21c60a4SVinod Koul 	mutex_unlock(&device_mutex);
1182b21c60a4SVinod Koul 	return 0;
1183b21c60a4SVinod Koul }
1184b21c60a4SVinod Koul EXPORT_SYMBOL_GPL(snd_compress_deregister);
1185b21c60a4SVinod Koul 
1186b21c60a4SVinod Koul MODULE_DESCRIPTION("ALSA Compressed offload framework");
1187b21c60a4SVinod Koul MODULE_AUTHOR("Vinod Koul <vinod.koul@linux.intel.com>");
1188b21c60a4SVinod Koul MODULE_LICENSE("GPL v2");
1189