xref: /linux/sound/core/compress_offload.c (revision d648843a)
1873e65bcSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2b21c60a4SVinod Koul /*
3b21c60a4SVinod Koul  *  compress_core.c - compress offload core
4b21c60a4SVinod Koul  *
5b21c60a4SVinod Koul  *  Copyright (C) 2011 Intel Corporation
6b21c60a4SVinod Koul  *  Authors:	Vinod Koul <vinod.koul@linux.intel.com>
7b21c60a4SVinod Koul  *		Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
8b21c60a4SVinod Koul  *  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
9b21c60a4SVinod Koul  *
10b21c60a4SVinod Koul  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
11b21c60a4SVinod Koul  */
12b21c60a4SVinod Koul #define FORMAT(fmt) "%s: %d: " fmt, __func__, __LINE__
13b21c60a4SVinod Koul #define pr_fmt(fmt) KBUILD_MODNAME ": " FORMAT(fmt)
14b21c60a4SVinod Koul 
15b21c60a4SVinod Koul #include <linux/file.h>
16b21c60a4SVinod Koul #include <linux/fs.h>
17b21c60a4SVinod Koul #include <linux/list.h>
18f0283b58SCharles Keepax #include <linux/math64.h>
19b21c60a4SVinod Koul #include <linux/mm.h>
20b21c60a4SVinod Koul #include <linux/mutex.h>
21b21c60a4SVinod Koul #include <linux/poll.h>
22b21c60a4SVinod Koul #include <linux/slab.h>
23b21c60a4SVinod Koul #include <linux/sched.h>
24f0283b58SCharles Keepax #include <linux/types.h>
25b21c60a4SVinod Koul #include <linux/uio.h>
26b21c60a4SVinod Koul #include <linux/uaccess.h>
27b21c60a4SVinod Koul #include <linux/module.h>
28c1036889SRavindra Lokhande #include <linux/compat.h>
29b21c60a4SVinod Koul #include <sound/core.h>
30b21c60a4SVinod Koul #include <sound/initval.h>
3131742724SRichard Fitzgerald #include <sound/info.h>
32b21c60a4SVinod Koul #include <sound/compress_params.h>
33b21c60a4SVinod Koul #include <sound/compress_offload.h>
34b21c60a4SVinod Koul #include <sound/compress_driver.h>
35b21c60a4SVinod Koul 
36462b3f16STakashi Iwai /* struct snd_compr_codec_caps overflows the ioctl bit size for some
37462b3f16STakashi Iwai  * architectures, so we need to disable the relevant ioctls.
38462b3f16STakashi Iwai  */
39462b3f16STakashi Iwai #if _IOC_SIZEBITS < 14
40462b3f16STakashi Iwai #define COMPR_CODEC_CAPS_OVERFLOW
41462b3f16STakashi Iwai #endif
42462b3f16STakashi Iwai 
43b21c60a4SVinod Koul /* TODO:
44b21c60a4SVinod Koul  * - add substream support for multiple devices in case of
45b21c60a4SVinod Koul  *	SND_DYNAMIC_MINORS is not used
46b21c60a4SVinod Koul  * - Multiple node representation
47b21c60a4SVinod Koul  *	driver should be able to register multiple nodes
48b21c60a4SVinod Koul  */
49b21c60a4SVinod Koul 
50b21c60a4SVinod Koul struct snd_compr_file {
51b21c60a4SVinod Koul 	unsigned long caps;
52b21c60a4SVinod Koul 	struct snd_compr_stream stream;
53b21c60a4SVinod Koul };
54b21c60a4SVinod Koul 
55a4f2d87cSCharles Keepax static void error_delayed_work(struct work_struct *work);
56a4f2d87cSCharles Keepax 
57b21c60a4SVinod Koul /*
58b21c60a4SVinod Koul  * a note on stream states used:
5941eb94fdSVinod Koul  * we use following states in the compressed core
60b21c60a4SVinod Koul  * SNDRV_PCM_STATE_OPEN: When stream has been opened.
61b21c60a4SVinod Koul  * SNDRV_PCM_STATE_SETUP: When stream has been initialized. This is done by
6241eb94fdSVinod Koul  *	calling SNDRV_COMPRESS_SET_PARAMS. Running streams will come to this
63b21c60a4SVinod Koul  *	state at stop by calling SNDRV_COMPRESS_STOP, or at end of drain.
64862bca5dSVinod Koul  * SNDRV_PCM_STATE_PREPARED: When a stream has been written to (for
65862bca5dSVinod Koul  *	playback only). User after setting up stream writes the data buffer
66862bca5dSVinod Koul  *	before starting the stream.
67b21c60a4SVinod Koul  * SNDRV_PCM_STATE_RUNNING: When stream has been started and is
68b21c60a4SVinod Koul  *	decoding/encoding and rendering/capturing data.
69b21c60a4SVinod Koul  * SNDRV_PCM_STATE_DRAINING: When stream is draining current data. This is done
70b21c60a4SVinod Koul  *	by calling SNDRV_COMPRESS_DRAIN.
71b21c60a4SVinod Koul  * SNDRV_PCM_STATE_PAUSED: When stream is paused. This is done by calling
72b21c60a4SVinod Koul  *	SNDRV_COMPRESS_PAUSE. It can be stopped or resumed by calling
73b21c60a4SVinod Koul  *	SNDRV_COMPRESS_STOP or SNDRV_COMPRESS_RESUME respectively.
74b21c60a4SVinod Koul  */
snd_compr_open(struct inode * inode,struct file * f)75b21c60a4SVinod Koul static int snd_compr_open(struct inode *inode, struct file *f)
76b21c60a4SVinod Koul {
77b21c60a4SVinod Koul 	struct snd_compr *compr;
78b21c60a4SVinod Koul 	struct snd_compr_file *data;
79b21c60a4SVinod Koul 	struct snd_compr_runtime *runtime;
80b21c60a4SVinod Koul 	enum snd_compr_direction dirn;
81b21c60a4SVinod Koul 	int maj = imajor(inode);
82b21c60a4SVinod Koul 	int ret;
83b21c60a4SVinod Koul 
8481cb3246SDan Carpenter 	if ((f->f_flags & O_ACCMODE) == O_WRONLY)
85b21c60a4SVinod Koul 		dirn = SND_COMPRESS_PLAYBACK;
8681cb3246SDan Carpenter 	else if ((f->f_flags & O_ACCMODE) == O_RDONLY)
87b21c60a4SVinod Koul 		dirn = SND_COMPRESS_CAPTURE;
8881cb3246SDan Carpenter 	else
89b21c60a4SVinod Koul 		return -EINVAL;
90b21c60a4SVinod Koul 
91b21c60a4SVinod Koul 	if (maj == snd_major)
92b21c60a4SVinod Koul 		compr = snd_lookup_minor_data(iminor(inode),
93b21c60a4SVinod Koul 					SNDRV_DEVICE_TYPE_COMPRESS);
94b21c60a4SVinod Koul 	else
95b21c60a4SVinod Koul 		return -EBADFD;
96b21c60a4SVinod Koul 
97b21c60a4SVinod Koul 	if (compr == NULL) {
98b21c60a4SVinod Koul 		pr_err("no device data!!!\n");
99b21c60a4SVinod Koul 		return -ENODEV;
100b21c60a4SVinod Koul 	}
101b21c60a4SVinod Koul 
102b21c60a4SVinod Koul 	if (dirn != compr->direction) {
103b21c60a4SVinod Koul 		pr_err("this device doesn't support this direction\n");
104a0830dbdSTakashi Iwai 		snd_card_unref(compr->card);
105b21c60a4SVinod Koul 		return -EINVAL;
106b21c60a4SVinod Koul 	}
107b21c60a4SVinod Koul 
108b21c60a4SVinod Koul 	data = kzalloc(sizeof(*data), GFP_KERNEL);
109a0830dbdSTakashi Iwai 	if (!data) {
110a0830dbdSTakashi Iwai 		snd_card_unref(compr->card);
111b21c60a4SVinod Koul 		return -ENOMEM;
112a0830dbdSTakashi Iwai 	}
113a4f2d87cSCharles Keepax 
114a4f2d87cSCharles Keepax 	INIT_DELAYED_WORK(&data->stream.error_work, error_delayed_work);
115a4f2d87cSCharles Keepax 
116b21c60a4SVinod Koul 	data->stream.ops = compr->ops;
117b21c60a4SVinod Koul 	data->stream.direction = dirn;
118b21c60a4SVinod Koul 	data->stream.private_data = compr->private_data;
119b21c60a4SVinod Koul 	data->stream.device = compr;
120b21c60a4SVinod Koul 	runtime = kzalloc(sizeof(*runtime), GFP_KERNEL);
121b21c60a4SVinod Koul 	if (!runtime) {
122b21c60a4SVinod Koul 		kfree(data);
123a0830dbdSTakashi Iwai 		snd_card_unref(compr->card);
124b21c60a4SVinod Koul 		return -ENOMEM;
125b21c60a4SVinod Koul 	}
126b21c60a4SVinod Koul 	runtime->state = SNDRV_PCM_STATE_OPEN;
127b21c60a4SVinod Koul 	init_waitqueue_head(&runtime->sleep);
128b21c60a4SVinod Koul 	data->stream.runtime = runtime;
129b21c60a4SVinod Koul 	f->private_data = (void *)data;
130*d648843aSTakashi Iwai 	scoped_guard(mutex, &compr->lock)
131b21c60a4SVinod Koul 		ret = compr->ops->open(&data->stream);
132b21c60a4SVinod Koul 	if (ret) {
133b21c60a4SVinod Koul 		kfree(runtime);
134b21c60a4SVinod Koul 		kfree(data);
135b21c60a4SVinod Koul 	}
136a0830dbdSTakashi Iwai 	snd_card_unref(compr->card);
137749d3223SCharles Keepax 	return ret;
138b21c60a4SVinod Koul }
139b21c60a4SVinod Koul 
snd_compr_free(struct inode * inode,struct file * f)140b21c60a4SVinod Koul static int snd_compr_free(struct inode *inode, struct file *f)
141b21c60a4SVinod Koul {
142b21c60a4SVinod Koul 	struct snd_compr_file *data = f->private_data;
143b26d19e4SLiam Girdwood 	struct snd_compr_runtime *runtime = data->stream.runtime;
144b26d19e4SLiam Girdwood 
145a4f2d87cSCharles Keepax 	cancel_delayed_work_sync(&data->stream.error_work);
146a4f2d87cSCharles Keepax 
147b26d19e4SLiam Girdwood 	switch (runtime->state) {
148b26d19e4SLiam Girdwood 	case SNDRV_PCM_STATE_RUNNING:
149b26d19e4SLiam Girdwood 	case SNDRV_PCM_STATE_DRAINING:
150b26d19e4SLiam Girdwood 	case SNDRV_PCM_STATE_PAUSED:
151b26d19e4SLiam Girdwood 		data->stream.ops->trigger(&data->stream, SNDRV_PCM_TRIGGER_STOP);
152b26d19e4SLiam Girdwood 		break;
153b26d19e4SLiam Girdwood 	default:
154b26d19e4SLiam Girdwood 		break;
155b26d19e4SLiam Girdwood 	}
156b26d19e4SLiam Girdwood 
157b21c60a4SVinod Koul 	data->stream.ops->free(&data->stream);
158d00f749bSSrinivas Kandagatla 	if (!data->stream.runtime->dma_buffer_p)
159b21c60a4SVinod Koul 		kfree(data->stream.runtime->buffer);
160b21c60a4SVinod Koul 	kfree(data->stream.runtime);
161b21c60a4SVinod Koul 	kfree(data);
162b21c60a4SVinod Koul 	return 0;
163b21c60a4SVinod Koul }
164b21c60a4SVinod Koul 
snd_compr_update_tstamp(struct snd_compr_stream * stream,struct snd_compr_tstamp * tstamp)16517ac8e5cSRichard Fitzgerald static int snd_compr_update_tstamp(struct snd_compr_stream *stream,
166b21c60a4SVinod Koul 		struct snd_compr_tstamp *tstamp)
167b21c60a4SVinod Koul {
168b21c60a4SVinod Koul 	if (!stream->ops->pointer)
16917ac8e5cSRichard Fitzgerald 		return -ENOTSUPP;
170b21c60a4SVinod Koul 	stream->ops->pointer(stream, tstamp);
171b21c60a4SVinod Koul 	pr_debug("dsp consumed till %d total %d bytes\n",
172b21c60a4SVinod Koul 		tstamp->byte_offset, tstamp->copied_total);
1735b1f79f7SCharles Keepax 	if (stream->direction == SND_COMPRESS_PLAYBACK)
174b21c60a4SVinod Koul 		stream->runtime->total_bytes_transferred = tstamp->copied_total;
1755b1f79f7SCharles Keepax 	else
1765b1f79f7SCharles Keepax 		stream->runtime->total_bytes_available = tstamp->copied_total;
17717ac8e5cSRichard Fitzgerald 	return 0;
178b21c60a4SVinod Koul }
179b21c60a4SVinod Koul 
snd_compr_calc_avail(struct snd_compr_stream * stream,struct snd_compr_avail * avail)180b21c60a4SVinod Koul static size_t snd_compr_calc_avail(struct snd_compr_stream *stream,
181b21c60a4SVinod Koul 		struct snd_compr_avail *avail)
182b21c60a4SVinod Koul {
18317ac8e5cSRichard Fitzgerald 	memset(avail, 0, sizeof(*avail));
184b21c60a4SVinod Koul 	snd_compr_update_tstamp(stream, &avail->tstamp);
18517ac8e5cSRichard Fitzgerald 	/* Still need to return avail even if tstamp can't be filled in */
186b21c60a4SVinod Koul 
187b21c60a4SVinod Koul 	if (stream->runtime->total_bytes_available == 0 &&
1885b1f79f7SCharles Keepax 			stream->runtime->state == SNDRV_PCM_STATE_SETUP &&
1895b1f79f7SCharles Keepax 			stream->direction == SND_COMPRESS_PLAYBACK) {
190b21c60a4SVinod Koul 		pr_debug("detected init and someone forgot to do a write\n");
191b21c60a4SVinod Koul 		return stream->runtime->buffer_size;
192b21c60a4SVinod Koul 	}
193b21c60a4SVinod Koul 	pr_debug("app wrote %lld, DSP consumed %lld\n",
194b21c60a4SVinod Koul 			stream->runtime->total_bytes_available,
195b21c60a4SVinod Koul 			stream->runtime->total_bytes_transferred);
196b21c60a4SVinod Koul 	if (stream->runtime->total_bytes_available ==
197b21c60a4SVinod Koul 				stream->runtime->total_bytes_transferred) {
1985b1f79f7SCharles Keepax 		if (stream->direction == SND_COMPRESS_PLAYBACK) {
199b21c60a4SVinod Koul 			pr_debug("both pointers are same, returning full avail\n");
200b21c60a4SVinod Koul 			return stream->runtime->buffer_size;
2015b1f79f7SCharles Keepax 		} else {
2025b1f79f7SCharles Keepax 			pr_debug("both pointers are same, returning no avail\n");
2035b1f79f7SCharles Keepax 			return 0;
2045b1f79f7SCharles Keepax 		}
205b21c60a4SVinod Koul 	}
206b21c60a4SVinod Koul 
2075b1f79f7SCharles Keepax 	avail->avail = stream->runtime->total_bytes_available -
2085b1f79f7SCharles Keepax 			stream->runtime->total_bytes_transferred;
2095b1f79f7SCharles Keepax 	if (stream->direction == SND_COMPRESS_PLAYBACK)
2105b1f79f7SCharles Keepax 		avail->avail = stream->runtime->buffer_size - avail->avail;
2115b1f79f7SCharles Keepax 
2124c28e32dSCharles Keepax 	pr_debug("ret avail as %lld\n", avail->avail);
2134c28e32dSCharles Keepax 	return avail->avail;
214b21c60a4SVinod Koul }
215b21c60a4SVinod Koul 
snd_compr_get_avail(struct snd_compr_stream * stream)216b21c60a4SVinod Koul static inline size_t snd_compr_get_avail(struct snd_compr_stream *stream)
217b21c60a4SVinod Koul {
218b21c60a4SVinod Koul 	struct snd_compr_avail avail;
219b21c60a4SVinod Koul 
220b21c60a4SVinod Koul 	return snd_compr_calc_avail(stream, &avail);
221b21c60a4SVinod Koul }
222b21c60a4SVinod Koul 
223b21c60a4SVinod Koul static int
snd_compr_ioctl_avail(struct snd_compr_stream * stream,unsigned long arg)224b21c60a4SVinod Koul snd_compr_ioctl_avail(struct snd_compr_stream *stream, unsigned long arg)
225b21c60a4SVinod Koul {
226b21c60a4SVinod Koul 	struct snd_compr_avail ioctl_avail;
227b21c60a4SVinod Koul 	size_t avail;
228b21c60a4SVinod Koul 
229b21c60a4SVinod Koul 	avail = snd_compr_calc_avail(stream, &ioctl_avail);
230b21c60a4SVinod Koul 	ioctl_avail.avail = avail;
231b21c60a4SVinod Koul 
232a4f2d87cSCharles Keepax 	switch (stream->runtime->state) {
233a4f2d87cSCharles Keepax 	case SNDRV_PCM_STATE_OPEN:
234a4f2d87cSCharles Keepax 		return -EBADFD;
235a4f2d87cSCharles Keepax 	case SNDRV_PCM_STATE_XRUN:
236a4f2d87cSCharles Keepax 		return -EPIPE;
237a4f2d87cSCharles Keepax 	default:
238a4f2d87cSCharles Keepax 		break;
239a4f2d87cSCharles Keepax 	}
240a4f2d87cSCharles Keepax 
241b21c60a4SVinod Koul 	if (copy_to_user((__u64 __user *)arg,
242b21c60a4SVinod Koul 				&ioctl_avail, sizeof(ioctl_avail)))
243b21c60a4SVinod Koul 		return -EFAULT;
244b21c60a4SVinod Koul 	return 0;
245b21c60a4SVinod Koul }
246b21c60a4SVinod Koul 
snd_compr_write_data(struct snd_compr_stream * stream,const char __user * buf,size_t count)247b21c60a4SVinod Koul static int snd_compr_write_data(struct snd_compr_stream *stream,
248b21c60a4SVinod Koul 	       const char __user *buf, size_t count)
249b21c60a4SVinod Koul {
250b21c60a4SVinod Koul 	void *dstn;
251b21c60a4SVinod Koul 	size_t copy;
252b21c60a4SVinod Koul 	struct snd_compr_runtime *runtime = stream->runtime;
253f0283b58SCharles Keepax 	/* 64-bit Modulus */
254f0283b58SCharles Keepax 	u64 app_pointer = div64_u64(runtime->total_bytes_available,
255f0283b58SCharles Keepax 				    runtime->buffer_size);
256f0283b58SCharles Keepax 	app_pointer = runtime->total_bytes_available -
257f0283b58SCharles Keepax 		      (app_pointer * runtime->buffer_size);
258b21c60a4SVinod Koul 
259f0283b58SCharles Keepax 	dstn = runtime->buffer + app_pointer;
260b21c60a4SVinod Koul 	pr_debug("copying %ld at %lld\n",
261f0283b58SCharles Keepax 			(unsigned long)count, app_pointer);
262f0283b58SCharles Keepax 	if (count < runtime->buffer_size - app_pointer) {
263b21c60a4SVinod Koul 		if (copy_from_user(dstn, buf, count))
264b21c60a4SVinod Koul 			return -EFAULT;
265b21c60a4SVinod Koul 	} else {
266f0283b58SCharles Keepax 		copy = runtime->buffer_size - app_pointer;
267b21c60a4SVinod Koul 		if (copy_from_user(dstn, buf, copy))
268b21c60a4SVinod Koul 			return -EFAULT;
269b21c60a4SVinod Koul 		if (copy_from_user(runtime->buffer, buf + copy, count - copy))
270b21c60a4SVinod Koul 			return -EFAULT;
271b21c60a4SVinod Koul 	}
272b21c60a4SVinod Koul 	/* if DSP cares, let it know data has been written */
273b21c60a4SVinod Koul 	if (stream->ops->ack)
274b21c60a4SVinod Koul 		stream->ops->ack(stream, count);
275b21c60a4SVinod Koul 	return count;
276b21c60a4SVinod Koul }
277b21c60a4SVinod Koul 
snd_compr_write(struct file * f,const char __user * buf,size_t count,loff_t * offset)278b21c60a4SVinod Koul static ssize_t snd_compr_write(struct file *f, const char __user *buf,
279b21c60a4SVinod Koul 		size_t count, loff_t *offset)
280b21c60a4SVinod Koul {
281b21c60a4SVinod Koul 	struct snd_compr_file *data = f->private_data;
282b21c60a4SVinod Koul 	struct snd_compr_stream *stream;
283b21c60a4SVinod Koul 	size_t avail;
284b21c60a4SVinod Koul 	int retval;
285b21c60a4SVinod Koul 
286b21c60a4SVinod Koul 	if (snd_BUG_ON(!data))
287b21c60a4SVinod Koul 		return -EFAULT;
288b21c60a4SVinod Koul 
289b21c60a4SVinod Koul 	stream = &data->stream;
290*d648843aSTakashi Iwai 	guard(mutex)(&stream->device->lock);
291b21c60a4SVinod Koul 	/* write is allowed when stream is running or has been steup */
292875f6fffSCharles Keepax 	switch (stream->runtime->state) {
293875f6fffSCharles Keepax 	case SNDRV_PCM_STATE_SETUP:
294875f6fffSCharles Keepax 	case SNDRV_PCM_STATE_PREPARED:
295875f6fffSCharles Keepax 	case SNDRV_PCM_STATE_RUNNING:
296875f6fffSCharles Keepax 		break;
297875f6fffSCharles Keepax 	default:
298b21c60a4SVinod Koul 		return -EBADFD;
299b21c60a4SVinod Koul 	}
300b21c60a4SVinod Koul 
301b21c60a4SVinod Koul 	avail = snd_compr_get_avail(stream);
302b21c60a4SVinod Koul 	pr_debug("avail returned %ld\n", (unsigned long)avail);
303b21c60a4SVinod Koul 	/* calculate how much we can write to buffer */
304b21c60a4SVinod Koul 	if (avail > count)
305b21c60a4SVinod Koul 		avail = count;
306b21c60a4SVinod Koul 
3074daf891cSCharles Keepax 	if (stream->ops->copy) {
3084daf891cSCharles Keepax 		char __user* cbuf = (char __user*)buf;
3094daf891cSCharles Keepax 		retval = stream->ops->copy(stream, cbuf, avail);
3104daf891cSCharles Keepax 	} else {
311b21c60a4SVinod Koul 		retval = snd_compr_write_data(stream, buf, avail);
3124daf891cSCharles Keepax 	}
313b21c60a4SVinod Koul 	if (retval > 0)
314b21c60a4SVinod Koul 		stream->runtime->total_bytes_available += retval;
315b21c60a4SVinod Koul 
316b21c60a4SVinod Koul 	/* while initiating the stream, write should be called before START
317b21c60a4SVinod Koul 	 * call, so in setup move state */
318b21c60a4SVinod Koul 	if (stream->runtime->state == SNDRV_PCM_STATE_SETUP) {
319b21c60a4SVinod Koul 		stream->runtime->state = SNDRV_PCM_STATE_PREPARED;
320b21c60a4SVinod Koul 		pr_debug("stream prepared, Houston we are good to go\n");
321b21c60a4SVinod Koul 	}
322b21c60a4SVinod Koul 
323b21c60a4SVinod Koul 	return retval;
324b21c60a4SVinod Koul }
325b21c60a4SVinod Koul 
326b21c60a4SVinod Koul 
snd_compr_read(struct file * f,char __user * buf,size_t count,loff_t * offset)327b21c60a4SVinod Koul static ssize_t snd_compr_read(struct file *f, char __user *buf,
328b21c60a4SVinod Koul 		size_t count, loff_t *offset)
329b21c60a4SVinod Koul {
33049bb6402SCharles Keepax 	struct snd_compr_file *data = f->private_data;
33149bb6402SCharles Keepax 	struct snd_compr_stream *stream;
33249bb6402SCharles Keepax 	size_t avail;
33349bb6402SCharles Keepax 	int retval;
33449bb6402SCharles Keepax 
33549bb6402SCharles Keepax 	if (snd_BUG_ON(!data))
33649bb6402SCharles Keepax 		return -EFAULT;
33749bb6402SCharles Keepax 
33849bb6402SCharles Keepax 	stream = &data->stream;
339*d648843aSTakashi Iwai 	guard(mutex)(&stream->device->lock);
34049bb6402SCharles Keepax 
34175481347SVinod Koul 	/* read is allowed when stream is running, paused, draining and setup
34275481347SVinod Koul 	 * (yes setup is state which we transition to after stop, so if user
34375481347SVinod Koul 	 * wants to read data after stop we allow that)
34475481347SVinod Koul 	 */
34575481347SVinod Koul 	switch (stream->runtime->state) {
34675481347SVinod Koul 	case SNDRV_PCM_STATE_OPEN:
34775481347SVinod Koul 	case SNDRV_PCM_STATE_PREPARED:
34875481347SVinod Koul 	case SNDRV_PCM_STATE_SUSPENDED:
34975481347SVinod Koul 	case SNDRV_PCM_STATE_DISCONNECTED:
350*d648843aSTakashi Iwai 		return -EBADFD;
351a4f2d87cSCharles Keepax 	case SNDRV_PCM_STATE_XRUN:
352*d648843aSTakashi Iwai 		return -EPIPE;
35349bb6402SCharles Keepax 	}
35449bb6402SCharles Keepax 
35549bb6402SCharles Keepax 	avail = snd_compr_get_avail(stream);
35649bb6402SCharles Keepax 	pr_debug("avail returned %ld\n", (unsigned long)avail);
35749bb6402SCharles Keepax 	/* calculate how much we can read from buffer */
35849bb6402SCharles Keepax 	if (avail > count)
35949bb6402SCharles Keepax 		avail = count;
36049bb6402SCharles Keepax 
361*d648843aSTakashi Iwai 	if (stream->ops->copy)
36249bb6402SCharles Keepax 		retval = stream->ops->copy(stream, buf, avail);
363*d648843aSTakashi Iwai 	else
364*d648843aSTakashi Iwai 		return -ENXIO;
36549bb6402SCharles Keepax 	if (retval > 0)
36649bb6402SCharles Keepax 		stream->runtime->total_bytes_transferred += retval;
36749bb6402SCharles Keepax 
36849bb6402SCharles Keepax 	return retval;
369b21c60a4SVinod Koul }
370b21c60a4SVinod Koul 
snd_compr_mmap(struct file * f,struct vm_area_struct * vma)371b21c60a4SVinod Koul static int snd_compr_mmap(struct file *f, struct vm_area_struct *vma)
372b21c60a4SVinod Koul {
373b21c60a4SVinod Koul 	return -ENXIO;
374b21c60a4SVinod Koul }
375b21c60a4SVinod Koul 
snd_compr_get_poll(struct snd_compr_stream * stream)376680ef72aSAl Viro static __poll_t snd_compr_get_poll(struct snd_compr_stream *stream)
377b21c60a4SVinod Koul {
378b21c60a4SVinod Koul 	if (stream->direction == SND_COMPRESS_PLAYBACK)
379a9a08845SLinus Torvalds 		return EPOLLOUT | EPOLLWRNORM;
380b21c60a4SVinod Koul 	else
381a9a08845SLinus Torvalds 		return EPOLLIN | EPOLLRDNORM;
382b21c60a4SVinod Koul }
383b21c60a4SVinod Koul 
snd_compr_poll(struct file * f,poll_table * wait)384680ef72aSAl Viro static __poll_t snd_compr_poll(struct file *f, poll_table *wait)
385b21c60a4SVinod Koul {
386b21c60a4SVinod Koul 	struct snd_compr_file *data = f->private_data;
387b21c60a4SVinod Koul 	struct snd_compr_stream *stream;
388b21c60a4SVinod Koul 	size_t avail;
389680ef72aSAl Viro 	__poll_t retval = 0;
390b21c60a4SVinod Koul 
391b21c60a4SVinod Koul 	if (snd_BUG_ON(!data))
392a9a08845SLinus Torvalds 		return EPOLLERR;
3935bd05390SCharles Keepax 
394b21c60a4SVinod Koul 	stream = &data->stream;
395b21c60a4SVinod Koul 
396*d648843aSTakashi Iwai 	guard(mutex)(&stream->device->lock);
397a4f2d87cSCharles Keepax 
398a4f2d87cSCharles Keepax 	switch (stream->runtime->state) {
399a4f2d87cSCharles Keepax 	case SNDRV_PCM_STATE_OPEN:
400a4f2d87cSCharles Keepax 	case SNDRV_PCM_STATE_XRUN:
401*d648843aSTakashi Iwai 		return snd_compr_get_poll(stream) | EPOLLERR;
402a4f2d87cSCharles Keepax 	default:
403a4f2d87cSCharles Keepax 		break;
404b21c60a4SVinod Koul 	}
405a4f2d87cSCharles Keepax 
406b21c60a4SVinod Koul 	poll_wait(f, &stream->runtime->sleep, wait);
407b21c60a4SVinod Koul 
408b21c60a4SVinod Koul 	avail = snd_compr_get_avail(stream);
409b21c60a4SVinod Koul 	pr_debug("avail is %ld\n", (unsigned long)avail);
410b21c60a4SVinod Koul 	/* check if we have at least one fragment to fill */
411b21c60a4SVinod Koul 	switch (stream->runtime->state) {
412b21c60a4SVinod Koul 	case SNDRV_PCM_STATE_DRAINING:
413b21c60a4SVinod Koul 		/* stream has been woken up after drain is complete
414b21c60a4SVinod Koul 		 * draining done so set stream state to stopped
415b21c60a4SVinod Koul 		 */
416b21c60a4SVinod Koul 		retval = snd_compr_get_poll(stream);
417b21c60a4SVinod Koul 		stream->runtime->state = SNDRV_PCM_STATE_SETUP;
418b21c60a4SVinod Koul 		break;
419b21c60a4SVinod Koul 	case SNDRV_PCM_STATE_RUNNING:
420b21c60a4SVinod Koul 	case SNDRV_PCM_STATE_PREPARED:
421b21c60a4SVinod Koul 	case SNDRV_PCM_STATE_PAUSED:
422b21c60a4SVinod Koul 		if (avail >= stream->runtime->fragment_size)
423b21c60a4SVinod Koul 			retval = snd_compr_get_poll(stream);
424b21c60a4SVinod Koul 		break;
425b21c60a4SVinod Koul 	default:
426*d648843aSTakashi Iwai 		return snd_compr_get_poll(stream) | EPOLLERR;
427b21c60a4SVinod Koul 	}
428*d648843aSTakashi Iwai 
429b21c60a4SVinod Koul 	return retval;
430b21c60a4SVinod Koul }
431b21c60a4SVinod Koul 
432b21c60a4SVinod Koul static int
snd_compr_get_caps(struct snd_compr_stream * stream,unsigned long arg)433b21c60a4SVinod Koul snd_compr_get_caps(struct snd_compr_stream *stream, unsigned long arg)
434b21c60a4SVinod Koul {
435b21c60a4SVinod Koul 	int retval;
436b21c60a4SVinod Koul 	struct snd_compr_caps caps;
437b21c60a4SVinod Koul 
438b21c60a4SVinod Koul 	if (!stream->ops->get_caps)
439b21c60a4SVinod Koul 		return -ENXIO;
440b21c60a4SVinod Koul 
4411c62e9f2SDan Carpenter 	memset(&caps, 0, sizeof(caps));
442b21c60a4SVinod Koul 	retval = stream->ops->get_caps(stream, &caps);
443b21c60a4SVinod Koul 	if (retval)
444b21c60a4SVinod Koul 		goto out;
445b21c60a4SVinod Koul 	if (copy_to_user((void __user *)arg, &caps, sizeof(caps)))
446b21c60a4SVinod Koul 		retval = -EFAULT;
447b21c60a4SVinod Koul out:
448b21c60a4SVinod Koul 	return retval;
449b21c60a4SVinod Koul }
450b21c60a4SVinod Koul 
451462b3f16STakashi Iwai #ifndef COMPR_CODEC_CAPS_OVERFLOW
452b21c60a4SVinod Koul static int
snd_compr_get_codec_caps(struct snd_compr_stream * stream,unsigned long arg)453b21c60a4SVinod Koul snd_compr_get_codec_caps(struct snd_compr_stream *stream, unsigned long arg)
454b21c60a4SVinod Koul {
455b21c60a4SVinod Koul 	int retval;
4569b022214STakashi Iwai 	struct snd_compr_codec_caps *caps __free(kfree) = NULL;
457b21c60a4SVinod Koul 
458b21c60a4SVinod Koul 	if (!stream->ops->get_codec_caps)
459b21c60a4SVinod Koul 		return -ENXIO;
460b21c60a4SVinod Koul 
46147966e97STakashi Iwai 	caps = kzalloc(sizeof(*caps), GFP_KERNEL);
462b21c60a4SVinod Koul 	if (!caps)
463b21c60a4SVinod Koul 		return -ENOMEM;
464b21c60a4SVinod Koul 
465b21c60a4SVinod Koul 	retval = stream->ops->get_codec_caps(stream, caps);
466b21c60a4SVinod Koul 	if (retval)
4679b022214STakashi Iwai 		return retval;
468b21c60a4SVinod Koul 	if (copy_to_user((void __user *)arg, caps, sizeof(*caps)))
4699b022214STakashi Iwai 		return -EFAULT;
470b21c60a4SVinod Koul 	return retval;
471b21c60a4SVinod Koul }
472462b3f16STakashi Iwai #endif /* !COMPR_CODEC_CAPS_OVERFLOW */
473b21c60a4SVinod Koul 
snd_compr_malloc_pages(struct snd_compr_stream * stream,size_t size)474b9759ef2SCezary Rojewski int snd_compr_malloc_pages(struct snd_compr_stream *stream, size_t size)
475b9759ef2SCezary Rojewski {
476b9759ef2SCezary Rojewski 	struct snd_dma_buffer *dmab;
477b9759ef2SCezary Rojewski 	int ret;
478b9759ef2SCezary Rojewski 
479b9759ef2SCezary Rojewski 	if (snd_BUG_ON(!(stream) || !(stream)->runtime))
480b9759ef2SCezary Rojewski 		return -EINVAL;
481b9759ef2SCezary Rojewski 	dmab = kzalloc(sizeof(*dmab), GFP_KERNEL);
482b9759ef2SCezary Rojewski 	if (!dmab)
483b9759ef2SCezary Rojewski 		return -ENOMEM;
484b9759ef2SCezary Rojewski 	dmab->dev = stream->dma_buffer.dev;
485b9759ef2SCezary Rojewski 	ret = snd_dma_alloc_pages(dmab->dev.type, dmab->dev.dev, size, dmab);
486b9759ef2SCezary Rojewski 	if (ret < 0) {
487b9759ef2SCezary Rojewski 		kfree(dmab);
488b9759ef2SCezary Rojewski 		return ret;
489b9759ef2SCezary Rojewski 	}
490b9759ef2SCezary Rojewski 
491b9759ef2SCezary Rojewski 	snd_compr_set_runtime_buffer(stream, dmab);
492b9759ef2SCezary Rojewski 	stream->runtime->dma_bytes = size;
493b9759ef2SCezary Rojewski 	return 1;
494b9759ef2SCezary Rojewski }
495b9759ef2SCezary Rojewski EXPORT_SYMBOL(snd_compr_malloc_pages);
496b9759ef2SCezary Rojewski 
snd_compr_free_pages(struct snd_compr_stream * stream)497b9759ef2SCezary Rojewski int snd_compr_free_pages(struct snd_compr_stream *stream)
498b9759ef2SCezary Rojewski {
499cd91fd9fSPierre-Louis Bossart 	struct snd_compr_runtime *runtime;
500b9759ef2SCezary Rojewski 
501b9759ef2SCezary Rojewski 	if (snd_BUG_ON(!(stream) || !(stream)->runtime))
502b9759ef2SCezary Rojewski 		return -EINVAL;
503cd91fd9fSPierre-Louis Bossart 	runtime = stream->runtime;
504b9759ef2SCezary Rojewski 	if (runtime->dma_area == NULL)
505b9759ef2SCezary Rojewski 		return 0;
506b9759ef2SCezary Rojewski 	if (runtime->dma_buffer_p != &stream->dma_buffer) {
507b9759ef2SCezary Rojewski 		/* It's a newly allocated buffer. Release it now. */
508b9759ef2SCezary Rojewski 		snd_dma_free_pages(runtime->dma_buffer_p);
509b9759ef2SCezary Rojewski 		kfree(runtime->dma_buffer_p);
510b9759ef2SCezary Rojewski 	}
511b9759ef2SCezary Rojewski 
512b9759ef2SCezary Rojewski 	snd_compr_set_runtime_buffer(stream, NULL);
513b9759ef2SCezary Rojewski 	return 0;
514b9759ef2SCezary Rojewski }
515b9759ef2SCezary Rojewski EXPORT_SYMBOL(snd_compr_free_pages);
516b9759ef2SCezary Rojewski 
517b21c60a4SVinod Koul /* revisit this with snd_pcm_preallocate_xxx */
snd_compr_allocate_buffer(struct snd_compr_stream * stream,struct snd_compr_params * params)518b21c60a4SVinod Koul static int snd_compr_allocate_buffer(struct snd_compr_stream *stream,
519b21c60a4SVinod Koul 		struct snd_compr_params *params)
520b21c60a4SVinod Koul {
521b21c60a4SVinod Koul 	unsigned int buffer_size;
522d00f749bSSrinivas Kandagatla 	void *buffer = NULL;
523b21c60a4SVinod Koul 
524b21c60a4SVinod Koul 	buffer_size = params->buffer.fragment_size * params->buffer.fragments;
525b21c60a4SVinod Koul 	if (stream->ops->copy) {
526b21c60a4SVinod Koul 		buffer = NULL;
527b21c60a4SVinod Koul 		/* if copy is defined the driver will be required to copy
528b21c60a4SVinod Koul 		 * the data from core
529b21c60a4SVinod Koul 		 */
530b21c60a4SVinod Koul 	} else {
531d00f749bSSrinivas Kandagatla 		if (stream->runtime->dma_buffer_p) {
532d00f749bSSrinivas Kandagatla 
533d00f749bSSrinivas Kandagatla 			if (buffer_size > stream->runtime->dma_buffer_p->bytes)
534b53a41eeSTakashi Iwai 				dev_err(stream->device->dev,
535d00f749bSSrinivas Kandagatla 						"Not enough DMA buffer");
536d00f749bSSrinivas Kandagatla 			else
537d00f749bSSrinivas Kandagatla 				buffer = stream->runtime->dma_buffer_p->area;
538d00f749bSSrinivas Kandagatla 
539d00f749bSSrinivas Kandagatla 		} else {
540b21c60a4SVinod Koul 			buffer = kmalloc(buffer_size, GFP_KERNEL);
541d00f749bSSrinivas Kandagatla 		}
542d00f749bSSrinivas Kandagatla 
543b21c60a4SVinod Koul 		if (!buffer)
544b21c60a4SVinod Koul 			return -ENOMEM;
545b21c60a4SVinod Koul 	}
546b21c60a4SVinod Koul 	stream->runtime->fragment_size = params->buffer.fragment_size;
547b21c60a4SVinod Koul 	stream->runtime->fragments = params->buffer.fragments;
548b21c60a4SVinod Koul 	stream->runtime->buffer = buffer;
549b21c60a4SVinod Koul 	stream->runtime->buffer_size = buffer_size;
550b21c60a4SVinod Koul 	return 0;
551b21c60a4SVinod Koul }
552b21c60a4SVinod Koul 
snd_compress_check_input(struct snd_compr_params * params)5534dc040a0SVinod Koul static int snd_compress_check_input(struct snd_compr_params *params)
5544dc040a0SVinod Koul {
5554dc040a0SVinod Koul 	/* first let's check the buffer parameter's */
5564dc040a0SVinod Koul 	if (params->buffer.fragment_size == 0 ||
557d3645b05SXiaojun Sang 	    params->buffer.fragments > U32_MAX / params->buffer.fragment_size ||
558678e2b44SDan Carpenter 	    params->buffer.fragments == 0)
5594dc040a0SVinod Koul 		return -EINVAL;
5604dc040a0SVinod Koul 
561fb4a9779SVinod Koul 	/* now codec parameters */
562fb4a9779SVinod Koul 	if (params->codec.id == 0 || params->codec.id > SND_AUDIOCODEC_MAX)
563fb4a9779SVinod Koul 		return -EINVAL;
564fb4a9779SVinod Koul 
565fb4a9779SVinod Koul 	if (params->codec.ch_in == 0 || params->codec.ch_out == 0)
566fb4a9779SVinod Koul 		return -EINVAL;
567fb4a9779SVinod Koul 
5684dc040a0SVinod Koul 	return 0;
5694dc040a0SVinod Koul }
5704dc040a0SVinod Koul 
571b21c60a4SVinod Koul static int
snd_compr_set_params(struct snd_compr_stream * stream,unsigned long arg)572b21c60a4SVinod Koul snd_compr_set_params(struct snd_compr_stream *stream, unsigned long arg)
573b21c60a4SVinod Koul {
5749b022214STakashi Iwai 	struct snd_compr_params *params __free(kfree) = NULL;
575b21c60a4SVinod Koul 	int retval;
576b21c60a4SVinod Koul 
5777ea9ee00SSrinivas Kandagatla 	if (stream->runtime->state == SNDRV_PCM_STATE_OPEN || stream->next_track) {
578b21c60a4SVinod Koul 		/*
579b21c60a4SVinod Koul 		 * we should allow parameter change only when stream has been
580b21c60a4SVinod Koul 		 * opened not in other cases
581b21c60a4SVinod Koul 		 */
582c2f14ba7SMarkus Elfring 		params = memdup_user((void __user *)arg, sizeof(*params));
583c2f14ba7SMarkus Elfring 		if (IS_ERR(params))
5849b022214STakashi Iwai 			return PTR_ERR(no_free_ptr(params));
5854dc040a0SVinod Koul 
5864dc040a0SVinod Koul 		retval = snd_compress_check_input(params);
5874dc040a0SVinod Koul 		if (retval)
5889b022214STakashi Iwai 			return retval;
5894dc040a0SVinod Koul 
590b21c60a4SVinod Koul 		retval = snd_compr_allocate_buffer(stream, params);
5919b022214STakashi Iwai 		if (retval)
5929b022214STakashi Iwai 			return -ENOMEM;
5934dc040a0SVinod Koul 
594b21c60a4SVinod Koul 		retval = stream->ops->set_params(stream, params);
595b21c60a4SVinod Koul 		if (retval)
5969b022214STakashi Iwai 			return retval;
59749bb6402SCharles Keepax 
5987ea9ee00SSrinivas Kandagatla 		if (stream->next_track)
5999b022214STakashi Iwai 			return retval;
6007ea9ee00SSrinivas Kandagatla 
6019727b490SJeeja KP 		stream->metadata_set = false;
6029727b490SJeeja KP 		stream->next_track = false;
60349bb6402SCharles Keepax 
60449bb6402SCharles Keepax 		stream->runtime->state = SNDRV_PCM_STATE_SETUP;
605769fab2aSJesper Juhl 	} else {
606b21c60a4SVinod Koul 		return -EPERM;
607769fab2aSJesper Juhl 	}
608b21c60a4SVinod Koul 	return retval;
609b21c60a4SVinod Koul }
610b21c60a4SVinod Koul 
611b21c60a4SVinod Koul static int
snd_compr_get_params(struct snd_compr_stream * stream,unsigned long arg)612b21c60a4SVinod Koul snd_compr_get_params(struct snd_compr_stream *stream, unsigned long arg)
613b21c60a4SVinod Koul {
6149b022214STakashi Iwai 	struct snd_codec *params __free(kfree) = NULL;
615b21c60a4SVinod Koul 	int retval;
616b21c60a4SVinod Koul 
617b21c60a4SVinod Koul 	if (!stream->ops->get_params)
618b21c60a4SVinod Koul 		return -EBADFD;
619b21c60a4SVinod Koul 
62047966e97STakashi Iwai 	params = kzalloc(sizeof(*params), GFP_KERNEL);
621b21c60a4SVinod Koul 	if (!params)
622b21c60a4SVinod Koul 		return -ENOMEM;
623b21c60a4SVinod Koul 	retval = stream->ops->get_params(stream, params);
624b21c60a4SVinod Koul 	if (retval)
6259b022214STakashi Iwai 		return retval;
626b21c60a4SVinod Koul 	if (copy_to_user((char __user *)arg, params, sizeof(*params)))
6279b022214STakashi Iwai 		return -EFAULT;
628b21c60a4SVinod Koul 	return retval;
629b21c60a4SVinod Koul }
630b21c60a4SVinod Koul 
6319727b490SJeeja KP static int
snd_compr_get_metadata(struct snd_compr_stream * stream,unsigned long arg)6329727b490SJeeja KP snd_compr_get_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->get_metadata)
6389727b490SJeeja KP 		return -ENXIO;
6399727b490SJeeja KP 
6409727b490SJeeja KP 	if (copy_from_user(&metadata, (void __user *)arg, sizeof(metadata)))
6419727b490SJeeja KP 		return -EFAULT;
6429727b490SJeeja KP 
6439727b490SJeeja KP 	retval = stream->ops->get_metadata(stream, &metadata);
6449727b490SJeeja KP 	if (retval != 0)
6459727b490SJeeja KP 		return retval;
6469727b490SJeeja KP 
6479727b490SJeeja KP 	if (copy_to_user((void __user *)arg, &metadata, sizeof(metadata)))
6489727b490SJeeja KP 		return -EFAULT;
6499727b490SJeeja KP 
6509727b490SJeeja KP 	return 0;
6519727b490SJeeja KP }
6529727b490SJeeja KP 
6539727b490SJeeja KP static int
snd_compr_set_metadata(struct snd_compr_stream * stream,unsigned long arg)6549727b490SJeeja KP snd_compr_set_metadata(struct snd_compr_stream *stream, unsigned long arg)
6559727b490SJeeja KP {
6569727b490SJeeja KP 	struct snd_compr_metadata metadata;
6579727b490SJeeja KP 	int retval;
6589727b490SJeeja KP 
6599727b490SJeeja KP 	if (!stream->ops->set_metadata)
6609727b490SJeeja KP 		return -ENXIO;
6619727b490SJeeja KP 	/*
6629727b490SJeeja KP 	* we should allow parameter change only when stream has been
6639727b490SJeeja KP 	* opened not in other cases
6649727b490SJeeja KP 	*/
6659727b490SJeeja KP 	if (copy_from_user(&metadata, (void __user *)arg, sizeof(metadata)))
6669727b490SJeeja KP 		return -EFAULT;
6679727b490SJeeja KP 
6689727b490SJeeja KP 	retval = stream->ops->set_metadata(stream, &metadata);
6699727b490SJeeja KP 	stream->metadata_set = true;
6709727b490SJeeja KP 
6719727b490SJeeja KP 	return retval;
6729727b490SJeeja KP }
6739727b490SJeeja KP 
674b21c60a4SVinod Koul static inline int
snd_compr_tstamp(struct snd_compr_stream * stream,unsigned long arg)675b21c60a4SVinod Koul snd_compr_tstamp(struct snd_compr_stream *stream, unsigned long arg)
676b21c60a4SVinod Koul {
67717ac8e5cSRichard Fitzgerald 	struct snd_compr_tstamp tstamp = {0};
67817ac8e5cSRichard Fitzgerald 	int ret;
679b21c60a4SVinod Koul 
68017ac8e5cSRichard Fitzgerald 	ret = snd_compr_update_tstamp(stream, &tstamp);
68117ac8e5cSRichard Fitzgerald 	if (ret == 0)
68217ac8e5cSRichard Fitzgerald 		ret = copy_to_user((struct snd_compr_tstamp __user *)arg,
683b21c60a4SVinod Koul 			&tstamp, sizeof(tstamp)) ? -EFAULT : 0;
68417ac8e5cSRichard Fitzgerald 	return ret;
685b21c60a4SVinod Koul }
686b21c60a4SVinod Koul 
snd_compr_pause(struct snd_compr_stream * stream)687b21c60a4SVinod Koul static int snd_compr_pause(struct snd_compr_stream *stream)
688b21c60a4SVinod Koul {
689b21c60a4SVinod Koul 	int retval;
690b21c60a4SVinod Koul 
6919be9f2d3SGyeongtaek Lee 	switch (stream->runtime->state) {
6929be9f2d3SGyeongtaek Lee 	case SNDRV_PCM_STATE_RUNNING:
693b21c60a4SVinod Koul 		retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_PAUSE_PUSH);
6946b18f793SVinod Koul 		if (!retval)
695b21c60a4SVinod Koul 			stream->runtime->state = SNDRV_PCM_STATE_PAUSED;
6969be9f2d3SGyeongtaek Lee 		break;
6979be9f2d3SGyeongtaek Lee 	case SNDRV_PCM_STATE_DRAINING:
6989be9f2d3SGyeongtaek Lee 		if (!stream->device->use_pause_in_draining)
6999be9f2d3SGyeongtaek Lee 			return -EPERM;
7009be9f2d3SGyeongtaek Lee 		retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_PAUSE_PUSH);
7019be9f2d3SGyeongtaek Lee 		if (!retval)
7029be9f2d3SGyeongtaek Lee 			stream->pause_in_draining = true;
7039be9f2d3SGyeongtaek Lee 		break;
7049be9f2d3SGyeongtaek Lee 	default:
7059be9f2d3SGyeongtaek Lee 		return -EPERM;
7069be9f2d3SGyeongtaek Lee 	}
707b21c60a4SVinod Koul 	return retval;
708b21c60a4SVinod Koul }
709b21c60a4SVinod Koul 
snd_compr_resume(struct snd_compr_stream * stream)710b21c60a4SVinod Koul static int snd_compr_resume(struct snd_compr_stream *stream)
711b21c60a4SVinod Koul {
712b21c60a4SVinod Koul 	int retval;
713b21c60a4SVinod Koul 
7149be9f2d3SGyeongtaek Lee 	switch (stream->runtime->state) {
7159be9f2d3SGyeongtaek Lee 	case SNDRV_PCM_STATE_PAUSED:
716b21c60a4SVinod Koul 		retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_PAUSE_RELEASE);
717b21c60a4SVinod Koul 		if (!retval)
718b21c60a4SVinod Koul 			stream->runtime->state = SNDRV_PCM_STATE_RUNNING;
7199be9f2d3SGyeongtaek Lee 		break;
7209be9f2d3SGyeongtaek Lee 	case SNDRV_PCM_STATE_DRAINING:
7219be9f2d3SGyeongtaek Lee 		if (!stream->pause_in_draining)
7229be9f2d3SGyeongtaek Lee 			return -EPERM;
7239be9f2d3SGyeongtaek Lee 		retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_PAUSE_RELEASE);
7249be9f2d3SGyeongtaek Lee 		if (!retval)
7259be9f2d3SGyeongtaek Lee 			stream->pause_in_draining = false;
7269be9f2d3SGyeongtaek Lee 		break;
7279be9f2d3SGyeongtaek Lee 	default:
7289be9f2d3SGyeongtaek Lee 		return -EPERM;
7299be9f2d3SGyeongtaek Lee 	}
730b21c60a4SVinod Koul 	return retval;
731b21c60a4SVinod Koul }
732b21c60a4SVinod Koul 
snd_compr_start(struct snd_compr_stream * stream)733b21c60a4SVinod Koul static int snd_compr_start(struct snd_compr_stream *stream)
734b21c60a4SVinod Koul {
735b21c60a4SVinod Koul 	int retval;
736b21c60a4SVinod Koul 
7374475f8c4SCharles Keepax 	switch (stream->runtime->state) {
7384475f8c4SCharles Keepax 	case SNDRV_PCM_STATE_SETUP:
7394475f8c4SCharles Keepax 		if (stream->direction != SND_COMPRESS_CAPTURE)
740b21c60a4SVinod Koul 			return -EPERM;
7414475f8c4SCharles Keepax 		break;
7424475f8c4SCharles Keepax 	case SNDRV_PCM_STATE_PREPARED:
7434475f8c4SCharles Keepax 		break;
7444475f8c4SCharles Keepax 	default:
7454475f8c4SCharles Keepax 		return -EPERM;
7464475f8c4SCharles Keepax 	}
7474475f8c4SCharles Keepax 
748b21c60a4SVinod Koul 	retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_START);
749b21c60a4SVinod Koul 	if (!retval)
750b21c60a4SVinod Koul 		stream->runtime->state = SNDRV_PCM_STATE_RUNNING;
751b21c60a4SVinod Koul 	return retval;
752b21c60a4SVinod Koul }
753b21c60a4SVinod Koul 
snd_compr_stop(struct snd_compr_stream * stream)754b21c60a4SVinod Koul static int snd_compr_stop(struct snd_compr_stream *stream)
755b21c60a4SVinod Koul {
756b21c60a4SVinod Koul 	int retval;
757b21c60a4SVinod Koul 
75826c3f154SCharles Keepax 	switch (stream->runtime->state) {
75926c3f154SCharles Keepax 	case SNDRV_PCM_STATE_OPEN:
76026c3f154SCharles Keepax 	case SNDRV_PCM_STATE_SETUP:
76126c3f154SCharles Keepax 	case SNDRV_PCM_STATE_PREPARED:
762b21c60a4SVinod Koul 		return -EPERM;
76326c3f154SCharles Keepax 	default:
76426c3f154SCharles Keepax 		break;
76526c3f154SCharles Keepax 	}
76626c3f154SCharles Keepax 
767b21c60a4SVinod Koul 	retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_STOP);
768b21c60a4SVinod Koul 	if (!retval) {
769f79a732aSVinod Koul 		/* clear flags and stop any drain wait */
770f79a732aSVinod Koul 		stream->partial_drain = false;
771f79a732aSVinod Koul 		stream->metadata_set = false;
7729be9f2d3SGyeongtaek Lee 		stream->pause_in_draining = false;
773917f4b5cSVinod Koul 		snd_compr_drain_notify(stream);
7748b21460aSVinod Koul 		stream->runtime->total_bytes_available = 0;
7758b21460aSVinod Koul 		stream->runtime->total_bytes_transferred = 0;
776b21c60a4SVinod Koul 	}
777b21c60a4SVinod Koul 	return retval;
778b21c60a4SVinod Koul }
779b21c60a4SVinod Koul 
error_delayed_work(struct work_struct * work)780a4f2d87cSCharles Keepax static void error_delayed_work(struct work_struct *work)
781a4f2d87cSCharles Keepax {
782a4f2d87cSCharles Keepax 	struct snd_compr_stream *stream;
783a4f2d87cSCharles Keepax 
784a4f2d87cSCharles Keepax 	stream = container_of(work, struct snd_compr_stream, error_work.work);
785a4f2d87cSCharles Keepax 
786*d648843aSTakashi Iwai 	guard(mutex)(&stream->device->lock);
787a4f2d87cSCharles Keepax 
788a4f2d87cSCharles Keepax 	stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_STOP);
789a4f2d87cSCharles Keepax 	wake_up(&stream->runtime->sleep);
790a4f2d87cSCharles Keepax }
791a4f2d87cSCharles Keepax 
792539311aaSTakashi Iwai /**
793a4f2d87cSCharles Keepax  * snd_compr_stop_error: Report a fatal error on a stream
794a4f2d87cSCharles Keepax  * @stream: pointer to stream
795a4f2d87cSCharles Keepax  * @state: state to transition the stream to
796a4f2d87cSCharles Keepax  *
797a4f2d87cSCharles Keepax  * Stop the stream and set its state.
798a4f2d87cSCharles Keepax  *
799a4f2d87cSCharles Keepax  * Should be called with compressed device lock held.
800b05d834eSTakashi Iwai  *
801b05d834eSTakashi Iwai  * Return: zero if successful, or a negative error code
802a4f2d87cSCharles Keepax  */
snd_compr_stop_error(struct snd_compr_stream * stream,snd_pcm_state_t state)803a4f2d87cSCharles Keepax int snd_compr_stop_error(struct snd_compr_stream *stream,
804a4f2d87cSCharles Keepax 			 snd_pcm_state_t state)
805a4f2d87cSCharles Keepax {
806a4f2d87cSCharles Keepax 	if (stream->runtime->state == state)
807a4f2d87cSCharles Keepax 		return 0;
808a4f2d87cSCharles Keepax 
809a4f2d87cSCharles Keepax 	stream->runtime->state = state;
810a4f2d87cSCharles Keepax 
811a4f2d87cSCharles Keepax 	pr_debug("Changing state to: %d\n", state);
812a4f2d87cSCharles Keepax 
813a4f2d87cSCharles Keepax 	queue_delayed_work(system_power_efficient_wq, &stream->error_work, 0);
814a4f2d87cSCharles Keepax 
815a4f2d87cSCharles Keepax 	return 0;
816a4f2d87cSCharles Keepax }
817a4f2d87cSCharles Keepax EXPORT_SYMBOL_GPL(snd_compr_stop_error);
818a4f2d87cSCharles Keepax 
snd_compress_wait_for_drain(struct snd_compr_stream * stream)819917f4b5cSVinod Koul static int snd_compress_wait_for_drain(struct snd_compr_stream *stream)
820917f4b5cSVinod Koul {
821f44f2a54SVinod Koul 	int ret;
822f44f2a54SVinod Koul 
823917f4b5cSVinod Koul 	/*
824917f4b5cSVinod Koul 	 * We are called with lock held. So drop the lock while we wait for
825cdb1ee3fSVinod Koul 	 * drain complete notification from the driver
826917f4b5cSVinod Koul 	 *
827917f4b5cSVinod Koul 	 * It is expected that driver will notify the drain completion and then
828917f4b5cSVinod Koul 	 * stream will be moved to SETUP state, even if draining resulted in an
829917f4b5cSVinod Koul 	 * error. We can trigger next track after this.
830917f4b5cSVinod Koul 	 */
831917f4b5cSVinod Koul 	stream->runtime->state = SNDRV_PCM_STATE_DRAINING;
832917f4b5cSVinod Koul 	mutex_unlock(&stream->device->lock);
833917f4b5cSVinod Koul 
834f44f2a54SVinod Koul 	/* we wait for drain to complete here, drain can return when
835f44f2a54SVinod Koul 	 * interruption occurred, wait returned error or success.
836f44f2a54SVinod Koul 	 * For the first two cases we don't do anything different here and
837f44f2a54SVinod Koul 	 * return after waking up
838f44f2a54SVinod Koul 	 */
839f44f2a54SVinod Koul 
840f44f2a54SVinod Koul 	ret = wait_event_interruptible(stream->runtime->sleep,
841f44f2a54SVinod Koul 			(stream->runtime->state != SNDRV_PCM_STATE_DRAINING));
842f44f2a54SVinod Koul 	if (ret == -ERESTARTSYS)
843c5a905d3SColin Ian King 		pr_debug("wait aborted by a signal\n");
844f44f2a54SVinod Koul 	else if (ret)
845f44f2a54SVinod Koul 		pr_debug("wait for drain failed with %d\n", ret);
846f44f2a54SVinod Koul 
847917f4b5cSVinod Koul 
848917f4b5cSVinod Koul 	wake_up(&stream->runtime->sleep);
849917f4b5cSVinod Koul 	mutex_lock(&stream->device->lock);
850917f4b5cSVinod Koul 
851f44f2a54SVinod Koul 	return ret;
852917f4b5cSVinod Koul }
853917f4b5cSVinod Koul 
snd_compr_drain(struct snd_compr_stream * stream)854b21c60a4SVinod Koul static int snd_compr_drain(struct snd_compr_stream *stream)
855b21c60a4SVinod Koul {
856b21c60a4SVinod Koul 	int retval;
857b21c60a4SVinod Koul 
85826c3f154SCharles Keepax 	switch (stream->runtime->state) {
85926c3f154SCharles Keepax 	case SNDRV_PCM_STATE_OPEN:
86026c3f154SCharles Keepax 	case SNDRV_PCM_STATE_SETUP:
86126c3f154SCharles Keepax 	case SNDRV_PCM_STATE_PREPARED:
8623b817994SCharles Keepax 	case SNDRV_PCM_STATE_PAUSED:
863b21c60a4SVinod Koul 		return -EPERM;
8643b817994SCharles Keepax 	case SNDRV_PCM_STATE_XRUN:
8653b817994SCharles Keepax 		return -EPIPE;
86626c3f154SCharles Keepax 	default:
86726c3f154SCharles Keepax 		break;
86826c3f154SCharles Keepax 	}
869917f4b5cSVinod Koul 
870b21c60a4SVinod Koul 	retval = stream->ops->trigger(stream, SND_COMPR_TRIGGER_DRAIN);
871917f4b5cSVinod Koul 	if (retval) {
872f44f2a54SVinod Koul 		pr_debug("SND_COMPR_TRIGGER_DRAIN failed %d\n", retval);
873b21c60a4SVinod Koul 		wake_up(&stream->runtime->sleep);
874917f4b5cSVinod Koul 		return retval;
875b21c60a4SVinod Koul 	}
876917f4b5cSVinod Koul 
877f44f2a54SVinod Koul 	return snd_compress_wait_for_drain(stream);
878b21c60a4SVinod Koul }
879b21c60a4SVinod Koul 
snd_compr_next_track(struct snd_compr_stream * stream)8809727b490SJeeja KP static int snd_compr_next_track(struct snd_compr_stream *stream)
8819727b490SJeeja KP {
8829727b490SJeeja KP 	int retval;
8839727b490SJeeja KP 
8849727b490SJeeja KP 	/* only a running stream can transition to next track */
8859727b490SJeeja KP 	if (stream->runtime->state != SNDRV_PCM_STATE_RUNNING)
8869727b490SJeeja KP 		return -EPERM;
8879727b490SJeeja KP 
888a70ab8a8SCharles Keepax 	/* next track doesn't have any meaning for capture streams */
889a70ab8a8SCharles Keepax 	if (stream->direction == SND_COMPRESS_CAPTURE)
890a70ab8a8SCharles Keepax 		return -EPERM;
891a70ab8a8SCharles Keepax 
892cdb1ee3fSVinod Koul 	/* you can signal next track if this is intended to be a gapless stream
8939727b490SJeeja KP 	 * and current track metadata is set
8949727b490SJeeja KP 	 */
8959727b490SJeeja KP 	if (stream->metadata_set == false)
8969727b490SJeeja KP 		return -EPERM;
8979727b490SJeeja KP 
8989727b490SJeeja KP 	retval = stream->ops->trigger(stream, SND_COMPR_TRIGGER_NEXT_TRACK);
8999727b490SJeeja KP 	if (retval != 0)
9009727b490SJeeja KP 		return retval;
9019727b490SJeeja KP 	stream->metadata_set = false;
9029727b490SJeeja KP 	stream->next_track = true;
9039727b490SJeeja KP 	return 0;
9049727b490SJeeja KP }
9059727b490SJeeja KP 
snd_compr_partial_drain(struct snd_compr_stream * stream)9069727b490SJeeja KP static int snd_compr_partial_drain(struct snd_compr_stream *stream)
9079727b490SJeeja KP {
9089727b490SJeeja KP 	int retval;
90926c3f154SCharles Keepax 
91026c3f154SCharles Keepax 	switch (stream->runtime->state) {
91126c3f154SCharles Keepax 	case SNDRV_PCM_STATE_OPEN:
91226c3f154SCharles Keepax 	case SNDRV_PCM_STATE_SETUP:
91326c3f154SCharles Keepax 	case SNDRV_PCM_STATE_PREPARED:
9143b817994SCharles Keepax 	case SNDRV_PCM_STATE_PAUSED:
9159727b490SJeeja KP 		return -EPERM;
9163b817994SCharles Keepax 	case SNDRV_PCM_STATE_XRUN:
9173b817994SCharles Keepax 		return -EPIPE;
91826c3f154SCharles Keepax 	default:
91926c3f154SCharles Keepax 		break;
92026c3f154SCharles Keepax 	}
92126c3f154SCharles Keepax 
922a70ab8a8SCharles Keepax 	/* partial drain doesn't have any meaning for capture streams */
923a70ab8a8SCharles Keepax 	if (stream->direction == SND_COMPRESS_CAPTURE)
924a70ab8a8SCharles Keepax 		return -EPERM;
925a70ab8a8SCharles Keepax 
9269727b490SJeeja KP 	/* stream can be drained only when next track has been signalled */
9279727b490SJeeja KP 	if (stream->next_track == false)
9289727b490SJeeja KP 		return -EPERM;
9299727b490SJeeja KP 
930f79a732aSVinod Koul 	stream->partial_drain = true;
9319727b490SJeeja KP 	retval = stream->ops->trigger(stream, SND_COMPR_TRIGGER_PARTIAL_DRAIN);
932917f4b5cSVinod Koul 	if (retval) {
933f44f2a54SVinod Koul 		pr_debug("Partial drain returned failure\n");
934917f4b5cSVinod Koul 		wake_up(&stream->runtime->sleep);
935917f4b5cSVinod Koul 		return retval;
936917f4b5cSVinod Koul 	}
9379727b490SJeeja KP 
9389727b490SJeeja KP 	stream->next_track = false;
939917f4b5cSVinod Koul 	return snd_compress_wait_for_drain(stream);
9409727b490SJeeja KP }
9419727b490SJeeja KP 
snd_compr_ioctl(struct file * f,unsigned int cmd,unsigned long arg)942b21c60a4SVinod Koul static long snd_compr_ioctl(struct file *f, unsigned int cmd, unsigned long arg)
943b21c60a4SVinod Koul {
944b21c60a4SVinod Koul 	struct snd_compr_file *data = f->private_data;
945b21c60a4SVinod Koul 	struct snd_compr_stream *stream;
946b21c60a4SVinod Koul 
947b21c60a4SVinod Koul 	if (snd_BUG_ON(!data))
948b21c60a4SVinod Koul 		return -EFAULT;
9495bd05390SCharles Keepax 
950b21c60a4SVinod Koul 	stream = &data->stream;
9515bd05390SCharles Keepax 
952*d648843aSTakashi Iwai 	guard(mutex)(&stream->device->lock);
953b21c60a4SVinod Koul 	switch (_IOC_NR(cmd)) {
954b21c60a4SVinod Koul 	case _IOC_NR(SNDRV_COMPRESS_IOCTL_VERSION):
955*d648843aSTakashi Iwai 		return put_user(SNDRV_COMPRESS_VERSION,
956b21c60a4SVinod Koul 				(int __user *)arg) ? -EFAULT : 0;
957b21c60a4SVinod Koul 	case _IOC_NR(SNDRV_COMPRESS_GET_CAPS):
958*d648843aSTakashi Iwai 		return snd_compr_get_caps(stream, arg);
959462b3f16STakashi Iwai #ifndef COMPR_CODEC_CAPS_OVERFLOW
960b21c60a4SVinod Koul 	case _IOC_NR(SNDRV_COMPRESS_GET_CODEC_CAPS):
961*d648843aSTakashi Iwai 		return snd_compr_get_codec_caps(stream, arg);
962462b3f16STakashi Iwai #endif
963b21c60a4SVinod Koul 	case _IOC_NR(SNDRV_COMPRESS_SET_PARAMS):
964*d648843aSTakashi Iwai 		return snd_compr_set_params(stream, arg);
965b21c60a4SVinod Koul 	case _IOC_NR(SNDRV_COMPRESS_GET_PARAMS):
966*d648843aSTakashi Iwai 		return snd_compr_get_params(stream, arg);
9679727b490SJeeja KP 	case _IOC_NR(SNDRV_COMPRESS_SET_METADATA):
968*d648843aSTakashi Iwai 		return snd_compr_set_metadata(stream, arg);
9699727b490SJeeja KP 	case _IOC_NR(SNDRV_COMPRESS_GET_METADATA):
970*d648843aSTakashi Iwai 		return snd_compr_get_metadata(stream, arg);
971b21c60a4SVinod Koul 	case _IOC_NR(SNDRV_COMPRESS_TSTAMP):
972*d648843aSTakashi Iwai 		return snd_compr_tstamp(stream, arg);
973b21c60a4SVinod Koul 	case _IOC_NR(SNDRV_COMPRESS_AVAIL):
974*d648843aSTakashi Iwai 		return snd_compr_ioctl_avail(stream, arg);
975b21c60a4SVinod Koul 	case _IOC_NR(SNDRV_COMPRESS_PAUSE):
976*d648843aSTakashi Iwai 		return snd_compr_pause(stream);
977b21c60a4SVinod Koul 	case _IOC_NR(SNDRV_COMPRESS_RESUME):
978*d648843aSTakashi Iwai 		return snd_compr_resume(stream);
979b21c60a4SVinod Koul 	case _IOC_NR(SNDRV_COMPRESS_START):
980*d648843aSTakashi Iwai 		return snd_compr_start(stream);
981b21c60a4SVinod Koul 	case _IOC_NR(SNDRV_COMPRESS_STOP):
982*d648843aSTakashi Iwai 		return snd_compr_stop(stream);
983b21c60a4SVinod Koul 	case _IOC_NR(SNDRV_COMPRESS_DRAIN):
984*d648843aSTakashi Iwai 		return snd_compr_drain(stream);
9859727b490SJeeja KP 	case _IOC_NR(SNDRV_COMPRESS_PARTIAL_DRAIN):
986*d648843aSTakashi Iwai 		return snd_compr_partial_drain(stream);
9879727b490SJeeja KP 	case _IOC_NR(SNDRV_COMPRESS_NEXT_TRACK):
988*d648843aSTakashi Iwai 		return snd_compr_next_track(stream);
989b21c60a4SVinod Koul 	}
990*d648843aSTakashi Iwai 
991*d648843aSTakashi Iwai 	return -ENOTTY;
992b21c60a4SVinod Koul }
993b21c60a4SVinod Koul 
994c1036889SRavindra Lokhande /* support of 32bit userspace on 64bit platforms */
995c1036889SRavindra Lokhande #ifdef CONFIG_COMPAT
snd_compr_ioctl_compat(struct file * file,unsigned int cmd,unsigned long arg)996c1036889SRavindra Lokhande static long snd_compr_ioctl_compat(struct file *file, unsigned int cmd,
997c1036889SRavindra Lokhande 						unsigned long arg)
998c1036889SRavindra Lokhande {
999c1036889SRavindra Lokhande 	return snd_compr_ioctl(file, cmd, (unsigned long)compat_ptr(arg));
1000c1036889SRavindra Lokhande }
1001c1036889SRavindra Lokhande #endif
1002c1036889SRavindra Lokhande 
1003b21c60a4SVinod Koul static const struct file_operations snd_compr_file_ops = {
1004b21c60a4SVinod Koul 		.owner =	THIS_MODULE,
1005b21c60a4SVinod Koul 		.open =		snd_compr_open,
1006b21c60a4SVinod Koul 		.release =	snd_compr_free,
1007b21c60a4SVinod Koul 		.write =	snd_compr_write,
1008b21c60a4SVinod Koul 		.read =		snd_compr_read,
1009b21c60a4SVinod Koul 		.unlocked_ioctl = snd_compr_ioctl,
1010c1036889SRavindra Lokhande #ifdef CONFIG_COMPAT
1011c1036889SRavindra Lokhande 		.compat_ioctl = snd_compr_ioctl_compat,
1012c1036889SRavindra Lokhande #endif
1013b21c60a4SVinod Koul 		.mmap =		snd_compr_mmap,
1014b21c60a4SVinod Koul 		.poll =		snd_compr_poll,
1015b21c60a4SVinod Koul };
1016b21c60a4SVinod Koul 
snd_compress_dev_register(struct snd_device * device)1017b21c60a4SVinod Koul static int snd_compress_dev_register(struct snd_device *device)
1018b21c60a4SVinod Koul {
10199725ce39SPierre-Louis Bossart 	int ret;
1020b21c60a4SVinod Koul 	struct snd_compr *compr;
1021b21c60a4SVinod Koul 
1022b21c60a4SVinod Koul 	if (snd_BUG_ON(!device || !device->device_data))
1023b21c60a4SVinod Koul 		return -EBADFD;
1024b21c60a4SVinod Koul 	compr = device->device_data;
1025b21c60a4SVinod Koul 
1026a931b9ceSGuneshwor Singh 	pr_debug("reg device %s, direction %d\n", compr->name,
1027b21c60a4SVinod Koul 			compr->direction);
1028b21c60a4SVinod Koul 	/* register compressed device */
102940a4b263STakashi Iwai 	ret = snd_register_device(SNDRV_DEVICE_TYPE_COMPRESS,
103004c5d5a4STakashi Iwai 				  compr->card, compr->device,
1031b53a41eeSTakashi Iwai 				  &snd_compr_file_ops, compr, compr->dev);
1032b21c60a4SVinod Koul 	if (ret < 0) {
1033c5a905d3SColin Ian King 		pr_err("snd_register_device failed %d\n", ret);
1034b21c60a4SVinod Koul 		return ret;
1035b21c60a4SVinod Koul 	}
1036b21c60a4SVinod Koul 	return ret;
1037b21c60a4SVinod Koul 
1038b21c60a4SVinod Koul }
1039b21c60a4SVinod Koul 
snd_compress_dev_disconnect(struct snd_device * device)1040b21c60a4SVinod Koul static int snd_compress_dev_disconnect(struct snd_device *device)
1041b21c60a4SVinod Koul {
1042b21c60a4SVinod Koul 	struct snd_compr *compr;
1043b21c60a4SVinod Koul 
1044b21c60a4SVinod Koul 	compr = device->device_data;
1045b53a41eeSTakashi Iwai 	snd_unregister_device(compr->dev);
1046b21c60a4SVinod Koul 	return 0;
1047b21c60a4SVinod Koul }
1048b21c60a4SVinod Koul 
104931742724SRichard Fitzgerald #ifdef CONFIG_SND_VERBOSE_PROCFS
snd_compress_proc_info_read(struct snd_info_entry * entry,struct snd_info_buffer * buffer)105031742724SRichard Fitzgerald static void snd_compress_proc_info_read(struct snd_info_entry *entry,
105131742724SRichard Fitzgerald 					struct snd_info_buffer *buffer)
105231742724SRichard Fitzgerald {
105331742724SRichard Fitzgerald 	struct snd_compr *compr = (struct snd_compr *)entry->private_data;
105431742724SRichard Fitzgerald 
105531742724SRichard Fitzgerald 	snd_iprintf(buffer, "card: %d\n", compr->card->number);
105631742724SRichard Fitzgerald 	snd_iprintf(buffer, "device: %d\n", compr->device);
105731742724SRichard Fitzgerald 	snd_iprintf(buffer, "stream: %s\n",
105831742724SRichard Fitzgerald 			compr->direction == SND_COMPRESS_PLAYBACK
105931742724SRichard Fitzgerald 				? "PLAYBACK" : "CAPTURE");
106031742724SRichard Fitzgerald 	snd_iprintf(buffer, "id: %s\n", compr->id);
106131742724SRichard Fitzgerald }
106231742724SRichard Fitzgerald 
snd_compress_proc_init(struct snd_compr * compr)106331742724SRichard Fitzgerald static int snd_compress_proc_init(struct snd_compr *compr)
106431742724SRichard Fitzgerald {
106531742724SRichard Fitzgerald 	struct snd_info_entry *entry;
106631742724SRichard Fitzgerald 	char name[16];
106731742724SRichard Fitzgerald 
106831742724SRichard Fitzgerald 	sprintf(name, "compr%i", compr->device);
106931742724SRichard Fitzgerald 	entry = snd_info_create_card_entry(compr->card, name,
107031742724SRichard Fitzgerald 					   compr->card->proc_root);
107131742724SRichard Fitzgerald 	if (!entry)
107231742724SRichard Fitzgerald 		return -ENOMEM;
10736a73cf46SJoe Perches 	entry->mode = S_IFDIR | 0555;
107431742724SRichard Fitzgerald 	compr->proc_root = entry;
107531742724SRichard Fitzgerald 
107631742724SRichard Fitzgerald 	entry = snd_info_create_card_entry(compr->card, "info",
107731742724SRichard Fitzgerald 					   compr->proc_root);
10784a471d7cSTakashi Iwai 	if (entry)
107931742724SRichard Fitzgerald 		snd_info_set_text_ops(entry, compr,
108031742724SRichard Fitzgerald 				      snd_compress_proc_info_read);
108131742724SRichard Fitzgerald 	compr->proc_info_entry = entry;
108231742724SRichard Fitzgerald 
108331742724SRichard Fitzgerald 	return 0;
108431742724SRichard Fitzgerald }
108531742724SRichard Fitzgerald 
snd_compress_proc_done(struct snd_compr * compr)108631742724SRichard Fitzgerald static void snd_compress_proc_done(struct snd_compr *compr)
108731742724SRichard Fitzgerald {
108831742724SRichard Fitzgerald 	snd_info_free_entry(compr->proc_info_entry);
108931742724SRichard Fitzgerald 	compr->proc_info_entry = NULL;
109031742724SRichard Fitzgerald 	snd_info_free_entry(compr->proc_root);
109131742724SRichard Fitzgerald 	compr->proc_root = NULL;
109231742724SRichard Fitzgerald }
1093e5241a8cSRichard Fitzgerald 
snd_compress_set_id(struct snd_compr * compr,const char * id)1094e5241a8cSRichard Fitzgerald static inline void snd_compress_set_id(struct snd_compr *compr, const char *id)
1095e5241a8cSRichard Fitzgerald {
109675b1a8f9SJoe Perches 	strscpy(compr->id, id, sizeof(compr->id));
1097e5241a8cSRichard Fitzgerald }
109831742724SRichard Fitzgerald #else
snd_compress_proc_init(struct snd_compr * compr)109931742724SRichard Fitzgerald static inline int snd_compress_proc_init(struct snd_compr *compr)
110031742724SRichard Fitzgerald {
110131742724SRichard Fitzgerald 	return 0;
110231742724SRichard Fitzgerald }
110331742724SRichard Fitzgerald 
snd_compress_proc_done(struct snd_compr * compr)110431742724SRichard Fitzgerald static inline void snd_compress_proc_done(struct snd_compr *compr)
110531742724SRichard Fitzgerald {
110631742724SRichard Fitzgerald }
1107e5241a8cSRichard Fitzgerald 
snd_compress_set_id(struct snd_compr * compr,const char * id)1108e5241a8cSRichard Fitzgerald static inline void snd_compress_set_id(struct snd_compr *compr, const char *id)
1109e5241a8cSRichard Fitzgerald {
1110e5241a8cSRichard Fitzgerald }
111131742724SRichard Fitzgerald #endif
111231742724SRichard Fitzgerald 
snd_compress_dev_free(struct snd_device * device)111304c5d5a4STakashi Iwai static int snd_compress_dev_free(struct snd_device *device)
111404c5d5a4STakashi Iwai {
111504c5d5a4STakashi Iwai 	struct snd_compr *compr;
111604c5d5a4STakashi Iwai 
111704c5d5a4STakashi Iwai 	compr = device->device_data;
111831742724SRichard Fitzgerald 	snd_compress_proc_done(compr);
1119b53a41eeSTakashi Iwai 	put_device(compr->dev);
112004c5d5a4STakashi Iwai 	return 0;
112104c5d5a4STakashi Iwai }
112204c5d5a4STakashi Iwai 
1123539311aaSTakashi Iwai /**
1124b21c60a4SVinod Koul  * snd_compress_new: create new compress device
1125b21c60a4SVinod Koul  * @card: sound card pointer
1126b21c60a4SVinod Koul  * @device: device number
1127b21c60a4SVinod Koul  * @dirn: device direction, should be of type enum snd_compr_direction
1128539311aaSTakashi Iwai  * @id: ID string
1129b21c60a4SVinod Koul  * @compr: compress device pointer
1130b05d834eSTakashi Iwai  *
1131b05d834eSTakashi Iwai  * Return: zero if successful, or a negative error code
1132b21c60a4SVinod Koul  */
snd_compress_new(struct snd_card * card,int device,int dirn,const char * id,struct snd_compr * compr)1133b21c60a4SVinod Koul int snd_compress_new(struct snd_card *card, int device,
1134e5241a8cSRichard Fitzgerald 			int dirn, const char *id, struct snd_compr *compr)
1135b21c60a4SVinod Koul {
1136f15ee210STakashi Iwai 	static const struct snd_device_ops ops = {
113704c5d5a4STakashi Iwai 		.dev_free = snd_compress_dev_free,
1138b21c60a4SVinod Koul 		.dev_register = snd_compress_dev_register,
1139b21c60a4SVinod Koul 		.dev_disconnect = snd_compress_dev_disconnect,
1140b21c60a4SVinod Koul 	};
114131742724SRichard Fitzgerald 	int ret;
1142b21c60a4SVinod Koul 
1143b21c60a4SVinod Koul 	compr->card = card;
1144b21c60a4SVinod Koul 	compr->device = device;
1145b21c60a4SVinod Koul 	compr->direction = dirn;
11462c4e3154STakashi Iwai 	mutex_init(&compr->lock);
114704c5d5a4STakashi Iwai 
1148e5241a8cSRichard Fitzgerald 	snd_compress_set_id(compr, id);
1149e5241a8cSRichard Fitzgerald 
1150b53a41eeSTakashi Iwai 	ret = snd_device_alloc(&compr->dev, card);
1151b53a41eeSTakashi Iwai 	if (ret)
1152b53a41eeSTakashi Iwai 		return ret;
1153b53a41eeSTakashi Iwai 	dev_set_name(compr->dev, "comprC%iD%i", card->number, device);
115404c5d5a4STakashi Iwai 
115531742724SRichard Fitzgerald 	ret = snd_device_new(card, SNDRV_DEV_COMPRESS, compr, &ops);
115631742724SRichard Fitzgerald 	if (ret == 0)
115731742724SRichard Fitzgerald 		snd_compress_proc_init(compr);
1158b53a41eeSTakashi Iwai 	else
1159b53a41eeSTakashi Iwai 		put_device(compr->dev);
116031742724SRichard Fitzgerald 
116131742724SRichard Fitzgerald 	return ret;
1162b21c60a4SVinod Koul }
1163b21c60a4SVinod Koul EXPORT_SYMBOL_GPL(snd_compress_new);
1164b21c60a4SVinod Koul 
1165b21c60a4SVinod Koul MODULE_DESCRIPTION("ALSA Compressed offload framework");
1166b21c60a4SVinod Koul MODULE_AUTHOR("Vinod Koul <vinod.koul@linux.intel.com>");
1167b21c60a4SVinod Koul MODULE_LICENSE("GPL v2");
1168