blob: ae63f0db53289dc8bffff3901b32303d0619097a [file] [log] [blame]
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001/* arch/arm/mach-msm/qdsp5/audio_voicememo.c
2 *
3 * Voice Memo device
4 *
5 * Copyright (C) 2008 Google, Inc.
6 * Copyright (C) 2008 HTC Corporation
Duy Truong790f06d2013-02-13 16:38:12 -08007 * Copyright (c) 2009-2012, The Linux Foundation. All rights reserved.
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07008 *
9 * This code is based in part on arch/arm/mach-msm/qdsp5/audio_mp3.c
10 *
11 * This software is licensed under the terms of the GNU General Public
12 * License version 2, as published by the Free Software Foundation, and
13 * may be copied, distributed, and modified under those terms.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
18 *
19 * See the GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, you can find it at http://www.fsf.org.
22 *
23 */
24
25#include <linux/module.h>
26#include <linux/fs.h>
27#include <linux/miscdevice.h>
28#include <linux/uaccess.h>
29#include <linux/kthread.h>
30#include <linux/wait.h>
31#include <linux/dma-mapping.h>
32#include <linux/delay.h>
33#include <linux/msm_audio_voicememo.h>
34#include <linux/slab.h>
35
36#include <asm/atomic.h>
37#include <asm/ioctls.h>
38#include <mach/msm_rpcrouter.h>
39#include <mach/debug_mm.h>
40
41#include "audmgr.h"
42
43#define SND_PROG_VERS "rs30000002:0x00020001"
44#define SND_PROG 0x30000002
45#define SND_VERS_COMP 0x00020001
46#define SND_VERS2_COMP 0x00030001
47
48#define SND_VOC_REC_START_PROC 19
49#define SND_VOC_REC_STOP_PROC 20
50#define SND_VOC_REC_PAUSE_PROC 21
51#define SND_VOC_REC_RESUME_PROC 22
52#define SND_VOC_REC_PUT_BUF_PROC 23
53
54#define SND_VOC_REC_AV_SYNC_CB_PTR_PROC 9
55#define SND_VOC_REC_CB_FUNC_TYPE_PROC 10
56
57#define REC_CLIENT_DATA 0x11223344
58#define DATA_CB_FUNC_ID 0x12345678
59#define AV_SYNC_CB_FUNC_ID 0x87654321
60#define CLIENT_DATA 0xaabbccdd
61
62#define RPC_TYPE_REQUEST 0
63#define RPC_TYPE_REPLY 1
64
65#define RPC_STATUS_FAILURE 0
66#define RPC_STATUS_SUCCESS 1
67
68#define RPC_VERSION 2
69
70#define RPC_COMMON_HDR_SZ (sizeof(uint32_t) * 2)
71#define RPC_REQUEST_HDR_SZ (sizeof(struct rpc_request_hdr))
72#define RPC_REPLY_HDR_SZ (sizeof(uint32_t) * 3)
73#define RPC_REPLY_SZ (sizeof(uint32_t) * 6)
74
75#define MAX_FRAME_SIZE 36 /* QCELP - 36, AMRNB - 32, EVRC - 24 */
76#define MAX_REC_BUF_COUNT 5 /* Maximum supported voc rec buffers */
77#define MAX_REC_BUF_SIZE (MAX_FRAME_SIZE * 10)
78#define MAX_VOICEMEMO_BUF_SIZE \
79 ((MAX_REC_BUF_SIZE)*MAX_REC_BUF_COUNT) /* 5 buffers for 200ms frame */
80#define MSM_AUD_BUFFER_UPDATE_WAIT_MS 2000
81
82enum rpc_voc_rec_status_type {
83 RPC_VOC_REC_STAT_SUCCESS = 1,
84 RPC_VOC_REC_STAT_DONE = 2,
85 RPC_VOC_REC_STAT_AUTO_STOP = 4,
86 RPC_VOC_REC_STAT_PAUSED = 8,
87 RPC_VOC_REC_STAT_RESUMED = 16,
88 RPC_VOC_REC_STAT_ERROR = 32,
89 RPC_VOC_REC_STAT_BUFFER_ERROR = 64,
90 RPC_VOC_REC_STAT_INVALID_PARAM = 128,
91 RPC_VOC_REC_STAT_INT_TIME = 256,
92 RPC_VOC_REC_STAT_DATA = 512,
93 RPC_VOC_REC_STAT_NOT_READY = 1024,
94 RPC_VOC_REC_STAT_INFORM_EVRC = 2048,
95 RPC_VOC_REC_STAT_INFORM_13K = 4096,
96 RPC_VOC_REC_STAT_INFORM_AMR = 8192,
97 RPC_VOC_REC_STAT_INFORM_MAX = 65535
98};
99
100struct rpc_snd_voc_rec_start_args {
101 uint32_t param_status; /* 1 = valid, 0 = not valid */
102 uint32_t rec_type;
103 uint32_t rec_interval_ms;
104 uint32_t auto_stop_ms;
105 uint32_t capability;
106 uint32_t max_rate;
107 uint32_t min_rate;
108 uint32_t frame_format;
109 uint32_t dtx_enable;
110 uint32_t data_req_ms;
111 uint32_t rec_client_data;
112
113 uint32_t cb_func_id;
114 uint32_t sync_cb_func_id;
115 uint32_t client_data;
116};
117
118struct rpc_snd_voc_rec_put_buf_args {
119 uint32_t buf;
120 uint32_t num_bytes;
121};
122
123struct snd_voc_rec_start_msg {
124 struct rpc_request_hdr hdr;
125 struct rpc_snd_voc_rec_start_args args;
126};
127
128struct snd_voc_rec_put_buf_msg {
129 struct rpc_request_hdr hdr;
130 struct rpc_snd_voc_rec_put_buf_args args;
131};
132
133struct snd_voc_rec_av_sync_cb_func_data {
134 uint32_t sync_cb_func_id;
135 uint32_t status; /* Pointer status (1 = valid, 0 = invalid) */
136 uint32_t num_samples;
137 uint32_t time_stamp[2];
138 uint32_t lost_samples;
139 uint32_t frame_index;
140 uint32_t client_data;
141};
142
143struct snd_voc_rec_cb_func_fw_data {
144 uint32_t fw_ptr_status; /* FW Pointer status (1=valid,0=invalid) */
145 uint32_t rec_buffer_size;
146 uint32_t data[MAX_REC_BUF_SIZE/4];
147 uint32_t rec_buffer_size_copy;
148 uint32_t rec_num_frames; /* Number of voice frames */
149 uint32_t rec_length; /* Valid data in record buffer =
150 * data_req_ms amount of data */
151 uint32_t client_data; /* A11 rec buffer pointer */
152 uint32_t rw_ptr_status; /* RW Pointer status (1=valid,0=invalid) */
153};
154
155struct snd_voc_rec_cb_func_rw_data {
156 uint32_t fw_ptr_status; /* FW Pointer status (1=valid,0=invalid) */
157 uint32_t rw_ptr_status; /* RW Pointer status (1=valid,0=invalid) */
158 uint32_t rec_buffer_size;
159 uint32_t data[MAX_REC_BUF_SIZE/4];
160 uint32_t rec_buffer_size_copy;
161 uint32_t rec_num_frames; /* Number of voice frames */
162 uint32_t rec_length; /* Valid data in record buffer =
163 * data_req_ms amount of data */
164 uint32_t client_data; /* A11 rec buffer pointer */
165};
166
167struct snd_voc_rec_data_cb_func_data {
168 uint32_t cb_func_id;
169 uint32_t status; /* Pointer status (1 = valid, 0 = invalid) */
170 uint32_t rec_status;
171
172 union {
173 struct snd_voc_rec_cb_func_fw_data fw_data;
174 struct snd_voc_rec_cb_func_rw_data rw_data;
175 } pkt;
176};
177
178struct buffer {
179 void *data;
180 unsigned size;
181 unsigned used; /* Usage actual recorded data */
182 unsigned addr;
183 unsigned numframes;
184};
185
186struct audio_voicememo {
187 uint32_t byte_count; /* Pass statistics to user space for
188 * time stamping */
189 uint32_t frame_count;
190
191 int opened;
192 int enabled;
193 int running;
194 int stopped;
195 int pause_resume;
196
197 uint32_t rpc_prog;
198 uint32_t rpc_ver;
199 uint32_t rpc_xid;
200 uint32_t rpc_status;
201
202 struct mutex lock;
203 struct mutex read_lock;
204 struct mutex dsp_lock;
205 wait_queue_head_t read_wait;
206 wait_queue_head_t wait;
207
208 struct buffer in[MAX_REC_BUF_COUNT];
209 char *rec_buf_ptr;
210 dma_addr_t phys;
211 uint32_t rec_buf_size;
212 uint8_t read_next; /* index to input buffers to be read next */
213 uint8_t fill_next; /* index to buffer that should be filled as
214 * data comes from A9 */
215
216 struct audmgr audmgr;
217
218 struct msm_audio_voicememo_config voicememo_cfg;
219
220 struct msm_rpc_endpoint *sndept;
221 struct task_struct *task;
222};
223
224static struct audio_voicememo the_audio_voicememo;
225
226static int audvoicememo_validate_usr_config(
227 struct msm_audio_voicememo_config *config)
228{
229 int rc = -1; /* error */
230
231 if (config->rec_type != RPC_VOC_REC_FORWARD &&
232 config->rec_type != RPC_VOC_REC_REVERSE &&
233 config->rec_type != RPC_VOC_REC_BOTH)
234 goto done;
235
236 /* QCELP, EVRC, AMR-NB only */
237 if (config->capability != RPC_VOC_CAP_IS733 &&
238 config->capability != RPC_VOC_CAP_IS127 &&
239 config->capability != RPC_VOC_CAP_AMR)
240 goto done;
241
242 /* QCP, AMR format supported */
243 if ((config->frame_format != RPC_VOC_PB_NATIVE_QCP) &&
244 (config->frame_format != RPC_VOC_PB_AMR))
245 goto done;
246
247 if ((config->frame_format == RPC_VOC_PB_AMR) &&
248 (config->capability != RPC_VOC_CAP_AMR))
249 goto done;
250
251 /* To make sure, max kernel buf size matches
252 * with max data request time */
253 if (config->data_req_ms > ((MAX_REC_BUF_SIZE/MAX_FRAME_SIZE)*20))
254 goto done;
255
256 rc = 0;
257done:
258 return rc;
259}
260
261static void audvoicememo_flush_buf(struct audio_voicememo *audio)
262{
263 uint8_t index;
264
265 for (index = 0; index < MAX_REC_BUF_COUNT; index++)
266 audio->in[index].used = 0;
267
268 audio->read_next = 0;
Manish Dewangan89a9f232012-02-09 17:14:40 +0530269 mutex_lock(&audio->dsp_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700270 audio->fill_next = 0;
Manish Dewangan89a9f232012-02-09 17:14:40 +0530271 mutex_unlock(&audio->dsp_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700272}
273
274static void audvoicememo_ioport_reset(struct audio_voicememo *audio)
275{
276 /* Make sure read/write thread are free from
277 * sleep and knowing that system is not able
278 * to process io request at the moment
279 */
280 wake_up(&audio->read_wait);
281 mutex_lock(&audio->read_lock);
282 audvoicememo_flush_buf(audio);
283 mutex_unlock(&audio->read_lock);
284}
285
286/* must be called with audio->lock held */
287static int audvoicememo_enable(struct audio_voicememo *audio)
288{
289 struct audmgr_config cfg;
290 struct snd_voc_rec_put_buf_msg bmsg;
291 struct snd_voc_rec_start_msg msg;
292 uint8_t index;
293 uint32_t offset = 0;
294 int rc;
295
296 if (audio->enabled)
297 return 0;
298
299 /* Codec / method configure to audmgr client */
300 cfg.tx_rate = RPC_AUD_DEF_SAMPLE_RATE_8000;
301 cfg.rx_rate = RPC_AUD_DEF_SAMPLE_RATE_NONE;
302 cfg.def_method = RPC_AUD_DEF_METHOD_RECORD;
303
304 if (audio->voicememo_cfg.capability == RPC_VOC_CAP_IS733)
305 cfg.codec = RPC_AUD_DEF_CODEC_VOC_13K;
306 else if (audio->voicememo_cfg.capability == RPC_VOC_CAP_IS127)
307 cfg.codec = RPC_AUD_DEF_CODEC_VOC_EVRC;
308 else
309 cfg.codec = RPC_AUD_DEF_CODEC_VOC_AMR; /* RPC_VOC_CAP_AMR */
310
311 cfg.snd_method = RPC_SND_METHOD_VOICE;
312 rc = audmgr_enable(&audio->audmgr, &cfg);
313
314 if (rc < 0)
315 return rc;
316
317 /* Configure VOC Rec buffer */
318 for (index = 0; index < MAX_REC_BUF_COUNT; index++) {
319 audio->in[index].data = audio->rec_buf_ptr + offset;
320 audio->in[index].addr = audio->phys + offset;
321 audio->in[index].size = audio->rec_buf_size;
322 audio->in[index].used = 0;
323 audio->in[index].numframes = 0;
324 offset += audio->rec_buf_size;
325 bmsg.args.buf = (uint32_t) audio->in[index].data;
326 bmsg.args.num_bytes = cpu_to_be32(audio->in[index].size);
327 MM_DBG("rec_buf_ptr=0x%8x, rec_buf_size = 0x%8x\n",
328 bmsg.args.buf, bmsg.args.num_bytes);
329
330 msm_rpc_setup_req(&bmsg.hdr, audio->rpc_prog, audio->rpc_ver,
331 SND_VOC_REC_PUT_BUF_PROC);
332 audio->rpc_xid = bmsg.hdr.xid;
333 audio->rpc_status = RPC_STATUS_FAILURE;
334 msm_rpc_write(audio->sndept, &bmsg, sizeof(bmsg));
335 rc = wait_event_timeout(audio->wait,
336 audio->rpc_status != RPC_STATUS_FAILURE, 1 * HZ);
337 if (rc == 0)
338 goto err;
339 }
340
341
342 /* Start Recording */
343 msg.args.param_status = cpu_to_be32(0x00000001);
344 msg.args.rec_type = cpu_to_be32(audio->voicememo_cfg.rec_type);
345 msg.args.rec_interval_ms =
346 cpu_to_be32(audio->voicememo_cfg.rec_interval_ms);
347 msg.args.auto_stop_ms = cpu_to_be32(audio->voicememo_cfg.auto_stop_ms);
348 msg.args.capability = cpu_to_be32(audio->voicememo_cfg.capability);
349 msg.args.max_rate = cpu_to_be32(audio->voicememo_cfg.max_rate);
350 msg.args.min_rate = cpu_to_be32(audio->voicememo_cfg.min_rate);
351 msg.args.frame_format = cpu_to_be32(audio->voicememo_cfg.frame_format);
352 msg.args.dtx_enable = cpu_to_be32(audio->voicememo_cfg.dtx_enable);
353 msg.args.data_req_ms = cpu_to_be32(audio->voicememo_cfg.data_req_ms);
354 msg.args.rec_client_data = cpu_to_be32(REC_CLIENT_DATA);
355 msg.args.cb_func_id = cpu_to_be32(DATA_CB_FUNC_ID);
356 msg.args.sync_cb_func_id = cpu_to_be32(AV_SYNC_CB_FUNC_ID);
357 msg.args.client_data = cpu_to_be32(CLIENT_DATA);
358
359 msm_rpc_setup_req(&msg.hdr, audio->rpc_prog, audio->rpc_ver,
360 SND_VOC_REC_START_PROC);
361
362 audio->rpc_xid = msg.hdr.xid;
363 audio->rpc_status = RPC_STATUS_FAILURE;
364 msm_rpc_write(audio->sndept, &msg, sizeof(msg));
365 rc = wait_event_timeout(audio->wait,
366 audio->rpc_status != RPC_STATUS_FAILURE, 1 * HZ);
367 if (rc == 0)
368 goto err;
369
370 audio->rpc_xid = 0;
371 audio->enabled = 1;
372 return 0;
373
374err:
375 audio->rpc_xid = 0;
376 audmgr_disable(&audio->audmgr);
377 MM_ERR("Fail\n");
378 return -1;
379}
380
381/* must be called with audio->lock held */
382static int audvoicememo_disable(struct audio_voicememo *audio)
383{
384 struct rpc_request_hdr rhdr;
385 int rc = 0;
386 if (audio->enabled) {
387 msm_rpc_setup_req(&rhdr, audio->rpc_prog, audio->rpc_ver,
388 SND_VOC_REC_STOP_PROC);
389 rc = msm_rpc_write(audio->sndept, &rhdr, sizeof(rhdr));
Chaithanya Krishna Bacharaju6565d3d2012-06-06 16:19:23 +0530390 rc = wait_event_timeout(audio->wait, audio->stopped == 1,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700391 1 * HZ);
Chaithanya Krishna Bacharaju6565d3d2012-06-06 16:19:23 +0530392 if (rc == 0)
393 audio->stopped = 1;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700394 wake_up(&audio->read_wait);
395 audmgr_disable(&audio->audmgr);
396 audio->enabled = 0;
397 }
398 return 0;
399}
400
401/* RPC Reply Generator */
402static void rpc_reply(struct msm_rpc_endpoint *ept, uint32_t xid)
403{
404 int rc = 0;
405 uint8_t reply_buf[sizeof(struct rpc_reply_hdr)];
406 struct rpc_reply_hdr *reply = (struct rpc_reply_hdr *)reply_buf;
407
408 MM_DBG("inside\n");
409 reply->xid = cpu_to_be32(xid);
410 reply->type = cpu_to_be32(RPC_TYPE_REPLY); /* reply */
411 reply->reply_stat = cpu_to_be32(RPCMSG_REPLYSTAT_ACCEPTED);
412
413 reply->data.acc_hdr.accept_stat = cpu_to_be32(RPC_ACCEPTSTAT_SUCCESS);
414 reply->data.acc_hdr.verf_flavor = 0;
415 reply->data.acc_hdr.verf_length = 0;
416
417 rc = msm_rpc_write(ept, reply_buf, sizeof(reply_buf));
418 if (rc < 0)
419 MM_ERR("could not write RPC response: %d\n", rc);
420}
421
422static void process_rpc_request(uint32_t proc, uint32_t xid,
423 void *data, int len, void *private)
424{
425 struct audio_voicememo *audio = private;
426
427 MM_DBG("inside\n");
428 /* Sending Ack before processing the request
429 * to make sure A9 get response immediate
430 * However, if there is validation of request planned
431 * may be move this reply Ack at the end */
432 rpc_reply(audio->sndept, xid);
433 switch (proc) {
434 case SND_VOC_REC_AV_SYNC_CB_PTR_PROC: {
435 MM_DBG("AV Sync CB:func_id=0x%8x,status=0x%x\n",
436 be32_to_cpu(( \
437 (struct snd_voc_rec_av_sync_cb_func_data *)\
438 data)->sync_cb_func_id),\
439 be32_to_cpu(( \
440 (struct snd_voc_rec_av_sync_cb_func_data *)\
441 data)->status));
442 break;
443 }
444 case SND_VOC_REC_CB_FUNC_TYPE_PROC: {
445 struct snd_voc_rec_data_cb_func_data *datacb_data
446 = (void *)(data);
447 struct snd_voc_rec_put_buf_msg bmsg;
448 uint32_t rec_status = be32_to_cpu(datacb_data->rec_status);
449
450 MM_DBG("Data CB:func_id=0x%8x,status=0x%x,\
451 rec_status=0x%x\n",
452 be32_to_cpu(datacb_data->cb_func_id),\
453 be32_to_cpu(datacb_data->status),\
454 be32_to_cpu(datacb_data->rec_status));
455
456 /* Data recorded */
457 if ((rec_status == RPC_VOC_REC_STAT_DATA) ||
458 (rec_status == RPC_VOC_REC_STAT_DONE)) {
459 if (datacb_data->pkt.fw_data.fw_ptr_status &&
Chaithanya Krishna Bacharaju6565d3d2012-06-06 16:19:23 +0530460 be32_to_cpu(datacb_data->pkt.fw_data.rec_length) &&
461 be32_to_cpu(datacb_data->pkt.fw_data.rec_length)
Chaithanya Krishna Bacharaju28988f22012-07-17 11:33:27 +0530462 <= MAX_REC_BUF_SIZE) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700463
464 MM_DBG("Copy FW link:rec_buf_size \
465 = 0x%08x, rec_length=0x%08x\n",
466 be32_to_cpu( \
467 datacb_data->pkt.fw_data. \
468 rec_buffer_size_copy),\
469 be32_to_cpu(datacb_data->pkt.fw_data. \
470 rec_length));
471
472 mutex_lock(&audio->dsp_lock);
473 memcpy(audio->in[audio->fill_next].data, \
474 &(datacb_data->pkt.fw_data.data[0]), \
475 be32_to_cpu(
476 datacb_data->pkt.fw_data.rec_length));
477 audio->in[audio->fill_next].used =
478 be32_to_cpu(
479 datacb_data->pkt.fw_data.rec_length);
480 audio->in[audio->fill_next].numframes =
481 be32_to_cpu(
482 datacb_data->pkt.fw_data.rec_num_frames);
483 mutex_unlock(&audio->dsp_lock);
484 } else if (datacb_data->pkt.rw_data.rw_ptr_status &&
Chaithanya Krishna Bacharaju6565d3d2012-06-06 16:19:23 +0530485 be32_to_cpu(datacb_data->pkt.rw_data.rec_length) &&
486 be32_to_cpu(datacb_data->pkt.rw_data.rec_length)
Chaithanya Krishna Bacharaju28988f22012-07-17 11:33:27 +0530487 <= MAX_REC_BUF_SIZE) {
Chaithanya Krishna Bacharaju6565d3d2012-06-06 16:19:23 +0530488
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700489 MM_DBG("Copy RW link:rec_buf_size \
490 =0x%08x, rec_length=0x%08x\n",
491 be32_to_cpu( \
492 datacb_data->pkt.rw_data. \
493 rec_buffer_size_copy),\
494 be32_to_cpu(datacb_data->pkt.rw_data. \
495 rec_length));
496
497 mutex_lock(&audio->dsp_lock);
498 memcpy(audio->in[audio->fill_next].data, \
499 &(datacb_data->pkt.rw_data.data[0]), \
500 be32_to_cpu(
501 datacb_data->pkt.rw_data.rec_length));
502 audio->in[audio->fill_next].used =
503 be32_to_cpu(
504 datacb_data->pkt.rw_data.rec_length);
505 audio->in[audio->fill_next].numframes =
506 be32_to_cpu(
507 datacb_data->pkt.rw_data.rec_num_frames);
508 mutex_unlock(&audio->dsp_lock);
Chaithanya Krishna Bacharaju6565d3d2012-06-06 16:19:23 +0530509 } else {
510 MM_ERR("FW: ptr_status %d, rec_length=0x%08x,"
511 "RW: ptr_status %d, rec_length=0x%08x\n",
Chaithanya Krishna Bacharaju28988f22012-07-17 11:33:27 +0530512 datacb_data->pkt.fw_data.fw_ptr_status, \
Chaithanya Krishna Bacharaju6565d3d2012-06-06 16:19:23 +0530513 be32_to_cpu( \
514 datacb_data->pkt.fw_data.rec_length), \
Chaithanya Krishna Bacharaju28988f22012-07-17 11:33:27 +0530515 datacb_data->pkt.rw_data.rw_ptr_status, \
Chaithanya Krishna Bacharaju6565d3d2012-06-06 16:19:23 +0530516 be32_to_cpu( \
Chaithanya Krishna Bacharaju28988f22012-07-17 11:33:27 +0530517 datacb_data->pkt.rw_data.rec_length));
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700518 }
519 if (rec_status != RPC_VOC_REC_STAT_DONE) {
520 /* Not end of record */
521 bmsg.args.buf = \
522 (uint32_t) audio->in[audio->fill_next].data;
523 bmsg.args.num_bytes = \
524 be32_to_cpu(audio->in[audio->fill_next].size);
525
526 if (++audio->fill_next == MAX_REC_BUF_COUNT)
527 audio->fill_next = 0;
528
529 msm_rpc_setup_req(&bmsg.hdr, audio->rpc_prog,
530 audio->rpc_ver, SND_VOC_REC_PUT_BUF_PROC);
531
532 msm_rpc_write(audio->sndept, &bmsg,
533 sizeof(bmsg));
534
535 wake_up(&audio->read_wait);
536 } else {
537 /* Indication record stopped gracefully */
538 MM_DBG("End Of Voice Record\n");
Chaithanya Krishna Bacharaju6565d3d2012-06-06 16:19:23 +0530539 audio->stopped = 1;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700540 wake_up(&audio->wait);
541 }
542 } else if (rec_status == RPC_VOC_REC_STAT_PAUSED) {
543 MM_DBG(" Voice Record PAUSED\n");
544 audio->pause_resume = 1;
545 } else if (rec_status == RPC_VOC_REC_STAT_RESUMED) {
546 MM_DBG(" Voice Record RESUMED\n");
547 audio->pause_resume = 0;
548 } else if ((rec_status == RPC_VOC_REC_STAT_ERROR) ||
549 (rec_status == RPC_VOC_REC_STAT_INVALID_PARAM) ||
550 (rec_status == RPC_VOC_REC_STAT_BUFFER_ERROR))
551 MM_ERR("error recording =0x%8x\n",
552 rec_status);
553 else if (rec_status == RPC_VOC_REC_STAT_INT_TIME)
554 MM_DBG("Frames recorded matches interval \
555 callback time\n");
556 else if (rec_status == RPC_VOC_REC_STAT_AUTO_STOP) {
557 MM_DBG(" Voice Record AUTO STOP\n");
Manish Dewangan89a9f232012-02-09 17:14:40 +0530558 mutex_lock(&audio->lock);
559 audio->stopped = 1;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700560 wake_up(&audio->read_wait);
561 audmgr_disable(&audio->audmgr);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700562 audvoicememo_ioport_reset(audio);
563 audio->stopped = 0;
564 audio->enabled = 0;
Manish Dewangan89a9f232012-02-09 17:14:40 +0530565 mutex_unlock(&audio->lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700566 }
567 break;
568 }
569 default:
570 MM_ERR("UNKNOWN PROC , proc = 0x%8x \n", proc);
571 }
572}
573
574static int voicememo_rpc_thread(void *data)
575{
576 struct audio_voicememo *audio = data;
577 struct rpc_request_hdr *hdr = NULL;
578 uint32_t type;
579 int len;
580
581 MM_DBG("start\n");
582
583 while (!kthread_should_stop()) {
584 kfree(hdr);
585 hdr = NULL;
586
587 len = msm_rpc_read(audio->sndept, (void **) &hdr, -1, -1);
588 MM_DBG("rpc_read len = 0x%x\n", len);
589 if (len < 0) {
590 MM_ERR("rpc read failed (%d)\n", len);
591 break;
592 }
593 if (len < RPC_COMMON_HDR_SZ)
594 continue;
595 type = be32_to_cpu(hdr->type);
596 if (type == RPC_TYPE_REPLY) {
597 struct rpc_reply_hdr *rep = (void *) hdr;
598 uint32_t status;
599 if (len < RPC_REPLY_HDR_SZ)
600 continue;
601 status = be32_to_cpu(rep->reply_stat);
602 if (status == RPCMSG_REPLYSTAT_ACCEPTED) {
603 status =
604 be32_to_cpu(rep->data.acc_hdr.accept_stat);
605
606 /* Confirm major RPC success during open*/
607 if ((audio->enabled == 0) &&
608 (status == RPC_ACCEPTSTAT_SUCCESS) &&
609 (audio->rpc_xid == rep->xid)) {
610 audio->rpc_status = \
611 RPC_STATUS_SUCCESS;
612 wake_up(&audio->wait);
613 }
614 MM_DBG("rpc_reply status 0x%8x\n", status);
615 } else {
616 MM_ERR("rpc_reply denied!\n");
617 }
618 /* process reply */
619 continue;
620 } else if (type == RPC_TYPE_REQUEST) {
621 if (len < RPC_REQUEST_HDR_SZ)
622 continue;
623 process_rpc_request(be32_to_cpu(hdr->procedure),
624 be32_to_cpu(hdr->xid),
625 (void *) (hdr + 1),
626 len - sizeof(*hdr),
627 audio);
628 } else
629 MM_ERR("Unexpected type (%d)\n", type);
630 }
631 MM_DBG("stop\n");
632 kfree(hdr);
633 hdr = NULL;
634
635 return 0;
636}
637
638/* ------------------- device --------------------- */
639static long audio_voicememo_ioctl(struct file *file,
640 unsigned int cmd, unsigned long arg)
641{
642 struct audio_voicememo *audio = file->private_data;
643 int rc = 0;
644
645 if (cmd == AUDIO_GET_STATS) {
646 struct msm_audio_stats stats;
647 mutex_lock(&audio->dsp_lock);
648 stats.byte_count = audio->byte_count;
649 stats.sample_count = audio->frame_count;
650 mutex_unlock(&audio->dsp_lock);
651 if (copy_to_user((void *) arg, &stats, sizeof(stats)))
652 return -EFAULT;
653 return 0;
654 }
655
656 mutex_lock(&audio->lock);
657 switch (cmd) {
658 case AUDIO_START: {
659 MM_DBG("AUDIO_START\n");
660 audio->byte_count = 0;
661 audio->frame_count = 0;
662 if (audio->voicememo_cfg.rec_type != RPC_VOC_REC_NONE)
663 rc = audvoicememo_enable(audio);
664 else
665 rc = -EINVAL;
666 MM_DBG("AUDIO_START rc %d\n", rc);
667 break;
668 }
669 case AUDIO_STOP: {
670 MM_DBG("AUDIO_STOP\n");
671 rc = audvoicememo_disable(audio);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700672 audvoicememo_ioport_reset(audio);
673 audio->stopped = 0;
674 MM_DBG("AUDIO_STOP rc %d\n", rc);
675 break;
676 }
677 case AUDIO_GET_CONFIG: {
678 struct msm_audio_config cfg;
679 MM_DBG("AUDIO_GET_CONFIG\n");
680 cfg.buffer_size = audio->rec_buf_size;
681 cfg.buffer_count = MAX_REC_BUF_COUNT;
682 cfg.sample_rate = 8000; /* Voice Encoder works on 8k,
683 * Mono */
684 cfg.channel_count = 1;
685 cfg.type = 0;
686 cfg.unused[0] = 0;
687 cfg.unused[1] = 0;
688 cfg.unused[2] = 0;
689 if (copy_to_user((void *) arg, &cfg, sizeof(cfg)))
690 rc = -EFAULT;
691 else
692 rc = 0;
693 MM_DBG("AUDIO_GET_CONFIG rc %d\n", rc);
694 break;
695 }
696 case AUDIO_GET_VOICEMEMO_CONFIG: {
697 MM_DBG("AUDIO_GET_VOICEMEMO_CONFIG\n");
698 if (copy_to_user((void *)arg, &audio->voicememo_cfg,
699 sizeof(audio->voicememo_cfg)))
700 rc = -EFAULT;
701 else
702 rc = 0;
703 MM_DBG("AUDIO_GET_VOICEMEMO_CONFIG rc %d\n", rc);
704 break;
705 }
706 case AUDIO_SET_VOICEMEMO_CONFIG: {
707 struct msm_audio_voicememo_config usr_config;
708 MM_DBG("AUDIO_SET_VOICEMEMO_CONFIG\n");
709 if (copy_from_user
710 (&usr_config, (void *)arg,
711 sizeof(usr_config))) {
712 rc = -EFAULT;
713 break;
714 }
715 if (audvoicememo_validate_usr_config(&usr_config)
716 == 0) {
717 audio->voicememo_cfg = usr_config;
718 rc = 0;
719 } else
720 rc = -EINVAL;
721 MM_DBG("AUDIO_SET_VOICEMEMO_CONFIG rc %d\n", rc);
722 break;
723 }
724 case AUDIO_PAUSE: {
725 struct rpc_request_hdr rhdr;
726 MM_DBG("AUDIO_PAUSE\n");
727 if (arg == 1)
728 msm_rpc_setup_req(&rhdr, audio->rpc_prog,
729 audio->rpc_ver, SND_VOC_REC_PAUSE_PROC);
730 else
731 msm_rpc_setup_req(&rhdr, audio->rpc_prog,
732 audio->rpc_ver, SND_VOC_REC_RESUME_PROC);
733
734 rc = msm_rpc_write(audio->sndept, &rhdr, sizeof(rhdr));
735 MM_DBG("AUDIO_PAUSE exit %d\n", rc);
736 break;
737 }
738 default:
739 MM_ERR("IOCTL %d not supported\n", cmd);
740 rc = -EINVAL;
741 }
742 mutex_unlock(&audio->lock);
743 return rc;
744}
745
746static ssize_t audio_voicememo_read(struct file *file,
747 char __user *buf,
748 size_t count, loff_t *pos)
749{
750 struct audio_voicememo *audio = file->private_data;
751 const char __user *start = buf;
752 int rc = 0;
753
754 mutex_lock(&audio->read_lock);
755
756 MM_DBG("buff read =0x%8x \n", count);
757
758 while (count > 0) {
759 rc = wait_event_interruptible_timeout(audio->read_wait,
760 (audio->in[audio->read_next].used > 0) ||
761 (audio->stopped),
762 msecs_to_jiffies(MSM_AUD_BUFFER_UPDATE_WAIT_MS));
763
764 if (rc == 0) {
765 rc = -ETIMEDOUT;
766 break;
767 } else if (rc < 0)
768 break;
769
770 if (audio->stopped) {
771 rc = -EBUSY;
772 break;
773 }
774 if (count < audio->in[audio->read_next].used) {
775 /* Read must happen in frame boundary. Since driver does
776 * not split frames, read count must be greater or
777 * equal to size of existing frames to copy
778 */
779 MM_DBG("read not in frame boundary\n");
780 break;
781 } else {
782 mutex_lock(&audio->dsp_lock);
783 dma_coherent_post_ops();
784 if (copy_to_user
785 (buf, audio->in[audio->read_next].data,
786 audio->in[audio->read_next].used)) {
787 MM_ERR("invalid addr %x \n", (unsigned int)buf);
788 rc = -EFAULT;
789 mutex_unlock(&audio->dsp_lock);
790 break;
791 }
792 count -= audio->in[audio->read_next].used;
793 audio->byte_count += audio->in[audio->read_next].used;
794 audio->frame_count +=
795 audio->in[audio->read_next].numframes;
796 buf += audio->in[audio->read_next].used;
797 audio->in[audio->read_next].used = 0;
798 mutex_unlock(&audio->dsp_lock);
799 if ((++audio->read_next) == MAX_REC_BUF_COUNT)
800 audio->read_next = 0;
801 if (audio->in[audio->read_next].used == 0)
802 break; /* No data ready at this moment
803 * Exit while loop to prevent
804 * output thread sleep too long
805 */
806 }
807 }
808 mutex_unlock(&audio->read_lock);
809 if (buf > start)
810 rc = buf - start;
811 MM_DBG("exit return =0x%8x\n", rc);
812 return rc;
813}
814
815static ssize_t audio_voicememo_write(struct file *file,
816 const char __user *buf,
817 size_t count, loff_t *pos)
818{
819 return -EINVAL;
820}
821
822static int audio_voicememo_release(struct inode *inode, struct file *file)
823{
824 struct audio_voicememo *audio = file->private_data;
825
826 mutex_lock(&audio->lock);
827 audvoicememo_disable(audio);
828 audvoicememo_flush_buf(audio);
829 audio->opened = 0;
830 mutex_unlock(&audio->lock);
831 return 0;
832}
833
834static int audio_voicememo_open(struct inode *inode, struct file *file)
835{
836 struct audio_voicememo *audio = &the_audio_voicememo;
837 int rc;
838
839 mutex_lock(&audio->lock);
840 if (audio->opened) {
841 rc = -EBUSY;
842 goto done;
843 }
844
845 rc = audmgr_open(&audio->audmgr);
846
847 if (rc)
848 goto done;
849
850 /*Set default param to None*/
851 memset(&audio->voicememo_cfg, 0, sizeof(audio->voicememo_cfg));
852
853 file->private_data = audio;
854 audio->opened = 1;
Manish Dewangana0ae5002012-03-06 15:58:31 +0530855 audio->stopped = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700856 rc = 0;
857done:
858 mutex_unlock(&audio->lock);
859 return rc;
860}
861
862static const struct file_operations audio_fops = {
863 .owner = THIS_MODULE,
864 .open = audio_voicememo_open,
865 .release = audio_voicememo_release,
866 .read = audio_voicememo_read,
867 .write = audio_voicememo_write,
868 .unlocked_ioctl = audio_voicememo_ioctl,
869};
870
871struct miscdevice audio_voicememo_misc = {
872 .minor = MISC_DYNAMIC_MINOR,
873 .name = "msm_voicememo",
874 .fops = &audio_fops,
875};
876
877static int audio_voicememo_probe(struct platform_device *pdev)
878{
879 int rc;
880
881 if ((pdev->id != (SND_VERS_COMP & RPC_VERSION_MAJOR_MASK)) &&
882 (pdev->id != (SND_VERS2_COMP & RPC_VERSION_MAJOR_MASK)))
883 return -EINVAL;
884
885 mutex_init(&the_audio_voicememo.lock);
886 mutex_init(&the_audio_voicememo.read_lock);
887 mutex_init(&the_audio_voicememo.dsp_lock);
888 init_waitqueue_head(&the_audio_voicememo.read_wait);
889 init_waitqueue_head(&the_audio_voicememo.wait);
890
891 the_audio_voicememo.rec_buf_ptr = dma_alloc_coherent(NULL,
892 MAX_VOICEMEMO_BUF_SIZE,
893 &the_audio_voicememo.phys, GFP_KERNEL);
894 if (the_audio_voicememo.rec_buf_ptr == NULL) {
895 MM_ERR("error allocating memory\n");
896 rc = -ENOMEM;
897 return rc;
898 }
899 the_audio_voicememo.rec_buf_size = MAX_REC_BUF_SIZE;
900 MM_DBG("rec_buf_ptr = 0x%8x, phys = 0x%8x \n",
901 (uint32_t) the_audio_voicememo.rec_buf_ptr, \
902 the_audio_voicememo.phys);
903
904 the_audio_voicememo.sndept = msm_rpc_connect_compatible(SND_PROG,
905 SND_VERS_COMP, MSM_RPC_UNINTERRUPTIBLE);
906 if (IS_ERR(the_audio_voicememo.sndept)) {
907 MM_DBG("connect failed with VERS \
908 = %x, trying again with another API\n",
909 SND_VERS_COMP);
910 the_audio_voicememo.sndept = msm_rpc_connect_compatible(
911 SND_PROG, SND_VERS2_COMP,
912 MSM_RPC_UNINTERRUPTIBLE);
913 if (IS_ERR(the_audio_voicememo.sndept)) {
914 rc = PTR_ERR(the_audio_voicememo.sndept);
915 the_audio_voicememo.sndept = NULL;
916 MM_ERR("Failed to connect to snd svc\n");
917 goto err;
918 }
919 the_audio_voicememo.rpc_ver = SND_VERS2_COMP;
920 } else
921 the_audio_voicememo.rpc_ver = SND_VERS_COMP;
922
923 the_audio_voicememo.task = kthread_run(voicememo_rpc_thread,
924 &the_audio_voicememo, "voicememo_rpc");
925 if (IS_ERR(the_audio_voicememo.task)) {
926 rc = PTR_ERR(the_audio_voicememo.task);
927 the_audio_voicememo.task = NULL;
928 msm_rpc_close(the_audio_voicememo.sndept);
929 the_audio_voicememo.sndept = NULL;
930 MM_ERR("Failed to create voicememo_rpc task\n");
931 goto err;
932 }
933 the_audio_voicememo.rpc_prog = SND_PROG;
934
935 return misc_register(&audio_voicememo_misc);
936err:
937 dma_free_coherent(NULL, MAX_VOICEMEMO_BUF_SIZE,
938 the_audio_voicememo.rec_buf_ptr,
939 the_audio_voicememo.phys);
940 the_audio_voicememo.rec_buf_ptr = NULL;
941 return rc;
942}
943
944static void __exit audio_voicememo_exit(void)
945{
946 /* Close the RPC connection to make thread to comeout */
947 msm_rpc_close(the_audio_voicememo.sndept);
948 the_audio_voicememo.sndept = NULL;
949 kthread_stop(the_audio_voicememo.task);
950 the_audio_voicememo.task = NULL;
951 if (the_audio_voicememo.rec_buf_ptr)
952 dma_free_coherent(NULL, MAX_VOICEMEMO_BUF_SIZE,
953 the_audio_voicememo.rec_buf_ptr,
954 the_audio_voicememo.phys);
955 the_audio_voicememo.rec_buf_ptr = NULL;
956 misc_deregister(&audio_voicememo_misc);
957}
958
959static char audio_voicememo_rpc_name[] = "rs00000000";
960
961static struct platform_driver audio_voicememo_driver = {
962 .probe = audio_voicememo_probe,
963 .driver = {
964 .owner = THIS_MODULE,
965 },
966 };
967
968static int __init audio_voicememo_init(void)
969{
970 snprintf(audio_voicememo_rpc_name, sizeof(audio_voicememo_rpc_name),
971 "rs%08x", SND_PROG);
972 audio_voicememo_driver.driver.name = audio_voicememo_rpc_name;
973 return platform_driver_register(&audio_voicememo_driver);
974}
975
976module_init(audio_voicememo_init);
977module_exit(audio_voicememo_exit);
978
979MODULE_DESCRIPTION("MSM Voice Memo driver");
980MODULE_LICENSE("GPL v2");
981MODULE_AUTHOR("QUALCOMM");