• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /asuswrt-rt-n18u-9.0.0.4.380.2695/release/src-rt-6.x.4708/linux/linux-2.6/drivers/staging/dream/qdsp5/
1
2/* arch/arm/mach-msm/qdsp5/audpp.c
3 *
4 * common code to deal with the AUDPP dsp task (audio postproc)
5 *
6 * Copyright (C) 2008 Google, Inc.
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/kernel.h>
20#include <linux/module.h>
21#include <linux/wait.h>
22#include <linux/delay.h>
23
24#include <asm/atomic.h>
25#include <asm/ioctls.h>
26#include <mach/msm_adsp.h>
27
28#include "audmgr.h"
29
30#include <mach/qdsp5/qdsp5audppcmdi.h>
31#include <mach/qdsp5/qdsp5audppmsg.h>
32
33/* for queue ids - should be relative to module number*/
34#include "adsp.h"
35
36#include "evlog.h"
37
38
39enum {
40	EV_NULL,
41	EV_ENABLE,
42	EV_DISABLE,
43	EV_EVENT,
44	EV_DATA,
45};
46
47static const char *dsp_log_strings[] = {
48	"NULL",
49	"ENABLE",
50	"DISABLE",
51	"EVENT",
52	"DATA",
53};
54
55DECLARE_LOG(dsp_log, 64, dsp_log_strings);
56
57static int __init _dsp_log_init(void)
58{
59	return ev_log_init(&dsp_log);
60}
61module_init(_dsp_log_init);
62#define LOG(id,arg) ev_log_write(&dsp_log, id, arg)
63
64static DEFINE_MUTEX(audpp_lock);
65
66#define CH_COUNT 5
67#define AUDPP_CLNT_MAX_COUNT 6
68#define AUDPP_AVSYNC_INFO_SIZE 7
69
70struct audpp_state {
71	struct msm_adsp_module *mod;
72	audpp_event_func func[AUDPP_CLNT_MAX_COUNT];
73	void *private[AUDPP_CLNT_MAX_COUNT];
74	struct mutex *lock;
75	unsigned open_count;
76	unsigned enabled;
77
78	/* which channels are actually enabled */
79	unsigned avsync_mask;
80
81	/* flags, 48 bits sample/bytes counter per channel */
82	uint16_t avsync[CH_COUNT * AUDPP_CLNT_MAX_COUNT + 1];
83};
84
85struct audpp_state the_audpp_state = {
86	.lock = &audpp_lock,
87};
88
89int audpp_send_queue1(void *cmd, unsigned len)
90{
91	return msm_adsp_write(the_audpp_state.mod,
92			      QDSP_uPAudPPCmd1Queue, cmd, len);
93}
94EXPORT_SYMBOL(audpp_send_queue1);
95
96int audpp_send_queue2(void *cmd, unsigned len)
97{
98	return msm_adsp_write(the_audpp_state.mod,
99			      QDSP_uPAudPPCmd2Queue, cmd, len);
100}
101EXPORT_SYMBOL(audpp_send_queue2);
102
103int audpp_send_queue3(void *cmd, unsigned len)
104{
105	return msm_adsp_write(the_audpp_state.mod,
106			      QDSP_uPAudPPCmd3Queue, cmd, len);
107}
108EXPORT_SYMBOL(audpp_send_queue3);
109
110static int audpp_dsp_config(int enable)
111{
112	audpp_cmd_cfg cmd;
113
114	cmd.cmd_id = AUDPP_CMD_CFG;
115	cmd.cfg = enable ? AUDPP_CMD_CFG_ENABLE : AUDPP_CMD_CFG_SLEEP;
116
117	return audpp_send_queue1(&cmd, sizeof(cmd));
118}
119
120static void audpp_broadcast(struct audpp_state *audpp, unsigned id,
121			    uint16_t *msg)
122{
123	unsigned n;
124	for (n = 0; n < AUDPP_CLNT_MAX_COUNT; n++) {
125		if (audpp->func[n])
126			audpp->func[n] (audpp->private[n], id, msg);
127	}
128}
129
130static void audpp_notify_clnt(struct audpp_state *audpp, unsigned clnt_id,
131			      unsigned id, uint16_t *msg)
132{
133	if (clnt_id < AUDPP_CLNT_MAX_COUNT && audpp->func[clnt_id])
134		audpp->func[clnt_id] (audpp->private[clnt_id], id, msg);
135}
136
137static void audpp_dsp_event(void *data, unsigned id, size_t len,
138			    void (*getevent)(void *ptr, size_t len))
139{
140	struct audpp_state *audpp = data;
141	uint16_t msg[8];
142
143	if (id == AUDPP_MSG_AVSYNC_MSG) {
144		getevent(audpp->avsync, sizeof(audpp->avsync));
145
146		/* mask off any channels we're not watching to avoid
147		 * cases where we might get one last update after
148		 * disabling avsync and end up in an odd state when
149		 * we next read...
150		 */
151		audpp->avsync[0] &= audpp->avsync_mask;
152		return;
153	}
154
155	getevent(msg, sizeof(msg));
156
157	LOG(EV_EVENT, (id << 16) | msg[0]);
158	LOG(EV_DATA, (msg[1] << 16) | msg[2]);
159
160	switch (id) {
161	case AUDPP_MSG_STATUS_MSG:{
162			unsigned cid = msg[0];
163			pr_info("audpp: status %d %d %d\n", cid, msg[1],
164				msg[2]);
165			if ((cid < 5) && audpp->func[cid])
166				audpp->func[cid] (audpp->private[cid], id, msg);
167			break;
168		}
169	case AUDPP_MSG_HOST_PCM_INTF_MSG:
170		if (audpp->func[5])
171			audpp->func[5] (audpp->private[5], id, msg);
172		break;
173	case AUDPP_MSG_PCMDMAMISSED:
174		pr_err("audpp: DMA missed obj=%x\n", msg[0]);
175		break;
176	case AUDPP_MSG_CFG_MSG:
177		if (msg[0] == AUDPP_MSG_ENA_ENA) {
178			pr_info("audpp: ENABLE\n");
179			audpp->enabled = 1;
180			audpp_broadcast(audpp, id, msg);
181		} else if (msg[0] == AUDPP_MSG_ENA_DIS) {
182			pr_info("audpp: DISABLE\n");
183			audpp->enabled = 0;
184			audpp_broadcast(audpp, id, msg);
185		} else {
186			pr_err("audpp: invalid config msg %d\n", msg[0]);
187		}
188		break;
189	case AUDPP_MSG_ROUTING_ACK:
190		audpp_broadcast(audpp, id, msg);
191		break;
192	case AUDPP_MSG_FLUSH_ACK:
193		audpp_notify_clnt(audpp, msg[0], id, msg);
194		break;
195	default:
196	  pr_info("audpp: unhandled msg id %x\n", id);
197	}
198}
199
200static struct msm_adsp_ops adsp_ops = {
201	.event = audpp_dsp_event,
202};
203
204static void audpp_fake_event(struct audpp_state *audpp, int id,
205			     unsigned event, unsigned arg)
206{
207	uint16_t msg[1];
208	msg[0] = arg;
209	audpp->func[id] (audpp->private[id], event, msg);
210}
211
212int audpp_enable(int id, audpp_event_func func, void *private)
213{
214	struct audpp_state *audpp = &the_audpp_state;
215	int res = 0;
216
217	if (id < -1 || id > 4)
218		return -EINVAL;
219
220	if (id == -1)
221		id = 5;
222
223	mutex_lock(audpp->lock);
224	if (audpp->func[id]) {
225		res = -EBUSY;
226		goto out;
227	}
228
229	audpp->func[id] = func;
230	audpp->private[id] = private;
231
232	LOG(EV_ENABLE, 1);
233	if (audpp->open_count++ == 0) {
234		pr_info("audpp: enable\n");
235		res = msm_adsp_get("AUDPPTASK", &audpp->mod, &adsp_ops, audpp);
236		if (res < 0) {
237			pr_err("audpp: cannot open AUDPPTASK\n");
238			audpp->open_count = 0;
239			audpp->func[id] = NULL;
240			audpp->private[id] = NULL;
241			goto out;
242		}
243		LOG(EV_ENABLE, 2);
244		msm_adsp_enable(audpp->mod);
245		audpp_dsp_config(1);
246	} else {
247		unsigned long flags;
248		local_irq_save(flags);
249		if (audpp->enabled)
250			audpp_fake_event(audpp, id,
251					 AUDPP_MSG_CFG_MSG, AUDPP_MSG_ENA_ENA);
252		local_irq_restore(flags);
253	}
254
255	res = 0;
256out:
257	mutex_unlock(audpp->lock);
258	return res;
259}
260EXPORT_SYMBOL(audpp_enable);
261
262void audpp_disable(int id, void *private)
263{
264	struct audpp_state *audpp = &the_audpp_state;
265	unsigned long flags;
266
267	if (id < -1 || id > 4)
268		return;
269
270	if (id == -1)
271		id = 5;
272
273	mutex_lock(audpp->lock);
274	LOG(EV_DISABLE, 1);
275	if (!audpp->func[id])
276		goto out;
277	if (audpp->private[id] != private)
278		goto out;
279
280	local_irq_save(flags);
281	audpp_fake_event(audpp, id, AUDPP_MSG_CFG_MSG, AUDPP_MSG_ENA_DIS);
282	audpp->func[id] = NULL;
283	audpp->private[id] = NULL;
284	local_irq_restore(flags);
285
286	if (--audpp->open_count == 0) {
287		pr_info("audpp: disable\n");
288		LOG(EV_DISABLE, 2);
289		audpp_dsp_config(0);
290		msm_adsp_disable(audpp->mod);
291		msm_adsp_put(audpp->mod);
292		audpp->mod = NULL;
293	}
294out:
295	mutex_unlock(audpp->lock);
296}
297EXPORT_SYMBOL(audpp_disable);
298
299#define BAD_ID(id) ((id < 0) || (id >= CH_COUNT))
300
301void audpp_avsync(int id, unsigned rate)
302{
303	unsigned long flags;
304	audpp_cmd_avsync cmd;
305
306	if (BAD_ID(id))
307		return;
308
309	local_irq_save(flags);
310	if (rate)
311		the_audpp_state.avsync_mask |= (1 << id);
312	else
313		the_audpp_state.avsync_mask &= (~(1 << id));
314	the_audpp_state.avsync[0] &= the_audpp_state.avsync_mask;
315	local_irq_restore(flags);
316
317	cmd.cmd_id = AUDPP_CMD_AVSYNC;
318	cmd.object_number = id;
319	cmd.interrupt_interval_lsw = rate;
320	cmd.interrupt_interval_msw = rate >> 16;
321	audpp_send_queue1(&cmd, sizeof(cmd));
322}
323EXPORT_SYMBOL(audpp_avsync);
324
325unsigned audpp_avsync_sample_count(int id)
326{
327	uint16_t *avsync = the_audpp_state.avsync;
328	unsigned val;
329	unsigned long flags;
330	unsigned mask;
331
332	if (BAD_ID(id))
333		return 0;
334
335	mask = 1 << id;
336	id = id * AUDPP_AVSYNC_INFO_SIZE + 2;
337	local_irq_save(flags);
338	if (avsync[0] & mask)
339		val = (avsync[id] << 16) | avsync[id + 1];
340	else
341		val = 0;
342	local_irq_restore(flags);
343
344	return val;
345}
346EXPORT_SYMBOL(audpp_avsync_sample_count);
347
348unsigned audpp_avsync_byte_count(int id)
349{
350	uint16_t *avsync = the_audpp_state.avsync;
351	unsigned val;
352	unsigned long flags;
353	unsigned mask;
354
355	if (BAD_ID(id))
356		return 0;
357
358	mask = 1 << id;
359	id = id * AUDPP_AVSYNC_INFO_SIZE + 5;
360	local_irq_save(flags);
361	if (avsync[0] & mask)
362		val = (avsync[id] << 16) | avsync[id + 1];
363	else
364		val = 0;
365	local_irq_restore(flags);
366
367	return val;
368}
369EXPORT_SYMBOL(audpp_avsync_byte_count);
370
371#define AUDPP_CMD_CFG_OBJ_UPDATE 0x8000
372#define AUDPP_CMD_VOLUME_PAN 0
373
374int audpp_set_volume_and_pan(unsigned id, unsigned volume, int pan)
375{
376	/* cmd, obj_cfg[7], cmd_type, volume, pan */
377	uint16_t cmd[11];
378
379	if (id > 6)
380		return -EINVAL;
381
382	memset(cmd, 0, sizeof(cmd));
383	cmd[0] = AUDPP_CMD_CFG_OBJECT_PARAMS;
384	cmd[1 + id] = AUDPP_CMD_CFG_OBJ_UPDATE;
385	cmd[8] = AUDPP_CMD_VOLUME_PAN;
386	cmd[9] = volume;
387	cmd[10] = pan;
388
389	return audpp_send_queue3(cmd, sizeof(cmd));
390}
391EXPORT_SYMBOL(audpp_set_volume_and_pan);
392
393int audpp_pause(unsigned id, int pause)
394{
395	/* pause 1 = pause 0 = resume */
396	u16 pause_cmd[AUDPP_CMD_DEC_CTRL_LEN / sizeof(unsigned short)];
397
398	if (id >= CH_COUNT)
399		return -EINVAL;
400
401	memset(pause_cmd, 0, sizeof(pause_cmd));
402
403	pause_cmd[0] = AUDPP_CMD_DEC_CTRL;
404	if (pause == 1)
405		pause_cmd[1 + id] = AUDPP_CMD_UPDATE_V | AUDPP_CMD_PAUSE_V;
406	else if (pause == 0)
407		pause_cmd[1 + id] = AUDPP_CMD_UPDATE_V | AUDPP_CMD_RESUME_V;
408	else
409		return -EINVAL;
410
411	return audpp_send_queue1(pause_cmd, sizeof(pause_cmd));
412}
413EXPORT_SYMBOL(audpp_pause);
414
415int audpp_flush(unsigned id)
416{
417	u16 flush_cmd[AUDPP_CMD_DEC_CTRL_LEN / sizeof(unsigned short)];
418
419	if (id >= CH_COUNT)
420		return -EINVAL;
421
422	memset(flush_cmd, 0, sizeof(flush_cmd));
423
424	flush_cmd[0] = AUDPP_CMD_DEC_CTRL;
425	flush_cmd[1 + id] = AUDPP_CMD_UPDATE_V | AUDPP_CMD_FLUSH_V;
426
427	return audpp_send_queue1(flush_cmd, sizeof(flush_cmd));
428}
429EXPORT_SYMBOL(audpp_flush);
430