PageRenderTime 40ms CodeModel.GetById 12ms app.highlight 25ms RepoModel.GetById 1ms app.codeStats 0ms

/arch/arm/mach-msm/qdsp6v2/pcm_in.c

https://bitbucket.org/sammyz/iscream_thunderc-2.6.35-rebase
C | 408 lines | 345 code | 47 blank | 16 comment | 35 complexity | 7312121065e2b066ba14e823da094045 MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.0, AGPL-1.0
  1/*
  2 * Copyright (C) 2009 Google, Inc.
  3 * Copyright (C) 2009 HTC Corporation
  4 * Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
  5 *
  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>
 27#include <asm/atomic.h>
 28#include <mach/debug_mm.h>
 29#include <mach/qdsp6v2/audio_dev_ctl.h>
 30#include <mach/qdsp6v2/apr_audio.h>
 31#include <mach/qdsp6v2/q6asm.h>
 32
 33#define MAX_BUF 2
 34#define BUFSZ (480 * 8)
 35
 36struct pcm {
 37	struct mutex lock;
 38	struct mutex read_lock;
 39	wait_queue_head_t wait;
 40	spinlock_t dsp_lock;
 41	struct audio_client *ac;
 42	uint32_t sample_rate;
 43	uint32_t channel_count;
 44	uint32_t buffer_size;
 45	uint32_t buffer_count;
 46	uint32_t rec_mode;
 47	uint32_t in_frame_info[MAX_BUF][2];
 48	atomic_t in_count;
 49	atomic_t in_enabled;
 50	atomic_t in_opened;
 51	atomic_t in_stopped;
 52};
 53
 54static void pcm_in_get_dsp_buffers(struct pcm*,
 55				uint32_t token, uint32_t *payload);
 56
 57void pcm_in_cb(uint32_t opcode, uint32_t token,
 58		uint32_t *payload, void *priv)
 59{
 60	struct pcm *pcm = (struct pcm *) priv;
 61	unsigned long flags;
 62
 63	spin_lock_irqsave(&pcm->dsp_lock, flags);
 64	switch (opcode) {
 65	case ASM_DATA_EVENT_READ_DONE:
 66		pcm_in_get_dsp_buffers(pcm, token, payload);
 67		break;
 68	default:
 69		break;
 70	}
 71	spin_unlock_irqrestore(&pcm->dsp_lock, flags);
 72}
 73
 74static void pcm_in_get_dsp_buffers(struct pcm *pcm,
 75				uint32_t token, uint32_t *payload)
 76{
 77	pcm->in_frame_info[token][0] = payload[7];
 78	pcm->in_frame_info[token][1] = payload[3];
 79	if (atomic_read(&pcm->in_count) <= pcm->buffer_count)
 80		atomic_inc(&pcm->in_count);
 81	wake_up(&pcm->wait);
 82}
 83
 84static int pcm_in_enable(struct pcm *pcm)
 85{
 86	if (atomic_read(&pcm->in_enabled))
 87		return 0;
 88	return q6asm_run(pcm->ac, 0, 0, 0);
 89}
 90
 91static int pcm_in_disable(struct pcm *pcm)
 92{
 93	int rc = 0;
 94
 95	if (atomic_read(&pcm->in_opened)) {
 96		atomic_set(&pcm->in_enabled, 0);
 97		atomic_set(&pcm->in_opened, 0);
 98		rc = q6asm_cmd(pcm->ac, CMD_CLOSE);
 99
100		atomic_set(&pcm->in_stopped, 1);
101		memset(pcm->in_frame_info, 0,
102				sizeof(char) * pcm->buffer_count * 2);
103		wake_up(&pcm->wait);
104	}
105	return rc;
106}
107
108static int config(struct pcm *pcm)
109{
110	int rc = 0;
111
112	pr_debug("%s: pcm prefill\n", __func__);
113	rc = q6asm_audio_client_buf_alloc(OUT, pcm->ac,
114				pcm->buffer_size, pcm->buffer_count);
115	if (rc < 0) {
116		pr_err("Audio Start: Buffer Allocation failed \
117						rc = %d\n", rc);
118		goto fail;
119	}
120
121	rc = q6asm_enc_cfg_blk_pcm(pcm->ac, pcm->sample_rate,
122						pcm->channel_count);
123	if (rc < 0) {
124		pr_err("%s: cmd media format block failed", __func__);
125		goto fail;
126	}
127fail:
128	return rc;
129}
130
131static long pcm_in_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
132{
133	struct pcm *pcm = file->private_data;
134	int rc = 0;
135
136	mutex_lock(&pcm->lock);
137	switch (cmd) {
138	case AUDIO_SET_VOLUME:
139		break;
140	case AUDIO_GET_STATS: {
141		struct msm_audio_stats stats;
142		memset(&stats, 0, sizeof(stats));
143		if (copy_to_user((void *) arg, &stats, sizeof(stats)))
144			rc = -EFAULT;
145		break;
146	}
147	case AUDIO_START: {
148		int cnt = 0;
149		if (atomic_read(&pcm->in_enabled)) {
150			pr_info("%s:AUDIO_START already over\n", __func__);
151			rc = 0;
152			break;
153		}
154		rc = config(pcm);
155		if (rc) {
156			pr_err("%s: IN Configuration failed\n", __func__);
157			rc = -EFAULT;
158			break;
159		}
160
161		rc = pcm_in_enable(pcm);
162		if (rc) {
163			pr_err("%s: In Enable failed\n", __func__);
164			rc = -EFAULT;
165			break;
166		}
167
168		atomic_set(&pcm->in_enabled, 1);
169
170		while (cnt++ < pcm->buffer_count)
171			q6asm_read(pcm->ac);
172		pr_info("%s: AUDIO_START session id[%d]\n", __func__,
173							pcm->ac->session);
174		break;
175	}
176	case AUDIO_GET_SESSION_ID: {
177		if (copy_to_user((void *) arg, &pcm->ac->session,
178					sizeof(unsigned short)))
179			rc = -EFAULT;
180		break;
181	}
182	case AUDIO_STOP:
183		break;
184	case AUDIO_FLUSH:
185		break;
186	case AUDIO_SET_CONFIG: {
187		struct msm_audio_config config;
188
189		if (copy_from_user(&config, (void *) arg, sizeof(config))) {
190			rc = -EFAULT;
191			break;
192		}
193		pr_debug("%s: buffer_size:%d channel_count:%d sample_rate:%d \
194			buffer_count:%d\n", __func__, config.buffer_size,
195			config.channel_count, config.sample_rate,
196			config.buffer_count);
197
198		if (!config.channel_count || config.channel_count > 2) {
199			rc = -EINVAL;
200			break;
201		}
202
203		if (config.sample_rate < 8000 || config.sample_rate > 48000) {
204			rc = -EINVAL;
205			break;
206		}
207
208		if (config.buffer_size % (config.channel_count * 480)) {
209			pr_err("%s: Buffer Size should be multiple of \
210					[480 * no. of channels]\n", __func__);
211			rc = -EINVAL;
212			break;
213		}
214
215		pcm->sample_rate = config.sample_rate;
216		pcm->channel_count = config.channel_count;
217		pcm->buffer_size = config.buffer_size;
218		pcm->buffer_count = config.buffer_count;
219		break;
220	}
221	case AUDIO_GET_CONFIG: {
222		struct msm_audio_config config;
223		config.buffer_size = pcm->buffer_size;
224		config.buffer_count = pcm->buffer_count;
225		config.sample_rate = pcm->sample_rate;
226		config.channel_count = pcm->channel_count;
227		config.unused[0] = 0;
228		config.unused[1] = 0;
229		config.unused[2] = 0;
230		if (copy_to_user((void *) arg, &config, sizeof(config)))
231			rc = -EFAULT;
232		break;
233	}
234	case AUDIO_ENABLE_AUDPRE: {
235
236		uint16_t enable_mask;
237
238		if (copy_from_user(&enable_mask, (void *) arg,
239						sizeof(enable_mask))) {
240			rc = -EFAULT;
241			break;
242		}
243		if (enable_mask & FLUENCE_ENABLE)
244			rc = auddev_cfg_tx_copp_topology(pcm->ac->session,
245					VPM_TX_DM_FLUENCE_COPP_TOPOLOGY);
246		else
247			rc = auddev_cfg_tx_copp_topology(pcm->ac->session,
248					DEFAULT_COPP_TOPOLOGY);
249		break;
250	}
251
252	default:
253		rc = -EINVAL;
254		break;
255	}
256	mutex_unlock(&pcm->lock);
257	return rc;
258}
259
260static int pcm_in_open(struct inode *inode, struct file *file)
261{
262	struct pcm *pcm;
263	int rc = 0;
264
265	pcm = kzalloc(sizeof(struct pcm), GFP_KERNEL);
266	if (!pcm)
267		return -ENOMEM;
268
269	pcm->channel_count = 1;
270	pcm->sample_rate = 8000;
271	pcm->buffer_size = BUFSZ;
272	pcm->buffer_count = MAX_BUF;
273
274	pcm->ac = q6asm_audio_client_alloc((app_cb)pcm_in_cb, (void *)pcm);
275	if (!pcm->ac) {
276		pr_err("%s: Could not allocate memory\n", __func__);
277		rc = -ENOMEM;
278		goto fail;
279	}
280
281	mutex_init(&pcm->lock);
282	mutex_init(&pcm->read_lock);
283	spin_lock_init(&pcm->dsp_lock);
284	init_waitqueue_head(&pcm->wait);
285
286	rc = q6asm_open_read(pcm->ac, FORMAT_LINEAR_PCM);
287	if (rc < 0) {
288		pr_err("%s: Cmd Open Failed\n", __func__);
289		goto fail;
290	}
291
292	atomic_set(&pcm->in_stopped, 0);
293	atomic_set(&pcm->in_enabled, 0);
294	atomic_set(&pcm->in_count, 0);
295	atomic_set(&pcm->in_opened, 1);
296	file->private_data = pcm;
297	pr_info("%s: pcm in open session id[%d]\n", __func__, pcm->ac->session);
298	return 0;
299fail:
300	if (pcm->ac)
301		q6asm_audio_client_free(pcm->ac);
302	kfree(pcm);
303	return rc;
304}
305
306static ssize_t pcm_in_read(struct file *file, char __user *buf,
307			  size_t count, loff_t *pos)
308{
309	struct pcm *pcm = file->private_data;
310	const char __user *start = buf;
311	void *data;
312	uint32_t offset = 0;
313	uint32_t size = 0;
314	uint32_t idx;
315	int rc = 0;
316
317	if (!atomic_read(&pcm->in_enabled))
318		return -EFAULT;
319	mutex_lock(&pcm->read_lock);
320	while (count > 0) {
321		rc = wait_event_timeout(pcm->wait,
322				(atomic_read(&pcm->in_count) ||
323				atomic_read(&pcm->in_stopped)), 5 * HZ);
324		if (!rc) {
325			pr_err("%s: wait_event_timeout failed\n", __func__);
326			goto fail;
327		}
328
329		if (atomic_read(&pcm->in_stopped) &&
330					!atomic_read(&pcm->in_count)) {
331			mutex_unlock(&pcm->read_lock);
332			return 0;
333		}
334
335		data = q6asm_is_cpu_buf_avail(OUT, pcm->ac, &size, &idx);
336		if ((count >= size) && data) {
337			offset = pcm->in_frame_info[idx][1];
338			if (copy_to_user(buf, data+offset, size)) {
339				pr_err("%s copy_to_user failed\n", __func__);
340				rc = -EFAULT;
341				goto fail;
342			}
343
344			count -= size;
345			buf += size;
346			atomic_dec(&pcm->in_count);
347			memset(&pcm->in_frame_info[idx], 0,
348						sizeof(uint32_t) * 2);
349
350			rc = q6asm_read(pcm->ac);
351			if (rc < 0) {
352				pr_err("%s q6asm_read faile\n", __func__);
353				goto fail;
354			}
355			rmb();
356			break;
357		} else {
358			pr_err("%s: short read data[%p] size[%d]\n",\
359						__func__, data, size);
360			break;
361		}
362	}
363	rc = buf-start;
364fail:
365	mutex_unlock(&pcm->read_lock);
366	return rc;
367}
368
369static int pcm_in_release(struct inode *inode, struct file *file)
370{
371	int rc = 0;
372	struct pcm *pcm = file->private_data;
373
374	pr_info("[%s:%s] release session id[%d]\n", __MM_FILE__,
375		__func__, pcm->ac->session);
376	mutex_lock(&pcm->lock);
377	/* remove this session from topology list */
378	auddev_cfg_tx_copp_topology(pcm->ac->session,
379				DEFAULT_COPP_TOPOLOGY);
380	mutex_unlock(&pcm->lock);
381
382	rc = pcm_in_disable(pcm);
383	 msm_clear_session_id(pcm->ac->session);
384	q6asm_audio_client_free(pcm->ac);
385	kfree(pcm);
386	return rc;
387}
388
389static const struct file_operations pcm_in_fops = {
390	.owner		= THIS_MODULE,
391	.open		= pcm_in_open,
392	.read		= pcm_in_read,
393	.release	= pcm_in_release,
394	.unlocked_ioctl	= pcm_in_ioctl,
395};
396
397struct miscdevice pcm_in_misc = {
398	.minor	= MISC_DYNAMIC_MINOR,
399	.name	= "msm_pcm_in",
400	.fops	= &pcm_in_fops,
401};
402
403static int __init pcm_in_init(void)
404{
405	return misc_register(&pcm_in_misc);
406}
407
408device_initcall(pcm_in_init);