blob: aaae7762a7371ca32a34b4af48bf7997d7f7ca6c [file] [log] [blame]
Laxminath Kasam692c6542012-02-21 11:17:47 +05301/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002 *
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/msm_audio.h>
17#include <linux/slab.h>
18#include <linux/wait.h>
19#include <linux/sched.h>
Laxminath Kasam692c6542012-02-21 11:17:47 +053020#include <linux/workqueue.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070021#include <asm/uaccess.h>
22#include <asm/atomic.h>
23#include <mach/qdsp6v2/audio_dev_ctl.h>
24#include <mach/debug_mm.h>
25#include <mach/qdsp6v2/q6voice.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070026#include <sound/apr_audio.h>
27#include <sound/q6adm.h>
28
29#ifndef MAX
30#define MAX(x, y) (((x) > (y)) ? (x) : (y))
31#endif
32
33
34static DEFINE_MUTEX(session_lock);
Laxminath Kasam692c6542012-02-21 11:17:47 +053035static struct workqueue_struct *msm_reset_device_work_queue;
36static void reset_device_work(struct work_struct *work);
37static DECLARE_WORK(msm_reset_device_work, reset_device_work);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070038
39struct audio_dev_ctrl_state {
40 struct msm_snddev_info *devs[AUDIO_DEV_CTL_MAX_DEV];
41 u32 num_dev;
42 atomic_t opened;
43 struct msm_snddev_info *voice_rx_dev;
44 struct msm_snddev_info *voice_tx_dev;
45 wait_queue_head_t wait;
46};
47
48static struct audio_dev_ctrl_state audio_dev_ctrl;
49struct event_listner event;
50
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070051struct session_freq {
52 int freq;
53 int evt;
54};
55
56struct audio_routing_info {
57 unsigned short mixer_mask[MAX_SESSIONS];
58 unsigned short audrec_mixer_mask[MAX_SESSIONS];
59 struct session_freq dec_freq[MAX_SESSIONS];
60 struct session_freq enc_freq[MAX_SESSIONS];
61 unsigned int copp_list[MAX_SESSIONS][AFE_MAX_PORTS];
62 int voice_tx_dev_id;
63 int voice_rx_dev_id;
64 int voice_tx_sample_rate;
65 int voice_rx_sample_rate;
66 signed int voice_tx_vol;
67 signed int voice_rx_vol;
68 int tx_mute;
69 int rx_mute;
70 int voice_state;
71 struct mutex copp_list_mutex;
72 struct mutex adm_mutex;
73};
74
75static struct audio_routing_info routing_info;
76
77struct audio_copp_topology {
78 struct mutex lock;
79 int session_cnt;
80 int session_id[MAX_SESSIONS];
81 int topolog_id[MAX_SESSIONS];
82};
83static struct audio_copp_topology adm_tx_topology_tbl;
84
85int msm_reset_all_device(void)
86{
87 int rc = 0;
88 int dev_id = 0;
89 struct msm_snddev_info *dev_info = NULL;
90
91 for (dev_id = 0; dev_id < audio_dev_ctrl.num_dev; dev_id++) {
92 dev_info = audio_dev_ctrl_find_dev(dev_id);
93 if (IS_ERR(dev_info)) {
94 pr_err("%s:pass invalid dev_id\n", __func__);
95 rc = PTR_ERR(dev_info);
96 return rc;
97 }
98 if (!dev_info->opened)
99 continue;
100 pr_debug("%s:Resetting device %d active on COPP %d"
101 "with %lld as routing\n", __func__,
102 dev_id, dev_info->copp_id, dev_info->sessions);
103 broadcast_event(AUDDEV_EVT_REL_PENDING,
104 dev_id,
105 SESSION_IGNORE);
106 rc = dev_info->dev_ops.close(dev_info);
107 if (rc < 0) {
108 pr_err("%s:Snd device failed close!\n", __func__);
109 return rc;
110 } else {
111 dev_info->opened = 0;
112 broadcast_event(AUDDEV_EVT_DEV_RLS,
113 dev_id,
114 SESSION_IGNORE);
115
116 if (dev_info->copp_id == VOICE_PLAYBACK_TX)
117 voice_start_playback(0);
118 }
119 dev_info->sessions = 0;
120 }
121 msm_clear_all_session();
122 return 0;
123}
124EXPORT_SYMBOL(msm_reset_all_device);
125
Laxminath Kasam692c6542012-02-21 11:17:47 +0530126static void reset_device_work(struct work_struct *work)
127{
128 msm_reset_all_device();
129}
130
131int reset_device(void)
132{
133 queue_work(msm_reset_device_work_queue, &msm_reset_device_work);
134 return 0;
135}
136EXPORT_SYMBOL(reset_device);
137
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700138int msm_set_copp_id(int session_id, int copp_id)
139{
140 int rc = 0;
141 int index;
142
143 if (session_id < 1 || session_id > 8)
144 return -EINVAL;
145 if (afe_validate_port(copp_id) < 0)
146 return -EINVAL;
147
148 index = afe_get_port_index(copp_id);
149 if (index < 0 || index > AFE_MAX_PORTS)
150 return -EINVAL;
151 pr_debug("%s: session[%d] copp_id[%d] index[%d]\n", __func__,
152 session_id, copp_id, index);
153 mutex_lock(&routing_info.copp_list_mutex);
Bharath Ramachandramurthyb85333e2011-07-29 12:33:37 -0700154 if (routing_info.copp_list[session_id][index] == COPP_IGNORE)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700155 routing_info.copp_list[session_id][index] = copp_id;
156 mutex_unlock(&routing_info.copp_list_mutex);
157
158 return rc;
159}
160EXPORT_SYMBOL(msm_set_copp_id);
161
162int msm_clear_copp_id(int session_id, int copp_id)
163{
164 int rc = 0;
165 int index = afe_get_port_index(copp_id);
166
167 if (session_id < 1 || session_id > 8)
168 return -EINVAL;
169 pr_debug("%s: session[%d] copp_id[%d] index[%d]\n", __func__,
170 session_id, copp_id, index);
171 mutex_lock(&routing_info.copp_list_mutex);
172 if (routing_info.copp_list[session_id][index] == copp_id)
Bharath Ramachandramurthyb85333e2011-07-29 12:33:37 -0700173 routing_info.copp_list[session_id][index] = COPP_IGNORE;
Swaminathan Sathappan88163a72011-08-01 16:01:14 -0700174#ifdef CONFIG_MSM8X60_RTAC
175 rtac_remove_adm_device(copp_id, session_id);
176#endif
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700177 mutex_unlock(&routing_info.copp_list_mutex);
178
179 return rc;
180}
181EXPORT_SYMBOL(msm_clear_copp_id);
182
183int msm_clear_session_id(int session_id)
184{
185 int rc = 0;
186 int i = 0;
187 if (session_id < 1 || session_id > 8)
188 return -EINVAL;
189 pr_debug("%s: session[%d]\n", __func__, session_id);
190 mutex_lock(&routing_info.adm_mutex);
191 mutex_lock(&routing_info.copp_list_mutex);
192 for (i = 0; i < AFE_MAX_PORTS; i++) {
Bharath Ramachandramurthyb85333e2011-07-29 12:33:37 -0700193 if (routing_info.copp_list[session_id][i] != COPP_IGNORE) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700194 rc = adm_close(routing_info.copp_list[session_id][i]);
195 if (rc < 0) {
196 pr_err("%s: adm close fail port[%d] rc[%d]\n",
197 __func__,
198 routing_info.copp_list[session_id][i],
199 rc);
200 continue;
201 }
Swaminathan Sathappan88163a72011-08-01 16:01:14 -0700202#ifdef CONFIG_MSM8X60_RTAC
203 rtac_remove_adm_device(
204 routing_info.copp_list[session_id][i], session_id);
205#endif
Bharath Ramachandramurthyb85333e2011-07-29 12:33:37 -0700206 routing_info.copp_list[session_id][i] = COPP_IGNORE;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700207 rc = 0;
208 }
209 }
210 mutex_unlock(&routing_info.copp_list_mutex);
211 mutex_unlock(&routing_info.adm_mutex);
212
213 return rc;
214}
215EXPORT_SYMBOL(msm_clear_session_id);
216
217int msm_clear_all_session()
218{
219 int rc = 0;
220 int i = 0, j = 0;
221 pr_info("%s:\n", __func__);
222 mutex_lock(&routing_info.adm_mutex);
223 mutex_lock(&routing_info.copp_list_mutex);
224 for (j = 1; j < MAX_SESSIONS; j++) {
225 for (i = 0; i < AFE_MAX_PORTS; i++) {
Bharath Ramachandramurthyb85333e2011-07-29 12:33:37 -0700226 if (routing_info.copp_list[j][i] != COPP_IGNORE) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700227 rc = adm_close(
228 routing_info.copp_list[j][i]);
229 if (rc < 0) {
230 pr_err("%s: adm close fail copp[%d]"
231 "session[%d] rc[%d]\n",
232 __func__,
233 routing_info.copp_list[j][i],
234 j, rc);
235 continue;
236 }
Bharath Ramachandramurthyb85333e2011-07-29 12:33:37 -0700237 routing_info.copp_list[j][i] = COPP_IGNORE;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700238 rc = 0;
239 }
240 }
241 }
242 mutex_unlock(&routing_info.copp_list_mutex);
243 mutex_unlock(&routing_info.adm_mutex);
244 return rc;
245}
246EXPORT_SYMBOL(msm_clear_all_session);
247
248int msm_get_voice_state(void)
249{
250 pr_debug("voice state %d\n", routing_info.voice_state);
251 return routing_info.voice_state;
252}
253EXPORT_SYMBOL(msm_get_voice_state);
254
Neema Shetty90189b82011-06-27 14:58:37 -0700255int msm_set_voice_mute(int dir, int mute, u32 session_id)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700256{
257 pr_debug("dir %x mute %x\n", dir, mute);
258 if (dir == DIR_TX) {
259 routing_info.tx_mute = mute;
260 broadcast_event(AUDDEV_EVT_DEVICE_VOL_MUTE_CHG,
Neema Shetty90189b82011-06-27 14:58:37 -0700261 routing_info.voice_tx_dev_id, session_id);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700262 } else
263 return -EPERM;
264 return 0;
265}
266EXPORT_SYMBOL(msm_set_voice_mute);
267
Neema Shetty90189b82011-06-27 14:58:37 -0700268int msm_set_voice_vol(int dir, s32 volume, u32 session_id)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700269{
270 if (dir == DIR_TX) {
271 routing_info.voice_tx_vol = volume;
272 broadcast_event(AUDDEV_EVT_DEVICE_VOL_MUTE_CHG,
273 routing_info.voice_tx_dev_id,
Neema Shetty90189b82011-06-27 14:58:37 -0700274 session_id);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700275 } else if (dir == DIR_RX) {
276 routing_info.voice_rx_vol = volume;
277 broadcast_event(AUDDEV_EVT_DEVICE_VOL_MUTE_CHG,
278 routing_info.voice_rx_dev_id,
Neema Shetty90189b82011-06-27 14:58:37 -0700279 session_id);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700280 } else
281 return -EINVAL;
282 return 0;
283}
284EXPORT_SYMBOL(msm_set_voice_vol);
285
286void msm_snddev_register(struct msm_snddev_info *dev_info)
287{
288 mutex_lock(&session_lock);
289 if (audio_dev_ctrl.num_dev < AUDIO_DEV_CTL_MAX_DEV) {
290 audio_dev_ctrl.devs[audio_dev_ctrl.num_dev] = dev_info;
291 /* roughly 0 DB for digital gain
292 * If default gain is not desirable, it is expected that
293 * application sets desired gain before activating sound
294 * device
295 */
296 dev_info->dev_volume = 75;
297 dev_info->sessions = 0x0;
298 dev_info->usage_count = 0;
299 audio_dev_ctrl.num_dev++;
300 } else
301 pr_err("%s: device registry max out\n", __func__);
302 mutex_unlock(&session_lock);
303}
304EXPORT_SYMBOL(msm_snddev_register);
305
306int msm_snddev_devcount(void)
307{
308 return audio_dev_ctrl.num_dev;
309}
310EXPORT_SYMBOL(msm_snddev_devcount);
311
312int msm_snddev_query(int dev_id)
313{
314 if (dev_id <= audio_dev_ctrl.num_dev)
315 return 0;
316 return -ENODEV;
317}
318EXPORT_SYMBOL(msm_snddev_query);
319
320int msm_snddev_is_set(int popp_id, int copp_id)
321{
322 return routing_info.mixer_mask[popp_id] & (0x1 << copp_id);
323}
324EXPORT_SYMBOL(msm_snddev_is_set);
325
326unsigned short msm_snddev_route_enc(int enc_id)
327{
328 if (enc_id >= MAX_SESSIONS)
329 return -EINVAL;
330 return routing_info.audrec_mixer_mask[enc_id];
331}
332EXPORT_SYMBOL(msm_snddev_route_enc);
333
334unsigned short msm_snddev_route_dec(int popp_id)
335{
336 if (popp_id >= MAX_SESSIONS)
337 return -EINVAL;
338 return routing_info.mixer_mask[popp_id];
339}
340EXPORT_SYMBOL(msm_snddev_route_dec);
341
342/*To check one->many case*/
343int msm_check_multicopp_per_stream(int session_id,
344 struct route_payload *payload)
345{
346 int i = 0;
347 int flag = 0;
348 pr_debug("%s: session_id=%d\n", __func__, session_id);
349 mutex_lock(&routing_info.copp_list_mutex);
350 for (i = 0; i < AFE_MAX_PORTS; i++) {
Bharath Ramachandramurthyb85333e2011-07-29 12:33:37 -0700351 if (routing_info.copp_list[session_id][i] == COPP_IGNORE)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700352 continue;
353 else {
354 pr_debug("Device enabled\n");
355 payload->copp_ids[flag++] =
356 routing_info.copp_list[session_id][i];
357 }
358 }
359 mutex_unlock(&routing_info.copp_list_mutex);
360 if (flag > 1) {
361 pr_debug("Multiple copp per stream case num_copps=%d\n", flag);
362 } else {
363 pr_debug("Stream routed to single copp\n");
364 }
365 payload->num_copps = flag;
366 return flag;
367}
368
369int msm_snddev_set_dec(int popp_id, int copp_id, int set,
370 int rate, int mode)
371{
Swaminathan Sathappan88163a72011-08-01 16:01:14 -0700372 int rc = 0, i = 0, num_copps;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700373 struct route_payload payload;
374
375 if ((popp_id >= MAX_SESSIONS) || (popp_id <= 0)) {
376 pr_err("%s: Invalid session id %d\n", __func__, popp_id);
377 return 0;
378 }
379
380 mutex_lock(&routing_info.adm_mutex);
381 if (set) {
Ben Romberger974a40d2011-07-18 15:08:21 -0700382 rc = adm_open(copp_id, ADM_PATH_PLAYBACK, rate, mode,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700383 DEFAULT_COPP_TOPOLOGY);
384 if (rc < 0) {
385 pr_err("%s: adm open fail rc[%d]\n", __func__, rc);
386 rc = -EINVAL;
387 mutex_unlock(&routing_info.adm_mutex);
388 return rc;
389 }
390 msm_set_copp_id(popp_id, copp_id);
391 pr_debug("%s:Session id=%d copp_id=%d\n",
392 __func__, popp_id, copp_id);
Bharath Ramachandramurthyb85333e2011-07-29 12:33:37 -0700393 memset(payload.copp_ids, COPP_IGNORE,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700394 (sizeof(unsigned int) * AFE_MAX_PORTS));
Swaminathan Sathappan88163a72011-08-01 16:01:14 -0700395 num_copps = msm_check_multicopp_per_stream(popp_id, &payload);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700396 /* Multiple streams per copp is handled, one stream at a time */
Ben Romberger974a40d2011-07-18 15:08:21 -0700397 rc = adm_matrix_map(popp_id, ADM_PATH_PLAYBACK, num_copps,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700398 payload.copp_ids, copp_id);
399 if (rc < 0) {
400 pr_err("%s: matrix map failed rc[%d]\n",
401 __func__, rc);
402 adm_close(copp_id);
403 rc = -EINVAL;
404 mutex_unlock(&routing_info.adm_mutex);
405 return rc;
406 }
Swaminathan Sathappan88163a72011-08-01 16:01:14 -0700407#ifdef CONFIG_MSM8X60_RTAC
408 for (i = 0; i < num_copps; i++)
409 rtac_add_adm_device(payload.copp_ids[i], popp_id);
410#endif
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700411 } else {
412 for (i = 0; i < AFE_MAX_PORTS; i++) {
413 if (routing_info.copp_list[popp_id][i] == copp_id) {
414 rc = adm_close(copp_id);
415 if (rc < 0) {
416 pr_err("%s: adm close fail copp[%d]"
417 "rc[%d]\n",
418 __func__, copp_id, rc);
419 rc = -EINVAL;
420 mutex_unlock(&routing_info.adm_mutex);
421 return rc;
422 }
423 msm_clear_copp_id(popp_id, copp_id);
424 break;
425 }
426 }
427 }
428
429 if (copp_id == VOICE_PLAYBACK_TX) {
430 /* Signal uplink playback. */
431 rc = voice_start_playback(set);
432 }
433 mutex_unlock(&routing_info.adm_mutex);
434 return rc;
435}
436EXPORT_SYMBOL(msm_snddev_set_dec);
437
438
439static int check_tx_copp_topology(int session_id)
440{
441 int cnt;
442 int ret_val = -ENOENT;
443
444 cnt = adm_tx_topology_tbl.session_cnt;
445 if (cnt) {
446 do {
447 if (adm_tx_topology_tbl.session_id[cnt-1]
448 == session_id)
449 ret_val = cnt-1;
450 } while (--cnt);
451 }
452
453 return ret_val;
454}
455
456static int add_to_tx_topology_lists(int session_id, int topology)
457{
458 int idx = 0, tbl_idx;
459 int ret_val = -ENOSPC;
460
461 mutex_lock(&adm_tx_topology_tbl.lock);
462
463 tbl_idx = check_tx_copp_topology(session_id);
464 if (tbl_idx == -ENOENT) {
465 while (adm_tx_topology_tbl.session_id[idx++])
466 ;
467 tbl_idx = idx-1;
468 }
469
470 if (tbl_idx < MAX_SESSIONS) {
471 adm_tx_topology_tbl.session_id[tbl_idx] = session_id;
472 adm_tx_topology_tbl.topolog_id[tbl_idx] = topology;
473 adm_tx_topology_tbl.session_cnt++;
474
475 ret_val = 0;
476 }
477 mutex_unlock(&adm_tx_topology_tbl.lock);
478 return ret_val;
479}
480
481static void remove_from_tx_topology_lists(int session_id)
482{
483 int tbl_idx;
484
485 mutex_lock(&adm_tx_topology_tbl.lock);
486 tbl_idx = check_tx_copp_topology(session_id);
487 if (tbl_idx != -ENOENT) {
488
489 adm_tx_topology_tbl.session_cnt--;
490 adm_tx_topology_tbl.session_id[tbl_idx] = 0;
491 adm_tx_topology_tbl.topolog_id[tbl_idx] = 0;
492 }
493 mutex_unlock(&adm_tx_topology_tbl.lock);
494}
495
496int auddev_cfg_tx_copp_topology(int session_id, int cfg)
497{
498 int ret = 0;
499
500 if (cfg == DEFAULT_COPP_TOPOLOGY)
501 remove_from_tx_topology_lists(session_id);
502 else {
503 switch (cfg) {
504 case VPM_TX_SM_ECNS_COPP_TOPOLOGY:
505 case VPM_TX_DM_FLUENCE_COPP_TOPOLOGY:
506 ret = add_to_tx_topology_lists(session_id, cfg);
507 break;
508
509 default:
510 ret = -ENODEV;
511 break;
512 }
513 }
514 return ret;
515}
516
517int msm_snddev_set_enc(int popp_id, int copp_id, int set,
518 int rate, int mode)
519{
520 int topology;
521 int tbl_idx;
522 int rc = 0, i = 0;
523 mutex_lock(&routing_info.adm_mutex);
524 if (set) {
525 mutex_lock(&adm_tx_topology_tbl.lock);
526 tbl_idx = check_tx_copp_topology(popp_id);
527 if (tbl_idx == -ENOENT)
528 topology = DEFAULT_COPP_TOPOLOGY;
529 else {
530 topology = adm_tx_topology_tbl.topolog_id[tbl_idx];
531 rate = 16000;
532 }
533 mutex_unlock(&adm_tx_topology_tbl.lock);
Ben Romberger974a40d2011-07-18 15:08:21 -0700534 rc = adm_open(copp_id, ADM_PATH_LIVE_REC, rate, mode, topology);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700535 if (rc < 0) {
536 pr_err("%s: adm open fail rc[%d]\n", __func__, rc);
537 rc = -EINVAL;
538 goto fail_cmd;
539 }
540
Ben Romberger974a40d2011-07-18 15:08:21 -0700541 rc = adm_matrix_map(popp_id, ADM_PATH_LIVE_REC, 1,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700542 (unsigned int *)&copp_id, copp_id);
543 if (rc < 0) {
544 pr_err("%s: matrix map failed rc[%d]\n", __func__, rc);
545 adm_close(copp_id);
546 rc = -EINVAL;
547 goto fail_cmd;
548 }
549 msm_set_copp_id(popp_id, copp_id);
Swaminathan Sathappan88163a72011-08-01 16:01:14 -0700550#ifdef CONFIG_MSM8X60_RTAC
551 rtac_add_adm_device(copp_id, popp_id);
552#endif
553
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700554 } else {
555 for (i = 0; i < AFE_MAX_PORTS; i++) {
556 if (routing_info.copp_list[popp_id][i] == copp_id) {
557 rc = adm_close(copp_id);
558 if (rc < 0) {
559 pr_err("%s: adm close fail copp[%d]"
560 "rc[%d]\n",
561 __func__, copp_id, rc);
562 rc = -EINVAL;
563 goto fail_cmd;
564 }
565 msm_clear_copp_id(popp_id, copp_id);
566 break;
567 }
568 }
569 }
570fail_cmd:
571 mutex_unlock(&routing_info.adm_mutex);
572 return rc;
573}
574EXPORT_SYMBOL(msm_snddev_set_enc);
575
576int msm_device_is_voice(int dev_id)
577{
578 if ((dev_id == routing_info.voice_rx_dev_id)
579 || (dev_id == routing_info.voice_tx_dev_id))
580 return 0;
581 else
582 return -EINVAL;
583}
584EXPORT_SYMBOL(msm_device_is_voice);
585
586int msm_set_voc_route(struct msm_snddev_info *dev_info,
587 int stream_type, int dev_id)
588{
589 int rc = 0;
590 u64 session_mask = 0;
591
592 mutex_lock(&session_lock);
593 switch (stream_type) {
594 case AUDIO_ROUTE_STREAM_VOICE_RX:
595 if (audio_dev_ctrl.voice_rx_dev)
596 audio_dev_ctrl.voice_rx_dev->sessions &= ~0xFFFF;
597
598 if (!(dev_info->capability & SNDDEV_CAP_RX) |
599 !(dev_info->capability & SNDDEV_CAP_VOICE)) {
600 rc = -EINVAL;
601 break;
602 }
603 audio_dev_ctrl.voice_rx_dev = dev_info;
604 if (audio_dev_ctrl.voice_rx_dev) {
605 session_mask =
606 ((u64)0x1) << (MAX_BIT_PER_CLIENT * \
607 ((int)AUDDEV_CLNT_VOC-1));
608 audio_dev_ctrl.voice_rx_dev->sessions |=
609 session_mask;
610 }
611 routing_info.voice_rx_dev_id = dev_id;
612 break;
613 case AUDIO_ROUTE_STREAM_VOICE_TX:
614 if (audio_dev_ctrl.voice_tx_dev)
615 audio_dev_ctrl.voice_tx_dev->sessions &= ~0xFFFF;
616
617 if (!(dev_info->capability & SNDDEV_CAP_TX) |
618 !(dev_info->capability & SNDDEV_CAP_VOICE)) {
619 rc = -EINVAL;
620 break;
621 }
622
623 audio_dev_ctrl.voice_tx_dev = dev_info;
624 if (audio_dev_ctrl.voice_rx_dev) {
625 session_mask =
626 ((u64)0x1) << (MAX_BIT_PER_CLIENT * \
627 ((int)AUDDEV_CLNT_VOC-1));
628 audio_dev_ctrl.voice_tx_dev->sessions |=
629 session_mask;
630 }
631 routing_info.voice_tx_dev_id = dev_id;
632 break;
633 default:
634 rc = -EINVAL;
635 }
636 mutex_unlock(&session_lock);
637 return rc;
638}
639EXPORT_SYMBOL(msm_set_voc_route);
640
641void msm_release_voc_thread(void)
642{
643 wake_up(&audio_dev_ctrl.wait);
644}
645EXPORT_SYMBOL(msm_release_voc_thread);
646
647int msm_snddev_get_enc_freq(session_id)
648{
649 return routing_info.enc_freq[session_id].freq;
650}
651EXPORT_SYMBOL(msm_snddev_get_enc_freq);
652
653int msm_get_voc_freq(int *tx_freq, int *rx_freq)
654{
655 *tx_freq = routing_info.voice_tx_sample_rate;
656 *rx_freq = routing_info.voice_rx_sample_rate;
657 return 0;
658}
659EXPORT_SYMBOL(msm_get_voc_freq);
660
661int msm_get_voc_route(u32 *rx_id, u32 *tx_id)
662{
663 int rc = 0;
664
665 if (!rx_id || !tx_id)
666 return -EINVAL;
667
668 mutex_lock(&session_lock);
669 if (!audio_dev_ctrl.voice_rx_dev || !audio_dev_ctrl.voice_tx_dev) {
670 rc = -ENODEV;
671 mutex_unlock(&session_lock);
672 return rc;
673 }
674
675 *rx_id = audio_dev_ctrl.voice_rx_dev->acdb_id;
676 *tx_id = audio_dev_ctrl.voice_tx_dev->acdb_id;
677
678 mutex_unlock(&session_lock);
679
680 return rc;
681}
682EXPORT_SYMBOL(msm_get_voc_route);
683
684struct msm_snddev_info *audio_dev_ctrl_find_dev(u32 dev_id)
685{
686 struct msm_snddev_info *info;
687
688 if ((audio_dev_ctrl.num_dev - 1) < dev_id) {
689 info = ERR_PTR(-ENODEV);
690 goto error;
691 }
692
693 info = audio_dev_ctrl.devs[dev_id];
694error:
695 return info;
696
697}
698EXPORT_SYMBOL(audio_dev_ctrl_find_dev);
699
700int snddev_voice_set_volume(int vol, int path)
701{
702 if (audio_dev_ctrl.voice_rx_dev
703 && audio_dev_ctrl.voice_tx_dev) {
704 if (path)
705 audio_dev_ctrl.voice_tx_dev->dev_volume = vol;
706 else
707 audio_dev_ctrl.voice_rx_dev->dev_volume = vol;
708 } else
709 return -ENODEV;
710 return 0;
711}
712EXPORT_SYMBOL(snddev_voice_set_volume);
713
714static int audio_dev_ctrl_get_devices(struct audio_dev_ctrl_state *dev_ctrl,
715 void __user *arg)
716{
717 int rc = 0;
718 u32 index;
719 struct msm_snd_device_list work_list;
720 struct msm_snd_device_info *work_tbl;
721
722 if (copy_from_user(&work_list, arg, sizeof(work_list))) {
723 rc = -EFAULT;
724 goto error;
725 }
726
727 if (work_list.num_dev > dev_ctrl->num_dev) {
728 rc = -EINVAL;
729 goto error;
730 }
731
732 work_tbl = kmalloc(work_list.num_dev *
733 sizeof(struct msm_snd_device_info), GFP_KERNEL);
734 if (!work_tbl) {
735 rc = -ENOMEM;
736 goto error;
737 }
738
739 for (index = 0; index < dev_ctrl->num_dev; index++) {
740 work_tbl[index].dev_id = index;
741 work_tbl[index].dev_cap = dev_ctrl->devs[index]->capability;
742 strlcpy(work_tbl[index].dev_name, dev_ctrl->devs[index]->name,
743 64);
744 }
745
746 if (copy_to_user((void *) (work_list.list), work_tbl,
747 work_list.num_dev * sizeof(struct msm_snd_device_info)))
748 rc = -EFAULT;
749 kfree(work_tbl);
750error:
751 return rc;
752}
753
754
755int auddev_register_evt_listner(u32 evt_id, u32 clnt_type, u32 clnt_id,
756 void (*listner)(u32 evt_id,
757 union auddev_evt_data *evt_payload,
758 void *private_data),
759 void *private_data)
760{
761 int rc;
762 struct msm_snd_evt_listner *callback = NULL;
763 struct msm_snd_evt_listner *new_cb;
764
765 new_cb = kzalloc(sizeof(struct msm_snd_evt_listner), GFP_KERNEL);
766 if (!new_cb) {
767 pr_err("No memory to add new listener node\n");
768 return -ENOMEM;
769 }
770
771 mutex_lock(&session_lock);
772 new_cb->cb_next = NULL;
773 new_cb->auddev_evt_listener = listner;
774 new_cb->evt_id = evt_id;
775 new_cb->clnt_type = clnt_type;
776 new_cb->clnt_id = clnt_id;
777 new_cb->private_data = private_data;
778 if (event.cb == NULL) {
779 event.cb = new_cb;
780 new_cb->cb_prev = NULL;
781 } else {
782 callback = event.cb;
783 for (; ;) {
784 if (callback->cb_next == NULL)
785 break;
786 else {
787 callback = callback->cb_next;
788 continue;
789 }
790 }
791 callback->cb_next = new_cb;
792 new_cb->cb_prev = callback;
793 }
794 event.num_listner++;
795 mutex_unlock(&session_lock);
796 rc = 0;
797 return rc;
798}
799EXPORT_SYMBOL(auddev_register_evt_listner);
800
801int auddev_unregister_evt_listner(u32 clnt_type, u32 clnt_id)
802{
803 struct msm_snd_evt_listner *callback = event.cb;
804 struct msm_snddev_info *info;
805 u64 session_mask = 0;
806 int i = 0;
807
808 mutex_lock(&session_lock);
809 while (callback != NULL) {
810 if ((callback->clnt_type == clnt_type)
811 && (callback->clnt_id == clnt_id))
812 break;
813 callback = callback->cb_next;
814 }
815 if (callback == NULL) {
816 mutex_unlock(&session_lock);
817 return -EINVAL;
818 }
819
820 if ((callback->cb_next == NULL) && (callback->cb_prev == NULL))
821 event.cb = NULL;
822 else if (callback->cb_next == NULL)
823 callback->cb_prev->cb_next = NULL;
824 else if (callback->cb_prev == NULL) {
825 callback->cb_next->cb_prev = NULL;
826 event.cb = callback->cb_next;
827 } else {
828 callback->cb_prev->cb_next = callback->cb_next;
829 callback->cb_next->cb_prev = callback->cb_prev;
830 }
831 kfree(callback);
832
833 session_mask = (((u64)0x1) << clnt_id) << (MAX_BIT_PER_CLIENT * \
834 ((int)clnt_type-1));
835 for (i = 0; i < audio_dev_ctrl.num_dev; i++) {
836 info = audio_dev_ctrl.devs[i];
837 info->sessions &= ~session_mask;
838 }
839 mutex_unlock(&session_lock);
840 return 0;
841}
842EXPORT_SYMBOL(auddev_unregister_evt_listner);
843
844int msm_snddev_withdraw_freq(u32 session_id, u32 capability, u32 clnt_type)
845{
846 int i = 0;
847 struct msm_snddev_info *info;
848 u64 session_mask = 0;
849
850 if ((clnt_type == AUDDEV_CLNT_VOC) && (session_id != 0))
851 return -EINVAL;
852 if ((clnt_type == AUDDEV_CLNT_DEC)
853 && (session_id >= MAX_SESSIONS))
854 return -EINVAL;
855 if ((clnt_type == AUDDEV_CLNT_ENC)
856 && (session_id >= MAX_SESSIONS))
857 return -EINVAL;
858
859 session_mask = (((u64)0x1) << session_id) << (MAX_BIT_PER_CLIENT * \
860 ((int)clnt_type-1));
861
862 for (i = 0; i < audio_dev_ctrl.num_dev; i++) {
863 info = audio_dev_ctrl.devs[i];
864 if ((info->sessions & session_mask)
865 && (info->capability & capability)) {
866 if (!(info->sessions & ~(session_mask)))
867 info->set_sample_rate = 0;
868 }
869 }
870 if (clnt_type == AUDDEV_CLNT_DEC)
871 routing_info.dec_freq[session_id].freq
872 = 0;
873 else if (clnt_type == AUDDEV_CLNT_ENC)
874 routing_info.enc_freq[session_id].freq
875 = 0;
876 else if (capability == SNDDEV_CAP_TX)
877 routing_info.voice_tx_sample_rate = 0;
878 else
879 routing_info.voice_rx_sample_rate = 48000;
880 return 0;
881}
882
883int msm_snddev_request_freq(int *freq, u32 session_id,
884 u32 capability, u32 clnt_type)
885{
886 int i = 0;
887 int rc = 0;
888 struct msm_snddev_info *info;
889 u32 set_freq;
890 u64 session_mask = 0;
891 u64 clnt_type_mask = 0;
892
893 pr_debug(": clnt_type 0x%08x\n", clnt_type);
894
895 if ((clnt_type == AUDDEV_CLNT_VOC) && (session_id != 0))
896 return -EINVAL;
897 if ((clnt_type == AUDDEV_CLNT_DEC)
898 && (session_id >= MAX_SESSIONS))
899 return -EINVAL;
900 if ((clnt_type == AUDDEV_CLNT_ENC)
901 && (session_id >= MAX_SESSIONS))
902 return -EINVAL;
903 session_mask = (((u64)0x1) << session_id) << (MAX_BIT_PER_CLIENT * \
904 ((int)clnt_type-1));
905 clnt_type_mask = (0xFFFF << (MAX_BIT_PER_CLIENT * (clnt_type-1)));
906 if (!(*freq == 8000) && !(*freq == 11025) &&
907 !(*freq == 12000) && !(*freq == 16000) &&
908 !(*freq == 22050) && !(*freq == 24000) &&
909 !(*freq == 32000) && !(*freq == 44100) &&
910 !(*freq == 48000))
911 return -EINVAL;
912
913 for (i = 0; i < audio_dev_ctrl.num_dev; i++) {
914 info = audio_dev_ctrl.devs[i];
915 if ((info->sessions & session_mask)
916 && (info->capability & capability)) {
917 rc = 0;
918 if ((info->sessions & ~clnt_type_mask)
919 && ((*freq != 8000) && (*freq != 16000)
920 && (*freq != 48000))) {
921 if (clnt_type == AUDDEV_CLNT_ENC) {
922 routing_info.enc_freq[session_id].freq
923 = 0;
924 return -EPERM;
925 } else if (clnt_type == AUDDEV_CLNT_DEC) {
926 routing_info.dec_freq[session_id].freq
927 = 0;
928 return -EPERM;
929 }
930 }
931 if (*freq == info->set_sample_rate) {
932 rc = info->set_sample_rate;
933 continue;
934 }
935 set_freq = MAX(*freq, info->set_sample_rate);
936
937
938 if (clnt_type == AUDDEV_CLNT_DEC) {
939 routing_info.dec_freq[session_id].evt = 1;
940 routing_info.dec_freq[session_id].freq
941 = set_freq;
942 } else if (clnt_type == AUDDEV_CLNT_ENC) {
943 routing_info.enc_freq[session_id].evt = 1;
944 routing_info.enc_freq[session_id].freq
945 = set_freq;
946 } else if (capability == SNDDEV_CAP_TX)
947 routing_info.voice_tx_sample_rate = set_freq;
948
949 rc = set_freq;
950 info->set_sample_rate = set_freq;
951 *freq = info->set_sample_rate;
952
953 if (info->opened) {
954 broadcast_event(AUDDEV_EVT_FREQ_CHG, i,
955 SESSION_IGNORE);
956 set_freq = info->dev_ops.set_freq(info,
957 set_freq);
958 broadcast_event(AUDDEV_EVT_DEV_RDY, i,
959 SESSION_IGNORE);
960 }
961 }
962 pr_debug("info->set_sample_rate = %d\n", info->set_sample_rate);
963 pr_debug("routing_info.enc_freq.freq = %d\n",
964 routing_info.enc_freq[session_id].freq);
965 }
966 return rc;
967}
968EXPORT_SYMBOL(msm_snddev_request_freq);
969
970int msm_snddev_enable_sidetone(u32 dev_id, u32 enable, uint16_t gain)
971{
972 int rc;
973 struct msm_snddev_info *dev_info;
974
975 pr_debug("dev_id %d enable %d\n", dev_id, enable);
976
977 dev_info = audio_dev_ctrl_find_dev(dev_id);
978
979 if (IS_ERR(dev_info)) {
980 pr_err("bad dev_id %d\n", dev_id);
981 rc = -EINVAL;
982 } else if (!dev_info->dev_ops.enable_sidetone) {
983 pr_debug("dev %d no sidetone support\n", dev_id);
984 rc = -EPERM;
985 } else
986 rc = dev_info->dev_ops.enable_sidetone(dev_info, enable, gain);
987
988 return rc;
989}
990EXPORT_SYMBOL(msm_snddev_enable_sidetone);
991
992int msm_enable_incall_recording(int popp_id, int rec_mode, int rate,
993 int channel_mode)
994{
995 int rc = 0;
996 unsigned int port_id[2];
997 port_id[0] = VOICE_RECORD_TX;
998 port_id[1] = VOICE_RECORD_RX;
999
1000 pr_debug("%s: popp_id %d, rec_mode %d, rate %d, channel_mode %d\n",
1001 __func__, popp_id, rec_mode, rate, channel_mode);
1002
1003 mutex_lock(&routing_info.adm_mutex);
1004
1005 if (rec_mode == VOC_REC_UPLINK) {
1006 rc = afe_start_pseudo_port(port_id[0]);
1007 if (rc < 0) {
1008 pr_err("%s: Error %d in Tx pseudo port start\n",
1009 __func__, rc);
1010
1011 goto fail_cmd;
1012 }
1013
Ben Romberger974a40d2011-07-18 15:08:21 -07001014 rc = adm_open(port_id[0], ADM_PATH_LIVE_REC, rate, channel_mode,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001015 DEFAULT_COPP_TOPOLOGY);
1016 if (rc < 0) {
1017 pr_err("%s: Error %d in ADM open %d\n",
1018 __func__, rc, port_id[0]);
1019
1020 goto fail_cmd;
1021 }
1022
Ben Romberger974a40d2011-07-18 15:08:21 -07001023 rc = adm_matrix_map(popp_id, ADM_PATH_LIVE_REC, 1,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001024 &port_id[0], port_id[0]);
1025 if (rc < 0) {
1026 pr_err("%s: Error %d in ADM matrix map %d\n",
1027 __func__, rc, port_id[0]);
1028
1029 goto fail_cmd;
1030 }
1031
1032 msm_set_copp_id(popp_id, port_id[0]);
1033
1034 } else if (rec_mode == VOC_REC_DOWNLINK) {
1035 rc = afe_start_pseudo_port(port_id[1]);
1036 if (rc < 0) {
1037 pr_err("%s: Error %d in Rx pseudo port start\n",
1038 __func__, rc);
1039
1040 goto fail_cmd;
1041 }
1042
Ben Romberger974a40d2011-07-18 15:08:21 -07001043 rc = adm_open(port_id[1], ADM_PATH_LIVE_REC, rate, channel_mode,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001044 DEFAULT_COPP_TOPOLOGY);
1045 if (rc < 0) {
1046 pr_err("%s: Error %d in ADM open %d\n",
1047 __func__, rc, port_id[1]);
1048
1049 goto fail_cmd;
1050 }
1051
Ben Romberger974a40d2011-07-18 15:08:21 -07001052 rc = adm_matrix_map(popp_id, ADM_PATH_LIVE_REC, 1,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001053 &port_id[1], port_id[1]);
1054 if (rc < 0) {
1055 pr_err("%s: Error %d in ADM matrix map %d\n",
1056 __func__, rc, port_id[1]);
1057
1058 goto fail_cmd;
1059 }
1060
1061 msm_set_copp_id(popp_id, port_id[1]);
1062
1063 } else if (rec_mode == VOC_REC_BOTH) {
1064 rc = afe_start_pseudo_port(port_id[0]);
1065 if (rc < 0) {
1066 pr_err("%s: Error %d in Tx pseudo port start\n",
1067 __func__, rc);
1068
1069 goto fail_cmd;
1070 }
1071
Ben Romberger974a40d2011-07-18 15:08:21 -07001072 rc = adm_open(port_id[0], ADM_PATH_LIVE_REC, rate, channel_mode,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001073 DEFAULT_COPP_TOPOLOGY);
1074 if (rc < 0) {
1075 pr_err("%s: Error %d in ADM open %d\n",
1076 __func__, rc, port_id[0]);
1077
1078 goto fail_cmd;
1079 }
1080
1081 msm_set_copp_id(popp_id, port_id[0]);
1082
1083 rc = afe_start_pseudo_port(port_id[1]);
1084 if (rc < 0) {
1085 pr_err("%s: Error %d in Rx pseudo port start\n",
1086 __func__, rc);
1087
1088 goto fail_cmd;
1089 }
1090
Ben Romberger974a40d2011-07-18 15:08:21 -07001091 rc = adm_open(port_id[1], ADM_PATH_LIVE_REC, rate, channel_mode,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001092 DEFAULT_COPP_TOPOLOGY);
1093 if (rc < 0) {
1094 pr_err("%s: Error %d in ADM open %d\n",
1095 __func__, rc, port_id[0]);
1096
1097 goto fail_cmd;
1098 }
1099
Ben Romberger974a40d2011-07-18 15:08:21 -07001100 rc = adm_matrix_map(popp_id, ADM_PATH_LIVE_REC, 2,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001101 &port_id[0], port_id[1]);
1102 if (rc < 0) {
1103 pr_err("%s: Error %d in ADM matrix map\n",
1104 __func__, rc);
1105
1106 goto fail_cmd;
1107 }
1108
1109 msm_set_copp_id(popp_id, port_id[1]);
1110 } else {
1111 pr_err("%s Unknown rec_mode %d\n", __func__, rec_mode);
1112
1113 goto fail_cmd;
1114 }
1115
1116 rc = voice_start_record(rec_mode, 1);
1117
1118fail_cmd:
1119 mutex_unlock(&routing_info.adm_mutex);
1120 return rc;
1121}
1122
1123int msm_disable_incall_recording(uint32_t popp_id, uint32_t rec_mode)
1124{
1125 int rc = 0;
1126 uint32_t port_id[2];
1127 port_id[0] = VOICE_RECORD_TX;
1128 port_id[1] = VOICE_RECORD_RX;
1129
1130 pr_debug("%s: popp_id %d, rec_mode %d\n", __func__, popp_id, rec_mode);
1131
1132 mutex_lock(&routing_info.adm_mutex);
1133
1134 rc = voice_start_record(rec_mode, 0);
1135 if (rc < 0) {
1136 pr_err("%s: Error %d stopping record\n", __func__, rc);
1137
1138 goto fail_cmd;
1139 }
1140
1141 if (rec_mode == VOC_REC_UPLINK) {
1142 rc = adm_close(port_id[0]);
1143 if (rc < 0) {
1144 pr_err("%s: Error %d in ADM close %d\n",
1145 __func__, rc, port_id[0]);
1146
1147 goto fail_cmd;
1148 }
1149
1150 msm_clear_copp_id(popp_id, port_id[0]);
1151
1152 rc = afe_stop_pseudo_port(port_id[0]);
1153 if (rc < 0) {
1154 pr_err("%s: Error %d in Tx pseudo port stop\n",
1155 __func__, rc);
1156 goto fail_cmd;
1157 }
1158
1159 } else if (rec_mode == VOC_REC_DOWNLINK) {
1160 rc = adm_close(port_id[1]);
1161 if (rc < 0) {
1162 pr_err("%s: Error %d in ADM close %d\n",
1163 __func__, rc, port_id[1]);
1164
1165 goto fail_cmd;
1166 }
1167
1168 msm_clear_copp_id(popp_id, port_id[1]);
1169
1170 rc = afe_stop_pseudo_port(port_id[1]);
1171 if (rc < 0) {
1172 pr_err("%s: Error %d in Rx pseudo port stop\n",
1173 __func__, rc);
1174 goto fail_cmd;
1175 }
1176 } else if (rec_mode == VOC_REC_BOTH) {
1177 rc = adm_close(port_id[0]);
1178 if (rc < 0) {
1179 pr_err("%s: Error %d in ADM close %d\n",
1180 __func__, rc, port_id[0]);
1181
1182 goto fail_cmd;
1183 }
1184
1185 msm_clear_copp_id(popp_id, port_id[0]);
1186
1187 rc = afe_stop_pseudo_port(port_id[0]);
1188 if (rc < 0) {
1189 pr_err("%s: Error %d in Tx pseudo port stop\n",
1190 __func__, rc);
1191 goto fail_cmd;
1192 }
1193
1194 rc = adm_close(port_id[1]);
1195 if (rc < 0) {
1196 pr_err("%s: Error %d in ADM close %d\n",
1197 __func__, rc, port_id[1]);
1198
1199 goto fail_cmd;
1200 }
1201
1202 msm_clear_copp_id(popp_id, port_id[1]);
1203
1204 rc = afe_stop_pseudo_port(port_id[1]);
1205 if (rc < 0) {
1206 pr_err("%s: Error %d in Rx pseudo port stop\n",
1207 __func__, rc);
1208 goto fail_cmd;
1209 }
1210 } else {
1211 pr_err("%s Unknown rec_mode %d\n", __func__, rec_mode);
1212
1213 goto fail_cmd;
1214 }
1215
1216fail_cmd:
1217 mutex_unlock(&routing_info.adm_mutex);
1218 return rc;
1219}
1220
1221static long audio_dev_ctrl_ioctl(struct file *file,
1222 unsigned int cmd, unsigned long arg)
1223{
1224 int rc = 0;
1225 struct audio_dev_ctrl_state *dev_ctrl = file->private_data;
1226
1227 mutex_lock(&session_lock);
1228 switch (cmd) {
1229 case AUDIO_GET_NUM_SND_DEVICE:
1230 rc = put_user(dev_ctrl->num_dev, (uint32_t __user *) arg);
1231 break;
1232 case AUDIO_GET_SND_DEVICES:
1233 rc = audio_dev_ctrl_get_devices(dev_ctrl, (void __user *) arg);
1234 break;
1235 case AUDIO_ENABLE_SND_DEVICE: {
1236 struct msm_snddev_info *dev_info;
1237 u32 dev_id;
1238
1239 if (get_user(dev_id, (u32 __user *) arg)) {
1240 rc = -EFAULT;
1241 break;
1242 }
1243 dev_info = audio_dev_ctrl_find_dev(dev_id);
1244 if (IS_ERR(dev_info))
1245 rc = PTR_ERR(dev_info);
1246 else {
1247 rc = dev_info->dev_ops.open(dev_info);
1248 if (!rc)
1249 dev_info->opened = 1;
1250 wake_up(&audio_dev_ctrl.wait);
1251 }
1252 break;
1253
1254 }
1255
1256 case AUDIO_DISABLE_SND_DEVICE: {
1257 struct msm_snddev_info *dev_info;
1258 u32 dev_id;
1259
1260 if (get_user(dev_id, (u32 __user *) arg)) {
1261 rc = -EFAULT;
1262 break;
1263 }
1264 dev_info = audio_dev_ctrl_find_dev(dev_id);
1265 if (IS_ERR(dev_info))
1266 rc = PTR_ERR(dev_info);
1267 else {
1268 rc = dev_info->dev_ops.close(dev_info);
1269 dev_info->opened = 0;
1270 }
1271 break;
1272 }
1273
1274 case AUDIO_ROUTE_STREAM: {
1275 struct msm_audio_route_config route_cfg;
1276 struct msm_snddev_info *dev_info;
1277
1278 if (copy_from_user(&route_cfg, (void __user *) arg,
1279 sizeof(struct msm_audio_route_config))) {
1280 rc = -EFAULT;
1281 break;
1282 }
1283 pr_debug("%s: route cfg %d %d type\n", __func__,
1284 route_cfg.dev_id, route_cfg.stream_type);
1285 dev_info = audio_dev_ctrl_find_dev(route_cfg.dev_id);
1286 if (IS_ERR(dev_info)) {
1287 pr_err("%s: pass invalid dev_id\n", __func__);
1288 rc = PTR_ERR(dev_info);
1289 break;
1290 }
1291
1292 switch (route_cfg.stream_type) {
1293
1294 case AUDIO_ROUTE_STREAM_VOICE_RX:
1295 if (!(dev_info->capability & SNDDEV_CAP_RX) |
1296 !(dev_info->capability & SNDDEV_CAP_VOICE)) {
1297 rc = -EINVAL;
1298 break;
1299 }
1300 dev_ctrl->voice_rx_dev = dev_info;
1301 break;
1302 case AUDIO_ROUTE_STREAM_VOICE_TX:
1303 if (!(dev_info->capability & SNDDEV_CAP_TX) |
1304 !(dev_info->capability & SNDDEV_CAP_VOICE)) {
1305 rc = -EINVAL;
1306 break;
1307 }
1308 dev_ctrl->voice_tx_dev = dev_info;
1309 break;
1310 }
1311 break;
1312 }
1313
1314 default:
1315 rc = -EINVAL;
1316 }
1317 mutex_unlock(&session_lock);
1318 return rc;
1319}
1320
1321static int audio_dev_ctrl_open(struct inode *inode, struct file *file)
1322{
1323 pr_debug("open audio_dev_ctrl\n");
1324 atomic_inc(&audio_dev_ctrl.opened);
1325 file->private_data = &audio_dev_ctrl;
1326 return 0;
1327}
1328
1329static int audio_dev_ctrl_release(struct inode *inode, struct file *file)
1330{
1331 pr_debug("release audio_dev_ctrl\n");
1332 atomic_dec(&audio_dev_ctrl.opened);
1333 return 0;
1334}
1335
1336static const struct file_operations audio_dev_ctrl_fops = {
1337 .owner = THIS_MODULE,
1338 .open = audio_dev_ctrl_open,
1339 .release = audio_dev_ctrl_release,
1340 .unlocked_ioctl = audio_dev_ctrl_ioctl,
1341};
1342
1343
1344struct miscdevice audio_dev_ctrl_misc = {
1345 .minor = MISC_DYNAMIC_MINOR,
1346 .name = "msm_audio_dev_ctrl",
1347 .fops = &audio_dev_ctrl_fops,
1348};
1349
1350/* session id is 64 bit routing mask per device
1351 * 0-15 for voice clients
1352 * 16-31 for Decoder clients
1353 * 32-47 for Encoder clients
1354 * 48-63 Do not care
1355 */
1356void broadcast_event(u32 evt_id, u32 dev_id, u64 session_id)
1357{
1358 int clnt_id = 0, i;
1359 union auddev_evt_data *evt_payload;
1360 struct msm_snd_evt_listner *callback;
1361 struct msm_snddev_info *dev_info = NULL;
1362 u64 session_mask = 0;
1363 static int pending_sent;
1364
1365 pr_debug(": evt_id = %d\n", evt_id);
1366
1367 if ((evt_id != AUDDEV_EVT_START_VOICE)
1368 && (evt_id != AUDDEV_EVT_END_VOICE)
1369 && (evt_id != AUDDEV_EVT_STREAM_VOL_CHG)
1370 && (evt_id != AUDDEV_EVT_VOICE_STATE_CHG)) {
1371 dev_info = audio_dev_ctrl_find_dev(dev_id);
1372 if (IS_ERR(dev_info)) {
1373 pr_err("%s: pass invalid dev_id(%d)\n",
1374 __func__, dev_id);
1375 return;
1376 }
1377 }
1378
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001379 if (event.cb != NULL)
1380 callback = event.cb;
1381 else
1382 return;
1383 mutex_lock(&session_lock);
1384
1385 if (evt_id == AUDDEV_EVT_VOICE_STATE_CHG)
1386 routing_info.voice_state = dev_id;
1387
1388 evt_payload = kzalloc(sizeof(union auddev_evt_data),
1389 GFP_KERNEL);
1390
1391 if (evt_payload == NULL) {
1392 pr_err("broadcast_event: cannot allocate memory\n");
1393 mutex_unlock(&session_lock);
1394 return;
1395 }
1396 for (; ;) {
1397 if (!(evt_id & callback->evt_id)) {
1398 if (callback->cb_next == NULL)
1399 break;
1400 else {
1401 callback = callback->cb_next;
1402 continue;
1403 }
1404 }
1405 clnt_id = callback->clnt_id;
1406 memset(evt_payload, 0, sizeof(union auddev_evt_data));
1407
1408 if ((evt_id == AUDDEV_EVT_START_VOICE)
Neema Shetty90189b82011-06-27 14:58:37 -07001409 || (evt_id == AUDDEV_EVT_END_VOICE)
1410 || evt_id == AUDDEV_EVT_DEVICE_VOL_MUTE_CHG)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001411 goto skip_check;
1412 if (callback->clnt_type == AUDDEV_CLNT_AUDIOCAL)
1413 goto aud_cal;
1414
1415 session_mask = (((u64)0x1) << clnt_id)
1416 << (MAX_BIT_PER_CLIENT * \
1417 ((int)callback->clnt_type-1));
1418
1419 if ((evt_id == AUDDEV_EVT_STREAM_VOL_CHG) || \
1420 (evt_id == AUDDEV_EVT_VOICE_STATE_CHG)) {
1421 pr_debug("AUDDEV_EVT_STREAM_VOL_CHG or\
1422 AUDDEV_EVT_VOICE_STATE_CHG\n");
1423 goto volume_strm;
1424 }
1425
1426 pr_debug("dev_info->sessions = %llu\n", dev_info->sessions);
1427
1428 if ((!session_id && !(dev_info->sessions & session_mask)) ||
1429 (session_id && ((dev_info->sessions & session_mask) !=
1430 session_id))) {
1431 if (callback->cb_next == NULL)
1432 break;
1433 else {
1434 callback = callback->cb_next;
1435 continue;
1436 }
1437 }
1438 if (evt_id == AUDDEV_EVT_DEV_CHG_VOICE)
1439 goto voc_events;
1440
1441volume_strm:
1442 if (callback->clnt_type == AUDDEV_CLNT_DEC) {
1443 pr_debug("AUDDEV_CLNT_DEC\n");
1444 if (evt_id == AUDDEV_EVT_STREAM_VOL_CHG) {
1445 pr_debug("clnt_id = %d, session_id = %llu\n",
1446 clnt_id, session_id);
1447 if (session_mask != session_id)
1448 goto sent_dec;
1449 else
1450 evt_payload->session_vol =
1451 msm_vol_ctl.volume;
1452 } else if (evt_id == AUDDEV_EVT_FREQ_CHG) {
1453 if (routing_info.dec_freq[clnt_id].evt) {
1454 routing_info.dec_freq[clnt_id].evt
1455 = 0;
1456 goto sent_dec;
1457 } else if (routing_info.dec_freq[clnt_id].freq
1458 == dev_info->set_sample_rate)
1459 goto sent_dec;
1460 else {
1461 evt_payload->freq_info.sample_rate
1462 = dev_info->set_sample_rate;
1463 evt_payload->freq_info.dev_type
1464 = dev_info->capability;
1465 evt_payload->freq_info.acdb_dev_id
1466 = dev_info->acdb_id;
1467 }
1468 } else if (evt_id == AUDDEV_EVT_VOICE_STATE_CHG)
1469 evt_payload->voice_state =
1470 routing_info.voice_state;
1471 else
1472 evt_payload->routing_id = dev_info->copp_id;
1473 callback->auddev_evt_listener(
1474 evt_id,
1475 evt_payload,
1476 callback->private_data);
1477sent_dec:
1478 if ((evt_id != AUDDEV_EVT_STREAM_VOL_CHG) &&
1479 (evt_id != AUDDEV_EVT_VOICE_STATE_CHG))
1480 routing_info.dec_freq[clnt_id].freq
1481 = dev_info->set_sample_rate;
1482
1483 if (callback->cb_next == NULL)
1484 break;
1485 else {
1486 callback = callback->cb_next;
1487 continue;
1488 }
1489 }
1490 if (callback->clnt_type == AUDDEV_CLNT_ENC) {
1491 pr_debug("AUDDEV_CLNT_ENC\n");
1492 if (evt_id == AUDDEV_EVT_FREQ_CHG) {
1493 if (routing_info.enc_freq[clnt_id].evt) {
1494 routing_info.enc_freq[clnt_id].evt
1495 = 0;
1496 goto sent_enc;
1497 } else {
1498 evt_payload->freq_info.sample_rate
1499 = dev_info->set_sample_rate;
1500 evt_payload->freq_info.dev_type
1501 = dev_info->capability;
1502 evt_payload->freq_info.acdb_dev_id
1503 = dev_info->acdb_id;
1504 }
1505 } else if (evt_id == AUDDEV_EVT_VOICE_STATE_CHG)
1506 evt_payload->voice_state =
1507 routing_info.voice_state;
1508 else
1509 evt_payload->routing_id = dev_info->copp_id;
1510 callback->auddev_evt_listener(
1511 evt_id,
1512 evt_payload,
1513 callback->private_data);
1514sent_enc:
1515 if (callback->cb_next == NULL)
1516 break;
1517 else {
1518 callback = callback->cb_next;
1519 continue;
1520 }
1521 }
1522aud_cal:
1523 if (callback->clnt_type == AUDDEV_CLNT_AUDIOCAL) {
1524 pr_debug("AUDDEV_CLNT_AUDIOCAL\n");
1525 if (evt_id == AUDDEV_EVT_VOICE_STATE_CHG)
1526 evt_payload->voice_state =
1527 routing_info.voice_state;
1528 else if (!dev_info->sessions)
1529 goto sent_aud_cal;
1530 else {
1531 evt_payload->audcal_info.dev_id =
1532 dev_info->copp_id;
1533 evt_payload->audcal_info.acdb_id =
1534 dev_info->acdb_id;
1535 evt_payload->audcal_info.dev_type =
1536 (dev_info->capability & SNDDEV_CAP_TX) ?
1537 SNDDEV_CAP_TX : SNDDEV_CAP_RX;
1538 evt_payload->audcal_info.sample_rate =
1539 dev_info->set_sample_rate ?
1540 dev_info->set_sample_rate :
1541 dev_info->sample_rate;
1542 }
1543 callback->auddev_evt_listener(
1544 evt_id,
1545 evt_payload,
1546 callback->private_data);
1547
1548sent_aud_cal:
1549 if (callback->cb_next == NULL)
1550 break;
1551 else {
1552 callback = callback->cb_next;
1553 continue;
1554 }
1555 }
1556skip_check:
1557voc_events:
1558 if (callback->clnt_type == AUDDEV_CLNT_VOC) {
1559 pr_debug("AUDDEV_CLNT_VOC\n");
1560 if (evt_id == AUDDEV_EVT_DEV_RLS) {
1561 if (!pending_sent)
1562 goto sent_voc;
1563 else
1564 pending_sent = 0;
1565 }
1566 if (evt_id == AUDDEV_EVT_REL_PENDING)
1567 pending_sent = 1;
1568
1569 if (evt_id == AUDDEV_EVT_DEVICE_VOL_MUTE_CHG) {
Neema Shetty90189b82011-06-27 14:58:37 -07001570 evt_payload->voc_vm_info.voice_session_id =
1571 session_id;
1572
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001573 if (dev_info->capability & SNDDEV_CAP_TX) {
1574 evt_payload->voc_vm_info.dev_type =
1575 SNDDEV_CAP_TX;
1576 evt_payload->voc_vm_info.acdb_dev_id =
1577 dev_info->acdb_id;
1578 evt_payload->
1579 voc_vm_info.dev_vm_val.mute =
1580 routing_info.tx_mute;
1581 } else {
1582 evt_payload->voc_vm_info.dev_type =
1583 SNDDEV_CAP_RX;
1584 evt_payload->voc_vm_info.acdb_dev_id =
1585 dev_info->acdb_id;
1586 evt_payload->
1587 voc_vm_info.dev_vm_val.vol =
1588 routing_info.voice_rx_vol;
1589 }
1590 } else if ((evt_id == AUDDEV_EVT_START_VOICE)
Neema Shetty90189b82011-06-27 14:58:37 -07001591 || (evt_id == AUDDEV_EVT_END_VOICE)) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001592 memset(evt_payload, 0,
1593 sizeof(union auddev_evt_data));
Neema Shetty90189b82011-06-27 14:58:37 -07001594
1595 evt_payload->voice_session_id = session_id;
1596 } else if (evt_id == AUDDEV_EVT_FREQ_CHG) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001597 if (routing_info.voice_tx_sample_rate
1598 != dev_info->set_sample_rate) {
1599 routing_info.voice_tx_sample_rate
1600 = dev_info->set_sample_rate;
1601 evt_payload->freq_info.sample_rate
1602 = dev_info->set_sample_rate;
1603 evt_payload->freq_info.dev_type
1604 = dev_info->capability;
1605 evt_payload->freq_info.acdb_dev_id
1606 = dev_info->acdb_id;
1607 } else
1608 goto sent_voc;
1609 } else if (evt_id == AUDDEV_EVT_VOICE_STATE_CHG)
1610 evt_payload->voice_state =
1611 routing_info.voice_state;
1612 else {
1613 evt_payload->voc_devinfo.dev_type =
1614 (dev_info->capability & SNDDEV_CAP_TX) ?
1615 SNDDEV_CAP_TX : SNDDEV_CAP_RX;
1616 evt_payload->voc_devinfo.acdb_dev_id =
1617 dev_info->acdb_id;
1618 evt_payload->voc_devinfo.dev_port_id =
1619 dev_info->copp_id;
1620 evt_payload->voc_devinfo.dev_sample =
1621 dev_info->set_sample_rate ?
1622 dev_info->set_sample_rate :
1623 dev_info->sample_rate;
1624 evt_payload->voc_devinfo.dev_id = dev_id;
1625 if (dev_info->capability & SNDDEV_CAP_RX) {
1626 for (i = 0; i < VOC_RX_VOL_ARRAY_NUM;
1627 i++) {
1628 evt_payload->
1629 voc_devinfo.max_rx_vol[i] =
1630 dev_info->max_voc_rx_vol[i];
1631 evt_payload
1632 ->voc_devinfo.min_rx_vol[i] =
1633 dev_info->min_voc_rx_vol[i];
1634 }
1635 }
1636 }
1637 callback->auddev_evt_listener(
1638 evt_id,
1639 evt_payload,
1640 callback->private_data);
1641 if (evt_id == AUDDEV_EVT_DEV_RLS)
1642 dev_info->sessions &= ~(0xFFFF);
1643sent_voc:
1644 if (callback->cb_next == NULL)
1645 break;
1646 else {
1647 callback = callback->cb_next;
1648 continue;
1649 }
1650 }
1651 }
1652 kfree(evt_payload);
1653 mutex_unlock(&session_lock);
1654}
1655EXPORT_SYMBOL(broadcast_event);
1656
1657
1658void mixer_post_event(u32 evt_id, u32 id)
1659{
1660
1661 pr_debug("evt_id = %d\n", evt_id);
1662 switch (evt_id) {
1663 case AUDDEV_EVT_DEV_CHG_VOICE: /* Called from Voice_route */
1664 broadcast_event(AUDDEV_EVT_DEV_CHG_VOICE, id, SESSION_IGNORE);
1665 break;
1666 case AUDDEV_EVT_DEV_RDY:
1667 broadcast_event(AUDDEV_EVT_DEV_RDY, id, SESSION_IGNORE);
1668 break;
1669 case AUDDEV_EVT_DEV_RLS:
1670 broadcast_event(AUDDEV_EVT_DEV_RLS, id, SESSION_IGNORE);
1671 break;
1672 case AUDDEV_EVT_REL_PENDING:
1673 broadcast_event(AUDDEV_EVT_REL_PENDING, id, SESSION_IGNORE);
1674 break;
1675 case AUDDEV_EVT_DEVICE_VOL_MUTE_CHG:
1676 broadcast_event(AUDDEV_EVT_DEVICE_VOL_MUTE_CHG, id,
1677 SESSION_IGNORE);
1678 break;
1679 case AUDDEV_EVT_STREAM_VOL_CHG:
1680 broadcast_event(AUDDEV_EVT_STREAM_VOL_CHG, id,
1681 SESSION_IGNORE);
1682 break;
1683 case AUDDEV_EVT_START_VOICE:
1684 broadcast_event(AUDDEV_EVT_START_VOICE,
1685 id, SESSION_IGNORE);
1686 break;
1687 case AUDDEV_EVT_END_VOICE:
1688 broadcast_event(AUDDEV_EVT_END_VOICE,
1689 id, SESSION_IGNORE);
1690 break;
1691 case AUDDEV_EVT_FREQ_CHG:
1692 broadcast_event(AUDDEV_EVT_FREQ_CHG, id, SESSION_IGNORE);
1693 break;
1694 default:
1695 break;
1696 }
1697}
1698EXPORT_SYMBOL(mixer_post_event);
1699
1700static int __init audio_dev_ctrl_init(void)
1701{
1702 init_waitqueue_head(&audio_dev_ctrl.wait);
1703
1704 event.cb = NULL;
Laxminath Kasam692c6542012-02-21 11:17:47 +05301705 msm_reset_device_work_queue = create_workqueue("reset_device");
1706 if (msm_reset_device_work_queue == NULL)
1707 return -ENOMEM;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001708 atomic_set(&audio_dev_ctrl.opened, 0);
1709 audio_dev_ctrl.num_dev = 0;
1710 audio_dev_ctrl.voice_tx_dev = NULL;
1711 audio_dev_ctrl.voice_rx_dev = NULL;
1712 routing_info.voice_state = VOICE_STATE_INVALID;
1713
1714 mutex_init(&adm_tx_topology_tbl.lock);
1715 mutex_init(&routing_info.copp_list_mutex);
1716 mutex_init(&routing_info.adm_mutex);
1717
Bharath Ramachandramurthyb85333e2011-07-29 12:33:37 -07001718 memset(routing_info.copp_list, COPP_IGNORE,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001719 (sizeof(unsigned int) * MAX_SESSIONS * AFE_MAX_PORTS));
1720 return misc_register(&audio_dev_ctrl_misc);
1721}
1722
1723static void __exit audio_dev_ctrl_exit(void)
1724{
Laxminath Kasam692c6542012-02-21 11:17:47 +05301725 destroy_workqueue(msm_reset_device_work_queue);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001726}
1727module_init(audio_dev_ctrl_init);
1728module_exit(audio_dev_ctrl_exit);
1729
1730MODULE_DESCRIPTION("MSM 8K Audio Device Control driver");
1731MODULE_LICENSE("GPL v2");