blob: cf7548d9347969fdddea65449424cbc989a2a454 [file] [log] [blame]
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001/*
2 * Copyright (C) 2009 Google, Inc.
3 * Copyright (C) 2009 HTC Corporation
Mohammad Johny Shaik4c1a0922013-10-24 13:40:08 -07004 * Copyright (c) 2010-2013, The Linux Foundation. All rights reserved.
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07005 *
6 * This software is licensed under the terms of the GNU General Public
7 * License version 2, as published by the Free Software Foundation, and
8 * may be copied, distributed, and modified under those terms.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 */
16
17#include <linux/fs.h>
18#include <linux/module.h>
19#include <linux/miscdevice.h>
20#include <linux/mutex.h>
21#include <linux/sched.h>
22#include <linux/uaccess.h>
23#include <linux/spinlock.h>
24#include <linux/slab.h>
25#include <linux/wait.h>
26#include <linux/msm_audio.h>
Stephen Boyd2fcabf92012-05-30 10:41:11 -070027#include <linux/pm_qos.h>
28
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070029#include <asm/atomic.h>
30#include <mach/debug_mm.h>
31#include <mach/qdsp6v2/audio_dev_ctl.h>
32#include <sound/q6asm.h>
33#include <sound/apr_audio.h>
Bharath Ramachandramurthy4613ce42011-08-23 14:39:44 -070034#include <linux/wakelock.h>
Stephen Boyd2fcabf92012-05-30 10:41:11 -070035#include <mach/cpuidle.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070036
37#define MAX_BUF 4
38#define BUFSZ (480 * 8)
39#define BUFFER_SIZE_MULTIPLE 4
40#define MIN_BUFFER_SIZE 160
41
42#define VOC_REC_NONE 0xFF
43
44struct pcm {
45 struct mutex lock;
46 struct mutex read_lock;
47 wait_queue_head_t wait;
48 spinlock_t dsp_lock;
49 struct audio_client *ac;
50 uint32_t sample_rate;
51 uint32_t channel_count;
52 uint32_t buffer_size;
53 uint32_t buffer_count;
54 uint32_t rec_mode;
55 uint32_t in_frame_info[MAX_BUF][2];
56 atomic_t in_count;
57 atomic_t in_enabled;
58 atomic_t in_opened;
59 atomic_t in_stopped;
Bharath Ramachandramurthy4613ce42011-08-23 14:39:44 -070060 struct wake_lock wakelock;
Stephen Boyd2fcabf92012-05-30 10:41:11 -070061 struct pm_qos_request pm_qos_req;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070062};
63
64static void pcm_in_get_dsp_buffers(struct pcm*,
65 uint32_t token, uint32_t *payload);
66
67void pcm_in_cb(uint32_t opcode, uint32_t token,
68 uint32_t *payload, void *priv)
69{
70 struct pcm *pcm = (struct pcm *) priv;
71 unsigned long flags;
72
73 spin_lock_irqsave(&pcm->dsp_lock, flags);
74 switch (opcode) {
75 case ASM_DATA_EVENT_READ_DONE:
76 pcm_in_get_dsp_buffers(pcm, token, payload);
77 break;
Laxminath Kasam692c6542012-02-21 11:17:47 +053078 case RESET_EVENTS:
79 reset_device();
80 break;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070081 default:
82 break;
83 }
84 spin_unlock_irqrestore(&pcm->dsp_lock, flags);
85}
Bharath Ramachandramurthy4613ce42011-08-23 14:39:44 -070086static void pcm_in_prevent_sleep(struct pcm *audio)
87{
88 pr_debug("%s:\n", __func__);
89 wake_lock(&audio->wakelock);
Stephen Boyd2fcabf92012-05-30 10:41:11 -070090 pm_qos_update_request(&audio->pm_qos_req,
91 msm_cpuidle_get_deep_idle_latency());
Bharath Ramachandramurthy4613ce42011-08-23 14:39:44 -070092}
93
94static void pcm_in_allow_sleep(struct pcm *audio)
95{
96 pr_debug("%s:\n", __func__);
Stephen Boyd2fcabf92012-05-30 10:41:11 -070097 pm_qos_update_request(&audio->pm_qos_req, PM_QOS_DEFAULT_VALUE);
Bharath Ramachandramurthy4613ce42011-08-23 14:39:44 -070098 wake_unlock(&audio->wakelock);
Bharath Ramachandramurthy4613ce42011-08-23 14:39:44 -070099}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700100
101static void pcm_in_get_dsp_buffers(struct pcm *pcm,
102 uint32_t token, uint32_t *payload)
103{
104 pcm->in_frame_info[token][0] = payload[7];
105 pcm->in_frame_info[token][1] = payload[3];
106 if (atomic_read(&pcm->in_count) <= pcm->buffer_count)
107 atomic_inc(&pcm->in_count);
108 wake_up(&pcm->wait);
109}
110
111static int pcm_in_enable(struct pcm *pcm)
112{
113 if (atomic_read(&pcm->in_enabled))
114 return 0;
115 return q6asm_run(pcm->ac, 0, 0, 0);
116}
117
118static int pcm_in_disable(struct pcm *pcm)
119{
120 int rc = 0;
121
122 if (atomic_read(&pcm->in_opened)) {
123 atomic_set(&pcm->in_enabled, 0);
124 atomic_set(&pcm->in_opened, 0);
125 rc = q6asm_cmd(pcm->ac, CMD_CLOSE);
126
127 atomic_set(&pcm->in_stopped, 1);
128 memset(pcm->in_frame_info, 0,
129 sizeof(char) * pcm->buffer_count * 2);
130 wake_up(&pcm->wait);
131 }
132 return rc;
133}
134
135static int config(struct pcm *pcm)
136{
137 int rc = 0;
138
139 pr_debug("%s: pcm prefill, buffer_size = %d\n", __func__,
140 pcm->buffer_size);
141 rc = q6asm_audio_client_buf_alloc(OUT, pcm->ac,
142 pcm->buffer_size, pcm->buffer_count);
143 if (rc < 0) {
144 pr_err("Audio Start: Buffer Allocation failed \
145 rc = %d\n", rc);
146 goto fail;
147 }
148
149 rc = q6asm_enc_cfg_blk_pcm(pcm->ac, pcm->sample_rate,
150 pcm->channel_count);
151 if (rc < 0) {
152 pr_err("%s: cmd media format block failed", __func__);
153 goto fail;
154 }
155fail:
156 return rc;
157}
158
159static long pcm_in_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
160{
161 struct pcm *pcm = file->private_data;
162 int rc = 0;
163
164 mutex_lock(&pcm->lock);
165 switch (cmd) {
166 case AUDIO_SET_VOLUME:
167 break;
168 case AUDIO_GET_STATS: {
169 struct msm_audio_stats stats;
170 memset(&stats, 0, sizeof(stats));
171 if (copy_to_user((void *) arg, &stats, sizeof(stats)))
172 rc = -EFAULT;
173 break;
174 }
175 case AUDIO_START: {
176 int cnt = 0;
177 if (atomic_read(&pcm->in_enabled)) {
178 pr_info("%s:AUDIO_START already over\n", __func__);
179 rc = 0;
180 break;
181 }
182 rc = config(pcm);
183 if (rc) {
184 pr_err("%s: IN Configuration failed\n", __func__);
185 rc = -EFAULT;
186 break;
187 }
188
189 rc = pcm_in_enable(pcm);
190 if (rc) {
191 pr_err("%s: In Enable failed\n", __func__);
192 rc = -EFAULT;
193 break;
194 }
Bharath Ramachandramurthy4613ce42011-08-23 14:39:44 -0700195 pcm_in_prevent_sleep(pcm);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700196 atomic_set(&pcm->in_enabled, 1);
197
198 while (cnt++ < pcm->buffer_count)
199 q6asm_read(pcm->ac);
200 pr_info("%s: AUDIO_START session id[%d]\n", __func__,
201 pcm->ac->session);
202
203 if (pcm->rec_mode != VOC_REC_NONE)
204 msm_enable_incall_recording(pcm->ac->session,
205 pcm->rec_mode, pcm->sample_rate, pcm->channel_count);
206
207 break;
208 }
209 case AUDIO_GET_SESSION_ID: {
210 if (copy_to_user((void *) arg, &pcm->ac->session,
211 sizeof(unsigned short)))
212 rc = -EFAULT;
213 break;
214 }
215 case AUDIO_STOP:
216 break;
217 case AUDIO_FLUSH:
218 break;
219 case AUDIO_SET_CONFIG: {
220 struct msm_audio_config config;
221
222 if (copy_from_user(&config, (void *) arg, sizeof(config))) {
223 rc = -EFAULT;
224 break;
225 }
226 pr_debug("%s: SET_CONFIG: buffer_size:%d channel_count:%d"
227 "sample_rate:%d, buffer_count:%d\n", __func__,
228 config.buffer_size, config.channel_count,
229 config.sample_rate, config.buffer_count);
230
231 if (!config.channel_count || config.channel_count > 2) {
232 rc = -EINVAL;
233 break;
234 }
235
236 if (config.sample_rate < 8000 || config.sample_rate > 48000) {
237 rc = -EINVAL;
238 break;
239 }
240
241 if ((config.buffer_size % (config.channel_count *
242 BUFFER_SIZE_MULTIPLE)) ||
243 (config.buffer_size < MIN_BUFFER_SIZE)) {
244 pr_err("%s: Buffer Size should be multiple of "
245 "[4 * no. of channels] and greater than 160\n",
246 __func__);
247 rc = -EINVAL;
248 break;
249 }
250
251 pcm->sample_rate = config.sample_rate;
252 pcm->channel_count = config.channel_count;
253 pcm->buffer_size = config.buffer_size;
254 pcm->buffer_count = config.buffer_count;
255 break;
256 }
257 case AUDIO_GET_CONFIG: {
258 struct msm_audio_config config;
Mohammad Johny Shaik4c1a0922013-10-24 13:40:08 -0700259 memset(&config, 0, sizeof(config));
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700260 config.buffer_size = pcm->buffer_size;
261 config.buffer_count = pcm->buffer_count;
262 config.sample_rate = pcm->sample_rate;
263 config.channel_count = pcm->channel_count;
264 config.unused[0] = 0;
265 config.unused[1] = 0;
266 config.unused[2] = 0;
267 if (copy_to_user((void *) arg, &config, sizeof(config)))
268 rc = -EFAULT;
269 break;
270 }
271 case AUDIO_ENABLE_AUDPRE: {
272
273 uint16_t enable_mask;
274
275 if (copy_from_user(&enable_mask, (void *) arg,
276 sizeof(enable_mask))) {
277 rc = -EFAULT;
278 break;
279 }
280 if (enable_mask & FLUENCE_ENABLE)
281 rc = auddev_cfg_tx_copp_topology(pcm->ac->session,
282 VPM_TX_DM_FLUENCE_COPP_TOPOLOGY);
283 else
284 rc = auddev_cfg_tx_copp_topology(pcm->ac->session,
285 DEFAULT_COPP_TOPOLOGY);
286 break;
287 }
288
289 case AUDIO_SET_INCALL: {
290 if (copy_from_user(&pcm->rec_mode,
291 (void *) arg,
292 sizeof(pcm->rec_mode))) {
293 rc = -EFAULT;
294 pr_err("%s: Error copying in-call mode\n", __func__);
295 break;
296 }
297
298 if (pcm->rec_mode != VOC_REC_UPLINK &&
299 pcm->rec_mode != VOC_REC_DOWNLINK &&
300 pcm->rec_mode != VOC_REC_BOTH) {
301 rc = -EINVAL;
302 pcm->rec_mode = VOC_REC_NONE;
303
304 pr_err("%s: Invalid %d in-call rec_mode\n",
305 __func__, pcm->rec_mode);
306 break;
307 }
308
309 pr_debug("%s: In-call rec_mode %d\n", __func__, pcm->rec_mode);
310 break;
311 }
312
313 default:
314 rc = -EINVAL;
315 break;
316 }
317 mutex_unlock(&pcm->lock);
318 return rc;
319}
320
321static int pcm_in_open(struct inode *inode, struct file *file)
322{
323 struct pcm *pcm;
324 int rc = 0;
Bharath Ramachandramurthy4613ce42011-08-23 14:39:44 -0700325 char name[24];
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700326
327 pcm = kzalloc(sizeof(struct pcm), GFP_KERNEL);
328 if (!pcm)
329 return -ENOMEM;
330
331 pcm->channel_count = 1;
332 pcm->sample_rate = 8000;
333 pcm->buffer_size = BUFSZ;
334 pcm->buffer_count = MAX_BUF;
335
336 pcm->ac = q6asm_audio_client_alloc((app_cb)pcm_in_cb, (void *)pcm);
337 if (!pcm->ac) {
338 pr_err("%s: Could not allocate memory\n", __func__);
339 rc = -ENOMEM;
340 goto fail;
341 }
342
343 mutex_init(&pcm->lock);
344 mutex_init(&pcm->read_lock);
345 spin_lock_init(&pcm->dsp_lock);
346 init_waitqueue_head(&pcm->wait);
347
348 rc = q6asm_open_read(pcm->ac, FORMAT_LINEAR_PCM);
349 if (rc < 0) {
350 pr_err("%s: Cmd Open Failed\n", __func__);
351 goto fail;
352 }
353
354 atomic_set(&pcm->in_stopped, 0);
355 atomic_set(&pcm->in_enabled, 0);
356 atomic_set(&pcm->in_count, 0);
357 atomic_set(&pcm->in_opened, 1);
Bharath Ramachandramurthy4613ce42011-08-23 14:39:44 -0700358 snprintf(name, sizeof name, "pcm_in_%x", pcm->ac->session);
359 wake_lock_init(&pcm->wakelock, WAKE_LOCK_SUSPEND, name);
360 snprintf(name, sizeof name, "pcm_in_idle_%x", pcm->ac->session);
Stephen Boyd2fcabf92012-05-30 10:41:11 -0700361 pm_qos_add_request(&pcm->pm_qos_req, PM_QOS_CPU_DMA_LATENCY,
362 PM_QOS_DEFAULT_VALUE);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700363
364 pcm->rec_mode = VOC_REC_NONE;
365
366 file->private_data = pcm;
367 pr_info("%s: pcm in open session id[%d]\n", __func__, pcm->ac->session);
368 return 0;
369fail:
370 if (pcm->ac)
371 q6asm_audio_client_free(pcm->ac);
372 kfree(pcm);
373 return rc;
374}
375
376static ssize_t pcm_in_read(struct file *file, char __user *buf,
377 size_t count, loff_t *pos)
378{
379 struct pcm *pcm = file->private_data;
380 const char __user *start = buf;
381 void *data;
382 uint32_t offset = 0;
383 uint32_t size = 0;
384 uint32_t idx;
385 int rc = 0;
386 int len = 0;
387
388 if (!atomic_read(&pcm->in_enabled))
389 return -EFAULT;
390 mutex_lock(&pcm->read_lock);
391 while (count > 0) {
392 rc = wait_event_timeout(pcm->wait,
393 (atomic_read(&pcm->in_count) ||
394 atomic_read(&pcm->in_stopped)), 5 * HZ);
395 if (!rc) {
396 pr_err("%s: wait_event_timeout failed\n", __func__);
397 goto fail;
398 }
399
400 if (atomic_read(&pcm->in_stopped) &&
401 !atomic_read(&pcm->in_count)) {
402 mutex_unlock(&pcm->read_lock);
403 return 0;
404 }
405
406 data = q6asm_is_cpu_buf_avail(OUT, pcm->ac, &size, &idx);
407 if (count >= size)
408 len = size;
409 else {
410 len = count;
411 pr_err("%s: short read data[%p]bytesavail[%d]"
412 "bytesrequest[%d]"
413 "bytesrejected%d]\n",\
414 __func__, data, size,
415 count, (size - count));
416 }
417 if ((len) && data) {
418 offset = pcm->in_frame_info[idx][1];
419 if (copy_to_user(buf, data+offset, len)) {
420 pr_err("%s copy_to_user failed len[%d]\n",
421 __func__, len);
422 rc = -EFAULT;
423 goto fail;
424 }
425 count -= len;
426 buf += len;
427 }
428 atomic_dec(&pcm->in_count);
429 memset(&pcm->in_frame_info[idx], 0,
430 sizeof(uint32_t) * 2);
431
432 rc = q6asm_read(pcm->ac);
433 if (rc < 0) {
434 pr_err("%s q6asm_read fail\n", __func__);
435 goto fail;
436 }
437 rmb();
438 break;
439 }
440 rc = buf-start;
441fail:
442 mutex_unlock(&pcm->read_lock);
443 return rc;
444}
445
446static int pcm_in_release(struct inode *inode, struct file *file)
447{
448 int rc = 0;
449 struct pcm *pcm = file->private_data;
450
451 pr_info("[%s:%s] release session id[%d]\n", __MM_FILE__,
452 __func__, pcm->ac->session);
453 mutex_lock(&pcm->lock);
454
455 if ((pcm->rec_mode != VOC_REC_NONE) && atomic_read(&pcm->in_enabled)) {
456 msm_disable_incall_recording(pcm->ac->session, pcm->rec_mode);
457
458 pcm->rec_mode = VOC_REC_NONE;
459 }
460
461 /* remove this session from topology list */
462 auddev_cfg_tx_copp_topology(pcm->ac->session,
463 DEFAULT_COPP_TOPOLOGY);
464 mutex_unlock(&pcm->lock);
465
466 rc = pcm_in_disable(pcm);
467 msm_clear_session_id(pcm->ac->session);
468 q6asm_audio_client_free(pcm->ac);
Bharath Ramachandramurthy4613ce42011-08-23 14:39:44 -0700469 pcm_in_allow_sleep(pcm);
470 wake_lock_destroy(&pcm->wakelock);
Stephen Boyd2fcabf92012-05-30 10:41:11 -0700471 pm_qos_remove_request(&pcm->pm_qos_req);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700472 kfree(pcm);
473 return rc;
474}
475
476static const struct file_operations pcm_in_fops = {
477 .owner = THIS_MODULE,
478 .open = pcm_in_open,
479 .read = pcm_in_read,
480 .release = pcm_in_release,
481 .unlocked_ioctl = pcm_in_ioctl,
482};
483
484struct miscdevice pcm_in_misc = {
485 .minor = MISC_DYNAMIC_MINOR,
486 .name = "msm_pcm_in",
487 .fops = &pcm_in_fops,
488};
489
490static int __init pcm_in_init(void)
491{
492 return misc_register(&pcm_in_misc);
493}
494
495device_initcall(pcm_in_init);