1 /* arch/arm/mach-msm/qdsp5/snd.c
3 * interface to "snd" service on the baseband cpu
5 * Copyright (C) 2008 HTC Corporation
7 * This software is licensed under the terms of the GNU General Public
8 * License version 2, as published by the Free Software Foundation, and
9 * may be copied, distributed, and modified under those terms.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
18 #include <linux/module.h>
20 #include <linux/miscdevice.h>
21 #include <linux/uaccess.h>
22 #include <linux/kthread.h>
23 #include <linux/delay.h>
24 #include <linux/msm_audio.h>
26 #include <asm/atomic.h>
27 #include <asm/ioctls.h>
28 #include <mach/board.h>
29 #include <mach/msm_rpcrouter.h>
34 struct msm_rpc_endpoint
*ept
;
35 struct msm_snd_endpoints
*snd_epts
;
38 static struct snd_ctxt the_snd
;
40 #define RPC_SND_PROG 0x30000002
41 #define RPC_SND_CB_PROG 0x31000002
42 #if CONFIG_MSM_AMSS_VERSION == 6210
43 #define RPC_SND_VERS 0x94756085 /* 2490720389 */
44 #elif (CONFIG_MSM_AMSS_VERSION == 6220) || \
45 (CONFIG_MSM_AMSS_VERSION == 6225)
46 #define RPC_SND_VERS 0xaa2b1a44 /* 2854951492 */
47 #elif CONFIG_MSM_AMSS_VERSION == 6350
48 #define RPC_SND_VERS MSM_RPC_VERS(1,0)
51 #define SND_SET_DEVICE_PROC 2
52 #define SND_SET_VOLUME_PROC 3
54 struct rpc_snd_set_device_args
{
63 struct rpc_snd_set_volume_args
{
72 struct snd_set_device_msg
{
73 struct rpc_request_hdr hdr
;
74 struct rpc_snd_set_device_args args
;
77 struct snd_set_volume_msg
{
78 struct rpc_request_hdr hdr
;
79 struct rpc_snd_set_volume_args args
;
82 struct snd_endpoint
*get_snd_endpoints(int *size
);
84 static inline int check_mute(int mute
)
86 return (mute
== SND_MUTE_MUTED
||
87 mute
== SND_MUTE_UNMUTED
) ? 0 : -EINVAL
;
90 static int get_endpoint(struct snd_ctxt
*snd
, unsigned long arg
)
93 struct msm_snd_endpoint ept
;
95 if (copy_from_user(&ept
, (void __user
*)arg
, sizeof(ept
))) {
96 pr_err("snd_ioctl get endpoint: invalid read pointer.\n");
101 if (index
< 0 || index
>= snd
->snd_epts
->num
) {
102 pr_err("snd_ioctl get endpoint: invalid index!\n");
106 ept
.id
= snd
->snd_epts
->endpoints
[index
].id
;
108 snd
->snd_epts
->endpoints
[index
].name
,
111 if (copy_to_user((void __user
*)arg
, &ept
, sizeof(ept
))) {
112 pr_err("snd_ioctl get endpoint: invalid write pointer.\n");
119 static long snd_ioctl(struct file
*file
, unsigned int cmd
, unsigned long arg
)
121 struct snd_set_device_msg dmsg
;
122 struct snd_set_volume_msg vmsg
;
123 struct msm_snd_device_config dev
;
124 struct msm_snd_volume_config vol
;
125 struct snd_ctxt
*snd
= file
->private_data
;
128 mutex_lock(&snd
->lock
);
131 if (copy_from_user(&dev
, (void __user
*) arg
, sizeof(dev
))) {
132 pr_err("snd_ioctl set device: invalid pointer.\n");
137 dmsg
.args
.device
= cpu_to_be32(dev
.device
);
138 dmsg
.args
.ear_mute
= cpu_to_be32(dev
.ear_mute
);
139 dmsg
.args
.mic_mute
= cpu_to_be32(dev
.mic_mute
);
140 if (check_mute(dev
.ear_mute
) < 0 ||
141 check_mute(dev
.mic_mute
) < 0) {
142 pr_err("snd_ioctl set device: invalid mute status.\n");
146 dmsg
.args
.cb_func
= -1;
147 dmsg
.args
.client_data
= 0;
149 pr_info("snd_set_device %d %d %d\n", dev
.device
,
150 dev
.ear_mute
, dev
.mic_mute
);
152 rc
= msm_rpc_call(snd
->ept
,
154 &dmsg
, sizeof(dmsg
), 5 * HZ
);
158 if (copy_from_user(&vol
, (void __user
*) arg
, sizeof(vol
))) {
159 pr_err("snd_ioctl set volume: invalid pointer.\n");
164 vmsg
.args
.device
= cpu_to_be32(vol
.device
);
165 vmsg
.args
.method
= cpu_to_be32(vol
.method
);
166 if (vol
.method
!= SND_METHOD_VOICE
) {
167 pr_err("snd_ioctl set volume: invalid method.\n");
172 vmsg
.args
.volume
= cpu_to_be32(vol
.volume
);
173 vmsg
.args
.cb_func
= -1;
174 vmsg
.args
.client_data
= 0;
176 pr_info("snd_set_volume %d %d %d\n", vol
.device
,
177 vol
.method
, vol
.volume
);
179 rc
= msm_rpc_call(snd
->ept
,
181 &vmsg
, sizeof(vmsg
), 5 * HZ
);
184 case SND_GET_NUM_ENDPOINTS
:
185 if (copy_to_user((void __user
*)arg
,
186 &snd
->snd_epts
->num
, sizeof(unsigned))) {
187 pr_err("snd_ioctl get endpoint: invalid pointer.\n");
192 case SND_GET_ENDPOINT
:
193 rc
= get_endpoint(snd
, arg
);
197 pr_err("snd_ioctl unknown command.\n");
201 mutex_unlock(&snd
->lock
);
206 static int snd_release(struct inode
*inode
, struct file
*file
)
208 struct snd_ctxt
*snd
= file
->private_data
;
210 mutex_lock(&snd
->lock
);
212 mutex_unlock(&snd
->lock
);
216 static int snd_open(struct inode
*inode
, struct file
*file
)
218 struct snd_ctxt
*snd
= &the_snd
;
221 mutex_lock(&snd
->lock
);
222 if (snd
->opened
== 0) {
223 if (snd
->ept
== NULL
) {
224 snd
->ept
= msm_rpc_connect(RPC_SND_PROG
, RPC_SND_VERS
,
225 MSM_RPC_UNINTERRUPTIBLE
);
226 if (IS_ERR(snd
->ept
)) {
227 rc
= PTR_ERR(snd
->ept
);
229 pr_err("snd: failed to connect snd svc\n");
233 file
->private_data
= snd
;
236 pr_err("snd already opened.\n");
241 mutex_unlock(&snd
->lock
);
245 static struct file_operations snd_fops
= {
246 .owner
= THIS_MODULE
,
248 .release
= snd_release
,
249 .unlocked_ioctl
= snd_ioctl
,
252 struct miscdevice snd_misc
= {
253 .minor
= MISC_DYNAMIC_MINOR
,
258 static int snd_probe(struct platform_device
*pdev
)
260 struct snd_ctxt
*snd
= &the_snd
;
261 mutex_init(&snd
->lock
);
262 snd
->snd_epts
= (struct msm_snd_endpoints
*)pdev
->dev
.platform_data
;
263 return misc_register(&snd_misc
);
266 static struct platform_driver snd_plat_driver
= {
270 .owner
= THIS_MODULE
,
274 static int __init
snd_init(void)
276 return platform_driver_register(&snd_plat_driver
);
279 module_init(snd_init
);