2 * This file is part of OMAP DSP driver (DSP Gateway version 3.3.1)
4 * Copyright (C) 2002-2006 Nokia Corporation. All rights reserved.
6 * Contact: Toshihiro Kobayashi <toshihiro.kobayashi@nokia.com>
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * version 2 as published by the Free Software Foundation.
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
24 #include <linux/init.h>
25 #include <linux/module.h>
26 #include <linux/platform_device.h>
27 #include <linux/sched.h>
28 #include <linux/mutex.h>
29 #include <linux/err.h>
30 #include <linux/clk.h>
31 #include <asm/delay.h>
32 #include <asm/arch/mailbox.h>
33 #include <asm/arch/dsp_common.h>
34 #include "dsp_mbcmd.h"
38 MODULE_AUTHOR("Toshihiro Kobayashi <toshihiro.kobayashi@nokia.com>");
39 MODULE_DESCRIPTION("OMAP DSP driver module");
40 MODULE_LICENSE("GPL");
43 static struct sync_seq *mbseq;
44 static u16 mbseq_expect_tmp;
45 static u16 *mbseq_expect = &mbseq_expect_tmp;
50 extern void mbx_wdsnd(struct mbcmd *mb);
51 extern void mbx_wdreq(struct mbcmd *mb);
52 extern void mbx_bksnd(struct mbcmd *mb);
53 extern void mbx_bkreq(struct mbcmd *mb);
54 extern void mbx_bkyld(struct mbcmd *mb);
55 extern void mbx_bksndp(struct mbcmd *mb);
56 extern void mbx_bkreqp(struct mbcmd *mb);
57 extern void mbx_tctl(struct mbcmd *mb);
58 extern void mbx_poll(struct mbcmd *mb);
59 #ifdef OLD_BINARY_SUPPORT
61 extern void mbx_wdt(struct mbcmd *mb);
63 extern void mbx_suspend(struct mbcmd *mb);
64 static void mbx_kfunc(struct mbcmd *mb);
65 extern void mbx_tcfg(struct mbcmd *mb);
66 extern void mbx_tadd(struct mbcmd *mb);
67 extern void mbx_tdel(struct mbcmd *mb);
68 extern void mbx_dspcfg(struct mbcmd *mb);
69 extern void mbx_regrw(struct mbcmd *mb);
70 extern void mbx_getvar(struct mbcmd *mb);
71 extern void mbx_err(struct mbcmd *mb);
72 extern void mbx_dbg(struct mbcmd *mb);
74 static const struct cmdinfo
75 cif_wdsnd = { "WDSND", CMD_L_TYPE_TID, mbx_wdsnd },
76 cif_wdreq = { "WDREQ", CMD_L_TYPE_TID, mbx_wdreq },
77 cif_bksnd = { "BKSND", CMD_L_TYPE_TID, mbx_bksnd },
78 cif_bkreq = { "BKREQ", CMD_L_TYPE_TID, mbx_bkreq },
79 cif_bkyld = { "BKYLD", CMD_L_TYPE_NULL, mbx_bkyld },
80 cif_bksndp = { "BKSNDP", CMD_L_TYPE_TID, mbx_bksndp },
81 cif_bkreqp = { "BKREQP", CMD_L_TYPE_TID, mbx_bkreqp },
82 cif_tctl = { "TCTL", CMD_L_TYPE_TID, mbx_tctl },
83 cif_poll = { "POLL", CMD_L_TYPE_NULL, mbx_poll },
84 #ifdef OLD_BINARY_SUPPORT
86 cif_wdt = { "WDT", CMD_L_TYPE_NULL, mbx_wdt },
88 cif_runlevel = { "RUNLEVEL", CMD_L_TYPE_SUBCMD, NULL },
89 cif_pm = { "PM", CMD_L_TYPE_SUBCMD, NULL },
90 cif_suspend = { "SUSPEND", CMD_L_TYPE_NULL, mbx_suspend },
91 cif_kfunc = { "KFUNC", CMD_L_TYPE_SUBCMD, mbx_kfunc },
92 cif_tcfg = { "TCFG", CMD_L_TYPE_TID, mbx_tcfg },
93 cif_tadd = { "TADD", CMD_L_TYPE_TID, mbx_tadd },
94 cif_tdel = { "TDEL", CMD_L_TYPE_TID, mbx_tdel },
95 cif_tstop = { "TSTOP", CMD_L_TYPE_TID, NULL },
96 cif_dspcfg = { "DSPCFG", CMD_L_TYPE_SUBCMD, mbx_dspcfg },
97 cif_regrw = { "REGRW", CMD_L_TYPE_SUBCMD, mbx_regrw },
98 cif_getvar = { "GETVAR", CMD_L_TYPE_SUBCMD, mbx_getvar },
99 cif_setvar = { "SETVAR", CMD_L_TYPE_SUBCMD, NULL },
100 cif_err = { "ERR", CMD_L_TYPE_SUBCMD, mbx_err },
101 cif_dbg = { "DBG", CMD_L_TYPE_NULL, mbx_dbg };
103 #define MBX_CMD_MAX 0x80
104 const struct cmdinfo *cmdinfo[MBX_CMD_MAX] = {
105 [MBX_CMD_DSP_WDSND] = &cif_wdsnd,
106 [MBX_CMD_DSP_WDREQ] = &cif_wdreq,
107 [MBX_CMD_DSP_BKSND] = &cif_bksnd,
108 [MBX_CMD_DSP_BKREQ] = &cif_bkreq,
109 [MBX_CMD_DSP_BKYLD] = &cif_bkyld,
110 [MBX_CMD_DSP_BKSNDP] = &cif_bksndp,
111 [MBX_CMD_DSP_BKREQP] = &cif_bkreqp,
112 [MBX_CMD_DSP_TCTL] = &cif_tctl,
113 [MBX_CMD_DSP_POLL] = &cif_poll,
114 #ifdef OLD_BINARY_SUPPORT
115 [MBX_CMD_DSP_WDT] = &cif_wdt, /* v3.3 obsolete */
117 [MBX_CMD_DSP_RUNLEVEL] = &cif_runlevel,
118 [MBX_CMD_DSP_PM] = &cif_pm,
119 [MBX_CMD_DSP_SUSPEND] = &cif_suspend,
120 [MBX_CMD_DSP_KFUNC] = &cif_kfunc,
121 [MBX_CMD_DSP_TCFG] = &cif_tcfg,
122 [MBX_CMD_DSP_TADD] = &cif_tadd,
123 [MBX_CMD_DSP_TDEL] = &cif_tdel,
124 [MBX_CMD_DSP_TSTOP] = &cif_tstop,
125 [MBX_CMD_DSP_DSPCFG] = &cif_dspcfg,
126 [MBX_CMD_DSP_REGRW] = &cif_regrw,
127 [MBX_CMD_DSP_GETVAR] = &cif_getvar,
128 [MBX_CMD_DSP_SETVAR] = &cif_setvar,
129 [MBX_CMD_DSP_ERR] = &cif_err,
130 [MBX_CMD_DSP_DBG] = &cif_dbg,
133 int sync_with_dsp(u16 *adr, u16 val, int try_cnt)
137 if (*(volatile u16 *)adr == val)
140 for (try = 0; try < try_cnt; try++) {
142 if (*(volatile u16 *)adr == val) {
145 "omapdsp: sync_with_dsp(): try = %d\n", try);
155 * __dsp_mbcmd_send_exarg(): mailbox dispatcher
157 int __dsp_mbcmd_send_exarg(struct mbcmd *mb, struct mb_exarg *arg,
160 static DEFINE_MUTEX(mbsend_lock);
164 * while MMU fault is set,
165 * only recovery command can be executed
167 if (dsp_err_isset(ERRCODE_MMU) && !recovery_flag) {
169 "mbx: mmu interrupt is set. %s is aborting.\n",
174 if (mutex_lock_interruptible(&mbsend_lock) < 0)
177 if (arg) { /* we have extra argument */
181 * even if ipbuf_sys_ad is in DSP internal memory,
182 * dsp_mem_enable() never cause to call PM mailbox command
183 * because in that case DSP memory should be always enabled.
184 * (see ipbuf_sys_hold_mem_active in ipbuf.c)
186 * Therefore, we can call this function here safely.
188 dsp_mem_enable(ipbuf_sys_ad);
189 if (sync_with_dsp(&ipbuf_sys_ad->s, TID_FREE, 10) < 0) {
190 printk(KERN_ERR "omapdsp: ipbuf_sys_ad is busy.\n");
191 dsp_mem_disable(ipbuf_sys_ad);
195 for (i = 0; i < arg->argc; i++) {
196 ipbuf_sys_ad->d[i] = arg->argv[i];
198 ipbuf_sys_ad->s = arg->tid;
199 dsp_mem_disable(ipbuf_sys_ad);
205 mblog_add(mb, DIR_A2D);
206 mblog_printcmd(mb, DIR_A2D);
208 ret = mbx_send(mbx_dsp, *(mbx_msg_t *)mb);
211 mutex_unlock(&mbsend_lock);
215 int dsp_mbcmd_send_and_wait_exarg(struct mbcmd *mb, struct mb_exarg *arg,
216 wait_queue_head_t *q)
219 DECLARE_WAITQUEUE(wait, current);
221 add_wait_queue(q, &wait);
222 current_state = current->state;
223 set_current_state(TASK_INTERRUPTIBLE);
224 if (dsp_mbcmd_send_exarg(mb, arg) < 0) {
225 set_current_state(current_state);
226 remove_wait_queue(q, &wait);
229 schedule_timeout(DSP_TIMEOUT);
230 set_current_state(current_state);
231 remove_wait_queue(q, &wait);
239 static void mbcmd_receiver(mbx_msg_t msg)
241 struct mbcmd *mb = (struct mbcmd *)&msg;
243 if (cmdinfo[mb->cmd_h] == NULL) {
245 "invalid message (%08x) for mbcmd_receiver().\n", msg);
251 mblog_add(mb, DIR_D2A);
252 mblog_printcmd(mb, DIR_D2A);
254 /* call handler for the command */
255 if (cmdinfo[mb->cmd_h]->handler)
256 cmdinfo[mb->cmd_h]->handler(mb);
258 printk(KERN_ERR "mbx: %s is not allowed from DSP.\n",
262 static int mbsync_hold_mem_active;
264 void dsp_mbx_start(void)
266 mbx_init_seq(mbx_dsp);
267 mbseq_expect_tmp = 0;
270 void dsp_mbx_stop(void)
273 mbseq_expect = &mbseq_expect_tmp;
276 int dsp_mbx_config(void *p)
280 if (dsp_address_validate(p, sizeof(struct sync_seq), "mbseq") < 0)
282 if (dsp_mem_type(p, sizeof(struct sync_seq)) != MEM_TYPE_EXTERN) {
284 "omapdsp: mbseq is placed in DSP internal memory.\n"
285 " It will prevent DSP from idling.\n");
286 mbsync_hold_mem_active = 1;
288 * dsp_mem_enable() never fails because
289 * it has been already enabled in dspcfg process and
290 * this will just increment the usecount.
292 dsp_mem_enable((void *)daram_base);
295 local_irq_save(flags);
297 mbseq->da_arm = mbseq_expect_tmp;
298 mbseq_expect = &mbseq->da_arm;
299 local_irq_restore(flags);
304 static int __init dsp_mbx_init(void)
309 for (i = 0; i < MBX_CMD_MAX; i++) {
310 if (cmdinfo[i] != NULL) {
311 ret = register_mbx_receiver(mbx_dsp, i, mbcmd_receiver);
321 unregister_mbx_receiver(mbx_dsp, i, mbcmd_receiver);
326 static void dsp_mbx_exit(void)
330 for (i = 0; i < MBX_CMD_MAX; i++) {
331 if (cmdinfo[i] != NULL)
332 unregister_mbx_receiver(mbx_dsp, i, mbcmd_receiver);
335 if (mbsync_hold_mem_active) {
336 dsp_mem_disable((void *)daram_base);
337 mbsync_hold_mem_active = 0;
342 * kernel function dispatcher
344 extern void mbx_fbctl_upd(void);
345 extern void mbx_fbctl_disable(struct mbcmd *mb);
347 static void mbx_kfunc_fbctl(struct mbcmd *mb)
354 mbx_fbctl_disable(mb);
358 "mbx: Unknown FBCTL from DSP: 0x%04x\n", mb->data);
362 static void mbx_kfunc_audio_pwr(unsigned short data)
366 omap_dsp_audio_pwr_up_request(0);
368 mbcompose_send(KFUNC, KFUNC_AUDIO_PWR, AUDIO_PWR_UP);
370 case AUDIO_PWR_DOWN1:
371 omap_dsp_audio_pwr_down_request(1);
373 case AUDIO_PWR_DOWN2:
374 omap_dsp_audio_pwr_down_request(2);
378 "mailbox: Unknown AUDIO_PWR from DSP: 0x%04x\n", data);
382 static void mbx_kfunc(struct mbcmd *mb)
388 case KFUNC_AUDIO_PWR:
389 mbx_kfunc_audio_pwr(mb->data);
393 "mbx: Unknown KFUNC from DSP: 0x%02x\n", mb->cmd_l);
397 extern int dsp_ctl_core_init(void);
398 extern void dsp_ctl_core_exit(void);
399 extern void dsp_ctl_init(void);
400 extern void dsp_ctl_exit(void);
401 extern int dsp_mem_init(void);
402 extern void dsp_mem_exit(void);
403 extern void mblog_init(void);
404 extern void mblog_exit(void);
405 extern int dsp_taskmod_init(void);
406 extern void dsp_taskmod_exit(void);
411 static void dsp_dev_release(struct device *dev)
418 static int __init dsp_drv_probe(struct platform_device *pdev)
422 printk(KERN_INFO "OMAP DSP driver initialization\n");
423 #ifdef CONFIG_ARCH_OMAP2
424 clk_enable(dsp_fck_handle);
425 clk_enable(dsp_ick_handle);
429 if ((ret = dsp_ctl_core_init()) < 0)
431 if ((ret = dsp_mem_init()) < 0)
435 if ((ret = dsp_taskmod_init()) < 0)
437 if ((ret = dsp_mbx_init()) < 0)
451 #ifdef CONFIG_ARCH_OMAP2
453 clk_disable(dsp_ick_handle);
454 clk_disable(dsp_fck_handle);
459 static int dsp_drv_remove(struct platform_device *pdev)
461 dsp_cpustat_request(CPUSTAT_RESET);
463 dsp_cfgstat_request(CFGSTAT_CLEAN);
472 #ifdef CONFIG_ARCH_OMAP2
474 clk_disable(dsp_ick_handle);
475 clk_disable(dsp_fck_handle);
481 static int dsp_drv_suspend(struct platform_device *pdev, pm_message_t state)
483 dsp_cfgstat_request(CFGSTAT_SUSPEND);
488 static int dsp_drv_resume(struct platform_device *pdev)
490 dsp_cfgstat_request(CFGSTAT_RESUME);
495 #define dsp_drv_suspend NULL
496 #define dsp_drv_resume NULL
497 #endif /* CONFIG_PM */
499 static struct resource dsp_resources[] = {
501 .start = INT_DSP_MMU,
502 .flags = IORESOURCE_IRQ,
506 struct platform_device dsp_device = {
510 .release = dsp_dev_release,
512 .num_resources = ARRAY_SIZE(&dsp_resources),
513 .resource = dsp_resources,
516 static struct platform_driver dsp_driver = {
517 .probe = dsp_drv_probe,
518 .remove = dsp_drv_remove,
519 .suspend = dsp_drv_suspend,
520 .resume = dsp_drv_resume,
526 static int __init omap_dsp_mod_init(void)
530 mbx_dsp = mbx_get("DSP");
531 if (IS_ERR(mbx_dsp)) {
532 printk(KERN_ERR "failed to get mailbox handler for DSP.\n");
536 ret = platform_device_register(&dsp_device);
538 printk(KERN_ERR "failed to register the DSP device: %d\n", ret);
542 ret = platform_driver_register(&dsp_driver);
544 printk(KERN_ERR "failed to register the DSP driver: %d\n", ret);
551 platform_device_unregister(&dsp_device);
556 static void __exit omap_dsp_mod_exit(void)
558 platform_driver_unregister(&dsp_driver);
559 platform_device_unregister(&dsp_device);
562 module_init(omap_dsp_mod_init);
563 module_exit(omap_dsp_mod_exit);