blob: a13084f61275e0a8ab5cab9f8edc2f035cd0a40b [file] [log] [blame]
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001/*
2 * Copyright (C) 2009 Google, Inc.
3 * Copyright (C) 2009 HTC Corporation
4 * Copyright (c) 2009, Code Aurora Forum. All rights reserved.
5 *
6 * Author: Brian Swetland <swetland@google.com>
7 *
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/fs.h>
20#include <linux/module.h>
21#include <linux/miscdevice.h>
22#include <linux/mutex.h>
23#include <linux/sched.h>
24#include <linux/wait.h>
25#include <linux/uaccess.h>
26
27#include <linux/msm_audio_qcp.h>
28#include <mach/msm_qdsp6_audiov2.h>
29#include "dal_audio.h"
30#include "dal_audio_format.h"
31#include <mach/debug_mm.h>
32
33
34struct qcelp {
35 struct mutex lock;
36 struct msm_audio_qcelp_enc_config cfg;
37 struct msm_audio_stream_config str_cfg;
38 struct audio_client *audio_client;
39};
40
41
42static long q6_qcelp_in_ioctl(struct file *file, unsigned int cmd,
43 unsigned long arg)
44{
45 struct qcelp *qcelp = file->private_data;
46 struct adsp_open_command rpc;
47 int rc = 0;
48
49 if (cmd == AUDIO_GET_STATS) {
50 struct msm_audio_stats stats;
51 memset(&stats, 0, sizeof(stats));
52 if (copy_to_user((void *) arg, &stats, sizeof(stats)))
53 return -EFAULT;
54 return 0;
55 }
56
57 mutex_lock(&qcelp->lock);
58 switch (cmd) {
59 case AUDIO_START:
60 if (qcelp->audio_client) {
61 rc = -EBUSY;
62 break;
63 } else {
64 qcelp->audio_client = q6audio_open(AUDIO_FLAG_READ,
65 qcelp->str_cfg.buffer_size);
66
67 if (!qcelp->audio_client) {
68 kfree(qcelp);
69 rc = -ENOMEM;
70 break;
71 }
72 }
73
74 tx_clk_freq = 8000;
75
76 memset(&rpc, 0, sizeof(rpc));
77
78 rpc.format_block.standard.format = ADSP_AUDIO_FORMAT_V13K_FS;
79 rpc.format_block.standard.channels = 1;
80 rpc.format_block.standard.bits_per_sample = 16;
81 rpc.format_block.standard.sampling_rate = 8000;
82 rpc.format_block.standard.is_signed = 1;
83 rpc.format_block.standard.is_interleaved = 0;
84 rpc.hdr.opcode = ADSP_AUDIO_IOCTL_CMD_OPEN_READ;
85 rpc.device = ADSP_AUDIO_DEVICE_ID_DEFAULT;
86 rpc.stream_context = ADSP_AUDIO_DEVICE_CONTEXT_RECORD;
87 rpc.buf_max_size = qcelp->str_cfg.buffer_size;
88 rpc.config.qcelp13k.min_rate = qcelp->cfg.min_bit_rate;
89 rpc.config.qcelp13k.max_rate = qcelp->cfg.max_bit_rate;
90
91 q6audio_start(qcelp->audio_client, &rpc, sizeof(rpc));
92 break;
93 case AUDIO_STOP:
94 break;
95 case AUDIO_FLUSH:
96 break;
97 case AUDIO_SET_VOLUME:
98 break;
99 case AUDIO_GET_STREAM_CONFIG:
100 if (copy_to_user((void *)arg, &qcelp->str_cfg,
101 sizeof(struct msm_audio_stream_config)))
102 rc = -EFAULT;
103 break;
104 case AUDIO_SET_STREAM_CONFIG:
105 if (copy_from_user(&qcelp->str_cfg, (void *)arg,
106 sizeof(struct msm_audio_stream_config))) {
107 rc = -EFAULT;
108 break;
109 }
110
111 if (qcelp->str_cfg.buffer_size < 35) {
112 pr_err("[%s:%s] Buffer size too small\n", __MM_FILE__,
113 __func__);
114 rc = -EINVAL;
115 break;
116 }
117
118 if (qcelp->str_cfg.buffer_count != 2)
119 pr_info("[%s:%s] Buffer count set to 2\n", __MM_FILE__,
120 __func__);
121 break;
122 case AUDIO_SET_QCELP_ENC_CONFIG:
123 if (copy_from_user(&qcelp->cfg, (void *) arg,
124 sizeof(struct msm_audio_qcelp_enc_config)))
125 rc = -EFAULT;
126
127 if (qcelp->cfg.min_bit_rate > 4 ||
128 qcelp->cfg.min_bit_rate < 1) {
129
130 pr_err("[%s:%s] invalid min bitrate\n", __MM_FILE__,
131 __func__);
132 rc = -EINVAL;
133 }
134 if (qcelp->cfg.max_bit_rate > 4 ||
135 qcelp->cfg.max_bit_rate < 1) {
136
137 pr_err("[%s:%s] invalid max bitrate\n", __MM_FILE__,
138 __func__);
139 rc = -EINVAL;
140 }
141
142 break;
143 case AUDIO_GET_QCELP_ENC_CONFIG:
144 if (copy_to_user((void *) arg, &qcelp->cfg,
145 sizeof(struct msm_audio_qcelp_enc_config)))
146 rc = -EFAULT;
147 break;
148
149 default:
150 rc = -EINVAL;
151 }
152
153 mutex_unlock(&qcelp->lock);
154 return rc;
155}
156
157static int q6_qcelp_in_open(struct inode *inode, struct file *file)
158{
159 struct qcelp *qcelp;
160 qcelp = kmalloc(sizeof(struct qcelp), GFP_KERNEL);
161 if (qcelp == NULL) {
162 pr_err("[%s:%s] Could not allocate memory for qcelp driver\n",
163 __MM_FILE__, __func__);
164 return -ENOMEM;
165 }
166
167 mutex_init(&qcelp->lock);
168 file->private_data = qcelp;
169 qcelp->audio_client = NULL;
170 qcelp->str_cfg.buffer_size = 35;
171 qcelp->str_cfg.buffer_count = 2;
172 qcelp->cfg.cdma_rate = CDMA_RATE_FULL;
173 qcelp->cfg.min_bit_rate = 1;
174 qcelp->cfg.max_bit_rate = 4;
175 return 0;
176}
177
178static ssize_t q6_qcelp_in_read(struct file *file, char __user *buf,
179 size_t count, loff_t *pos)
180{
181 struct audio_client *ac;
182 struct audio_buffer *ab;
183 const char __user *start = buf;
184 struct qcelp *qcelp = file->private_data;
185 int xfer = 0;
186 int res;
187
188 mutex_lock(&qcelp->lock);
189 ac = qcelp->audio_client;
190 if (!ac) {
191 res = -ENODEV;
192 goto fail;
193 }
194 while (count > xfer) {
195 ab = ac->buf + ac->cpu_buf;
196
197 if (ab->used)
198 wait_event(ac->wait, (ab->used == 0));
199
200 xfer = ab->actual_size;
201
202 if (copy_to_user(buf, ab->data, xfer)) {
203 res = -EFAULT;
204 goto fail;
205 }
206
207 buf += xfer;
208 count -= xfer;
209
210 ab->used = 1;
211 q6audio_read(ac, ab);
212 ac->cpu_buf ^= 1;
213 }
214
215 res = buf - start;
216
217fail:
218 mutex_unlock(&qcelp->lock);
219
220 return res;
221}
222
223static int q6_qcelp_in_release(struct inode *inode, struct file *file)
224{
225 int rc = 0;
226 struct qcelp *qcelp = file->private_data;
227
228 mutex_lock(&qcelp->lock);
229 if (qcelp->audio_client)
230 rc = q6audio_close(qcelp->audio_client);
231 mutex_unlock(&qcelp->lock);
232 kfree(qcelp);
233 return rc;
234}
235
236static const struct file_operations q6_qcelp_in_fops = {
237 .owner = THIS_MODULE,
238 .open = q6_qcelp_in_open,
239 .read = q6_qcelp_in_read,
240 .release = q6_qcelp_in_release,
241 .unlocked_ioctl = q6_qcelp_in_ioctl,
242};
243
244struct miscdevice q6_qcelp_in_misc = {
245 .minor = MISC_DYNAMIC_MINOR,
246 .name = "msm_qcelp_in",
247 .fops = &q6_qcelp_in_fops,
248};
249
250static int __init q6_qcelp_in_init(void)
251{
252 return misc_register(&q6_qcelp_in_misc);
253}
254
255device_initcall(q6_qcelp_in_init);