blob: 3f379dcd27030569ce6b79a2b85ce7a506ec03eb [file] [log] [blame]
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001/* arch/arm/mach-msm/qdsp5/snd.c
2 *
3 * interface to "snd" service on the baseband cpu
4 *
5 * Copyright (C) 2008 HTC Corporation
Duy Truong790f06d2013-02-13 16:38:12 -08006 * Copyright (c) 2009, The Linux Foundation. All rights reserved.
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07007 *
8 * This software is licensed under the terms of the GNU General Public
9 * License version 2, as published by the Free Software Foundation, and
10 * may be copied, distributed, and modified under those terms.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 */
18
19#include <linux/module.h>
20#include <linux/fs.h>
21#include <linux/miscdevice.h>
22#include <linux/uaccess.h>
23#include <linux/kthread.h>
24#include <linux/delay.h>
25#include <linux/msm_audio.h>
26#include <linux/seq_file.h>
27#include <asm/atomic.h>
28#include <asm/ioctls.h>
29#include <mach/board.h>
30#include <mach/msm_rpcrouter.h>
31#include <mach/debug_mm.h>
32
33struct snd_ctxt {
34 struct mutex lock;
35 int opened;
36 struct msm_rpc_endpoint *ept;
37 struct msm_snd_endpoints *snd_epts;
38};
39
40struct snd_sys_ctxt {
41 struct mutex lock;
42 struct msm_rpc_endpoint *ept;
43};
44
45static struct snd_sys_ctxt the_snd_sys;
46
47static struct snd_ctxt the_snd;
48
49#define RPC_SND_PROG 0x30000002
50#define RPC_SND_CB_PROG 0x31000002
51
52#define RPC_SND_VERS 0x00020001
53#define RPC_SND_VERS2 0x00030001
54
55#define SND_SET_DEVICE_PROC 2
56#define SND_SET_VOLUME_PROC 3
57#define SND_AVC_CTL_PROC 29
58#define SND_AGC_CTL_PROC 30
59
60struct rpc_snd_set_device_args {
61 uint32_t device;
62 uint32_t ear_mute;
63 uint32_t mic_mute;
64
65 uint32_t cb_func;
66 uint32_t client_data;
67};
68
69struct rpc_snd_set_volume_args {
70 uint32_t device;
71 uint32_t method;
72 uint32_t volume;
73
74 uint32_t cb_func;
75 uint32_t client_data;
76};
77
78struct rpc_snd_avc_ctl_args {
79 uint32_t avc_ctl;
80 uint32_t cb_func;
81 uint32_t client_data;
82};
83
84struct rpc_snd_agc_ctl_args {
85 uint32_t agc_ctl;
86 uint32_t cb_func;
87 uint32_t client_data;
88};
89
90struct snd_set_device_msg {
91 struct rpc_request_hdr hdr;
92 struct rpc_snd_set_device_args args;
93};
94
95struct snd_set_volume_msg {
96 struct rpc_request_hdr hdr;
97 struct rpc_snd_set_volume_args args;
98};
99
100struct snd_avc_ctl_msg {
101 struct rpc_request_hdr hdr;
102 struct rpc_snd_avc_ctl_args args;
103};
104
105struct snd_agc_ctl_msg {
106 struct rpc_request_hdr hdr;
107 struct rpc_snd_agc_ctl_args args;
108};
109
110struct snd_endpoint *get_snd_endpoints(int *size);
111
112static inline int check_mute(int mute)
113{
114 return (mute == SND_MUTE_MUTED ||
115 mute == SND_MUTE_UNMUTED) ? 0 : -EINVAL;
116}
117
118static int get_endpoint(struct snd_ctxt *snd, unsigned long arg)
119{
120 int rc = 0, index;
121 struct msm_snd_endpoint ept;
122
123 if (copy_from_user(&ept, (void __user *)arg, sizeof(ept))) {
124 MM_ERR("snd_ioctl get endpoint: invalid read pointer\n");
125 return -EFAULT;
126 }
127
128 index = ept.id;
129 if (index < 0 || index >= snd->snd_epts->num) {
130 MM_ERR("snd_ioctl get endpoint: invalid index!\n");
131 return -EINVAL;
132 }
133
134 ept.id = snd->snd_epts->endpoints[index].id;
135 strncpy(ept.name,
136 snd->snd_epts->endpoints[index].name,
137 sizeof(ept.name));
138
139 if (copy_to_user((void __user *)arg, &ept, sizeof(ept))) {
140 MM_ERR("snd_ioctl get endpoint: invalid write pointer\n");
141 rc = -EFAULT;
142 }
143
144 return rc;
145}
146
147static long snd_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
148{
149 struct snd_set_device_msg dmsg;
150 struct snd_set_volume_msg vmsg;
151 struct snd_avc_ctl_msg avc_msg;
152 struct snd_agc_ctl_msg agc_msg;
153
154 struct msm_snd_device_config dev;
155 struct msm_snd_volume_config vol;
156 struct snd_ctxt *snd = file->private_data;
157 int rc = 0;
158
159 uint32_t avc, agc;
160
161 mutex_lock(&snd->lock);
162 switch (cmd) {
163 case SND_SET_DEVICE:
164 if (copy_from_user(&dev, (void __user *) arg, sizeof(dev))) {
165 MM_ERR("set device: invalid pointer\n");
166 rc = -EFAULT;
167 break;
168 }
169
170 dmsg.args.device = cpu_to_be32(dev.device);
171 dmsg.args.ear_mute = cpu_to_be32(dev.ear_mute);
172 dmsg.args.mic_mute = cpu_to_be32(dev.mic_mute);
173 if (check_mute(dev.ear_mute) < 0 ||
174 check_mute(dev.mic_mute) < 0) {
175 MM_ERR("set device: invalid mute status\n");
176 rc = -EINVAL;
177 break;
178 }
179 dmsg.args.cb_func = -1;
180 dmsg.args.client_data = 0;
181
182 MM_INFO("snd_set_device %d %d %d\n", dev.device,
183 dev.ear_mute, dev.mic_mute);
184
185 rc = msm_rpc_call(snd->ept,
186 SND_SET_DEVICE_PROC,
187 &dmsg, sizeof(dmsg), 5 * HZ);
188 break;
189
190 case SND_SET_VOLUME:
191 if (copy_from_user(&vol, (void __user *) arg, sizeof(vol))) {
192 MM_ERR("set volume: invalid pointer\n");
193 rc = -EFAULT;
194 break;
195 }
196
197 vmsg.args.device = cpu_to_be32(vol.device);
198 vmsg.args.method = cpu_to_be32(vol.method);
199 if (vol.method != SND_METHOD_VOICE) {
200 MM_ERR("set volume: invalid method\n");
201 rc = -EINVAL;
202 break;
203 }
204
205 vmsg.args.volume = cpu_to_be32(vol.volume);
206 vmsg.args.cb_func = -1;
207 vmsg.args.client_data = 0;
208
209 MM_INFO("snd_set_volume %d %d %d\n", vol.device,
210 vol.method, vol.volume);
211
212 rc = msm_rpc_call(snd->ept,
213 SND_SET_VOLUME_PROC,
214 &vmsg, sizeof(vmsg), 5 * HZ);
215 break;
216
217 case SND_AVC_CTL:
218 if (get_user(avc, (uint32_t __user *) arg)) {
219 rc = -EFAULT;
220 break;
221 } else if ((avc != 1) && (avc != 0)) {
222 rc = -EINVAL;
223 break;
224 }
225
226 avc_msg.args.avc_ctl = cpu_to_be32(avc);
227 avc_msg.args.cb_func = -1;
228 avc_msg.args.client_data = 0;
229
230 MM_INFO("snd_avc_ctl %d\n", avc);
231
232 rc = msm_rpc_call(snd->ept,
233 SND_AVC_CTL_PROC,
234 &avc_msg, sizeof(avc_msg), 5 * HZ);
235 break;
236
237 case SND_AGC_CTL:
238 if (get_user(agc, (uint32_t __user *) arg)) {
239 rc = -EFAULT;
240 break;
241 } else if ((agc != 1) && (agc != 0)) {
242 rc = -EINVAL;
243 break;
244 }
245 agc_msg.args.agc_ctl = cpu_to_be32(agc);
246 agc_msg.args.cb_func = -1;
247 agc_msg.args.client_data = 0;
248
249 MM_INFO("snd_agc_ctl %d\n", agc);
250
251 rc = msm_rpc_call(snd->ept,
252 SND_AGC_CTL_PROC,
253 &agc_msg, sizeof(agc_msg), 5 * HZ);
254 break;
255
256 case SND_GET_NUM_ENDPOINTS:
257 if (copy_to_user((void __user *)arg,
258 &snd->snd_epts->num, sizeof(unsigned))) {
259 MM_ERR("get endpoint: invalid pointer\n");
260 rc = -EFAULT;
261 }
262 break;
263
264 case SND_GET_ENDPOINT:
265 rc = get_endpoint(snd, arg);
266 break;
267
268 default:
269 MM_ERR("unknown command\n");
270 rc = -EINVAL;
271 break;
272 }
273 mutex_unlock(&snd->lock);
274
275 return rc;
276}
277
278static int snd_release(struct inode *inode, struct file *file)
279{
280 struct snd_ctxt *snd = file->private_data;
281 int rc;
282
283 mutex_lock(&snd->lock);
284 rc = msm_rpc_close(snd->ept);
285 if (rc < 0)
286 MM_ERR("msm_rpc_close failed\n");
287 snd->ept = NULL;
288 snd->opened = 0;
289 mutex_unlock(&snd->lock);
290 return 0;
291}
292static int snd_sys_release(void)
293{
294 struct snd_sys_ctxt *snd_sys = &the_snd_sys;
295 int rc = 0;
296
297 mutex_lock(&snd_sys->lock);
298 rc = msm_rpc_close(snd_sys->ept);
299 if (rc < 0)
300 MM_ERR("msm_rpc_close failed\n");
301 snd_sys->ept = NULL;
302 mutex_unlock(&snd_sys->lock);
303 return rc;
304}
305static int snd_open(struct inode *inode, struct file *file)
306{
307 struct snd_ctxt *snd = &the_snd;
308 int rc = 0;
309
310 mutex_lock(&snd->lock);
311 if (snd->opened == 0) {
312 if (snd->ept == NULL) {
313 snd->ept = msm_rpc_connect_compatible(RPC_SND_PROG,
314 RPC_SND_VERS, 0);
315 if (IS_ERR(snd->ept)) {
316 MM_DBG("connect failed with current VERS \
317 = %x, trying again with another API\n",
318 RPC_SND_VERS2);
319 snd->ept =
320 msm_rpc_connect_compatible(RPC_SND_PROG,
321 RPC_SND_VERS2, 0);
322 }
323 if (IS_ERR(snd->ept)) {
324 rc = PTR_ERR(snd->ept);
325 snd->ept = NULL;
326 MM_ERR("failed to connect snd svc\n");
327 goto err;
328 }
329 }
330 file->private_data = snd;
331 snd->opened = 1;
332 } else {
333 MM_ERR("snd already opened\n");
334 rc = -EBUSY;
335 }
336
337err:
338 mutex_unlock(&snd->lock);
339 return rc;
340}
341static int snd_sys_open(void)
342{
343 struct snd_sys_ctxt *snd_sys = &the_snd_sys;
344 int rc = 0;
345
346 mutex_lock(&snd_sys->lock);
347 if (snd_sys->ept == NULL) {
348 snd_sys->ept = msm_rpc_connect_compatible(RPC_SND_PROG,
349 RPC_SND_VERS, 0);
350 if (IS_ERR(snd_sys->ept)) {
351 MM_DBG("connect failed with current VERS \
352 = %x, trying again with another API\n",
353 RPC_SND_VERS2);
354 snd_sys->ept = msm_rpc_connect_compatible(RPC_SND_PROG,
355 RPC_SND_VERS2, 0);
356 }
357 if (IS_ERR(snd_sys->ept)) {
358 rc = PTR_ERR(snd_sys->ept);
359 snd_sys->ept = NULL;
360 MM_ERR("failed to connect snd svc\n");
361 goto err;
362 }
363 } else
364 MM_DBG("snd already opened\n");
365
366err:
367 mutex_unlock(&snd_sys->lock);
368 return rc;
369}
370
371static struct file_operations snd_fops = {
372 .owner = THIS_MODULE,
373 .open = snd_open,
374 .release = snd_release,
375 .unlocked_ioctl = snd_ioctl,
376};
377
378struct miscdevice snd_misc = {
379 .minor = MISC_DYNAMIC_MINOR,
380 .name = "msm_snd",
381 .fops = &snd_fops,
382};
383
384static long snd_agc_enable(unsigned long arg)
385{
386 struct snd_sys_ctxt *snd_sys = &the_snd_sys;
387 struct snd_agc_ctl_msg agc_msg;
388 int rc = 0;
389
390 if ((arg != 1) && (arg != 0))
391 return -EINVAL;
392
393 agc_msg.args.agc_ctl = cpu_to_be32(arg);
394 agc_msg.args.cb_func = -1;
395 agc_msg.args.client_data = 0;
396
397 MM_DBG("snd_agc_ctl %ld,%d\n", arg, agc_msg.args.agc_ctl);
398
399 rc = msm_rpc_call(snd_sys->ept,
400 SND_AGC_CTL_PROC,
401 &agc_msg, sizeof(agc_msg), 5 * HZ);
402 return rc;
403}
404
405static long snd_avc_enable(unsigned long arg)
406{
407 struct snd_sys_ctxt *snd_sys = &the_snd_sys;
408 struct snd_avc_ctl_msg avc_msg;
409 int rc = 0;
410
411 if ((arg != 1) && (arg != 0))
412 return -EINVAL;
413
414 avc_msg.args.avc_ctl = cpu_to_be32(arg);
415
416 avc_msg.args.cb_func = -1;
417 avc_msg.args.client_data = 0;
418
419 MM_DBG("snd_avc_ctl %ld,%d\n", arg, avc_msg.args.avc_ctl);
420
421 rc = msm_rpc_call(snd_sys->ept,
422 SND_AVC_CTL_PROC,
423 &avc_msg, sizeof(avc_msg), 5 * HZ);
424 return rc;
425}
426
427static ssize_t snd_agc_store(struct device *dev,
428 struct device_attribute *attr, const char *buf, size_t size)
429{
430 ssize_t status;
431 struct snd_sys_ctxt *snd_sys = &the_snd_sys;
432 int rc = 0;
433
434 rc = snd_sys_open();
435 if (rc)
436 return rc;
437
438 mutex_lock(&snd_sys->lock);
439
440 if (sysfs_streq(buf, "enable"))
441 status = snd_agc_enable(1);
442 else if (sysfs_streq(buf, "disable"))
443 status = snd_agc_enable(0);
444 else
445 status = -EINVAL;
446
447 mutex_unlock(&snd_sys->lock);
448 rc = snd_sys_release();
449 if (rc)
450 return rc;
451
452 return status ? : size;
453}
454
455static ssize_t snd_avc_store(struct device *dev,
456 struct device_attribute *attr, const char *buf, size_t size)
457{
458 ssize_t status;
459 struct snd_sys_ctxt *snd_sys = &the_snd_sys;
460 int rc = 0;
461
462 rc = snd_sys_open();
463 if (rc)
464 return rc;
465
466 mutex_lock(&snd_sys->lock);
467
468 if (sysfs_streq(buf, "enable"))
469 status = snd_avc_enable(1);
470 else if (sysfs_streq(buf, "disable"))
471 status = snd_avc_enable(0);
472 else
473 status = -EINVAL;
474
475 mutex_unlock(&snd_sys->lock);
476 rc = snd_sys_release();
477 if (rc)
478 return rc;
479
480 return status ? : size;
481}
482
483static long snd_vol_enable(const char *arg)
484{
485 struct snd_sys_ctxt *snd_sys = &the_snd_sys;
486 struct snd_set_volume_msg vmsg;
487 struct msm_snd_volume_config vol;
488 int rc = 0;
489
490 rc = sscanf(arg, "%d %d %d", &vol.device, &vol.method, &vol.volume);
491 if (rc != 3) {
492 MM_ERR("Invalid arguments. Usage: <device> <method> \
493 <volume>\n");
494 rc = -EINVAL;
495 return rc;
496 }
497
498 vmsg.args.device = cpu_to_be32(vol.device);
499 vmsg.args.method = cpu_to_be32(vol.method);
500 if (vol.method != SND_METHOD_VOICE) {
501 MM_ERR("snd_ioctl set volume: invalid method\n");
502 rc = -EINVAL;
503 return rc;
504 }
505
506 vmsg.args.volume = cpu_to_be32(vol.volume);
507 vmsg.args.cb_func = -1;
508 vmsg.args.client_data = 0;
509
510 MM_DBG("snd_set_volume %d %d %d\n", vol.device, vol.method,
511 vol.volume);
512
513 rc = msm_rpc_call(snd_sys->ept,
514 SND_SET_VOLUME_PROC,
515 &vmsg, sizeof(vmsg), 5 * HZ);
516 return rc;
517}
518
519static long snd_dev_enable(const char *arg)
520{
521 struct snd_sys_ctxt *snd_sys = &the_snd_sys;
522 struct snd_set_device_msg dmsg;
523 struct msm_snd_device_config dev;
524 int rc = 0;
525
526 rc = sscanf(arg, "%d %d %d", &dev.device, &dev.ear_mute, &dev.mic_mute);
527 if (rc != 3) {
528 MM_ERR("Invalid arguments. Usage: <device> <ear_mute> \
529 <mic_mute>\n");
530 rc = -EINVAL;
531 return rc;
532 }
533 dmsg.args.device = cpu_to_be32(dev.device);
534 dmsg.args.ear_mute = cpu_to_be32(dev.ear_mute);
535 dmsg.args.mic_mute = cpu_to_be32(dev.mic_mute);
536 if (check_mute(dev.ear_mute) < 0 ||
537 check_mute(dev.mic_mute) < 0) {
538 MM_ERR("snd_ioctl set device: invalid mute status\n");
539 rc = -EINVAL;
540 return rc;
541 }
542 dmsg.args.cb_func = -1;
543 dmsg.args.client_data = 0;
544
545 MM_INFO("snd_set_device %d %d %d\n", dev.device, dev.ear_mute,
546 dev.mic_mute);
547
548 rc = msm_rpc_call(snd_sys->ept,
549 SND_SET_DEVICE_PROC,
550 &dmsg, sizeof(dmsg), 5 * HZ);
551 return rc;
552}
553
554static ssize_t snd_dev_store(struct device *dev,
555 struct device_attribute *attr, const char *buf, size_t size)
556{
557 ssize_t status;
558 struct snd_sys_ctxt *snd_sys = &the_snd_sys;
559 int rc = 0;
560
561 rc = snd_sys_open();
562 if (rc)
563 return rc;
564
565 mutex_lock(&snd_sys->lock);
566 status = snd_dev_enable(buf);
567 mutex_unlock(&snd_sys->lock);
568
569 rc = snd_sys_release();
570 if (rc)
571 return rc;
572
573 return status ? : size;
574}
575
576static ssize_t snd_vol_store(struct device *dev,
577 struct device_attribute *attr, const char *buf, size_t size)
578{
579 ssize_t status;
580 struct snd_sys_ctxt *snd_sys = &the_snd_sys;
581 int rc = 0;
582
583 rc = snd_sys_open();
584 if (rc)
585 return rc;
586
587 mutex_lock(&snd_sys->lock);
588 status = snd_vol_enable(buf);
589 mutex_unlock(&snd_sys->lock);
590
591 rc = snd_sys_release();
592 if (rc)
593 return rc;
594
595 return status ? : size;
596}
597
598static DEVICE_ATTR(agc, S_IWUSR | S_IRUGO,
599 NULL, snd_agc_store);
600
601static DEVICE_ATTR(avc, S_IWUSR | S_IRUGO,
602 NULL, snd_avc_store);
603
604static DEVICE_ATTR(device, S_IWUSR | S_IRUGO,
605 NULL, snd_dev_store);
606
607static DEVICE_ATTR(volume, S_IWUSR | S_IRUGO,
608 NULL, snd_vol_store);
609
610static int snd_probe(struct platform_device *pdev)
611{
612 struct snd_ctxt *snd = &the_snd;
613 struct snd_sys_ctxt *snd_sys = &the_snd_sys;
614 int rc = 0;
615
616 mutex_init(&snd->lock);
617 mutex_init(&snd_sys->lock);
618 snd_sys->ept = NULL;
619 snd->snd_epts = (struct msm_snd_endpoints *)pdev->dev.platform_data;
620 rc = misc_register(&snd_misc);
621 if (rc)
622 return rc;
623
624 rc = device_create_file(snd_misc.this_device, &dev_attr_agc);
625 if (rc) {
626 misc_deregister(&snd_misc);
627 return rc;
628 }
629
630 rc = device_create_file(snd_misc.this_device, &dev_attr_avc);
631 if (rc) {
632 device_remove_file(snd_misc.this_device,
633 &dev_attr_agc);
634 misc_deregister(&snd_misc);
635 return rc;
636 }
637
638 rc = device_create_file(snd_misc.this_device, &dev_attr_device);
639 if (rc) {
640 device_remove_file(snd_misc.this_device,
641 &dev_attr_agc);
642 device_remove_file(snd_misc.this_device,
643 &dev_attr_avc);
644 misc_deregister(&snd_misc);
645 return rc;
646 }
647
648 rc = device_create_file(snd_misc.this_device, &dev_attr_volume);
649 if (rc) {
650 device_remove_file(snd_misc.this_device,
651 &dev_attr_agc);
652 device_remove_file(snd_misc.this_device,
653 &dev_attr_avc);
654 device_remove_file(snd_misc.this_device,
655 &dev_attr_device);
656 misc_deregister(&snd_misc);
657 }
658
659 return rc;
660}
661
662static struct platform_driver snd_plat_driver = {
663 .probe = snd_probe,
664 .driver = {
665 .name = "msm_snd",
666 .owner = THIS_MODULE,
667 },
668};
669
670static int __init snd_init(void)
671{
672 return platform_driver_register(&snd_plat_driver);
673}
674
675module_init(snd_init);