blob: 6e7961cae0ec4f616d8ed47a138d471f4cdc38e8 [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#include <linux/module.h>
14#include <linux/fs.h>
15#include <linux/miscdevice.h>
16#include <linux/list.h>
17#include <linux/uaccess.h>
18#include <linux/mutex.h>
19#include <linux/wakelock.h>
20#include <linux/slab.h>
21#include <linux/wait.h>
22#include <linux/sched.h>
23#include <linux/spinlock.h>
24#include <linux/msm_audio_mvs.h>
25#include <mach/qdsp6v2/q6voice.h>
26
27/* Each buffer is 20 ms, queue holds 200 ms of data. */
28#define MVS_MAX_Q_LEN 10
29
30/* Length of the DSP frame info header added to the voc packet. */
31#define DSP_FRAME_HDR_LEN 1
32
33enum audio_mvs_state_type {
34 AUDIO_MVS_CLOSED,
35 AUDIO_MVS_STARTED,
36 AUDIO_MVS_STOPPED
37};
38
39struct audio_mvs_buf_node {
40 struct list_head list;
Chaithanya Krishna Bacharaju2f2e8a52011-10-17 14:54:07 +053041 struct q6_msm_audio_mvs_frame frame;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070042};
43
44struct audio_mvs_info_type {
45 enum audio_mvs_state_type state;
46
47 uint32_t mvs_mode;
48 uint32_t rate_type;
49 uint32_t dtx_mode;
Chaithanya Krishna Bacharaju2f2e8a52011-10-17 14:54:07 +053050 struct q_min_max_rate min_max_rate;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070051
52 struct list_head in_queue;
53 struct list_head free_in_queue;
54
55 struct list_head out_queue;
56 struct list_head free_out_queue;
57
Panneer Arumugam72cb0872011-07-27 14:49:06 -070058 wait_queue_head_t in_wait;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070059 wait_queue_head_t out_wait;
60
61 struct mutex lock;
62 struct mutex in_lock;
63 struct mutex out_lock;
64
65 spinlock_t dsp_lock;
66
67 struct wake_lock suspend_lock;
68 struct wake_lock idle_lock;
69
70 void *memory_chunk;
71};
72
73static struct audio_mvs_info_type audio_mvs_info;
74
75static uint32_t audio_mvs_get_rate(uint32_t mvs_mode, uint32_t rate_type)
76{
77 uint32_t cvs_rate;
78
79 if (mvs_mode == MVS_MODE_AMR_WB)
80 cvs_rate = rate_type - MVS_AMR_MODE_0660;
81 else
82 cvs_rate = rate_type;
83
84 pr_debug("%s: CVS rate is %d for MVS mode %d\n",
85 __func__, cvs_rate, mvs_mode);
86
87 return cvs_rate;
88}
89
90static void audio_mvs_process_ul_pkt(uint8_t *voc_pkt,
91 uint32_t pkt_len,
92 void *private_data)
93{
94 struct audio_mvs_buf_node *buf_node = NULL;
95 struct audio_mvs_info_type *audio = private_data;
96 unsigned long dsp_flags;
97
98 /* Copy up-link packet into out_queue. */
99 spin_lock_irqsave(&audio->dsp_lock, dsp_flags);
100
101 if (!list_empty(&audio->free_out_queue)) {
102 buf_node = list_first_entry(&audio->free_out_queue,
103 struct audio_mvs_buf_node,
104 list);
105 list_del(&buf_node->list);
106
107 switch (audio->mvs_mode) {
108 case MVS_MODE_AMR:
109 case MVS_MODE_AMR_WB: {
110 /* Remove the DSP frame info header. Header format:
111 * Bits 0-3: Frame rate
112 * Bits 4-7: Frame type
113 */
Chaithanya Krishna Bacharaju2f2e8a52011-10-17 14:54:07 +0530114 buf_node->frame.header.frame_type =
115 ((*voc_pkt) & 0xF0) >> 4;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700116 voc_pkt = voc_pkt + DSP_FRAME_HDR_LEN;
117
118 buf_node->frame.len = pkt_len - DSP_FRAME_HDR_LEN;
119
120 memcpy(&buf_node->frame.voc_pkt[0],
121 voc_pkt,
122 buf_node->frame.len);
123
124 list_add_tail(&buf_node->list, &audio->out_queue);
125 break;
126 }
127
128 case MVS_MODE_IS127: {
Chaithanya Krishna Bacharaju2f2e8a52011-10-17 14:54:07 +0530129 buf_node->frame.header.packet_rate = (*voc_pkt) & 0x0F;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700130 voc_pkt = voc_pkt + DSP_FRAME_HDR_LEN;
131
132 buf_node->frame.len = pkt_len - DSP_FRAME_HDR_LEN;
133
134 memcpy(&buf_node->frame.voc_pkt[0],
135 voc_pkt,
136 buf_node->frame.len);
137
138 list_add_tail(&buf_node->list, &audio->out_queue);
139 break;
140 }
141
142 case MVS_MODE_G729A: {
143 /* G729 frames are 10ms each, but the DSP works with
144 * 20ms frames and sends two 10ms frames per buffer.
145 * Extract the two frames and put them in separate
146 * buffers.
147 */
148 /* Remove the first DSP frame info header.
149 * Header format:
150 * Bits 0-1: Frame type
151 */
Chaithanya Krishna Bacharaju2f2e8a52011-10-17 14:54:07 +0530152 buf_node->frame.header.frame_type = (*voc_pkt) & 0x03;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700153 voc_pkt = voc_pkt + DSP_FRAME_HDR_LEN;
154
155 /* There are two frames in the buffer. Length of the
156 * first frame:
157 */
158 buf_node->frame.len = (pkt_len -
159 2 * DSP_FRAME_HDR_LEN) / 2;
160
161 memcpy(&buf_node->frame.voc_pkt[0],
162 voc_pkt,
163 buf_node->frame.len);
164 voc_pkt = voc_pkt + buf_node->frame.len;
165
166 list_add_tail(&buf_node->list, &audio->out_queue);
167
168 /* Get another buffer from the free Q and fill in the
169 * second frame.
170 */
171 if (!list_empty(&audio->free_out_queue)) {
172 buf_node =
173 list_first_entry(&audio->free_out_queue,
174 struct audio_mvs_buf_node,
175 list);
176 list_del(&buf_node->list);
177
178 /* Remove the second DSP frame info header.
179 * Header format:
180 * Bits 0-1: Frame type
181 */
Chaithanya Krishna Bacharaju2f2e8a52011-10-17 14:54:07 +0530182 buf_node->frame.header.frame_type =
183 (*voc_pkt) & 0x03;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700184 voc_pkt = voc_pkt + DSP_FRAME_HDR_LEN;
185
186 /* There are two frames in the buffer. Length
187 * of the first frame:
188 */
189 buf_node->frame.len = (pkt_len -
190 2 * DSP_FRAME_HDR_LEN) / 2;
191
192 memcpy(&buf_node->frame.voc_pkt[0],
193 voc_pkt,
194 buf_node->frame.len);
195
196 list_add_tail(&buf_node->list,
197 &audio->out_queue);
198
199 } else {
200 /* Drop the second frame. */
201 pr_err("%s: UL data dropped, read is slow\n",
202 __func__);
203 }
204
205 break;
206 }
207
Chaithanya Krishna Bacharaju68993b52011-10-18 08:41:30 +0530208 case MVS_MODE_G711:
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700209 case MVS_MODE_G711A: {
210 /* G711 frames are 10ms each, but the DSP works with
211 * 20ms frames and sends two 10ms frames per buffer.
212 * Extract the two frames and put them in separate
213 * buffers.
214 */
215 /* Remove the first DSP frame info header.
Chaithanya Krishna Bacharaju68993b52011-10-18 08:41:30 +0530216 * Header format: G711A
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700217 * Bits 0-1: Frame type
218 * Bits 2-3: Frame rate
Chaithanya Krishna Bacharaju68993b52011-10-18 08:41:30 +0530219 *
220 * Header format: G711
221 * Bits 2-3: Frame rate
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700222 */
Chaithanya Krishna Bacharaju68993b52011-10-18 08:41:30 +0530223 if (audio->mvs_mode == MVS_MODE_G711A)
224 buf_node->frame.header.frame_type =
225 (*voc_pkt) & 0x03;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700226 voc_pkt = voc_pkt + DSP_FRAME_HDR_LEN;
227
228 /* There are two frames in the buffer. Length of the
229 * first frame:
230 */
231 buf_node->frame.len = (pkt_len -
232 2 * DSP_FRAME_HDR_LEN) / 2;
233
234 memcpy(&buf_node->frame.voc_pkt[0],
235 voc_pkt,
236 buf_node->frame.len);
237 voc_pkt = voc_pkt + buf_node->frame.len;
238
239 list_add_tail(&buf_node->list, &audio->out_queue);
240
241 /* Get another buffer from the free Q and fill in the
242 * second frame.
243 */
244 if (!list_empty(&audio->free_out_queue)) {
245 buf_node =
246 list_first_entry(&audio->free_out_queue,
247 struct audio_mvs_buf_node,
248 list);
249 list_del(&buf_node->list);
250
251 /* Remove the second DSP frame info header.
252 * Header format:
253 * Bits 0-1: Frame type
254 * Bits 2-3: Frame rate
255 */
Chaithanya Krishna Bacharaju68993b52011-10-18 08:41:30 +0530256 if (audio->mvs_mode == MVS_MODE_G711A)
257 buf_node->frame.header.frame_type =
Chaithanya Krishna Bacharaju2f2e8a52011-10-17 14:54:07 +0530258 (*voc_pkt) & 0x03;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700259 voc_pkt = voc_pkt + DSP_FRAME_HDR_LEN;
260
261 /* There are two frames in the buffer. Length
262 * of the second frame:
263 */
264 buf_node->frame.len = (pkt_len -
265 2 * DSP_FRAME_HDR_LEN) / 2;
266
267 memcpy(&buf_node->frame.voc_pkt[0],
268 voc_pkt,
269 buf_node->frame.len);
270
271 list_add_tail(&buf_node->list,
272 &audio->out_queue);
273 } else {
274 /* Drop the second frame. */
275 pr_err("%s: UL data dropped, read is slow\n",
276 __func__);
277 }
278 break;
279 }
280
Chaithanya Krishna Bacharaju68993b52011-10-18 08:41:30 +0530281 case MVS_MODE_IS733:
Chaithanya Krishna Bacharaju2f2e8a52011-10-17 14:54:07 +0530282 case MVS_MODE_4GV_NB:
283 case MVS_MODE_4GV_WB: {
284 /* Remove the DSP frame info header.
285 * Header format:
286 * Bits 0-3: frame rate
287 */
288 buf_node->frame.header.packet_rate = (*voc_pkt) & 0x0F;
289 voc_pkt = voc_pkt + DSP_FRAME_HDR_LEN;
290 buf_node->frame.len = pkt_len - DSP_FRAME_HDR_LEN;
291
292 memcpy(&buf_node->frame.voc_pkt[0],
293 voc_pkt,
294 buf_node->frame.len);
Chaithanya Krishna Bacharaju68993b52011-10-18 08:41:30 +0530295
296 list_add_tail(&buf_node->list, &audio->out_queue);
297 break;
298 }
299
300 case MVS_MODE_EFR:
301 case MVS_MODE_FR:
302 case MVS_MODE_HR: {
303 /*
304 * Remove the DSP frame info header
305 * Header Format
306 * Bit 0: bfi unused for uplink
307 * Bit 1-2: sid applies to both uplink and downlink
308 * Bit 3: taf unused for uplink
309 * MVS_MODE_HR
310 * Bit 4: ufi unused for uplink
311 */
312 buf_node->frame.header.gsm_frame_type.sid =
313 ((*voc_pkt) & 0x06) >> 1;
314 voc_pkt = voc_pkt + DSP_FRAME_HDR_LEN;
315 buf_node->frame.len = pkt_len - DSP_FRAME_HDR_LEN;
316
317 memcpy(&buf_node->frame.voc_pkt[0],
318 voc_pkt,
319 buf_node->frame.len);
320
Chaithanya Krishna Bacharaju2f2e8a52011-10-17 14:54:07 +0530321 list_add_tail(&buf_node->list, &audio->out_queue);
322 break;
323 }
324
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700325 default: {
Chaithanya Krishna Bacharaju2f2e8a52011-10-17 14:54:07 +0530326 buf_node->frame.header.frame_type = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700327
328 buf_node->frame.len = pkt_len;
329
330 memcpy(&buf_node->frame.voc_pkt[0],
331 voc_pkt,
332 buf_node->frame.len);
333
334 list_add_tail(&buf_node->list, &audio->out_queue);
335 }
336 }
337 } else {
338 pr_err("%s: UL data dropped, read is slow\n", __func__);
339 }
340
341 spin_unlock_irqrestore(&audio->dsp_lock, dsp_flags);
342
343 wake_up(&audio->out_wait);
344}
345
346static void audio_mvs_process_dl_pkt(uint8_t *voc_pkt,
347 uint32_t *pkt_len,
348 void *private_data)
349{
350 struct audio_mvs_buf_node *buf_node = NULL;
351 struct audio_mvs_info_type *audio = private_data;
352 unsigned long dsp_flags;
353
354 spin_lock_irqsave(&audio->dsp_lock, dsp_flags);
355
356 if (!list_empty(&audio->in_queue)) {
357 uint32_t rate_type = audio_mvs_get_rate(audio->mvs_mode,
358 audio->rate_type);
359
360 buf_node = list_first_entry(&audio->in_queue,
361 struct audio_mvs_buf_node,
362 list);
363 list_del(&buf_node->list);
364
365 switch (audio->mvs_mode) {
366 case MVS_MODE_AMR:
367 case MVS_MODE_AMR_WB: {
368 /* Add the DSP frame info header. Header format:
369 * Bits 0-3: Frame rate
370 * Bits 4-7: Frame type
371 */
Chaithanya Krishna Bacharaju2f2e8a52011-10-17 14:54:07 +0530372 *voc_pkt =
373 ((buf_node->frame.header.frame_type & 0x0F) << 4) |
374 (rate_type & 0x0F);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700375 voc_pkt = voc_pkt + DSP_FRAME_HDR_LEN;
376
377 *pkt_len = buf_node->frame.len + DSP_FRAME_HDR_LEN;
378
379 memcpy(voc_pkt,
380 &buf_node->frame.voc_pkt[0],
381 buf_node->frame.len);
382
383 list_add_tail(&buf_node->list, &audio->free_in_queue);
384 break;
385 }
386
387 case MVS_MODE_IS127: {
388 /* Add the DSP frame info header. Header format:
389 * Bits 0-3: Frame rate
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700390 */
Chaithanya Krishna Bacharaju2f2e8a52011-10-17 14:54:07 +0530391 *voc_pkt = buf_node->frame.header.packet_rate & 0x0F;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700392 voc_pkt = voc_pkt + DSP_FRAME_HDR_LEN;
393
394 *pkt_len = buf_node->frame.len + DSP_FRAME_HDR_LEN;
395
396 memcpy(voc_pkt,
397 &buf_node->frame.voc_pkt[0],
398 buf_node->frame.len);
399
400 list_add_tail(&buf_node->list, &audio->free_in_queue);
401 break;
402 }
403
404 case MVS_MODE_G729A: {
405 /* G729 frames are 10ms each but the DSP expects 20ms
406 * worth of data, so send two 10ms frames per buffer.
407 */
408 /* Add the first DSP frame info header. Header format:
409 * Bits 0-1: Frame type
410 */
Chaithanya Krishna Bacharaju2f2e8a52011-10-17 14:54:07 +0530411 *voc_pkt = buf_node->frame.header.frame_type & 0x03;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700412 voc_pkt = voc_pkt + DSP_FRAME_HDR_LEN;
413
414 *pkt_len = buf_node->frame.len + DSP_FRAME_HDR_LEN;
415
416 memcpy(voc_pkt,
417 &buf_node->frame.voc_pkt[0],
418 buf_node->frame.len);
419 voc_pkt = voc_pkt + buf_node->frame.len;
420
421 list_add_tail(&buf_node->list, &audio->free_in_queue);
422
423 if (!list_empty(&audio->in_queue)) {
424 /* Get the second buffer. */
425 buf_node = list_first_entry(&audio->in_queue,
426 struct audio_mvs_buf_node,
427 list);
428 list_del(&buf_node->list);
429
430 /* Add the second DSP frame info header.
431 * Header format:
432 * Bits 0-1: Frame type
433 */
Chaithanya Krishna Bacharaju2f2e8a52011-10-17 14:54:07 +0530434 *voc_pkt = buf_node->frame.header.frame_type
435 & 0x03;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700436 voc_pkt = voc_pkt + DSP_FRAME_HDR_LEN;
437
438 *pkt_len = *pkt_len +
439 buf_node->frame.len + DSP_FRAME_HDR_LEN;
440
441 memcpy(voc_pkt,
442 &buf_node->frame.voc_pkt[0],
443 buf_node->frame.len);
444
445 list_add_tail(&buf_node->list,
446 &audio->free_in_queue);
447 } else {
448 /* Only 10ms worth of data is available, signal
449 * erasure frame.
450 */
451 *voc_pkt = MVS_G729A_ERASURE & 0x03;
452
453 *pkt_len = *pkt_len + DSP_FRAME_HDR_LEN;
454 }
455
456 break;
457 }
458
Chaithanya Krishna Bacharaju68993b52011-10-18 08:41:30 +0530459 case MVS_MODE_G711:
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700460 case MVS_MODE_G711A: {
461 /* G711 frames are 10ms each but the DSP expects 20ms
462 * worth of data, so send two 10ms frames per buffer.
463 */
464 /* Add the first DSP frame info header. Header format:
465 * Bits 0-1: Frame type
466 * Bits 2-3: Frame rate
467 */
468 *voc_pkt = ((rate_type & 0x0F) << 2) |
Chaithanya Krishna Bacharaju2f2e8a52011-10-17 14:54:07 +0530469 (buf_node->frame.header.frame_type & 0x03);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700470 voc_pkt = voc_pkt + DSP_FRAME_HDR_LEN;
471
472 *pkt_len = buf_node->frame.len + DSP_FRAME_HDR_LEN;
473
474 memcpy(voc_pkt,
475 &buf_node->frame.voc_pkt[0],
476 buf_node->frame.len);
477 voc_pkt = voc_pkt + buf_node->frame.len;
478
479 list_add_tail(&buf_node->list, &audio->free_in_queue);
480
481 if (!list_empty(&audio->in_queue)) {
482 /* Get the second buffer. */
483 buf_node = list_first_entry(&audio->in_queue,
484 struct audio_mvs_buf_node,
485 list);
486 list_del(&buf_node->list);
487
488 /* Add the second DSP frame info header.
489 * Header format:
490 * Bits 0-1: Frame type
491 * Bits 2-3: Frame rate
492 */
493 *voc_pkt = ((rate_type & 0x0F) << 2) |
Chaithanya Krishna Bacharaju2f2e8a52011-10-17 14:54:07 +0530494 (buf_node->frame.header.frame_type & 0x03);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700495 voc_pkt = voc_pkt + DSP_FRAME_HDR_LEN;
496
497 *pkt_len = *pkt_len +
498 buf_node->frame.len + DSP_FRAME_HDR_LEN;
499
500 memcpy(voc_pkt,
501 &buf_node->frame.voc_pkt[0],
502 buf_node->frame.len);
503
504 list_add_tail(&buf_node->list,
505 &audio->free_in_queue);
506 } else {
507 /* Only 10ms worth of data is available, signal
508 * erasure frame.
509 */
510 *voc_pkt = ((rate_type & 0x0F) << 2) |
511 (MVS_G711A_ERASURE & 0x03);
512
513 *pkt_len = *pkt_len + DSP_FRAME_HDR_LEN;
514 }
515 break;
516 }
517
Chaithanya Krishna Bacharaju68993b52011-10-18 08:41:30 +0530518 case MVS_MODE_IS733:
Chaithanya Krishna Bacharaju2f2e8a52011-10-17 14:54:07 +0530519 case MVS_MODE_4GV_NB:
520 case MVS_MODE_4GV_WB: {
521 /* Add the DSP frame info header. Header format:
522 * Bits 0-3 : Frame rate
523 */
524 *voc_pkt = buf_node->frame.header.packet_rate & 0x0F;
525 voc_pkt = voc_pkt + DSP_FRAME_HDR_LEN;
526 *pkt_len = buf_node->frame.len + DSP_FRAME_HDR_LEN;
527
528 memcpy(voc_pkt,
529 &buf_node->frame.voc_pkt[0],
530 buf_node->frame.len);
531
532 list_add_tail(&buf_node->list, &audio->free_in_queue);
533 break;
534 }
535
Chaithanya Krishna Bacharaju68993b52011-10-18 08:41:30 +0530536 case MVS_MODE_EFR:
537 case MVS_MODE_FR:
538 case MVS_MODE_HR: {
539 /*
540 * Remove the DSP frame info header
541 * Header Format
542 * Bit 0: bfi applies only for downlink
543 * Bit 1-2: sid applies for downlink and uplink
544 * Bit 3: taf applies only for downlink
545 * MVS_MODE_HR
546 * Bit 4: ufi applies only for downlink
547 */
548 *voc_pkt =
549 ((buf_node->frame.header.gsm_frame_type.bfi
550 & 0x01) |
551 ((buf_node->frame.header.gsm_frame_type.sid
552 & 0x03) << 1) |
553 ((buf_node->frame.header.gsm_frame_type.taf
554 & 0x01) << 3));
555
556 if (audio->mvs_mode == MVS_MODE_HR) {
557 *voc_pkt = (*voc_pkt |
558 ((buf_node->frame.header.gsm_frame_type.ufi
559 & 0x01) << 4) |
560 ((0 & 0x07) << 5));
561 } else {
562 *voc_pkt = (*voc_pkt |
563 ((0 & 0x0F) << 4));
564 }
565
566 voc_pkt = voc_pkt + DSP_FRAME_HDR_LEN;
567 *pkt_len = buf_node->frame.len + DSP_FRAME_HDR_LEN;
568
569 memcpy(voc_pkt,
570 &buf_node->frame.voc_pkt[0],
571 buf_node->frame.len);
572
573 list_add_tail(&buf_node->list, &audio->free_in_queue);
574
575 break;
576 }
577
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700578 default: {
579 *pkt_len = buf_node->frame.len;
580
581 memcpy(voc_pkt,
582 &buf_node->frame.voc_pkt[0],
583 buf_node->frame.len);
584
585 list_add_tail(&buf_node->list, &audio->free_in_queue);
586 }
587 }
588 } else {
589 *pkt_len = 0;
590
591 pr_info("%s: No DL data available to send to MVS\n", __func__);
592 }
593
594 spin_unlock_irqrestore(&audio->dsp_lock, dsp_flags);
Panneer Arumugam72cb0872011-07-27 14:49:06 -0700595 wake_up(&audio->in_wait);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700596}
597
598static uint32_t audio_mvs_get_media_type(uint32_t mvs_mode, uint32_t rate_type)
599{
600 uint32_t media_type;
601
602 switch (mvs_mode) {
Chaithanya Krishna Bacharaju68993b52011-10-18 08:41:30 +0530603 case MVS_MODE_IS733:
604 media_type = VSS_MEDIA_ID_13K_MODEM;
605 break;
606
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700607 case MVS_MODE_IS127:
608 media_type = VSS_MEDIA_ID_EVRC_MODEM;
609 break;
610
Chaithanya Krishna Bacharaju2f2e8a52011-10-17 14:54:07 +0530611 case MVS_MODE_4GV_NB:
612 media_type = VSS_MEDIA_ID_4GV_NB_MODEM;
613 break;
614
615 case MVS_MODE_4GV_WB:
616 media_type = VSS_MEDIA_ID_4GV_WB_MODEM;
617 break;
618
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700619 case MVS_MODE_AMR:
620 media_type = VSS_MEDIA_ID_AMR_NB_MODEM;
621 break;
622
Chaithanya Krishna Bacharaju68993b52011-10-18 08:41:30 +0530623 case MVS_MODE_EFR:
624 media_type = VSS_MEDIA_ID_EFR_MODEM;
625 break;
626
627 case MVS_MODE_FR:
628 media_type = VSS_MEDIA_ID_FR_MODEM;
629 break;
630
631 case MVS_MODE_HR:
632 media_type = VSS_MEDIA_ID_HR_MODEM;
633 break;
634
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700635 case MVS_MODE_LINEAR_PCM:
636 media_type = VSS_MEDIA_ID_PCM_NB;
637 break;
638
639 case MVS_MODE_PCM:
640 media_type = VSS_MEDIA_ID_PCM_NB;
641 break;
642
643 case MVS_MODE_AMR_WB:
644 media_type = VSS_MEDIA_ID_AMR_WB_MODEM;
645 break;
646
647 case MVS_MODE_G729A:
648 media_type = VSS_MEDIA_ID_G729;
649 break;
650
Chaithanya Krishna Bacharaju68993b52011-10-18 08:41:30 +0530651 case MVS_MODE_G711:
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700652 case MVS_MODE_G711A:
653 if (rate_type == MVS_G711A_MODE_MULAW)
654 media_type = VSS_MEDIA_ID_G711_MULAW;
655 else
656 media_type = VSS_MEDIA_ID_G711_ALAW;
657 break;
658
Neema Shetty5b3641d2011-07-29 20:30:25 -0700659 case MVS_MODE_PCM_WB:
660 media_type = VSS_MEDIA_ID_PCM_WB;
661 break;
662
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700663 default:
664 media_type = VSS_MEDIA_ID_PCM_NB;
665 }
666
667 pr_debug("%s: media_type is 0x%x\n", __func__, media_type);
668
669 return media_type;
670}
671
672static uint32_t audio_mvs_get_network_type(uint32_t mvs_mode)
673{
674 uint32_t network_type;
675
676 switch (mvs_mode) {
Chaithanya Krishna Bacharaju68993b52011-10-18 08:41:30 +0530677 case MVS_MODE_IS733:
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700678 case MVS_MODE_IS127:
Chaithanya Krishna Bacharaju2f2e8a52011-10-17 14:54:07 +0530679 case MVS_MODE_4GV_NB:
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700680 case MVS_MODE_AMR:
Chaithanya Krishna Bacharaju68993b52011-10-18 08:41:30 +0530681 case MVS_MODE_EFR:
682 case MVS_MODE_FR:
683 case MVS_MODE_HR:
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700684 case MVS_MODE_LINEAR_PCM:
Chaithanya Krishna Bacharaju68993b52011-10-18 08:41:30 +0530685 case MVS_MODE_G711:
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700686 case MVS_MODE_PCM:
687 case MVS_MODE_G729A:
688 case MVS_MODE_G711A:
689 network_type = VSS_NETWORK_ID_VOIP_NB;
690 break;
691
Chaithanya Krishna Bacharaju2f2e8a52011-10-17 14:54:07 +0530692 case MVS_MODE_4GV_WB:
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700693 case MVS_MODE_AMR_WB:
Neema Shetty5b3641d2011-07-29 20:30:25 -0700694 case MVS_MODE_PCM_WB:
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700695 network_type = VSS_NETWORK_ID_VOIP_WB;
696 break;
697
698 default:
699 network_type = VSS_NETWORK_ID_DEFAULT;
700 }
701
702 pr_debug("%s: network_type is 0x%x\n", __func__, network_type);
703
704 return network_type;
705}
706
707static int audio_mvs_start(struct audio_mvs_info_type *audio)
708{
709 int rc = 0;
710
711 pr_info("%s\n", __func__);
712
713 /* Prevent sleep. */
714 wake_lock(&audio->suspend_lock);
715 wake_lock(&audio->idle_lock);
716
717 rc = voice_set_voc_path_full(1);
718
719 if (rc == 0) {
720 voice_register_mvs_cb(audio_mvs_process_ul_pkt,
721 audio_mvs_process_dl_pkt,
722 audio);
723
724 voice_config_vocoder(
725 audio_mvs_get_media_type(audio->mvs_mode, audio->rate_type),
726 audio_mvs_get_rate(audio->mvs_mode, audio->rate_type),
727 audio_mvs_get_network_type(audio->mvs_mode),
Chaithanya Krishna Bacharaju2f2e8a52011-10-17 14:54:07 +0530728 audio->dtx_mode,
729 audio->min_max_rate);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700730
731 audio->state = AUDIO_MVS_STARTED;
732 } else {
733 pr_err("%s: Error %d setting voc path to full\n", __func__, rc);
734 }
735
736 return rc;
737}
738
739static int audio_mvs_stop(struct audio_mvs_info_type *audio)
740{
741 int rc = 0;
742
743 pr_info("%s\n", __func__);
744
745 voice_set_voc_path_full(0);
746
747 audio->state = AUDIO_MVS_STOPPED;
748
749 /* Allow sleep. */
750 wake_unlock(&audio->suspend_lock);
751 wake_unlock(&audio->idle_lock);
752
753 return rc;
754}
755
756static int audio_mvs_open(struct inode *inode, struct file *file)
757{
758 int rc = 0;
759 int i;
760 int offset = 0;
761 struct audio_mvs_buf_node *buf_node = NULL;
762
763 pr_info("%s\n", __func__);
764
765 mutex_lock(&audio_mvs_info.lock);
766
767 /* Allocate input and output buffers. */
768 audio_mvs_info.memory_chunk = kmalloc(2 * MVS_MAX_Q_LEN *
769 sizeof(struct audio_mvs_buf_node),
770 GFP_KERNEL);
771
772 if (audio_mvs_info.memory_chunk != NULL) {
773 for (i = 0; i < MVS_MAX_Q_LEN; i++) {
774 buf_node = audio_mvs_info.memory_chunk + offset;
775
776 list_add_tail(&buf_node->list,
777 &audio_mvs_info.free_in_queue);
778
779 offset = offset + sizeof(struct audio_mvs_buf_node);
780 }
781
782 for (i = 0; i < MVS_MAX_Q_LEN; i++) {
783 buf_node = audio_mvs_info.memory_chunk + offset;
784
785 list_add_tail(&buf_node->list,
786 &audio_mvs_info.free_out_queue);
787
788 offset = offset + sizeof(struct audio_mvs_buf_node);
789 }
790
791 audio_mvs_info.state = AUDIO_MVS_STOPPED;
792
793 file->private_data = &audio_mvs_info;
794
795 } else {
796 pr_err("%s: No memory for IO buffers\n", __func__);
797
798 rc = -ENOMEM;
799 }
800
801 mutex_unlock(&audio_mvs_info.lock);
802
803 return rc;
804}
805
806static int audio_mvs_release(struct inode *inode, struct file *file)
807{
808 struct list_head *ptr = NULL;
809 struct list_head *next = NULL;
810 struct audio_mvs_buf_node *buf_node = NULL;
811 struct audio_mvs_info_type *audio = file->private_data;
812
813 pr_info("%s\n", __func__);
814
815 mutex_lock(&audio->lock);
816
817 if (audio->state == AUDIO_MVS_STARTED)
818 audio_mvs_stop(audio);
819
820 /* Free input and output memory. */
821 mutex_lock(&audio->in_lock);
822
823 list_for_each_safe(ptr, next, &audio->in_queue) {
824 buf_node = list_entry(ptr, struct audio_mvs_buf_node, list);
825 list_del(&buf_node->list);
826 }
827
828 list_for_each_safe(ptr, next, &audio->free_in_queue) {
829 buf_node = list_entry(ptr, struct audio_mvs_buf_node, list);
830 list_del(&buf_node->list);
831 }
832
833 mutex_unlock(&audio->in_lock);
834
835
836 mutex_lock(&audio->out_lock);
837
838 list_for_each_safe(ptr, next, &audio->out_queue) {
839 buf_node = list_entry(ptr, struct audio_mvs_buf_node, list);
840 list_del(&buf_node->list);
841 }
842
843 list_for_each_safe(ptr, next, &audio->free_out_queue) {
844 buf_node = list_entry(ptr, struct audio_mvs_buf_node, list);
845 list_del(&buf_node->list);
846 }
847
848 mutex_unlock(&audio->out_lock);
849
850 kfree(audio->memory_chunk);
851 audio->memory_chunk = NULL;
852
853 audio->state = AUDIO_MVS_CLOSED;
854
855 mutex_unlock(&audio->lock);
856
857 return 0;
858}
859
860static ssize_t audio_mvs_read(struct file *file,
861 char __user *buf,
862 size_t count,
863 loff_t *pos)
864{
865 int rc = 0;
866 struct audio_mvs_buf_node *buf_node = NULL;
867 struct audio_mvs_info_type *audio = file->private_data;
868
869 pr_debug("%s:\n", __func__);
870
871 rc = wait_event_interruptible_timeout(audio->out_wait,
872 (!list_empty(&audio->out_queue) ||
873 audio->state == AUDIO_MVS_STOPPED),
874 1 * HZ);
875
876 if (rc > 0) {
877 mutex_lock(&audio->out_lock);
878
879 if ((audio->state == AUDIO_MVS_STARTED) &&
880 (!list_empty(&audio->out_queue))) {
881
Chaithanya Krishna Bacharaju2f2e8a52011-10-17 14:54:07 +0530882 if (count >= sizeof(struct q6_msm_audio_mvs_frame)) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700883 buf_node = list_first_entry(&audio->out_queue,
884 struct audio_mvs_buf_node,
885 list);
886 list_del(&buf_node->list);
887
888 rc = copy_to_user(buf,
889 &buf_node->frame,
Chaithanya Krishna Bacharaju2f2e8a52011-10-17 14:54:07 +0530890 sizeof(struct q6_msm_audio_mvs_frame));
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700891
892 if (rc == 0) {
893 rc = buf_node->frame.len +
Chaithanya Krishna Bacharaju2f2e8a52011-10-17 14:54:07 +0530894 sizeof(buf_node->frame.header) +
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700895 sizeof(buf_node->frame.len);
896 } else {
897 pr_err("%s: Copy to user retuned %d",
898 __func__, rc);
899
900 rc = -EFAULT;
901 }
902
903 list_add_tail(&buf_node->list,
904 &audio->free_out_queue);
905 } else {
906 pr_err("%s: Read count %d < sizeof(frame) %d",
907 __func__, count,
Chaithanya Krishna Bacharaju2f2e8a52011-10-17 14:54:07 +0530908 sizeof(struct q6_msm_audio_mvs_frame));
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700909
910 rc = -ENOMEM;
911 }
912 } else {
913 pr_err("%s: Read performed in state %d\n",
914 __func__, audio->state);
915
916 rc = -EPERM;
917 }
918
919 mutex_unlock(&audio->out_lock);
920
921 } else if (rc == 0) {
922 pr_err("%s: No UL data available\n", __func__);
923
924 rc = -ETIMEDOUT;
925 } else {
926 pr_err("%s: Read was interrupted\n", __func__);
927
928 rc = -ERESTARTSYS;
929 }
930
931 return rc;
932}
933
934static ssize_t audio_mvs_write(struct file *file,
935 const char __user *buf,
936 size_t count,
937 loff_t *pos)
938{
939 int rc = 0;
940 struct audio_mvs_buf_node *buf_node = NULL;
941 struct audio_mvs_info_type *audio = file->private_data;
942
943 pr_debug("%s:\n", __func__);
944
Panneer Arumugam72cb0872011-07-27 14:49:06 -0700945 rc = wait_event_interruptible_timeout(audio->in_wait,
946 (!list_empty(&audio->free_in_queue) ||
947 audio->state == AUDIO_MVS_STOPPED), 1 * HZ);
948 if (rc > 0) {
949 mutex_lock(&audio->in_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700950
Panneer Arumugam72cb0872011-07-27 14:49:06 -0700951 if (audio->state == AUDIO_MVS_STARTED) {
Chaithanya Krishna Bacharaju2f2e8a52011-10-17 14:54:07 +0530952 if (count <= sizeof(struct q6_msm_audio_mvs_frame)) {
Panneer Arumugam72cb0872011-07-27 14:49:06 -0700953 if (!list_empty(&audio->free_in_queue)) {
954 buf_node =
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700955 list_first_entry(&audio->free_in_queue,
Panneer Arumugam72cb0872011-07-27 14:49:06 -0700956 struct audio_mvs_buf_node, list);
957 list_del(&buf_node->list);
958 rc = copy_from_user(&buf_node->frame,
959 buf,
960 count);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700961
Panneer Arumugam72cb0872011-07-27 14:49:06 -0700962 list_add_tail(&buf_node->list,
963 &audio->in_queue);
964 } else {
965 pr_err("%s: No free DL buffs\n",
966 __func__);
967 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700968 } else {
Panneer Arumugam72cb0872011-07-27 14:49:06 -0700969 pr_err("%s: Write count %d < sizeof(frame) %d",
970 __func__, count,
Chaithanya Krishna Bacharaju2f2e8a52011-10-17 14:54:07 +0530971 sizeof(struct q6_msm_audio_mvs_frame));
Panneer Arumugam72cb0872011-07-27 14:49:06 -0700972
973 rc = -ENOMEM;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700974 }
975 } else {
Panneer Arumugam72cb0872011-07-27 14:49:06 -0700976 pr_err("%s: Write performed in invalid state %d\n",
977 __func__, audio->state);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700978
Panneer Arumugam72cb0872011-07-27 14:49:06 -0700979 rc = -EPERM;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700980 }
Panneer Arumugam72cb0872011-07-27 14:49:06 -0700981
982 mutex_unlock(&audio->in_lock);
983 } else if (rc == 0) {
984 pr_err("%s: No free DL buffs\n", __func__);
985
986 rc = -ETIMEDOUT;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700987 } else {
Panneer Arumugam72cb0872011-07-27 14:49:06 -0700988 pr_err("%s: write was interrupted\n", __func__);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700989
Panneer Arumugam72cb0872011-07-27 14:49:06 -0700990 rc = -ERESTARTSYS;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700991 }
992
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700993 return rc;
994}
995
996static long audio_mvs_ioctl(struct file *file,
997 unsigned int cmd,
998 unsigned long arg)
999{
1000 int rc = 0;
1001 struct audio_mvs_info_type *audio = file->private_data;
1002
1003 pr_info("%s:\n", __func__);
1004
1005 switch (cmd) {
1006 case AUDIO_GET_MVS_CONFIG: {
1007 struct msm_audio_mvs_config config;
1008
1009 pr_info("%s: IOCTL GET_MVS_CONFIG\n", __func__);
1010
1011 mutex_lock(&audio->lock);
1012
1013 config.mvs_mode = audio->mvs_mode;
1014 config.rate_type = audio->rate_type;
1015 config.dtx_mode = audio->dtx_mode;
Chaithanya Krishna Bacharaju2f2e8a52011-10-17 14:54:07 +05301016 config.min_max_rate.min_rate = audio->min_max_rate.min_rate;
1017 config.min_max_rate.max_rate = audio->min_max_rate.max_rate;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001018 mutex_unlock(&audio->lock);
1019
1020 rc = copy_to_user((void *)arg, &config, sizeof(config));
1021 if (rc == 0)
1022 rc = sizeof(config);
1023 else
1024 pr_err("%s: Config copy failed %d\n", __func__, rc);
1025
1026 break;
1027 }
1028
1029 case AUDIO_SET_MVS_CONFIG: {
1030 struct msm_audio_mvs_config config;
1031
1032 pr_info("%s: IOCTL SET_MVS_CONFIG\n", __func__);
1033
1034 rc = copy_from_user(&config, (void *)arg, sizeof(config));
1035 if (rc == 0) {
1036 mutex_lock(&audio->lock);
1037
1038 if (audio->state == AUDIO_MVS_STOPPED) {
1039 audio->mvs_mode = config.mvs_mode;
1040 audio->rate_type = config.rate_type;
1041 audio->dtx_mode = config.dtx_mode;
Chaithanya Krishna Bacharaju2f2e8a52011-10-17 14:54:07 +05301042 audio->min_max_rate.min_rate =
1043 config.min_max_rate.min_rate;
1044 audio->min_max_rate.max_rate =
1045 config.min_max_rate.max_rate;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001046 } else {
1047 pr_err("%s: Set confg called in state %d\n",
1048 __func__, audio->state);
1049
1050 rc = -EPERM;
1051 }
1052
1053 mutex_unlock(&audio->lock);
1054 } else {
1055 pr_err("%s: Config copy failed %d\n", __func__, rc);
1056 }
1057
1058 break;
1059 }
1060
1061 case AUDIO_START: {
1062 pr_info("%s: IOCTL START\n", __func__);
1063
1064 mutex_lock(&audio->lock);
1065
1066 if (audio->state == AUDIO_MVS_STOPPED) {
1067 rc = audio_mvs_start(audio);
1068
1069 if (rc != 0)
1070 audio_mvs_stop(audio);
1071 } else {
1072 pr_err("%s: Start called in invalid state %d\n",
1073 __func__, audio->state);
1074
1075 rc = -EPERM;
1076 }
1077
1078 mutex_unlock(&audio->lock);
1079
1080 break;
1081 }
1082
1083 case AUDIO_STOP: {
1084 pr_info("%s: IOCTL STOP\n", __func__);
1085
1086 mutex_lock(&audio->lock);
1087
1088 if (audio->state == AUDIO_MVS_STARTED) {
1089 rc = audio_mvs_stop(audio);
1090 } else {
1091 pr_err("%s: Stop called in invalid state %d\n",
1092 __func__, audio->state);
1093
1094 rc = -EPERM;
1095 }
1096
1097 mutex_unlock(&audio->lock);
1098
1099 break;
1100 }
1101
1102 default: {
1103 pr_err("%s: Unknown IOCTL %d\n", __func__, cmd);
1104 }
1105 }
1106
1107 return rc;
1108}
1109
1110static const struct file_operations audio_mvs_fops = {
1111 .owner = THIS_MODULE,
1112 .open = audio_mvs_open,
1113 .release = audio_mvs_release,
1114 .read = audio_mvs_read,
1115 .write = audio_mvs_write,
1116 .unlocked_ioctl = audio_mvs_ioctl
1117};
1118
1119struct miscdevice audio_mvs_misc = {
1120 .minor = MISC_DYNAMIC_MINOR,
1121 .name = "msm_mvs",
1122 .fops = &audio_mvs_fops
1123};
1124
1125static int __init audio_mvs_init(void)
1126{
1127 int rc = 0;
1128
1129 memset(&audio_mvs_info, 0, sizeof(audio_mvs_info));
1130
Panneer Arumugam72cb0872011-07-27 14:49:06 -07001131 init_waitqueue_head(&audio_mvs_info.in_wait);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001132 init_waitqueue_head(&audio_mvs_info.out_wait);
1133
1134 mutex_init(&audio_mvs_info.lock);
1135 mutex_init(&audio_mvs_info.in_lock);
1136 mutex_init(&audio_mvs_info.out_lock);
1137
1138 spin_lock_init(&audio_mvs_info.dsp_lock);
1139
1140 INIT_LIST_HEAD(&audio_mvs_info.in_queue);
1141 INIT_LIST_HEAD(&audio_mvs_info.free_in_queue);
1142 INIT_LIST_HEAD(&audio_mvs_info.out_queue);
1143 INIT_LIST_HEAD(&audio_mvs_info.free_out_queue);
1144
1145 wake_lock_init(&audio_mvs_info.suspend_lock,
1146 WAKE_LOCK_SUSPEND,
1147 "audio_mvs_suspend");
1148 wake_lock_init(&audio_mvs_info.idle_lock,
1149 WAKE_LOCK_IDLE,
1150 "audio_mvs_idle");
1151
1152 rc = misc_register(&audio_mvs_misc);
1153
1154 return rc;
1155}
1156
1157static void __exit audio_mvs_exit(void){
1158 pr_info("%s:\n", __func__);
1159
1160 misc_deregister(&audio_mvs_misc);
1161}
1162
1163module_init(audio_mvs_init);
1164module_exit(audio_mvs_exit);
1165
1166MODULE_DESCRIPTION("MSM MVS driver");
1167MODULE_LICENSE("GPL v2");