blob: 3cf4e256b23cdb1825554c32d45493c90d4ec762 [file] [log] [blame]
Vasudeva Rao Thumati86edf6c2011-07-06 16:25:13 +05301/* Copyright (c) 2010-2011, 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
14#include <linux/module.h>
15#include <linux/fs.h>
16#include <linux/miscdevice.h>
17#include <linux/uaccess.h>
18#include <linux/sched.h>
19#include <linux/wait.h>
20#include <linux/dma-mapping.h>
21#include <linux/slab.h>
22#include <linux/msm_audio_qcp.h>
23#include <asm/atomic.h>
24#include <asm/ioctls.h>
25#include <sound/q6asm.h>
26#include <sound/apr_audio.h>
27#include "audio_utils.h"
28
29/* Buffer with meta*/
30#define PCM_BUF_SIZE (4096 + sizeof(struct meta_in))
31
32/* Maximum 10 frames in buffer with meta */
33#define FRAME_SIZE (1 + ((35+sizeof(struct meta_out_dsp)) * 10))
34
35void q6asm_qcelp_in_cb(uint32_t opcode, uint32_t token,
36 uint32_t *payload, void *priv)
37{
38 struct q6audio_in * audio = (struct q6audio_in *)priv;
39 unsigned long flags;
40
41 pr_debug("%s:session id %d: opcode - %d\n", __func__,
42 audio->ac->session, opcode);
43
44 spin_lock_irqsave(&audio->dsp_lock, flags);
45 switch (opcode) {
46 case ASM_DATA_EVENT_READ_DONE:
47 audio_in_get_dsp_frames(audio, token, payload);
48 break;
49 case ASM_DATA_EVENT_WRITE_DONE:
50 atomic_inc(&audio->in_count);
51 wake_up(&audio->write_wait);
52 break;
53 case ASM_DATA_CMDRSP_EOS:
54 audio->eos_rsp = 1;
55 wake_up(&audio->read_wait);
56 break;
57 case ASM_STREAM_CMDRSP_GET_ENCDEC_PARAM:
58 break;
59 case ASM_STREAM_CMDRSP_GET_PP_PARAMS:
60 break;
61 case ASM_SESSION_EVENT_TX_OVERFLOW:
62 pr_err("%s:session id %d:ASM_SESSION_EVENT_TX_OVERFLOW\n",
63 __func__, audio->ac->session);
64 break;
65 default:
66 pr_err("%s:session id %d: Ignore opcode[0x%x]\n", __func__,
67 audio->ac->session, opcode);
68 break;
69 }
70 spin_unlock_irqrestore(&audio->dsp_lock, flags);
71}
72/* ------------------- device --------------------- */
73static long qcelp_in_ioctl(struct file *file,
74 unsigned int cmd, unsigned long arg)
75{
76 struct q6audio_in *audio = file->private_data;
77 int rc = 0;
78 int cnt = 0;
79
80 switch (cmd) {
81 case AUDIO_START: {
82 struct msm_audio_qcelp_enc_config *enc_cfg;
83 enc_cfg = audio->enc_cfg;
84 pr_debug("%s:session id %d: default buf alloc[%d]\n", __func__,
85 audio->ac->session, audio->buf_alloc);
86 if (audio->enabled == 1) {
87 pr_info("%s:AUDIO_START already over\n", __func__);
88 rc = 0;
89 break;
90 }
91 rc = audio_in_buf_alloc(audio);
92 if (rc < 0) {
93 pr_err("%s:session id %d: buffer allocation failed\n",
94 __func__, audio->ac->session);
95 break;
96 }
97
98 /* reduced_rate_level, rate_modulation_cmd set to zero
99 currently not configurable from user space */
100 rc = q6asm_enc_cfg_blk_qcelp(audio->ac,
101 audio->buf_cfg.frames_per_buf,
102 enc_cfg->min_bit_rate,
103 enc_cfg->max_bit_rate, 0, 0);
104
105 if (rc < 0) {
106 pr_err("%s:session id %d: cmd qcelp media format block\
107 failed\n", __func__, audio->ac->session);
108 break;
109 }
110 if (audio->feedback == NON_TUNNEL_MODE) {
111 rc = q6asm_media_format_block_pcm(audio->ac,
112 audio->pcm_cfg.sample_rate,
113 audio->pcm_cfg.channel_count);
114
115 if (rc < 0) {
116 pr_err("%s:session id %d: media format block\
117 failed\n", __func__, audio->ac->session);
118 break;
119 }
120 }
121 pr_debug("%s:session id %d: AUDIO_START enable[%d]\n", __func__,
122 audio->ac->session, audio->enabled);
123 rc = audio_in_enable(audio);
124 if (!rc) {
125 audio->enabled = 1;
126 } else {
127 audio->enabled = 0;
128 pr_err("%s:session id %d: Audio Start procedure failed\
129 rc=%d\n", __func__, audio->ac->session, rc);
130 break;
131 }
132 while (cnt++ < audio->str_cfg.buffer_count)
133 q6asm_read(audio->ac); /* Push buffer to DSP */
134 rc = 0;
135 pr_debug("%s:session id %d: AUDIO_START success enable[%d]\n",
136 __func__, audio->ac->session, audio->enabled);
137 break;
138 }
139 case AUDIO_STOP: {
140 pr_debug("%s:session id %d: AUDIO_STOP\n", __func__,
141 audio->ac->session);
142 rc = audio_in_disable(audio);
143 if (rc < 0) {
144 pr_err("%s:session id %d: Audio Stop procedure failed\
145 rc=%d\n", __func__, audio->ac->session,
146 rc);
147 break;
148 }
149 break;
150 }
151 case AUDIO_GET_QCELP_ENC_CONFIG: {
152 if (copy_to_user((void *)arg, audio->enc_cfg,
153 sizeof(struct msm_audio_qcelp_enc_config)))
154 rc = -EFAULT;
155 break;
156 }
157 case AUDIO_SET_QCELP_ENC_CONFIG: {
158 struct msm_audio_qcelp_enc_config cfg;
159 struct msm_audio_qcelp_enc_config *enc_cfg;
160 enc_cfg = audio->enc_cfg;
161 if (copy_from_user(&cfg, (void *) arg,
162 sizeof(struct msm_audio_qcelp_enc_config))) {
163 rc = -EFAULT;
164 break;
165 }
166
167 if (cfg.min_bit_rate > 4 ||
168 cfg.min_bit_rate < 1) {
169 pr_err("%s:session id %d: invalid min bitrate\n",
170 __func__, audio->ac->session);
171 rc = -EINVAL;
172 break;
173 }
174 if (cfg.max_bit_rate > 4 ||
175 cfg.max_bit_rate < 1) {
176 pr_err("%s:session id %d: invalid max bitrate\n",
177 __func__, audio->ac->session);
178 rc = -EINVAL;
179 break;
180 }
181 enc_cfg->min_bit_rate = cfg.min_bit_rate;
182 enc_cfg->max_bit_rate = cfg.max_bit_rate;
183 pr_debug("%s:session id %d: min_bit_rate= 0x%x\
184 max_bit_rate=0x%x\n", __func__,
185 audio->ac->session, enc_cfg->min_bit_rate,
186 enc_cfg->max_bit_rate);
187 break;
188 }
189 default:
190 rc = -EINVAL;
191 }
192 return rc;
193}
194
195static int qcelp_in_open(struct inode *inode, struct file *file)
196{
197 struct q6audio_in *audio = NULL;
198 struct msm_audio_qcelp_enc_config *enc_cfg;
199 int rc = 0;
200
201 audio = kzalloc(sizeof(struct q6audio_in), GFP_KERNEL);
202
203 if (audio == NULL) {
Vasudeva Rao Thumati86edf6c2011-07-06 16:25:13 +0530204 pr_err("%s: Could not allocate memory for qcelp\
205 driver\n", __func__);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700206 return -ENOMEM;
207 }
208 /* Allocate memory for encoder config param */
209 audio->enc_cfg = kzalloc(sizeof(struct msm_audio_qcelp_enc_config),
210 GFP_KERNEL);
211 if (audio->enc_cfg == NULL) {
212 pr_err("%s:session id %d: Could not allocate memory for aac\
213 config param\n", __func__, audio->ac->session);
214 kfree(audio);
215 return -ENOMEM;
216 }
217 enc_cfg = audio->enc_cfg;
218
219 mutex_init(&audio->lock);
220 mutex_init(&audio->read_lock);
221 mutex_init(&audio->write_lock);
222 spin_lock_init(&audio->dsp_lock);
223 init_waitqueue_head(&audio->read_wait);
224 init_waitqueue_head(&audio->write_wait);
225
226 /* Settings will be re-config at AUDIO_SET_CONFIG,
227 * but at least we need to have initial config
228 */
229 audio->str_cfg.buffer_size = FRAME_SIZE;
230 audio->str_cfg.buffer_count = FRAME_NUM;
231 audio->min_frame_size = 35;
232 audio->max_frames_per_buf = 10;
233 audio->pcm_cfg.buffer_size = PCM_BUF_SIZE;
234 audio->pcm_cfg.buffer_count = PCM_BUF_COUNT;
235 enc_cfg->min_bit_rate = 4;
236 enc_cfg->max_bit_rate = 4;
237 audio->pcm_cfg.channel_count = 1;
238 audio->pcm_cfg.sample_rate = 8000;
239 audio->buf_cfg.meta_info_enable = 0x01;
240 audio->buf_cfg.frames_per_buf = 0x01;
241
242 audio->ac = q6asm_audio_client_alloc((app_cb)q6asm_qcelp_in_cb,
243 (void *)audio);
244
245 if (!audio->ac) {
Vasudeva Rao Thumati86edf6c2011-07-06 16:25:13 +0530246 pr_err("%s: Could not allocate memory for audio\
247 client\n", __func__);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700248 kfree(audio->enc_cfg);
249 kfree(audio);
250 return -ENOMEM;
251 }
252
253 /* open qcelp encoder in T/NT mode */
254 if ((file->f_mode & FMODE_WRITE) &&
255 (file->f_mode & FMODE_READ)) {
256 audio->feedback = NON_TUNNEL_MODE;
257 rc = q6asm_open_read_write(audio->ac, FORMAT_V13K,
258 FORMAT_LINEAR_PCM);
259 if (rc < 0) {
260 pr_err("%s:session id %d: NT mode Open failed rc=%d\n",
261 __func__, audio->ac->session, rc);
262 rc = -ENODEV;
263 goto fail;
264 }
265 pr_info("%s:session id %d: NT mode encoder success\n", __func__,
266 audio->ac->session);
267 } else if (!(file->f_mode & FMODE_WRITE) &&
268 (file->f_mode & FMODE_READ)) {
269 audio->feedback = TUNNEL_MODE;
270 rc = q6asm_open_read(audio->ac, FORMAT_V13K);
271 if (rc < 0) {
272 pr_err("%s:session id %d: T mode Open failed rc=%d\n",
273 __func__, audio->ac->session, rc);
274 rc = -ENODEV;
275 goto fail;
276 }
277 /* register for tx overflow (valid for tunnel mode only) */
278 rc = q6asm_reg_tx_overflow(audio->ac, 0x01);
279 if (rc < 0) {
280 pr_err("%s:session id %d: TX Overflow registration\
281 failed rc=%d\n", __func__, audio->ac->session, rc);
282 rc = -ENODEV;
283 goto fail;
284 }
285 pr_info("%s:session id %d: T mode encoder success\n", __func__,
286 audio->ac->session);
287 } else {
288 pr_err("%s:session id %d: Unexpected mode\n", __func__,
289 audio->ac->session);
290 rc = -EACCES;
291 goto fail;
292 }
293
294 audio->opened = 1;
295 atomic_set(&audio->in_count, PCM_BUF_COUNT);
296 atomic_set(&audio->out_count, 0x00);
297 audio->enc_ioctl = qcelp_in_ioctl;
298 file->private_data = audio;
299
300 pr_info("%s:session id %d: success\n", __func__, audio->ac->session);
301 return 0;
302fail:
303 q6asm_audio_client_free(audio->ac);
304 kfree(audio->enc_cfg);
305 kfree(audio);
306 return rc;
307}
308
309static const struct file_operations audio_in_fops = {
310 .owner = THIS_MODULE,
311 .open = qcelp_in_open,
312 .release = audio_in_release,
313 .read = audio_in_read,
314 .write = audio_in_write,
315 .unlocked_ioctl = audio_in_ioctl,
316};
317
318struct miscdevice audio_qcelp_in_misc = {
319 .minor = MISC_DYNAMIC_MINOR,
320 .name = "msm_qcelp_in",
321 .fops = &audio_in_fops,
322};
323
324static int __init qcelp_in_init(void)
325{
326 return misc_register(&audio_qcelp_in_misc);
327}
328
329device_initcall(qcelp_in_init);