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.h>
34 #include <asm/arch/dsp_common.h>
35 #include "dsp_mbcmd.h"
39 MODULE_AUTHOR("Toshihiro Kobayashi <toshihiro.kobayashi@nokia.com>");
40 MODULE_DESCRIPTION("OMAP DSP driver module");
41 MODULE_LICENSE("GPL");
43 static struct sync_seq *mbseq;
44 static u16 mbseq_expect_tmp;
45 static u16 *mbseq_expect = &mbseq_expect_tmp;
47 extern int dsp_mem_late_init(void);
52 extern void mbox_wdsnd(struct mbcmd *mb);
53 extern void mbox_wdreq(struct mbcmd *mb);
54 extern void mbox_bksnd(struct mbcmd *mb);
55 extern void mbox_bkreq(struct mbcmd *mb);
56 extern void mbox_bkyld(struct mbcmd *mb);
57 extern void mbox_bksndp(struct mbcmd *mb);
58 extern void mbox_bkreqp(struct mbcmd *mb);
59 extern void mbox_tctl(struct mbcmd *mb);
60 extern void mbox_poll(struct mbcmd *mb);
61 #ifdef OLD_BINARY_SUPPORT
63 extern void mbox_wdt(struct mbcmd *mb);
65 extern void mbox_suspend(struct mbcmd *mb);
66 static void mbox_kfunc(struct mbcmd *mb);
67 extern void mbox_tcfg(struct mbcmd *mb);
68 extern void mbox_tadd(struct mbcmd *mb);
69 extern void mbox_tdel(struct mbcmd *mb);
70 extern void mbox_dspcfg(struct mbcmd *mb);
71 extern void mbox_regrw(struct mbcmd *mb);
72 extern void mbox_getvar(struct mbcmd *mb);
73 extern void mbox_err(struct mbcmd *mb);
74 extern void mbox_dbg(struct mbcmd *mb);
76 static const struct cmdinfo
77 cif_wdsnd = { "WDSND", CMD_L_TYPE_TID, mbox_wdsnd },
78 cif_wdreq = { "WDREQ", CMD_L_TYPE_TID, mbox_wdreq },
79 cif_bksnd = { "BKSND", CMD_L_TYPE_TID, mbox_bksnd },
80 cif_bkreq = { "BKREQ", CMD_L_TYPE_TID, mbox_bkreq },
81 cif_bkyld = { "BKYLD", CMD_L_TYPE_NULL, mbox_bkyld },
82 cif_bksndp = { "BKSNDP", CMD_L_TYPE_TID, mbox_bksndp },
83 cif_bkreqp = { "BKREQP", CMD_L_TYPE_TID, mbox_bkreqp },
84 cif_tctl = { "TCTL", CMD_L_TYPE_TID, mbox_tctl },
85 cif_poll = { "POLL", CMD_L_TYPE_NULL, mbox_poll },
86 #ifdef OLD_BINARY_SUPPORT
88 cif_wdt = { "WDT", CMD_L_TYPE_NULL, mbox_wdt },
90 cif_runlevel = { "RUNLEVEL", CMD_L_TYPE_SUBCMD, NULL },
91 cif_pm = { "PM", CMD_L_TYPE_SUBCMD, NULL },
92 cif_suspend = { "SUSPEND", CMD_L_TYPE_NULL, mbox_suspend },
93 cif_kfunc = { "KFUNC", CMD_L_TYPE_SUBCMD, mbox_kfunc },
94 cif_tcfg = { "TCFG", CMD_L_TYPE_TID, mbox_tcfg },
95 cif_tadd = { "TADD", CMD_L_TYPE_TID, mbox_tadd },
96 cif_tdel = { "TDEL", CMD_L_TYPE_TID, mbox_tdel },
97 cif_tstop = { "TSTOP", CMD_L_TYPE_TID, NULL },
98 cif_dspcfg = { "DSPCFG", CMD_L_TYPE_SUBCMD, mbox_dspcfg },
99 cif_regrw = { "REGRW", CMD_L_TYPE_SUBCMD, mbox_regrw },
100 cif_getvar = { "GETVAR", CMD_L_TYPE_SUBCMD, mbox_getvar },
101 cif_setvar = { "SETVAR", CMD_L_TYPE_SUBCMD, NULL },
102 cif_err = { "ERR", CMD_L_TYPE_SUBCMD, mbox_err },
103 cif_dbg = { "DBG", CMD_L_TYPE_NULL, mbox_dbg };
105 #define MBOX_CMD_MAX 0x80
106 const struct cmdinfo *cmdinfo[MBOX_CMD_MAX] = {
107 [MBOX_CMD_DSP_WDSND] = &cif_wdsnd,
108 [MBOX_CMD_DSP_WDREQ] = &cif_wdreq,
109 [MBOX_CMD_DSP_BKSND] = &cif_bksnd,
110 [MBOX_CMD_DSP_BKREQ] = &cif_bkreq,
111 [MBOX_CMD_DSP_BKYLD] = &cif_bkyld,
112 [MBOX_CMD_DSP_BKSNDP] = &cif_bksndp,
113 [MBOX_CMD_DSP_BKREQP] = &cif_bkreqp,
114 [MBOX_CMD_DSP_TCTL] = &cif_tctl,
115 [MBOX_CMD_DSP_POLL] = &cif_poll,
116 #ifdef OLD_BINARY_SUPPORT
117 [MBOX_CMD_DSP_WDT] = &cif_wdt, /* v3.3 obsolete */
119 [MBOX_CMD_DSP_RUNLEVEL] = &cif_runlevel,
120 [MBOX_CMD_DSP_PM] = &cif_pm,
121 [MBOX_CMD_DSP_SUSPEND] = &cif_suspend,
122 [MBOX_CMD_DSP_KFUNC] = &cif_kfunc,
123 [MBOX_CMD_DSP_TCFG] = &cif_tcfg,
124 [MBOX_CMD_DSP_TADD] = &cif_tadd,
125 [MBOX_CMD_DSP_TDEL] = &cif_tdel,
126 [MBOX_CMD_DSP_TSTOP] = &cif_tstop,
127 [MBOX_CMD_DSP_DSPCFG] = &cif_dspcfg,
128 [MBOX_CMD_DSP_REGRW] = &cif_regrw,
129 [MBOX_CMD_DSP_GETVAR] = &cif_getvar,
130 [MBOX_CMD_DSP_SETVAR] = &cif_setvar,
131 [MBOX_CMD_DSP_ERR] = &cif_err,
132 [MBOX_CMD_DSP_DBG] = &cif_dbg,
135 #define list_for_each_entry_safe_natural(p,n,h,m) \
136 list_for_each_entry_safe(p,n,h,m)
137 #define __BUILD_KFUNC(fn, dir) \
138 static int __dsp_kfunc_##fn##_devices(struct omap_dsp *dsp, int type, int stage)\
140 struct dsp_kfunc_device *p, *tmp; \
143 list_for_each_entry_safe_##dir(p, tmp, dsp->kdev_list, entry) { \
144 if (type && (p->type != type)) \
148 ret = p->fn(p, stage); \
150 printk(KERN_ERR "%s %s failed\n", #fn, p->name); \
156 #define BUILD_KFUNC(fn, dir) \
157 __BUILD_KFUNC(fn, dir) \
158 static inline int dsp_kfunc_##fn##_devices(struct omap_dsp *dsp) \
160 return __dsp_kfunc_##fn##_devices(dsp, 0, 0); \
162 #define BUILD_KFUNC_CTL(fn, dir) \
163 __BUILD_KFUNC(fn, dir) \
164 static inline int dsp_kfunc_##fn##_devices(struct omap_dsp *dsp, int type, int stage) \
166 return __dsp_kfunc_##fn##_devices(dsp, type, stage); \
169 BUILD_KFUNC(probe, natural)
170 BUILD_KFUNC(remove, reverse)
171 BUILD_KFUNC_CTL(enable, natural)
172 BUILD_KFUNC_CTL(disable, reverse)
174 int sync_with_dsp(u16 *adr, u16 val, int try_cnt)
178 if (*(volatile u16 *)adr == val)
181 for (try = 0; try < try_cnt; try++) {
183 if (*(volatile u16 *)adr == val) {
185 pr_info("omapdsp: sync_with_dsp(): try = %d\n", try);
194 static int mbcmd_sender_prepare(void *data)
196 struct mb_exarg *arg = data;
199 * even if ipbuf_sys_ad is in DSP internal memory,
200 * dsp_mem_enable() never cause to call PM mailbox command
201 * because in that case DSP memory should be always enabled.
202 * (see ipbuf_sys_hold_mem_active in ipbuf.c)
204 * Therefore, we can call this function here safely.
206 dsp_mem_enable(ipbuf_sys_ad);
207 if (sync_with_dsp(&ipbuf_sys_ad->s, TID_FREE, 10) < 0) {
208 printk(KERN_ERR "omapdsp: ipbuf_sys_ad is busy.\n");
213 for (i = 0; i < arg->argc; i++) {
214 ipbuf_sys_ad->d[i] = arg->argv[i];
216 ipbuf_sys_ad->s = arg->tid;
218 dsp_mem_disable(ipbuf_sys_ad);
223 * __dsp_mbcmd_send_exarg(): mailbox dispatcher
225 int __dsp_mbcmd_send_exarg(struct mbcmd *mb, struct mb_exarg *arg,
230 if (unlikely(omap_dsp->enabled == 0)) {
231 ret = dsp_kfunc_enable_devices(omap_dsp,
232 DSP_KFUNC_DEV_TYPE_COMMON, 0);
234 omap_dsp->enabled = 1;
238 * while MMU fault is set,
239 * only recovery command can be executed
241 if (dsp_err_isset(ERRCODE_MMU) && !recovery_flag) {
243 "mbox: mmu interrupt is set. %s is aborting.\n",
248 ret = omap_mbox_msg_send(omap_dsp->mbox,
249 *(mbox_msg_t *)mb, (void*)arg);
256 mblog_add(mb, DIR_A2D);
261 int dsp_mbcmd_send_and_wait_exarg(struct mbcmd *mb, struct mb_exarg *arg,
262 wait_queue_head_t *q)
267 prepare_to_wait(q, &wait, TASK_INTERRUPTIBLE);
268 ret = dsp_mbcmd_send_exarg(mb, arg);
271 schedule_timeout(DSP_TIMEOUT);
273 finish_wait(q, &wait);
280 static int mbcmd_receiver(void* msg)
282 struct mbcmd *mb = (struct mbcmd *)&msg;
284 if (cmdinfo[mb->cmd_h] == NULL) {
286 "invalid message (%08x) for mbcmd_receiver().\n",
293 mblog_add(mb, DIR_D2A);
295 /* call handler for the command */
296 if (cmdinfo[mb->cmd_h]->handler)
297 cmdinfo[mb->cmd_h]->handler(mb);
299 printk(KERN_ERR "mbox: %s is not allowed from DSP.\n",
304 static int mbsync_hold_mem_active;
306 void dsp_mbox_start(void)
308 omap_mbox_init_seq(omap_dsp->mbox);
309 mbseq_expect_tmp = 0;
312 void dsp_mbox_stop(void)
315 mbseq_expect = &mbseq_expect_tmp;
318 int dsp_mbox_config(void *p)
322 if (dsp_address_validate(p, sizeof(struct sync_seq), "mbseq") < 0)
324 if (dsp_mem_type(p, sizeof(struct sync_seq)) != MEM_TYPE_EXTERN) {
326 "omapdsp: mbseq is placed in DSP internal memory.\n"
327 " It will prevent DSP from idling.\n");
328 mbsync_hold_mem_active = 1;
330 * dsp_mem_enable() never fails because
331 * it has been already enabled in dspcfg process and
332 * this will just increment the usecount.
334 dsp_mem_enable((void *)daram_base);
337 local_irq_save(flags);
339 mbseq->da_arm = mbseq_expect_tmp;
340 mbseq_expect = &mbseq->da_arm;
341 local_irq_restore(flags);
346 static int __init dsp_mbox_init(void)
348 omap_dsp->mbox = omap_mbox_get("dsp");
349 if (IS_ERR(omap_dsp->mbox)) {
350 printk(KERN_ERR "failed to get mailbox handler for DSP.\n");
354 omap_dsp->mbox->rxq->callback = mbcmd_receiver;
355 omap_dsp->mbox->txq->callback = mbcmd_sender_prepare;
360 static void dsp_mbox_exit(void)
362 omap_dsp->mbox->txq->callback = NULL;
363 omap_dsp->mbox->rxq->callback = NULL;
365 omap_mbox_put(omap_dsp->mbox);
367 if (mbsync_hold_mem_active) {
368 dsp_mem_disable((void *)daram_base);
369 mbsync_hold_mem_active = 0;
374 * kernel function dispatcher
376 extern void mbox_fbctl_upd(void);
377 extern void mbox_fbctl_disable(struct mbcmd *mb);
379 static void mbox_kfunc_fbctl(struct mbcmd *mb)
386 mbox_fbctl_disable(mb);
390 "mbox: Unknown FBCTL from DSP: 0x%04x\n", mb->data);
395 * dspgw: KFUNC message handler
397 static void mbox_kfunc_power(unsigned short data)
402 case DVFS_START: /* ACK from DSP */
406 ret = dsp_kfunc_enable_devices(omap_dsp,
407 DSP_KFUNC_DEV_TYPE_AUDIO, 0);
411 case AUDIO_PWR_DOWN: /* == AUDIO_PWR_DOWN1 */
412 ret = dsp_kfunc_disable_devices(omap_dsp,
413 DSP_KFUNC_DEV_TYPE_AUDIO, 1);
415 case AUDIO_PWR_DOWN2:
416 ret = dsp_kfunc_disable_devices(omap_dsp,
417 DSP_KFUNC_DEV_TYPE_AUDIO, 2);
420 ret = dsp_kfunc_disable_devices(omap_dsp,
421 DSP_KFUNC_DEV_TYPE_COMMON, 0);
423 omap_dsp->enabled = 0;
427 "mailbox: Unknown PWR from DSP: 0x%04x\n", data);
431 if (unlikely(ret < 0)) {
432 printk(KERN_ERR "mailbox: PWR(0x%04x) failed\n", data);
436 if (likely(ret == 0))
439 mbcompose_send(KFUNC, KFUNC_POWER, data);
442 static void mbox_kfunc(struct mbcmd *mb)
446 mbox_kfunc_fbctl(mb);
449 mbox_kfunc_power(mb->data);
453 "mbox: Unknown KFUNC from DSP: 0x%02x\n", mb->cmd_l);
457 #if defined(CONFIG_ARCH_OMAP1)
458 static inline void dsp_clk_enable(void) {}
459 static inline void dsp_clk_disable(void) {}
460 #elif defined(CONFIG_ARCH_OMAP2)
461 static inline void dsp_clk_enable(void)
465 /*XXX should be handled in mach-omap[1,2] XXX*/
466 prm_write_mod_reg(OMAP24XX_FORCESTATE | (1 << OMAP_POWERSTATE_SHIFT),
467 OMAP24XX_DSP_MOD, PM_PWSTCTRL);
469 r = cm_read_mod_reg(OMAP24XX_DSP_MOD, CM_AUTOIDLE);
470 r |= OMAP2420_AUTO_DSP_IPI;
471 cm_write_mod_reg(r, OMAP24XX_DSP_MOD, CM_AUTOIDLE);
473 r = cm_read_mod_reg(OMAP24XX_DSP_MOD, CM_CLKSTCTRL);
474 r |= OMAP24XX_AUTOSTATE_DSP;
475 cm_write_mod_reg(r, OMAP24XX_DSP_MOD, CM_CLKSTCTRL);
477 clk_enable(dsp_fck_handle);
478 clk_enable(dsp_ick_handle);
481 static inline void dsp_clk_disable(void)
484 clk_disable(dsp_ick_handle);
485 clk_disable(dsp_fck_handle);
487 prm_write_mod_reg(OMAP24XX_FORCESTATE | (3 << OMAP_POWERSTATE_SHIFT),
488 OMAP24XX_DSP_MOD, PM_PWSTCTRL);
492 int dsp_late_init(void)
497 ret = dsp_mem_late_init();
500 ret = dsp_mbox_init();
503 #ifdef CONFIG_ARCH_OMAP1
504 dsp_set_idle_boot_base(IDLEPG_BASE, IDLEPG_SIZE);
506 ret = dsp_kfunc_enable_devices(omap_dsp,
507 DSP_KFUNC_DEV_TYPE_COMMON, 0);
510 omap_dsp->enabled = 1;
522 extern int dsp_ctl_core_init(void);
523 extern void dsp_ctl_core_exit(void);
524 extern int dsp_ctl_init(void);
525 extern void dsp_ctl_exit(void);
526 extern int dsp_mem_init(void);
527 extern void dsp_mem_exit(void);
528 extern void mblog_init(void);
529 extern void mblog_exit(void);
530 extern int dsp_taskmod_init(void);
531 extern void dsp_taskmod_exit(void);
536 static int __init dsp_drv_probe(struct platform_device *pdev)
539 struct omap_dsp *info;
540 struct dsp_platform_data *pdata = pdev->dev.platform_data;
542 dev_info(&pdev->dev, "OMAP DSP driver initialization\n");
544 info = kzalloc(sizeof(struct omap_dsp), GFP_KERNEL);
545 if (unlikely(info == NULL)) {
546 dev_dbg(&pdev->dev, "no memory for info\n");
549 platform_set_drvdata(pdev, info);
552 mutex_init(&info->lock);
553 info->dev = &pdev->dev;
554 info->kdev_list = &pdata->kdev_list;
556 ret = dsp_kfunc_probe_devices(info);
562 ret = dsp_ctl_core_init();
565 ret = dsp_mem_init();
568 ret = dsp_ctl_init();
572 ret = dsp_taskmod_init();
586 dsp_kfunc_remove_devices(info);
593 static int dsp_drv_remove(struct platform_device *pdev)
595 struct omap_dsp *info = platform_get_drvdata(pdev);
597 dsp_cpustat_request(CPUSTAT_RESET);
599 dsp_cfgstat_request(CFGSTAT_CLEAN);
608 #ifdef CONFIG_ARCH_OMAP2
610 clk_disable(dsp_ick_handle);
611 clk_disable(dsp_fck_handle);
613 dsp_kfunc_remove_devices(info);
619 #if defined(CONFIG_PM) && defined(CONFIG_ARCH_OMAP1)
620 static int dsp_drv_suspend(struct platform_device *pdev, pm_message_t state)
622 dsp_cfgstat_request(CFGSTAT_SUSPEND);
627 static int dsp_drv_resume(struct platform_device *pdev)
629 dsp_cfgstat_request(CFGSTAT_RESUME);
634 #define dsp_drv_suspend NULL
635 #define dsp_drv_resume NULL
636 #endif /* CONFIG_PM */
638 static struct platform_driver dsp_driver = {
639 .probe = dsp_drv_probe,
640 .remove = dsp_drv_remove,
641 .suspend = dsp_drv_suspend,
642 .resume = dsp_drv_resume,
648 static int __init omap_dsp_mod_init(void)
650 return platform_driver_register(&dsp_driver);
653 static void __exit omap_dsp_mod_exit(void)
655 platform_driver_unregister(&dsp_driver);
658 /* module dependency: need mailbox module that have mbox_dsp_info */
659 extern struct omap_mbox mbox_dsp_info;
660 struct omap_mbox *mbox_dep = &mbox_dsp_info;
662 module_init(omap_dsp_mod_init);
663 module_exit(omap_dsp_mod_exit);