blob: bff9369a58a311be7dd1f081ec970b50df281988 [file] [log] [blame]
Vinod Koulb21c60a2011-12-23 10:36:39 +05301/*
2 * compress_core.c - compress offload core
3 *
4 * Copyright (C) 2011 Intel Corporation
5 * Authors: Vinod Koul <vinod.koul@linux.intel.com>
6 * Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
7 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; version 2 of the License.
12 *
13 * This program is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License along
19 * with this program; if not, write to the Free Software Foundation, Inc.,
20 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
21 *
22 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
23 *
24 */
25#define FORMAT(fmt) "%s: %d: " fmt, __func__, __LINE__
26#define pr_fmt(fmt) KBUILD_MODNAME ": " FORMAT(fmt)
27
28#include <linux/file.h>
29#include <linux/fs.h>
30#include <linux/list.h>
Charles Keepax91075e92013-04-18 11:03:46 +010031#include <linux/math64.h>
Vinod Koulb21c60a2011-12-23 10:36:39 +053032#include <linux/mm.h>
33#include <linux/mutex.h>
34#include <linux/poll.h>
35#include <linux/slab.h>
36#include <linux/sched.h>
Charles Keepax91075e92013-04-18 11:03:46 +010037#include <linux/types.h>
Vinod Koulb21c60a2011-12-23 10:36:39 +053038#include <linux/uio.h>
39#include <linux/uaccess.h>
40#include <linux/module.h>
41#include <sound/core.h>
42#include <sound/initval.h>
43#include <sound/compress_params.h>
44#include <sound/compress_offload.h>
45#include <sound/compress_driver.h>
46
47/* TODO:
48 * - add substream support for multiple devices in case of
49 * SND_DYNAMIC_MINORS is not used
50 * - Multiple node representation
51 * driver should be able to register multiple nodes
52 */
53
54static DEFINE_MUTEX(device_mutex);
55
56struct snd_compr_file {
57 unsigned long caps;
58 struct snd_compr_stream stream;
59};
60
61/*
62 * a note on stream states used:
63 * we use follwing states in the compressed core
64 * SNDRV_PCM_STATE_OPEN: When stream has been opened.
65 * SNDRV_PCM_STATE_SETUP: When stream has been initialized. This is done by
66 * calling SNDRV_COMPRESS_SET_PARAMS. running streams will come to this
67 * state at stop by calling SNDRV_COMPRESS_STOP, or at end of drain.
68 * SNDRV_PCM_STATE_RUNNING: When stream has been started and is
69 * decoding/encoding and rendering/capturing data.
70 * SNDRV_PCM_STATE_DRAINING: When stream is draining current data. This is done
71 * by calling SNDRV_COMPRESS_DRAIN.
72 * SNDRV_PCM_STATE_PAUSED: When stream is paused. This is done by calling
73 * SNDRV_COMPRESS_PAUSE. It can be stopped or resumed by calling
74 * SNDRV_COMPRESS_STOP or SNDRV_COMPRESS_RESUME respectively.
75 */
76static int snd_compr_open(struct inode *inode, struct file *f)
77{
78 struct snd_compr *compr;
79 struct snd_compr_file *data;
80 struct snd_compr_runtime *runtime;
81 enum snd_compr_direction dirn;
82 int maj = imajor(inode);
83 int ret;
84
Dan Carpenterb3602d22012-09-11 14:12:43 +030085 if ((f->f_flags & O_ACCMODE) == O_WRONLY)
Vinod Koulb21c60a2011-12-23 10:36:39 +053086 dirn = SND_COMPRESS_PLAYBACK;
Dan Carpenterb3602d22012-09-11 14:12:43 +030087 else if ((f->f_flags & O_ACCMODE) == O_RDONLY)
Vinod Koulb21c60a2011-12-23 10:36:39 +053088 dirn = SND_COMPRESS_CAPTURE;
Dan Carpenterb3602d22012-09-11 14:12:43 +030089 else
Vinod Koulb21c60a2011-12-23 10:36:39 +053090 return -EINVAL;
Vinod Koulb21c60a2011-12-23 10:36:39 +053091
92 if (maj == snd_major)
93 compr = snd_lookup_minor_data(iminor(inode),
94 SNDRV_DEVICE_TYPE_COMPRESS);
95 else
96 return -EBADFD;
97
98 if (compr == NULL) {
99 pr_err("no device data!!!\n");
100 return -ENODEV;
101 }
102
103 if (dirn != compr->direction) {
104 pr_err("this device doesn't support this direction\n");
105 return -EINVAL;
106 }
107
108 data = kzalloc(sizeof(*data), GFP_KERNEL);
109 if (!data)
110 return -ENOMEM;
111 data->stream.ops = compr->ops;
112 data->stream.direction = dirn;
113 data->stream.private_data = compr->private_data;
114 data->stream.device = compr;
115 runtime = kzalloc(sizeof(*runtime), GFP_KERNEL);
116 if (!runtime) {
117 kfree(data);
118 return -ENOMEM;
119 }
120 runtime->state = SNDRV_PCM_STATE_OPEN;
121 init_waitqueue_head(&runtime->sleep);
122 data->stream.runtime = runtime;
123 f->private_data = (void *)data;
124 mutex_lock(&compr->lock);
125 ret = compr->ops->open(&data->stream);
126 mutex_unlock(&compr->lock);
127 if (ret) {
128 kfree(runtime);
129 kfree(data);
130 }
131 return ret;
132}
133
134static int snd_compr_free(struct inode *inode, struct file *f)
135{
136 struct snd_compr_file *data = f->private_data;
Liam Girdwood8cb83072013-06-07 18:24:28 +0100137 struct snd_compr_runtime *runtime = data->stream.runtime;
138
139 switch (runtime->state) {
140 case SNDRV_PCM_STATE_RUNNING:
141 case SNDRV_PCM_STATE_DRAINING:
142 case SNDRV_PCM_STATE_PAUSED:
143 data->stream.ops->trigger(&data->stream, SNDRV_PCM_TRIGGER_STOP);
144 break;
145 default:
146 break;
147 }
148
Vinod Koulb21c60a2011-12-23 10:36:39 +0530149 data->stream.ops->free(&data->stream);
150 kfree(data->stream.runtime->buffer);
151 kfree(data->stream.runtime);
152 kfree(data);
153 return 0;
154}
155
Richard Fitzgerald291f8162013-02-11 13:44:53 +0000156static int snd_compr_update_tstamp(struct snd_compr_stream *stream,
Vinod Koulb21c60a2011-12-23 10:36:39 +0530157 struct snd_compr_tstamp *tstamp)
158{
Aviral Gupta38e1eaa2014-06-20 10:47:16 +0530159 int err = 0;
Vinod Koulb21c60a2011-12-23 10:36:39 +0530160 if (!stream->ops->pointer)
Richard Fitzgerald291f8162013-02-11 13:44:53 +0000161 return -ENOTSUPP;
Aviral Gupta38e1eaa2014-06-20 10:47:16 +0530162 err = stream->ops->pointer(stream, tstamp);
163 if (err)
164 return err;
Vinod Koulb21c60a2011-12-23 10:36:39 +0530165 pr_debug("dsp consumed till %d total %d bytes\n",
166 tstamp->byte_offset, tstamp->copied_total);
Charles Keepaxcbce8462013-04-18 11:01:03 +0100167 if (stream->direction == SND_COMPRESS_PLAYBACK)
168 stream->runtime->total_bytes_transferred = tstamp->copied_total;
169 else
170 stream->runtime->total_bytes_available = tstamp->copied_total;
Richard Fitzgerald291f8162013-02-11 13:44:53 +0000171 return 0;
Vinod Koulb21c60a2011-12-23 10:36:39 +0530172}
173
174static size_t snd_compr_calc_avail(struct snd_compr_stream *stream,
175 struct snd_compr_avail *avail)
176{
Richard Fitzgerald291f8162013-02-11 13:44:53 +0000177 memset(avail, 0, sizeof(*avail));
Vinod Koulb21c60a2011-12-23 10:36:39 +0530178 snd_compr_update_tstamp(stream, &avail->tstamp);
Richard Fitzgerald291f8162013-02-11 13:44:53 +0000179 /* Still need to return avail even if tstamp can't be filled in */
Vinod Koulb21c60a2011-12-23 10:36:39 +0530180
Vinod Koulb21c60a2011-12-23 10:36:39 +0530181 if (stream->runtime->total_bytes_available == 0 &&
Charles Keepaxcbce8462013-04-18 11:01:03 +0100182 stream->runtime->state == SNDRV_PCM_STATE_SETUP &&
183 stream->direction == SND_COMPRESS_PLAYBACK) {
Vinod Koulb21c60a2011-12-23 10:36:39 +0530184 pr_debug("detected init and someone forgot to do a write\n");
185 return stream->runtime->buffer_size;
186 }
187 pr_debug("app wrote %lld, DSP consumed %lld\n",
188 stream->runtime->total_bytes_available,
189 stream->runtime->total_bytes_transferred);
190 if (stream->runtime->total_bytes_available ==
191 stream->runtime->total_bytes_transferred) {
Charles Keepaxcbce8462013-04-18 11:01:03 +0100192 if (stream->direction == SND_COMPRESS_PLAYBACK) {
193 pr_debug("both pointers are same, returning full avail\n");
194 return stream->runtime->buffer_size;
195 } else {
196 pr_debug("both pointers are same, returning no avail\n");
197 return 0;
198 }
Vinod Koulb21c60a2011-12-23 10:36:39 +0530199 }
200
Charles Keepaxcbce8462013-04-18 11:01:03 +0100201 avail->avail = stream->runtime->total_bytes_available -
202 stream->runtime->total_bytes_transferred;
203 if (stream->direction == SND_COMPRESS_PLAYBACK)
204 avail->avail = stream->runtime->buffer_size - avail->avail;
205
Charles Keepax96309cd2013-04-18 10:59:23 +0100206 pr_debug("ret avail as %lld\n", avail->avail);
207 return avail->avail;
Vinod Koulb21c60a2011-12-23 10:36:39 +0530208}
209
210static inline size_t snd_compr_get_avail(struct snd_compr_stream *stream)
211{
212 struct snd_compr_avail avail;
213
214 return snd_compr_calc_avail(stream, &avail);
215}
216
217static int
218snd_compr_ioctl_avail(struct snd_compr_stream *stream, unsigned long arg)
219{
220 struct snd_compr_avail ioctl_avail;
221 size_t avail;
222
223 avail = snd_compr_calc_avail(stream, &ioctl_avail);
224 ioctl_avail.avail = avail;
225
226 if (copy_to_user((__u64 __user *)arg,
227 &ioctl_avail, sizeof(ioctl_avail)))
228 return -EFAULT;
229 return 0;
230}
231
232static int snd_compr_write_data(struct snd_compr_stream *stream,
233 const char __user *buf, size_t count)
234{
235 void *dstn;
236 size_t copy;
237 struct snd_compr_runtime *runtime = stream->runtime;
Charles Keepax91075e92013-04-18 11:03:46 +0100238 /* 64-bit Modulus */
239 u64 app_pointer = div64_u64(runtime->total_bytes_available,
240 runtime->buffer_size);
241 app_pointer = runtime->total_bytes_available -
242 (app_pointer * runtime->buffer_size);
Vinod Koulb21c60a2011-12-23 10:36:39 +0530243
Charles Keepax91075e92013-04-18 11:03:46 +0100244 dstn = runtime->buffer + app_pointer;
Damir Didjusto76816cc2014-01-14 01:32:00 +0100245 pr_debug("copying %zu at %lld\n",
246 count, app_pointer);
Charles Keepax91075e92013-04-18 11:03:46 +0100247 if (count < runtime->buffer_size - app_pointer) {
Vinod Koulb21c60a2011-12-23 10:36:39 +0530248 if (copy_from_user(dstn, buf, count))
249 return -EFAULT;
Vinod Koulb21c60a2011-12-23 10:36:39 +0530250 } else {
Charles Keepax91075e92013-04-18 11:03:46 +0100251 copy = runtime->buffer_size - app_pointer;
Vinod Koulb21c60a2011-12-23 10:36:39 +0530252 if (copy_from_user(dstn, buf, copy))
253 return -EFAULT;
254 if (copy_from_user(runtime->buffer, buf + copy, count - copy))
255 return -EFAULT;
Vinod Koulb21c60a2011-12-23 10:36:39 +0530256 }
257 /* if DSP cares, let it know data has been written */
258 if (stream->ops->ack)
259 stream->ops->ack(stream, count);
260 return count;
261}
262
263static ssize_t snd_compr_write(struct file *f, const char __user *buf,
264 size_t count, loff_t *offset)
265{
266 struct snd_compr_file *data = f->private_data;
267 struct snd_compr_stream *stream;
268 size_t avail;
269 int retval;
270
271 if (snd_BUG_ON(!data))
272 return -EFAULT;
273
274 stream = &data->stream;
275 mutex_lock(&stream->device->lock);
276 /* write is allowed when stream is running or has been steup */
277 if (stream->runtime->state != SNDRV_PCM_STATE_SETUP &&
278 stream->runtime->state != SNDRV_PCM_STATE_RUNNING) {
279 mutex_unlock(&stream->device->lock);
280 return -EBADFD;
281 }
282
283 avail = snd_compr_get_avail(stream);
Damir Didjusto76816cc2014-01-14 01:32:00 +0100284 pr_debug("avail returned %zu\n", avail);
Vinod Koulb21c60a2011-12-23 10:36:39 +0530285 /* calculate how much we can write to buffer */
286 if (avail > count)
287 avail = count;
288
Charles Keepax416e1d52013-04-18 11:01:38 +0100289 if (stream->ops->copy) {
290 char __user* cbuf = (char __user*)buf;
291 retval = stream->ops->copy(stream, cbuf, avail);
292 } else {
Vinod Koulb21c60a2011-12-23 10:36:39 +0530293 retval = snd_compr_write_data(stream, buf, avail);
Charles Keepax416e1d52013-04-18 11:01:38 +0100294 }
Vinod Koulb21c60a2011-12-23 10:36:39 +0530295 if (retval > 0)
296 stream->runtime->total_bytes_available += retval;
297
298 /* while initiating the stream, write should be called before START
299 * call, so in setup move state */
300 if (stream->runtime->state == SNDRV_PCM_STATE_SETUP) {
301 stream->runtime->state = SNDRV_PCM_STATE_PREPARED;
302 pr_debug("stream prepared, Houston we are good to go\n");
303 }
304
305 mutex_unlock(&stream->device->lock);
306 return retval;
307}
308
309
310static ssize_t snd_compr_read(struct file *f, char __user *buf,
311 size_t count, loff_t *offset)
312{
Charles Keepaxed52fa32013-04-18 11:02:08 +0100313 struct snd_compr_file *data = f->private_data;
314 struct snd_compr_stream *stream;
315 size_t avail;
316 int retval;
317
318 if (snd_BUG_ON(!data))
319 return -EFAULT;
320
321 stream = &data->stream;
322 mutex_lock(&stream->device->lock);
323
324 /* read is allowed when stream is running */
325 if (stream->runtime->state != SNDRV_PCM_STATE_RUNNING) {
326 retval = -EBADFD;
327 goto out;
328 }
329
330 avail = snd_compr_get_avail(stream);
Damir Didjusto76816cc2014-01-14 01:32:00 +0100331 pr_debug("avail returned %zu\n", avail);
Charles Keepaxed52fa32013-04-18 11:02:08 +0100332 /* calculate how much we can read from buffer */
333 if (avail > count)
334 avail = count;
335
336 if (stream->ops->copy) {
337 retval = stream->ops->copy(stream, buf, avail);
338 } else {
339 retval = -ENXIO;
340 goto out;
341 }
342 if (retval > 0)
343 stream->runtime->total_bytes_transferred += retval;
344
345out:
346 mutex_unlock(&stream->device->lock);
347 return retval;
Vinod Koulb21c60a2011-12-23 10:36:39 +0530348}
349
350static int snd_compr_mmap(struct file *f, struct vm_area_struct *vma)
351{
352 return -ENXIO;
353}
354
355static inline int snd_compr_get_poll(struct snd_compr_stream *stream)
356{
357 if (stream->direction == SND_COMPRESS_PLAYBACK)
358 return POLLOUT | POLLWRNORM;
359 else
360 return POLLIN | POLLRDNORM;
361}
362
363static unsigned int snd_compr_poll(struct file *f, poll_table *wait)
364{
365 struct snd_compr_file *data = f->private_data;
366 struct snd_compr_stream *stream;
367 size_t avail;
368 int retval = 0;
369
370 if (snd_BUG_ON(!data))
371 return -EFAULT;
372 stream = &data->stream;
373 if (snd_BUG_ON(!stream))
374 return -EFAULT;
375
376 mutex_lock(&stream->device->lock);
377 if (stream->runtime->state == SNDRV_PCM_STATE_PAUSED ||
378 stream->runtime->state == SNDRV_PCM_STATE_OPEN) {
379 retval = -EBADFD;
380 goto out;
381 }
382 poll_wait(f, &stream->runtime->sleep, wait);
383
384 avail = snd_compr_get_avail(stream);
Damir Didjusto76816cc2014-01-14 01:32:00 +0100385 pr_debug("avail is %zu\n", avail);
Vinod Koulb21c60a2011-12-23 10:36:39 +0530386 /* check if we have at least one fragment to fill */
387 switch (stream->runtime->state) {
388 case SNDRV_PCM_STATE_DRAINING:
389 /* stream has been woken up after drain is complete
390 * draining done so set stream state to stopped
391 */
392 retval = snd_compr_get_poll(stream);
393 stream->runtime->state = SNDRV_PCM_STATE_SETUP;
394 break;
395 case SNDRV_PCM_STATE_RUNNING:
396 case SNDRV_PCM_STATE_PREPARED:
397 case SNDRV_PCM_STATE_PAUSED:
398 if (avail >= stream->runtime->fragment_size)
399 retval = snd_compr_get_poll(stream);
400 break;
401 default:
402 if (stream->direction == SND_COMPRESS_PLAYBACK)
403 retval = POLLOUT | POLLWRNORM | POLLERR;
404 else
405 retval = POLLIN | POLLRDNORM | POLLERR;
406 break;
407 }
408out:
409 mutex_unlock(&stream->device->lock);
410 return retval;
411}
412
413static int
414snd_compr_get_caps(struct snd_compr_stream *stream, unsigned long arg)
415{
416 int retval;
417 struct snd_compr_caps caps;
418
419 if (!stream->ops->get_caps)
420 return -ENXIO;
421
Dan Carpenter9bb2f992013-04-21 14:07:29 +0300422 memset(&caps, 0, sizeof(caps));
Vinod Koulb21c60a2011-12-23 10:36:39 +0530423 retval = stream->ops->get_caps(stream, &caps);
424 if (retval)
425 goto out;
426 if (copy_to_user((void __user *)arg, &caps, sizeof(caps)))
427 retval = -EFAULT;
428out:
429 return retval;
430}
431
432static int
433snd_compr_get_codec_caps(struct snd_compr_stream *stream, unsigned long arg)
434{
435 int retval;
436 struct snd_compr_codec_caps *caps;
437
438 if (!stream->ops->get_codec_caps)
439 return -ENXIO;
440
Takashi Iwaiaedeb852013-04-22 10:38:26 +0200441 caps = kzalloc(sizeof(*caps), GFP_KERNEL);
Vinod Koulb21c60a2011-12-23 10:36:39 +0530442 if (!caps)
443 return -ENOMEM;
444
445 retval = stream->ops->get_codec_caps(stream, caps);
446 if (retval)
447 goto out;
448 if (copy_to_user((void __user *)arg, caps, sizeof(*caps)))
449 retval = -EFAULT;
450
451out:
452 kfree(caps);
453 return retval;
454}
455
456/* revisit this with snd_pcm_preallocate_xxx */
457static int snd_compr_allocate_buffer(struct snd_compr_stream *stream,
458 struct snd_compr_params *params)
459{
460 unsigned int buffer_size;
461 void *buffer;
462
463 buffer_size = params->buffer.fragment_size * params->buffer.fragments;
464 if (stream->ops->copy) {
465 buffer = NULL;
466 /* if copy is defined the driver will be required to copy
467 * the data from core
468 */
469 } else {
470 buffer = kmalloc(buffer_size, GFP_KERNEL);
471 if (!buffer)
472 return -ENOMEM;
473 }
474 stream->runtime->fragment_size = params->buffer.fragment_size;
475 stream->runtime->fragments = params->buffer.fragments;
476 stream->runtime->buffer = buffer;
477 stream->runtime->buffer_size = buffer_size;
478 return 0;
479}
480
Vinod Koul213332b2012-09-17 11:51:25 +0530481static int snd_compress_check_input(struct snd_compr_params *params)
482{
483 /* first let's check the buffer parameter's */
484 if (params->buffer.fragment_size == 0 ||
Dan Carpenter7dd2cab2014-07-16 09:37:00 +0300485 params->buffer.fragments > INT_MAX / params->buffer.fragment_size)
Vinod Koul213332b2012-09-17 11:51:25 +0530486 return -EINVAL;
487
Vinod Koul2b836f22012-09-17 11:51:26 +0530488 /* now codec parameters */
489 if (params->codec.id == 0 || params->codec.id > SND_AUDIOCODEC_MAX)
490 return -EINVAL;
491
492 if (params->codec.ch_in == 0 || params->codec.ch_out == 0)
493 return -EINVAL;
494
495 if (!(params->codec.sample_rate & SNDRV_PCM_RATE_8000_192000))
496 return -EINVAL;
497
Vinod Koul213332b2012-09-17 11:51:25 +0530498 return 0;
499}
500
Vinod Koulb21c60a2011-12-23 10:36:39 +0530501static int
502snd_compr_set_params(struct snd_compr_stream *stream, unsigned long arg)
503{
504 struct snd_compr_params *params;
505 int retval;
506
507 if (stream->runtime->state == SNDRV_PCM_STATE_OPEN) {
508 /*
509 * we should allow parameter change only when stream has been
510 * opened not in other cases
511 */
512 params = kmalloc(sizeof(*params), GFP_KERNEL);
513 if (!params)
514 return -ENOMEM;
Jesper Juhl769fab22012-01-23 21:02:57 +0100515 if (copy_from_user(params, (void __user *)arg, sizeof(*params))) {
516 retval = -EFAULT;
517 goto out;
518 }
Vinod Koul213332b2012-09-17 11:51:25 +0530519
520 retval = snd_compress_check_input(params);
521 if (retval)
522 goto out;
523
Vinod Koulb21c60a2011-12-23 10:36:39 +0530524 retval = snd_compr_allocate_buffer(stream, params);
525 if (retval) {
Jesper Juhl769fab22012-01-23 21:02:57 +0100526 retval = -ENOMEM;
527 goto out;
Vinod Koulb21c60a2011-12-23 10:36:39 +0530528 }
Vinod Koul213332b2012-09-17 11:51:25 +0530529
Vinod Koulb21c60a2011-12-23 10:36:39 +0530530 retval = stream->ops->set_params(stream, params);
531 if (retval)
532 goto out;
Charles Keepaxed52fa32013-04-18 11:02:08 +0100533
Jeeja KPa62e24e2013-02-14 16:52:51 +0530534 stream->metadata_set = false;
535 stream->next_track = false;
Charles Keepaxed52fa32013-04-18 11:02:08 +0100536
537 if (stream->direction == SND_COMPRESS_PLAYBACK)
538 stream->runtime->state = SNDRV_PCM_STATE_SETUP;
539 else
540 stream->runtime->state = SNDRV_PCM_STATE_PREPARED;
Jesper Juhl769fab22012-01-23 21:02:57 +0100541 } else {
Vinod Koulb21c60a2011-12-23 10:36:39 +0530542 return -EPERM;
Jesper Juhl769fab22012-01-23 21:02:57 +0100543 }
Vinod Koulb21c60a2011-12-23 10:36:39 +0530544out:
545 kfree(params);
546 return retval;
547}
548
549static int
550snd_compr_get_params(struct snd_compr_stream *stream, unsigned long arg)
551{
552 struct snd_codec *params;
553 int retval;
554
555 if (!stream->ops->get_params)
556 return -EBADFD;
557
Takashi Iwaiaedeb852013-04-22 10:38:26 +0200558 params = kzalloc(sizeof(*params), GFP_KERNEL);
Vinod Koulb21c60a2011-12-23 10:36:39 +0530559 if (!params)
560 return -ENOMEM;
561 retval = stream->ops->get_params(stream, params);
562 if (retval)
563 goto out;
564 if (copy_to_user((char __user *)arg, params, sizeof(*params)))
565 retval = -EFAULT;
566
567out:
568 kfree(params);
569 return retval;
570}
571
Jeeja KPa62e24e2013-02-14 16:52:51 +0530572static int
573snd_compr_get_metadata(struct snd_compr_stream *stream, unsigned long arg)
574{
575 struct snd_compr_metadata metadata;
576 int retval;
577
578 if (!stream->ops->get_metadata)
579 return -ENXIO;
580
581 if (copy_from_user(&metadata, (void __user *)arg, sizeof(metadata)))
582 return -EFAULT;
583
584 retval = stream->ops->get_metadata(stream, &metadata);
585 if (retval != 0)
586 return retval;
587
588 if (copy_to_user((void __user *)arg, &metadata, sizeof(metadata)))
589 return -EFAULT;
590
591 return 0;
592}
593
594static int
595snd_compr_set_metadata(struct snd_compr_stream *stream, unsigned long arg)
596{
597 struct snd_compr_metadata metadata;
598 int retval;
599
600 if (!stream->ops->set_metadata)
601 return -ENXIO;
602 /*
603 * we should allow parameter change only when stream has been
604 * opened not in other cases
605 */
606 if (copy_from_user(&metadata, (void __user *)arg, sizeof(metadata)))
607 return -EFAULT;
608
609 retval = stream->ops->set_metadata(stream, &metadata);
610 stream->metadata_set = true;
611
612 return retval;
613}
614
Vinod Koulb21c60a2011-12-23 10:36:39 +0530615static inline int
616snd_compr_tstamp(struct snd_compr_stream *stream, unsigned long arg)
617{
Krishnankutty Kolathappilly1914ca82013-11-06 10:08:39 -0800618 struct snd_compr_tstamp tstamp;
Richard Fitzgerald291f8162013-02-11 13:44:53 +0000619 int ret;
Vinod Koulb21c60a2011-12-23 10:36:39 +0530620
Krishnankutty Kolathappilly1914ca82013-11-06 10:08:39 -0800621 memset(&tstamp, 0, sizeof(tstamp));
Richard Fitzgerald291f8162013-02-11 13:44:53 +0000622 ret = snd_compr_update_tstamp(stream, &tstamp);
623 if (ret == 0)
624 ret = copy_to_user((struct snd_compr_tstamp __user *)arg,
625 &tstamp, sizeof(tstamp)) ? -EFAULT : 0;
626 return ret;
Vinod Koulb21c60a2011-12-23 10:36:39 +0530627}
628
629static int snd_compr_pause(struct snd_compr_stream *stream)
630{
631 int retval;
632
633 if (stream->runtime->state != SNDRV_PCM_STATE_RUNNING)
634 return -EPERM;
635 retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_PAUSE_PUSH);
Vinod Koul2e49f242012-06-12 16:16:17 +0530636 if (!retval)
Vinod Koulb21c60a2011-12-23 10:36:39 +0530637 stream->runtime->state = SNDRV_PCM_STATE_PAUSED;
Vinod Koulb21c60a2011-12-23 10:36:39 +0530638 return retval;
639}
640
641static int snd_compr_resume(struct snd_compr_stream *stream)
642{
643 int retval;
644
645 if (stream->runtime->state != SNDRV_PCM_STATE_PAUSED)
646 return -EPERM;
647 retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_PAUSE_RELEASE);
648 if (!retval)
649 stream->runtime->state = SNDRV_PCM_STATE_RUNNING;
650 return retval;
651}
652
653static int snd_compr_start(struct snd_compr_stream *stream)
654{
655 int retval;
656
657 if (stream->runtime->state != SNDRV_PCM_STATE_PREPARED)
658 return -EPERM;
659 retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_START);
660 if (!retval)
661 stream->runtime->state = SNDRV_PCM_STATE_RUNNING;
662 return retval;
663}
664
665static int snd_compr_stop(struct snd_compr_stream *stream)
666{
667 int retval;
668
669 if (stream->runtime->state == SNDRV_PCM_STATE_PREPARED ||
670 stream->runtime->state == SNDRV_PCM_STATE_SETUP)
671 return -EPERM;
672 retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_STOP);
673 if (!retval) {
674 stream->runtime->state = SNDRV_PCM_STATE_SETUP;
675 wake_up(&stream->runtime->sleep);
Vinod Koulc2e7a5b2012-06-12 16:16:18 +0530676 stream->runtime->total_bytes_available = 0;
677 stream->runtime->total_bytes_transferred = 0;
Vinod Koulb21c60a2011-12-23 10:36:39 +0530678 }
679 return retval;
680}
681
Eric Laurent3022a8012013-08-28 14:46:54 -0700682/* this fn is called without lock being held and we change stream states here
683 * so while using the stream state auquire the lock but relase before invoking
684 * DSP as the call will possibly take a while
685 */
Vinod Koulb21c60a2011-12-23 10:36:39 +0530686static int snd_compr_drain(struct snd_compr_stream *stream)
687{
688 int retval;
689
Eric Laurent3022a8012013-08-28 14:46:54 -0700690 mutex_lock(&stream->device->lock);
Vinod Koulb21c60a2011-12-23 10:36:39 +0530691 if (stream->runtime->state == SNDRV_PCM_STATE_PREPARED ||
Eric Laurent3022a8012013-08-28 14:46:54 -0700692 stream->runtime->state == SNDRV_PCM_STATE_SETUP) {
693 retval = -EPERM;
694 goto ret;
695 }
696 mutex_unlock(&stream->device->lock);
Vinod Koulb21c60a2011-12-23 10:36:39 +0530697 retval = stream->ops->trigger(stream, SND_COMPR_TRIGGER_DRAIN);
Eric Laurent3022a8012013-08-28 14:46:54 -0700698 mutex_lock(&stream->device->lock);
Vinod Koulb21c60a2011-12-23 10:36:39 +0530699 if (!retval) {
700 stream->runtime->state = SNDRV_PCM_STATE_DRAINING;
701 wake_up(&stream->runtime->sleep);
702 }
Eric Laurent3022a8012013-08-28 14:46:54 -0700703ret:
704 mutex_unlock(&stream->device->lock);
Vinod Koulb21c60a2011-12-23 10:36:39 +0530705 return retval;
706}
707
Jeeja KPa62e24e2013-02-14 16:52:51 +0530708static int snd_compr_next_track(struct snd_compr_stream *stream)
709{
710 int retval;
711
712 /* only a running stream can transition to next track */
713 if (stream->runtime->state != SNDRV_PCM_STATE_RUNNING)
714 return -EPERM;
715
716 /* you can signal next track isf this is intended to be a gapless stream
717 * and current track metadata is set
718 */
719 if (stream->metadata_set == false)
720 return -EPERM;
721
722 retval = stream->ops->trigger(stream, SND_COMPR_TRIGGER_NEXT_TRACK);
723 if (retval != 0)
724 return retval;
725 stream->metadata_set = false;
726 stream->next_track = true;
727 return 0;
728}
729
730static int snd_compr_partial_drain(struct snd_compr_stream *stream)
731{
732 int retval;
Eric Laurent3022a8012013-08-28 14:46:54 -0700733
734 mutex_lock(&stream->device->lock);
Jeeja KPa62e24e2013-02-14 16:52:51 +0530735 if (stream->runtime->state == SNDRV_PCM_STATE_PREPARED ||
Eric Laurent3022a8012013-08-28 14:46:54 -0700736 stream->runtime->state == SNDRV_PCM_STATE_SETUP) {
737 mutex_unlock(&stream->device->lock);
Jeeja KPa62e24e2013-02-14 16:52:51 +0530738 return -EPERM;
Eric Laurent3022a8012013-08-28 14:46:54 -0700739 }
740 mutex_unlock(&stream->device->lock);
Jeeja KPa62e24e2013-02-14 16:52:51 +0530741 /* stream can be drained only when next track has been signalled */
742 if (stream->next_track == false)
743 return -EPERM;
744
745 retval = stream->ops->trigger(stream, SND_COMPR_TRIGGER_PARTIAL_DRAIN);
746
747 stream->next_track = false;
748 return retval;
749}
750
Eric Laurent57a95b62013-08-28 14:42:27 -0700751static int snd_compress_simple_ioctls(struct file *file,
752 struct snd_compr_stream *stream,
753 unsigned int cmd, unsigned long arg)
754{
755 int retval = -ENOTTY;
756
757 switch (_IOC_NR(cmd)) {
758 case _IOC_NR(SNDRV_COMPRESS_IOCTL_VERSION):
Haynes Mathew Georgea2c1b0e2013-07-23 15:20:18 -0700759 retval = put_user(SNDRV_COMPRESS_VERSION,
Eric Laurent57a95b62013-08-28 14:42:27 -0700760 (int __user *)arg) ? -EFAULT : 0;
761 break;
762
763 case _IOC_NR(SNDRV_COMPRESS_GET_CAPS):
764 retval = snd_compr_get_caps(stream, arg);
765 break;
766
767 case _IOC_NR(SNDRV_COMPRESS_GET_CODEC_CAPS):
768 retval = snd_compr_get_codec_caps(stream, arg);
769 break;
770
771
772 case _IOC_NR(SNDRV_COMPRESS_TSTAMP):
773 retval = snd_compr_tstamp(stream, arg);
774 break;
775
776 case _IOC_NR(SNDRV_COMPRESS_AVAIL):
777 retval = snd_compr_ioctl_avail(stream, arg);
778 break;
779
780 /* drain and partial drain need special handling
781 * we need to drop the locks here as the streams would get blocked on the
782 * dsp to get drained. The locking would be handled in respective
783 * function here
784 */
785 case _IOC_NR(SNDRV_COMPRESS_DRAIN):
786 retval = snd_compr_drain(stream);
787 break;
788
789 case _IOC_NR(SNDRV_COMPRESS_PARTIAL_DRAIN):
790 retval = snd_compr_partial_drain(stream);
791 break;
792 }
793
794 return retval;
795}
796
Vinod Koulb21c60a2011-12-23 10:36:39 +0530797static long snd_compr_ioctl(struct file *f, unsigned int cmd, unsigned long arg)
798{
799 struct snd_compr_file *data = f->private_data;
800 struct snd_compr_stream *stream;
801 int retval = -ENOTTY;
802
803 if (snd_BUG_ON(!data))
804 return -EFAULT;
805 stream = &data->stream;
806 if (snd_BUG_ON(!stream))
807 return -EFAULT;
Eric Laurent57a95b62013-08-28 14:42:27 -0700808
Vinod Koulb21c60a2011-12-23 10:36:39 +0530809 mutex_lock(&stream->device->lock);
810 switch (_IOC_NR(cmd)) {
Vinod Koulb21c60a2011-12-23 10:36:39 +0530811 case _IOC_NR(SNDRV_COMPRESS_SET_PARAMS):
812 retval = snd_compr_set_params(stream, arg);
813 break;
Eric Laurent57a95b62013-08-28 14:42:27 -0700814
Vinod Koulb21c60a2011-12-23 10:36:39 +0530815 case _IOC_NR(SNDRV_COMPRESS_GET_PARAMS):
816 retval = snd_compr_get_params(stream, arg);
817 break;
Eric Laurent57a95b62013-08-28 14:42:27 -0700818
Jeeja KPa62e24e2013-02-14 16:52:51 +0530819 case _IOC_NR(SNDRV_COMPRESS_SET_METADATA):
820 retval = snd_compr_set_metadata(stream, arg);
821 break;
Eric Laurent57a95b62013-08-28 14:42:27 -0700822
Jeeja KPa62e24e2013-02-14 16:52:51 +0530823 case _IOC_NR(SNDRV_COMPRESS_GET_METADATA):
824 retval = snd_compr_get_metadata(stream, arg);
825 break;
Eric Laurent57a95b62013-08-28 14:42:27 -0700826
Vinod Koulb21c60a2011-12-23 10:36:39 +0530827 case _IOC_NR(SNDRV_COMPRESS_PAUSE):
828 retval = snd_compr_pause(stream);
829 break;
Eric Laurent57a95b62013-08-28 14:42:27 -0700830
Vinod Koulb21c60a2011-12-23 10:36:39 +0530831 case _IOC_NR(SNDRV_COMPRESS_RESUME):
832 retval = snd_compr_resume(stream);
833 break;
Eric Laurent57a95b62013-08-28 14:42:27 -0700834
Vinod Koulb21c60a2011-12-23 10:36:39 +0530835 case _IOC_NR(SNDRV_COMPRESS_START):
836 retval = snd_compr_start(stream);
837 break;
Eric Laurent57a95b62013-08-28 14:42:27 -0700838
Vinod Koulb21c60a2011-12-23 10:36:39 +0530839 case _IOC_NR(SNDRV_COMPRESS_STOP):
840 retval = snd_compr_stop(stream);
841 break;
Eric Laurent57a95b62013-08-28 14:42:27 -0700842
Jeeja KPa62e24e2013-02-14 16:52:51 +0530843 case _IOC_NR(SNDRV_COMPRESS_NEXT_TRACK):
844 retval = snd_compr_next_track(stream);
845 break;
846
Eric Laurent57a95b62013-08-28 14:42:27 -0700847 default:
848 mutex_unlock(&stream->device->lock);
849 return snd_compress_simple_ioctls(f, stream, cmd, arg);
850
Vinod Koulb21c60a2011-12-23 10:36:39 +0530851 }
Eric Laurent57a95b62013-08-28 14:42:27 -0700852
Vinod Koulb21c60a2011-12-23 10:36:39 +0530853 mutex_unlock(&stream->device->lock);
854 return retval;
855}
856
857static const struct file_operations snd_compr_file_ops = {
Damir Didjusto76816cc2014-01-14 01:32:00 +0100858 .owner = THIS_MODULE,
859 .open = snd_compr_open,
860 .release = snd_compr_free,
861 .write = snd_compr_write,
862 .read = snd_compr_read,
Vinod Koulb21c60a2011-12-23 10:36:39 +0530863 .unlocked_ioctl = snd_compr_ioctl,
Damir Didjusto76816cc2014-01-14 01:32:00 +0100864 .compat_ioctl = snd_compr_ioctl,
865 .mmap = snd_compr_mmap,
866 .poll = snd_compr_poll,
Vinod Koulb21c60a2011-12-23 10:36:39 +0530867};
868
869static int snd_compress_dev_register(struct snd_device *device)
870{
871 int ret = -EINVAL;
872 char str[16];
873 struct snd_compr *compr;
874
875 if (snd_BUG_ON(!device || !device->device_data))
876 return -EBADFD;
877 compr = device->device_data;
878
879 sprintf(str, "comprC%iD%i", compr->card->number, compr->device);
880 pr_debug("reg %s for device %s, direction %d\n", str, compr->name,
881 compr->direction);
882 /* register compressed device */
883 ret = snd_register_device(SNDRV_DEVICE_TYPE_COMPRESS, compr->card,
884 compr->device, &snd_compr_file_ops, compr, str);
885 if (ret < 0) {
886 pr_err("snd_register_device failed\n %d", ret);
887 return ret;
888 }
889 return ret;
890
891}
892
893static int snd_compress_dev_disconnect(struct snd_device *device)
894{
895 struct snd_compr *compr;
896
897 compr = device->device_data;
898 snd_unregister_device(compr->direction, compr->card, compr->device);
899 return 0;
900}
901
902/*
903 * snd_compress_new: create new compress device
904 * @card: sound card pointer
905 * @device: device number
906 * @dirn: device direction, should be of type enum snd_compr_direction
907 * @compr: compress device pointer
908 */
909int snd_compress_new(struct snd_card *card, int device,
910 int dirn, struct snd_compr *compr)
911{
912 static struct snd_device_ops ops = {
913 .dev_free = NULL,
914 .dev_register = snd_compress_dev_register,
915 .dev_disconnect = snd_compress_dev_disconnect,
916 };
917
918 compr->card = card;
919 compr->device = device;
920 compr->direction = dirn;
921 return snd_device_new(card, SNDRV_DEV_COMPRESS, compr, &ops);
922}
923EXPORT_SYMBOL_GPL(snd_compress_new);
924
Liam Girdwoodd824e522013-05-23 18:47:42 +0100925/*
926 * snd_compress_free: free compress device
927 * @card: sound card pointer
928 * @compr: compress device pointer
929 */
930void snd_compress_free(struct snd_card *card, struct snd_compr *compr)
931{
932 snd_device_free(card, compr);
933}
934EXPORT_SYMBOL_GPL(snd_compress_free);
935
Vinod Koulb21c60a2011-12-23 10:36:39 +0530936static int snd_compress_add_device(struct snd_compr *device)
937{
938 int ret;
939
940 if (!device->card)
941 return -EINVAL;
942
943 /* register the card */
944 ret = snd_card_register(device->card);
945 if (ret)
946 goto out;
947 return 0;
948
949out:
950 pr_err("failed with %d\n", ret);
951 return ret;
952
953}
954
955static int snd_compress_remove_device(struct snd_compr *device)
956{
957 return snd_card_free(device->card);
958}
959
960/**
961 * snd_compress_register - register compressed device
962 *
963 * @device: compressed device to register
964 */
965int snd_compress_register(struct snd_compr *device)
966{
967 int retval;
968
969 if (device->name == NULL || device->dev == NULL || device->ops == NULL)
970 return -EINVAL;
971
972 pr_debug("Registering compressed device %s\n", device->name);
973 if (snd_BUG_ON(!device->ops->open))
974 return -EINVAL;
975 if (snd_BUG_ON(!device->ops->free))
976 return -EINVAL;
977 if (snd_BUG_ON(!device->ops->set_params))
978 return -EINVAL;
979 if (snd_BUG_ON(!device->ops->trigger))
980 return -EINVAL;
981
982 mutex_init(&device->lock);
983
984 /* register a compressed card */
985 mutex_lock(&device_mutex);
986 retval = snd_compress_add_device(device);
987 mutex_unlock(&device_mutex);
988 return retval;
989}
990EXPORT_SYMBOL_GPL(snd_compress_register);
991
992int snd_compress_deregister(struct snd_compr *device)
993{
994 pr_debug("Removing compressed device %s\n", device->name);
995 mutex_lock(&device_mutex);
996 snd_compress_remove_device(device);
997 mutex_unlock(&device_mutex);
998 return 0;
999}
1000EXPORT_SYMBOL_GPL(snd_compress_deregister);
1001
1002static int __init snd_compress_init(void)
1003{
1004 return 0;
1005}
1006
1007static void __exit snd_compress_exit(void)
1008{
1009}
1010
1011module_init(snd_compress_init);
1012module_exit(snd_compress_exit);
1013
1014MODULE_DESCRIPTION("ALSA Compressed offload framework");
1015MODULE_AUTHOR("Vinod Koul <vinod.koul@linux.intel.com>");
1016MODULE_LICENSE("GPL v2");