blob: 2e31ba02d8e15e368fc029730c6e7a50424e828b [file] [log] [blame]
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
2 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12*/
13
14#include <linux/module.h>
15#include <linux/fs.h>
16#include <linux/miscdevice.h>
17#include <linux/uaccess.h>
18#include <linux/sched.h>
19#include <linux/wait.h>
20#include <linux/dma-mapping.h>
21#include <linux/slab.h>
22#include <asm/atomic.h>
23#include <asm/ioctls.h>
24#include <sound/q6asm.h>
25#include <sound/apr_audio.h>
26#include "audio_utils.h"
27
28static int audio_in_pause(struct q6audio_in *audio)
29{
30 int rc;
31
32 rc = q6asm_cmd(audio->ac, CMD_PAUSE);
33 if (rc < 0)
34 pr_err("%s:session id %d: pause cmd failed rc=%d\n", __func__,
35 audio->ac->session, rc);
36
37 return rc;
38}
39
40static int audio_in_flush(struct q6audio_in *audio)
41{
42 int rc;
43
44 pr_debug("%s:session id %d: flush\n", __func__, audio->ac->session);
45 /* Implicitly issue a pause to the decoder before flushing */
46 rc = audio_in_pause(audio);
47 if (rc < 0) {
48 pr_err("%s:session id %d: pause cmd failed rc=%d\n", __func__,
49 audio->ac->session, rc);
50 return rc;
51 }
52
53 rc = q6asm_cmd(audio->ac, CMD_FLUSH);
54 if (rc < 0) {
55 pr_err("%s:session id %d: flush cmd failed rc=%d\n", __func__,
56 audio->ac->session, rc);
57 return rc;
58 }
59 audio->rflush = 1;
60 audio->wflush = 1;
61 memset(audio->out_frame_info, 0, sizeof(audio->out_frame_info));
62 wake_up(&audio->read_wait);
63 /* get read_lock to ensure no more waiting read thread */
64 mutex_lock(&audio->read_lock);
65 audio->rflush = 0;
66 mutex_unlock(&audio->read_lock);
67 wake_up(&audio->write_wait);
68 /* get write_lock to ensure no more waiting write thread */
69 mutex_lock(&audio->write_lock);
70 audio->wflush = 0;
71 mutex_unlock(&audio->write_lock);
72 pr_debug("%s:session id %d: in_bytes %d\n", __func__,
73 audio->ac->session, atomic_read(&audio->in_bytes));
74 pr_debug("%s:session id %d: in_samples %d\n", __func__,
75 audio->ac->session, atomic_read(&audio->in_samples));
76 atomic_set(&audio->in_bytes, 0);
77 atomic_set(&audio->in_samples, 0);
78 return 0;
79}
80
81void audio_in_get_dsp_frames(struct q6audio_in *audio,
82 uint32_t token, uint32_t *payload)
83{
84 uint32_t index;
85
86 index = token;
87 pr_debug("%s:session id %d: index=%d nr frames=%d offset[%d]\n",
88 __func__, audio->ac->session, token, payload[7],
89 payload[3]);
90 pr_debug("%s:session id %d: timemsw=%d lsw=%d\n", __func__,
91 audio->ac->session, payload[4], payload[5]);
92 pr_debug("%s:session id %d: uflags=0x%8x uid=0x%8x\n", __func__,
93 audio->ac->session, payload[6], payload[8]);
94 pr_debug("%s:session id %d: enc frame size=0x%8x\n", __func__,
95 audio->ac->session, payload[2]);
96
97 audio->out_frame_info[index][0] = payload[7];
98 audio->out_frame_info[index][1] = payload[3];
99
100 /* statistics of read */
101 atomic_add(payload[2], &audio->in_bytes);
102 atomic_add(payload[7], &audio->in_samples);
103
104 if (atomic_read(&audio->out_count) <= audio->str_cfg.buffer_count) {
105 atomic_inc(&audio->out_count);
106 wake_up(&audio->read_wait);
107 }
108}
109
110/* must be called with audio->lock held */
111int audio_in_enable(struct q6audio_in *audio)
112{
113 if (audio->enabled)
114 return 0;
115
116 /* 2nd arg: 0 -> run immediately
117 3rd arg: 0 -> msw_ts, 4th arg: 0 ->lsw_ts */
118 return q6asm_run(audio->ac, 0x00, 0x00, 0x00);
119}
120
121/* must be called with audio->lock held */
122int audio_in_disable(struct q6audio_in *audio)
123{
124 int rc = 0;
125 if (audio->opened) {
126 audio->enabled = 0;
127 audio->opened = 0;
128 pr_debug("%s:session id %d: inbytes[%d] insamples[%d]\n",
129 __func__, audio->ac->session,
130 atomic_read(&audio->in_bytes),
131 atomic_read(&audio->in_samples));
132
133 rc = q6asm_cmd(audio->ac, CMD_CLOSE);
134 if (rc < 0)
135 pr_err("%s:session id %d: Failed to close the\
136 session rc=%d\n", __func__, audio->ac->session,
137 rc);
138 audio->stopped = 1;
139 memset(audio->out_frame_info, 0,
140 sizeof(audio->out_frame_info));
141 wake_up(&audio->read_wait);
142 wake_up(&audio->write_wait);
143 }
144 pr_debug("%s:session id %d: enabled[%d]\n", __func__,
145 audio->ac->session, audio->enabled);
146 return rc;
147}
148
149int audio_in_buf_alloc(struct q6audio_in *audio)
150{
151 int rc = 0;
152
153 switch (audio->buf_alloc) {
154 case NO_BUF_ALLOC:
155 if (audio->feedback == NON_TUNNEL_MODE) {
156 rc = q6asm_audio_client_buf_alloc(IN,
157 audio->ac,
158 ALIGN_BUF_SIZE(audio->pcm_cfg.buffer_size),
159 audio->pcm_cfg.buffer_count);
160 if (rc < 0) {
161 pr_err("%s:session id %d: Buffer Alloc\
162 failed\n", __func__,
163 audio->ac->session);
164 rc = -ENOMEM;
165 break;
166 }
167 audio->buf_alloc |= BUF_ALLOC_IN;
168 }
169 rc = q6asm_audio_client_buf_alloc(OUT, audio->ac,
170 ALIGN_BUF_SIZE(audio->str_cfg.buffer_size),
171 audio->str_cfg.buffer_count);
172 if (rc < 0) {
173 pr_err("%s:session id %d: Buffer Alloc failed rc=%d\n",
174 __func__, audio->ac->session, rc);
175 rc = -ENOMEM;
176 break;
177 }
178 audio->buf_alloc |= BUF_ALLOC_OUT;
179 break;
180 case BUF_ALLOC_IN:
181 rc = q6asm_audio_client_buf_alloc(OUT, audio->ac,
182 ALIGN_BUF_SIZE(audio->str_cfg.buffer_size),
183 audio->str_cfg.buffer_count);
184 if (rc < 0) {
185 pr_err("%s:session id %d: Buffer Alloc failed rc=%d\n",
186 __func__, audio->ac->session, rc);
187 rc = -ENOMEM;
188 break;
189 }
190 audio->buf_alloc |= BUF_ALLOC_OUT;
191 break;
192 case BUF_ALLOC_OUT:
193 if (audio->feedback == NON_TUNNEL_MODE) {
194 rc = q6asm_audio_client_buf_alloc(IN, audio->ac,
195 ALIGN_BUF_SIZE(audio->pcm_cfg.buffer_size),
196 audio->pcm_cfg.buffer_count);
197 if (rc < 0) {
198 pr_err("%s:session id %d: Buffer Alloc\
199 failed\n", __func__,
200 audio->ac->session);
201 rc = -ENOMEM;
202 break;
203 }
204 audio->buf_alloc |= BUF_ALLOC_IN;
205 }
206 break;
207 default:
208 pr_debug("%s:session id %d: buf[%d]\n", __func__,
209 audio->ac->session, audio->buf_alloc);
210 }
211
212 return rc;
213}
214/* ------------------- device --------------------- */
215long audio_in_ioctl(struct file *file,
216 unsigned int cmd, unsigned long arg)
217{
218 struct q6audio_in *audio = file->private_data;
219 int rc = 0;
220
221 if (cmd == AUDIO_GET_STATS) {
222 struct msm_audio_stats stats;
223 stats.byte_count = atomic_read(&audio->in_bytes);
224 stats.sample_count = atomic_read(&audio->in_samples);
225 if (copy_to_user((void *) arg, &stats, sizeof(stats)))
226 return -EFAULT;
227 return rc;
228 }
229
230 mutex_lock(&audio->lock);
231 switch (cmd) {
232 case AUDIO_FLUSH: {
233 /* Make sure we're stopped and we wake any threads
234 * that might be blocked holding the read_lock.
235 * While audio->stopped read threads will always
236 * exit immediately.
237 */
238 rc = audio_in_flush(audio);
239 if (rc < 0)
240 pr_err("%s:session id %d: Flush Fail rc=%d\n",
241 __func__, audio->ac->session, rc);
242 break;
243 }
244 case AUDIO_PAUSE: {
245 pr_debug("%s:session id %d: AUDIO_PAUSE\n", __func__,
246 audio->ac->session);
247 if (audio->enabled)
248 audio_in_pause(audio);
249 break;
250 }
251 case AUDIO_GET_STREAM_CONFIG: {
252 struct msm_audio_stream_config cfg;
253 memset(&cfg, 0, sizeof(cfg));
254 cfg.buffer_size = audio->str_cfg.buffer_size;
255 cfg.buffer_count = audio->str_cfg.buffer_count;
256 if (copy_to_user((void *)arg, &cfg, sizeof(cfg)))
257 rc = -EFAULT;
258 pr_debug("%s:session id %d: AUDIO_GET_STREAM_CONFIG %d %d\n",
259 __func__, audio->ac->session, cfg.buffer_size,
260 cfg.buffer_count);
261 break;
262 }
263 case AUDIO_SET_STREAM_CONFIG: {
264 struct msm_audio_stream_config cfg;
265 if (copy_from_user(&cfg, (void *)arg, sizeof(cfg))) {
266 rc = -EFAULT;
267 break;
268 }
269 /* Minimum single frame size,
270 but with in maximum frames number */
271 if ((cfg.buffer_size < (audio->min_frame_size+ \
272 sizeof(struct meta_out_dsp))) ||
273 (cfg.buffer_count < FRAME_NUM)) {
274 rc = -EINVAL;
275 break;
276 }
277 audio->str_cfg.buffer_size = cfg.buffer_size;
278 audio->str_cfg.buffer_count = cfg.buffer_count;
279 rc = q6asm_audio_client_buf_alloc(OUT, audio->ac,
280 ALIGN_BUF_SIZE(audio->str_cfg.buffer_size),
281 audio->str_cfg.buffer_count);
282 if (rc < 0) {
283 pr_err("%s: session id %d: Buffer Alloc failed rc=%d\n",
284 __func__, audio->ac->session, rc);
285 rc = -ENOMEM;
286 break;
287 }
288 audio->buf_alloc |= BUF_ALLOC_OUT;
289 rc = 0;
290 pr_debug("%s:session id %d: AUDIO_SET_STREAM_CONFIG %d %d\n",
291 __func__, audio->ac->session,
292 audio->str_cfg.buffer_size,
293 audio->str_cfg.buffer_count);
294 break;
295 }
296 case AUDIO_GET_SESSION_ID: {
297 if (copy_to_user((void *) arg, &audio->ac->session,
298 sizeof(unsigned short))) {
299 rc = -EFAULT;
300 }
301 break;
302 }
303 case AUDIO_SET_BUF_CFG: {
304 struct msm_audio_buf_cfg cfg;
305 if (copy_from_user(&cfg, (void *)arg, sizeof(cfg))) {
306 rc = -EFAULT;
307 break;
308 }
309 if ((audio->feedback == NON_TUNNEL_MODE) &&
310 !cfg.meta_info_enable) {
311 rc = -EFAULT;
312 break;
313 }
314
315 /* Restrict the num of frames per buf to coincide with
316 * default buf size */
317 if (cfg.frames_per_buf > audio->max_frames_per_buf) {
318 rc = -EFAULT;
319 break;
320 }
321 audio->buf_cfg.meta_info_enable = cfg.meta_info_enable;
322 audio->buf_cfg.frames_per_buf = cfg.frames_per_buf;
323 pr_debug("%s:session id %d: Set-buf-cfg: meta[%d]\
324 framesperbuf[%d]\n", __func__,
325 audio->ac->session, cfg.meta_info_enable,
326 cfg.frames_per_buf);
327 break;
328 }
329 case AUDIO_GET_BUF_CFG: {
330 pr_debug("%s:session id %d: Get-buf-cfg: meta[%d]\
331 framesperbuf[%d]\n", __func__,
332 audio->ac->session, audio->buf_cfg.meta_info_enable,
333 audio->buf_cfg.frames_per_buf);
334
335 if (copy_to_user((void *)arg, &audio->buf_cfg,
336 sizeof(struct msm_audio_buf_cfg)))
337 rc = -EFAULT;
338 break;
339 }
340 case AUDIO_GET_CONFIG: {
341 if (copy_to_user((void *)arg, &audio->pcm_cfg,
342 sizeof(struct msm_audio_config)))
343 rc = -EFAULT;
344 break;
345
346 }
347 case AUDIO_SET_CONFIG: {
348 struct msm_audio_config cfg;
349 if (copy_from_user(&cfg, (void *)arg, sizeof(cfg))) {
350 rc = -EFAULT;
351 break;
352 }
353 if (audio->feedback != NON_TUNNEL_MODE) {
354 pr_err("%s:session id %d: Not sufficient permission to"
355 "change the record mode\n", __func__,
356 audio->ac->session);
357 rc = -EACCES;
358 break;
359 }
360 if ((cfg.buffer_count > PCM_BUF_COUNT) ||
361 (cfg.buffer_count == 1))
362 cfg.buffer_count = PCM_BUF_COUNT;
363
364 audio->pcm_cfg.buffer_count = cfg.buffer_count;
365 audio->pcm_cfg.buffer_size = cfg.buffer_size;
366 audio->pcm_cfg.channel_count = cfg.channel_count;
367 audio->pcm_cfg.sample_rate = cfg.sample_rate;
368 rc = q6asm_audio_client_buf_alloc(IN, audio->ac,
369 ALIGN_BUF_SIZE(audio->pcm_cfg.buffer_size),
370 audio->pcm_cfg.buffer_count);
371 if (rc < 0) {
372 pr_err("%s:session id %d: Buffer Alloc failed\n",
373 __func__, audio->ac->session);
374 rc = -ENOMEM;
375 break;
376 }
377 audio->buf_alloc |= BUF_ALLOC_IN;
378 rc = 0;
379 pr_debug("%s:session id %d: AUDIO_SET_CONFIG %d %d\n", __func__,
380 audio->ac->session, audio->pcm_cfg.buffer_count,
381 audio->pcm_cfg.buffer_size);
382 break;
383 }
384 default:
385 /* call codec specific ioctl */
386 rc = audio->enc_ioctl(file, cmd, arg);
387 }
388 mutex_unlock(&audio->lock);
389 return rc;
390}
391
392ssize_t audio_in_read(struct file *file,
393 char __user *buf,
394 size_t count, loff_t *pos)
395{
396 struct q6audio_in *audio = file->private_data;
397 const char __user *start = buf;
398 unsigned char *data;
399 uint32_t offset = 0;
400 uint32_t size = 0;
401 int rc = 0;
402 uint32_t idx;
403 struct meta_out_dsp meta;
404 uint32_t bytes_to_copy = 0;
405 uint32_t mfield_size = (audio->buf_cfg.meta_info_enable == 0) ? 0 :
406 (sizeof(unsigned char) +
407 (sizeof(struct meta_out_dsp)*(audio->buf_cfg.frames_per_buf)));
408
409 pr_debug("%s:session id %d: read - %d\n", __func__, audio->ac->session,
410 count);
411 if (!audio->enabled)
412 return -EFAULT;
413 mutex_lock(&audio->read_lock);
414 while (count > 0) {
415 rc = wait_event_interruptible(
416 audio->read_wait,
417 ((atomic_read(&audio->out_count) > 0) ||
418 (audio->stopped) ||
419 audio->rflush || audio->eos_rsp));
420
421 if (rc < 0)
422 break;
423
424 if ((audio->stopped && !(atomic_read(&audio->out_count))) ||
425 audio->rflush) {
426 pr_debug("%s:session id %d: driver in stop state or\
427 flush,No more buf to read", __func__,
428 audio->ac->session);
429 rc = 0;/* End of File */
430 break;
431 }
432 if (!(atomic_read(&audio->out_count)) &&
433 (audio->eos_rsp == 1) &&
434 (count >= (sizeof(unsigned char) +
435 sizeof(struct meta_out_dsp)))) {
436 unsigned char num_of_frames;
437 pr_info("%s:session id %d: eos %d at output\n",
438 __func__, audio->ac->session, audio->eos_rsp);
439 if (buf != start)
440 break;
441 num_of_frames = 0xFF;
442 if (copy_to_user(buf, &num_of_frames,
443 sizeof(unsigned char))) {
444 rc = -EFAULT;
445 break;
446 }
447 buf += sizeof(unsigned char);
448 meta.frame_size = 0xFFFF;
449 meta.encoded_pcm_samples = 0xFFFF;
450 meta.msw_ts = 0x00;
451 meta.lsw_ts = 0x00;
452 meta.nflags = AUD_EOS_SET;
453 audio->eos_rsp = 0;
454 if (copy_to_user(buf, &meta, sizeof(meta))) {
455 rc = -EFAULT;
456 break;
457 }
458 buf += sizeof(meta);
459 break;
460 }
461 data = (unsigned char *)q6asm_is_cpu_buf_avail(OUT, audio->ac,
462 &size, &idx);
463 if ((count >= (size + mfield_size)) && data) {
464 if (audio->buf_cfg.meta_info_enable) {
465 if (copy_to_user(buf,
466 &audio->out_frame_info[idx][0],
467 sizeof(unsigned char))) {
468 rc = -EFAULT;
469 break;
470 }
471 bytes_to_copy =
472 (size + audio->out_frame_info[idx][1]);
473 /* Number of frames information copied */
474 buf += sizeof(unsigned char);
475 count -= sizeof(unsigned char);
476 } else {
477 offset = audio->out_frame_info[idx][1];
478 bytes_to_copy = size;
479 }
480
481 pr_debug("%s:session id %d: offset=%d nr of frames= %d\n",
482 __func__, audio->ac->session,
483 audio->out_frame_info[idx][1],
484 audio->out_frame_info[idx][0]);
485
486 if (copy_to_user(buf, &data[offset], bytes_to_copy)) {
487 rc = -EFAULT;
488 break;
489 }
490 count -= bytes_to_copy;
491 buf += bytes_to_copy;
492 } else {
493 pr_err("%s:session id %d: short read data[%p]\
494 bytesavail[%d]bytesrequest[%d]\n", __func__,
495 audio->ac->session,
496 data, size, count);
497 }
498 atomic_dec(&audio->out_count);
499 q6asm_read(audio->ac);
500 break;
501 }
502 mutex_unlock(&audio->read_lock);
503
504 pr_debug("%s:session id %d: read: %d bytes\n", __func__,
505 audio->ac->session, (buf-start));
506 if (buf > start)
507 return buf - start;
508 return rc;
509}
510
511static int extract_meta_info(char *buf, unsigned long *msw_ts,
512 unsigned long *lsw_ts, unsigned int *flags)
513{
514 struct meta_in *meta = (struct meta_in *)buf;
515 *msw_ts = meta->ntimestamp.highpart;
516 *lsw_ts = meta->ntimestamp.lowpart;
517 *flags = meta->nflags;
518 return 0;
519}
520
521ssize_t audio_in_write(struct file *file,
522 const char __user *buf,
523 size_t count, loff_t *pos)
524{
525 struct q6audio_in *audio = file->private_data;
526 const char __user *start = buf;
527 size_t xfer = 0;
528 char *cpy_ptr;
529 int rc = 0;
530 unsigned char *data;
531 uint32_t size = 0;
532 uint32_t idx = 0;
533 uint32_t nflags = 0;
534 unsigned long msw_ts = 0;
535 unsigned long lsw_ts = 0;
536 uint32_t mfield_size = (audio->buf_cfg.meta_info_enable == 0) ? 0 :
537 sizeof(struct meta_in);
538
539 pr_debug("%s:session id %d: to write[%d]\n", __func__,
540 audio->ac->session, count);
541 if (!audio->enabled)
542 return -EFAULT;
543 mutex_lock(&audio->write_lock);
544
545 while (count > 0) {
546 rc = wait_event_interruptible(audio->write_wait,
547 ((atomic_read(&audio->in_count) > 0) ||
548 (audio->stopped) ||
549 (audio->wflush)));
550 if (rc < 0)
551 break;
552 if (audio->stopped || audio->wflush) {
553 pr_debug("%s: session id %d: stop or flush\n", __func__,
554 audio->ac->session);
555 rc = -EBUSY;
556 break;
557 }
558 data = (unsigned char *)q6asm_is_cpu_buf_avail(IN, audio->ac,
559 &size, &idx);
560 if (!data) {
561 pr_debug("%s:session id %d: No buf available\n",
562 __func__, audio->ac->session);
563 continue;
564 }
565 cpy_ptr = data;
566 if (audio->buf_cfg.meta_info_enable) {
567 if (buf == start) {
568 /* Processing beginning of user buffer */
569 if (copy_from_user(cpy_ptr, buf, mfield_size)) {
570 rc = -EFAULT;
571 break;
572 }
573 /* Check if EOS flag is set and buffer has
574 * contains just meta field
575 */
576 extract_meta_info(cpy_ptr, &msw_ts, &lsw_ts,
577 &nflags);
578 buf += mfield_size;
579 if (count == mfield_size) {
580 /* send the EOS and return */
581 pr_debug("%s:session id %d: send EOS\
582 0x%8x\n", __func__,
583 audio->ac->session, nflags);
584 break;
585 }
586 count -= mfield_size;
587 } else {
588 pr_debug("%s:session id %d: continuous\
589 buffer\n", __func__, audio->ac->session);
590 }
591 }
592 xfer = (count > (audio->pcm_cfg.buffer_size)) ?
593 (audio->pcm_cfg.buffer_size) : count;
594
595 if (copy_from_user(cpy_ptr, buf, xfer)) {
596 rc = -EFAULT;
597 break;
598 }
599 rc = q6asm_write(audio->ac, xfer, msw_ts, lsw_ts, 0x00);
600 if (rc < 0) {
601 rc = -EFAULT;
602 break;
603 }
604 atomic_dec(&audio->in_count);
605 count -= xfer;
606 buf += xfer;
607 }
608 mutex_unlock(&audio->write_lock);
609 pr_debug("%s:session id %d: eos_condition 0x%8x buf[0x%x]\
610 start[0x%x]\n", __func__, audio->ac->session,
611 nflags, (int) buf, (int) start);
612 if (nflags & AUD_EOS_SET) {
613 rc = q6asm_cmd(audio->ac, CMD_EOS);
614 pr_info("%s:session id %d: eos %d at input\n", __func__,
615 audio->ac->session, audio->eos_rsp);
616 }
617 pr_debug("%s:session id %d: Written %d Avail Buf[%d]", __func__,
618 audio->ac->session, (buf - start - mfield_size),
619 atomic_read(&audio->in_count));
620 if (!rc) {
621 if (buf > start)
622 return buf - start;
623 }
624 return rc;
625}
626
627int audio_in_release(struct inode *inode, struct file *file)
628{
629 struct q6audio_in *audio = file->private_data;
630 pr_info("%s: session id %d\n", __func__, audio->ac->session);
631 mutex_lock(&audio->lock);
632 audio_in_disable(audio);
633 q6asm_audio_client_free(audio->ac);
634 mutex_unlock(&audio->lock);
635 kfree(audio->enc_cfg);
636 kfree(audio);
637 return 0;
638}
639