2 * linux/arch/arm/mach-omap/dsp/dsp_core.c
6 * Copyright (C) 2002-2005 Nokia Corporation
8 * Written by Toshihiro Kobayashi <toshihiro.kobayashi@nokia.com>
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
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. See the
18 * 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, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 * 2005/06/07: DSP Gateway version 3.3
27 #include <linux/init.h>
28 #include <linux/module.h>
29 #include <linux/slab.h>
30 #include <linux/platform_device.h>
31 #include <linux/devfs_fs_kernel.h>
32 #include <linux/sched.h>
33 #include <linux/interrupt.h>
34 #include <linux/proc_fs.h>
35 #include <asm/uaccess.h>
37 #include <asm/signal.h>
38 #include <asm/delay.h>
40 #include <asm/arch/dsp_common.h>
41 #include <asm/arch/dsp.h>
42 #include "hardware_dsp.h"
47 MODULE_AUTHOR("Toshihiro Kobayashi <toshihiro.kobayashi@nokia.com>");
48 MODULE_DESCRIPTION("OMAP DSP driver module");
49 MODULE_LICENSE("GPL");
51 enum mbseq_check_level {
52 MBSEQ_CHECK_NONE, /* no check */
53 MBSEQ_CHECK_VERBOSE, /* discard the illegal command and
55 MBSEQ_CHECK_SILENT, /* discard the illegal command */
58 static enum mbseq_check_level mbseq_check_level = MBSEQ_CHECK_VERBOSE;
60 static int mbx1_valid;
61 static struct sync_seq *mbseq;
62 static unsigned short mbseq_expect_tmp;
63 static unsigned short *mbseq_expect = &mbseq_expect_tmp;
68 extern void mbx1_wdsnd(struct mbcmd *mb);
69 extern void mbx1_wdreq(struct mbcmd *mb);
70 extern void mbx1_bksnd(struct mbcmd *mb);
71 extern void mbx1_bkreq(struct mbcmd *mb);
72 extern void mbx1_bkyld(struct mbcmd *mb);
73 extern void mbx1_bksndp(struct mbcmd *mb);
74 extern void mbx1_bkreqp(struct mbcmd *mb);
75 extern void mbx1_tctl(struct mbcmd *mb);
76 extern void mbx1_poll(struct mbcmd *mb);
77 #ifdef OLD_BINARY_SUPPORT
79 extern void mbx1_wdt(struct mbcmd *mb);
81 extern void mbx1_suspend(struct mbcmd *mb);
82 static void mbx1_kfunc(struct mbcmd *mb);
83 extern void mbx1_tcfg(struct mbcmd *mb);
84 extern void mbx1_tadd(struct mbcmd *mb);
85 extern void mbx1_tdel(struct mbcmd *mb);
86 extern void mbx1_dspcfg(struct mbcmd *mb);
87 extern void mbx1_regrw(struct mbcmd *mb);
88 extern void mbx1_getvar(struct mbcmd *mb);
89 extern void mbx1_err(struct mbcmd *mb);
90 extern void mbx1_dbg(struct mbcmd *mb);
92 static const struct cmdinfo
93 cif_null = { "Unknown", CMD_L_TYPE_NULL, NULL },
94 cif_wdsnd = { "WDSND", CMD_L_TYPE_TID, mbx1_wdsnd },
95 cif_wdreq = { "WDREQ", CMD_L_TYPE_TID, mbx1_wdreq },
96 cif_bksnd = { "BKSND", CMD_L_TYPE_TID, mbx1_bksnd },
97 cif_bkreq = { "BKREQ", CMD_L_TYPE_TID, mbx1_bkreq },
98 cif_bkyld = { "BKYLD", CMD_L_TYPE_NULL, mbx1_bkyld },
99 cif_bksndp = { "BKSNDP", CMD_L_TYPE_TID, mbx1_bksndp },
100 cif_bkreqp = { "BKREQP", CMD_L_TYPE_TID, mbx1_bkreqp },
101 cif_tctl = { "TCTL", CMD_L_TYPE_TID, mbx1_tctl },
102 cif_poll = { "POLL", CMD_L_TYPE_NULL, mbx1_poll },
103 #ifdef OLD_BINARY_SUPPORT
105 cif_wdt = { "WDT", CMD_L_TYPE_NULL, mbx1_wdt },
107 cif_runlevel = { "RUNLEVEL", CMD_L_TYPE_SUBCMD, NULL },
108 cif_pm = { "PM", CMD_L_TYPE_SUBCMD, NULL },
109 cif_suspend = { "SUSPEND", CMD_L_TYPE_NULL, mbx1_suspend },
110 cif_kfunc = { "KFUNC", CMD_L_TYPE_SUBCMD, mbx1_kfunc },
111 cif_tcfg = { "TCFG", CMD_L_TYPE_TID, mbx1_tcfg },
112 cif_tadd = { "TADD", CMD_L_TYPE_TID, mbx1_tadd },
113 cif_tdel = { "TDEL", CMD_L_TYPE_TID, mbx1_tdel },
114 cif_tstop = { "TSTOP", CMD_L_TYPE_TID, NULL },
115 cif_dspcfg = { "DSPCFG", CMD_L_TYPE_SUBCMD, mbx1_dspcfg },
116 cif_regrw = { "REGRW", CMD_L_TYPE_SUBCMD, mbx1_regrw },
117 cif_getvar = { "GETVAR", CMD_L_TYPE_SUBCMD, mbx1_getvar },
118 cif_setvar = { "SETVAR", CMD_L_TYPE_SUBCMD, NULL },
119 cif_err = { "ERR", CMD_L_TYPE_SUBCMD, mbx1_err },
120 cif_dbg = { "DBG", CMD_L_TYPE_NULL, mbx1_dbg };
122 const struct cmdinfo *cmdinfo[128] = {
123 /*00*/ &cif_null, &cif_null, &cif_null, &cif_null,
124 &cif_null, &cif_null, &cif_null, &cif_null,
125 &cif_null, &cif_null, &cif_null, &cif_null,
126 &cif_null, &cif_null, &cif_null, &cif_null,
127 /*10*/ &cif_wdsnd, &cif_wdreq, &cif_null, &cif_null,
128 &cif_null, &cif_null, &cif_null, &cif_null,
129 &cif_null, &cif_null, &cif_null, &cif_null,
130 &cif_null, &cif_null, &cif_null, &cif_null,
131 /*20*/ &cif_bksnd, &cif_bkreq, &cif_null, &cif_bkyld,
132 &cif_bksndp, &cif_bkreqp, &cif_null, &cif_null,
133 &cif_null, &cif_null, &cif_null, &cif_null,
134 &cif_null, &cif_null, &cif_null, &cif_null,
135 /*30*/ &cif_tctl, &cif_null, &cif_poll, &cif_null,
136 &cif_null, &cif_null, &cif_null, &cif_null,
137 &cif_null, &cif_null, &cif_null, &cif_null,
138 &cif_null, &cif_null, &cif_null, &cif_null,
139 /*40*/ &cif_null, &cif_null, &cif_null, &cif_null,
140 &cif_null, &cif_null, &cif_null, &cif_null,
141 &cif_null, &cif_null, &cif_null, &cif_null,
142 &cif_null, &cif_null, &cif_null, &cif_null,
143 #ifdef OLD_BINARY_SUPPORT
145 /*50*/ &cif_wdt, &cif_runlevel, &cif_pm, &cif_suspend,
147 /*50*/ &cif_null, &cif_runlevel, &cif_pm, &cif_suspend,
149 &cif_kfunc, &cif_null, &cif_null, &cif_null,
150 &cif_null, &cif_null, &cif_null, &cif_null,
151 &cif_null, &cif_null, &cif_null, &cif_null,
152 /*60*/ &cif_tcfg, &cif_null, &cif_tadd, &cif_tdel,
153 &cif_null, &cif_tstop, &cif_null, &cif_null,
154 &cif_null, &cif_null, &cif_null, &cif_null,
155 &cif_null, &cif_null, &cif_null, &cif_null,
156 /*70*/ &cif_dspcfg, &cif_null, &cif_regrw, &cif_null,
157 &cif_getvar, &cif_setvar, &cif_null, &cif_null,
158 &cif_err, &cif_dbg, &cif_null, &cif_null,
159 &cif_null, &cif_null, &cif_null, &cif_null
162 int sync_with_dsp(unsigned short *syncwd, unsigned short tid, int try_cnt)
166 if (*(volatile unsigned short *)syncwd == tid)
169 for (try = 0; try < try_cnt; try++) {
171 if (*(volatile unsigned short *)syncwd == tid) {
174 "omapdsp: sync_with_dsp(): try = %d\n", try);
183 static __inline__ int mbsync_irq_save(unsigned long *flags, int try_cnt)
187 local_irq_save(*flags);
188 if (omap_readw(MAILBOX_ARM2DSP1_Flag) == 0)
191 * mailbox is busy. wait for some usecs...
193 local_irq_restore(*flags);
194 for (cnt = 0; cnt < try_cnt; cnt++) {
196 local_irq_save(*flags);
197 if (omap_readw(MAILBOX_ARM2DSP1_Flag) == 0) /* success! */
199 local_irq_restore(*flags);
206 #ifdef CONFIG_OMAP_DSP_MBCMD_VERBOSE
207 #define print_mb_busy_abort(mb) \
209 "mbx: mailbox is busy. %s is aborting.\n", cmd_name(*mb))
210 #define print_mb_mmu_abort(mb) \
212 "mbx: mmu interrupt is set. %s is aborting.\n", cmd_name(*mb))
213 #else /* CONFIG_OMAP_DSP_MBCMD_VERBOSE */
214 #define print_mb_busy_abort(mb) do {} while(0)
215 #define print_mb_mmu_abort(mb) do {} while(0)
216 #endif /* !CONFIG_OMAP_DSP_MBCMD_VERBOSE */
218 int __mbcmd_send(struct mbcmd *mb)
220 struct mbcmd_hw *mb_hw = (struct mbcmd_hw *)mb;
224 * DSP mailbox interrupt latency must be less than 1ms.
226 if (mbsync_irq_save(&flags, 1000) < 0) {
227 print_mb_busy_abort(mb);
232 mb->seq = mbseq->ad_arm;
236 mblog_add(mb, DIR_A2D);
237 mblog_printcmd(mb, DIR_A2D);
239 omap_writew(mb_hw->data, MAILBOX_ARM2DSP1);
240 omap_writew(mb_hw->cmd, MAILBOX_ARM2DSP1b);
242 local_irq_restore(flags);
247 * __dsp_mbcmd_send(): mailbox dispatcher
249 int __dsp_mbcmd_send(struct mbcmd *mb, struct mb_exarg *arg, int recovery_flag)
251 static DECLARE_MUTEX(mbsend_sem);
255 * while MMU fault is set,
256 * only recovery command can be executed
258 if (dsp_err_mmu_isset() && !recovery_flag) {
259 print_mb_mmu_abort(mb);
263 if (down_interruptible(&mbsend_sem) < 0)
266 if (arg) { /* we have extra argument */
270 * even if ipbuf_sys_ad is in DSP internal memory,
271 * dsp_mem_enable() never cause to call PM mailbox command
272 * because in that case DSP memory should be always enabled.
273 * (see ipbuf_sys_hold_mem_active in ipbuf.c)
275 * Therefore, we can call this function here safely.
277 dsp_mem_enable(ipbuf_sys_ad);
278 if (sync_with_dsp(&ipbuf_sys_ad->s, OMAP_DSP_TID_FREE, 10) < 0) {
279 printk(KERN_ERR "omapdsp: ipbuf_sys_ad is busy.\n");
280 dsp_mem_disable(ipbuf_sys_ad);
284 for (i = 0; i < arg->argc; i++) {
285 ipbuf_sys_ad->d[i] = arg->argv[i];
287 ipbuf_sys_ad->s = arg->tid;
288 dsp_mem_disable(ipbuf_sys_ad);
291 ret = __mbcmd_send(mb);
298 int __dsp_mbcmd_send_and_wait(struct mbcmd *mb, struct mb_exarg *arg,
299 wait_queue_head_t *q)
302 DECLARE_WAITQUEUE(wait, current);
304 add_wait_queue(q, &wait);
305 current_state = current->state;
306 set_current_state(TASK_INTERRUPTIBLE);
307 if (dsp_mbcmd_send_exarg(mb, arg) < 0) {
308 set_current_state(current_state);
309 remove_wait_queue(q, &wait);
312 schedule_timeout(DSP_TIMEOUT);
313 set_current_state(current_state);
314 remove_wait_queue(q, &wait);
319 int __dsp_mbsend(unsigned char cmdh, unsigned char cmdl, unsigned short data,
324 mbcmd_set(mb, cmdh, cmdl, data);
325 return __dsp_mbcmd_send(&mb, NULL, recovery_flag);
328 static int mbsync_hold_mem_active;
330 void dsp_mb_start(void)
332 mbx1_valid = 1; /* start interpreting */
333 mbseq_expect_tmp = 0;
336 void dsp_mb_stop(void)
338 mbx1_valid = 0; /* stop interpreting */
339 if (mbsync_hold_mem_active) {
340 dsp_mem_disable((void *)daram_base);
341 mbsync_hold_mem_active = 0;
344 mbseq_expect = &mbseq_expect_tmp;
347 int dsp_mb_config(void *p)
351 if (dsp_address_validate(p, sizeof(struct sync_seq), "mbseq") < 0)
353 if (dsp_mem_type(p, sizeof(struct sync_seq)) != MEM_TYPE_EXTERN) {
355 "omapdsp: mbseq is placed in DSP internal memory.\n"
356 " It will prevent DSP from idling.\n");
357 mbsync_hold_mem_active = 1;
359 * dsp_mem_enable() never fails because
360 * it has been already enabled in dspcfg process and
361 * this will just increment the usecount.
363 dsp_mem_enable((void *)daram_base);
366 local_irq_save(flags);
368 mbseq->da_arm = mbseq_expect_tmp;
369 mbseq_expect = &mbseq->da_arm;
370 local_irq_restore(flags);
380 struct mbcmd mb[MBQ_DEPTH];
387 #define mbq_inc(p) do { if (++(p) == MBQ_DEPTH) (p) = 0; } while(0)
392 static void do_mbx1(void)
396 disable_irq(INT_D2A_MB1);
397 if ((mbq.rp == mbq.wp) && !mbq.full)
399 enable_irq(INT_D2A_MB1);
404 mb = &mbq.mb[mbq.rp];
406 mblog_add(mb, DIR_D2A);
407 mblog_printcmd(mb, DIR_D2A);
410 * call handler for each command
412 if (cmdinfo[mb->cmd_h]->handler)
413 cmdinfo[mb->cmd_h]->handler(mb);
414 else if (cmdinfo[mb->cmd_h] != &cif_null)
415 printk(KERN_ERR "mbx: %s is not allowed from DSP.\n",
419 "mbx: Unrecognized command: "
420 "cmd=0x%04x, data=0x%04x\n",
421 ((struct mbcmd_hw *)mb)->cmd & 0x7fff, mb->data);
423 disable_irq(INT_D2A_MB1);
425 if (mbq.rp == mbq.wp)
427 /* if mbq has been full, now we have a room. */
430 enable_irq(INT_D2A_MB1);
432 enable_irq(INT_D2A_MB1);
436 static DECLARE_WORK(mbx1_work, (void (*)(void *))do_mbx1, NULL);
439 * kernel function dispatcher
441 extern void mbx1_fbctl_upd(void);
442 extern void mbx1_fbctl_disable(void);
444 static void mbx1_kfunc_fbctl(unsigned short data)
447 case OMAP_DSP_MBCMD_FBCTL_UPD:
450 case OMAP_DSP_MBCMD_FBCTL_DISABLE:
451 mbx1_fbctl_disable();
455 "mailbox: Unknown FBCTL from DSP: 0x%04x\n", data);
459 static void mbx1_kfunc_audio_pwr(unsigned short data)
464 case OMAP_DSP_MBCMD_AUDIO_PWR_UP:
465 omap_dsp_audio_pwr_up_request(0);
467 mbcmd_set(mb, MBCMD(KFUNC), OMAP_DSP_MBCMD_KFUNC_AUDIO_PWR,
468 OMAP_DSP_MBCMD_AUDIO_PWR_UP);
471 case OMAP_DSP_MBCMD_AUDIO_PWR_DOWN1:
472 omap_dsp_audio_pwr_down_request(1);
474 case OMAP_DSP_MBCMD_AUDIO_PWR_DOWN2:
475 omap_dsp_audio_pwr_down_request(2);
479 "mailbox: Unknown AUDIO_PWR from DSP: 0x%04x\n", data);
483 static void mbx1_kfunc(struct mbcmd *mb)
486 case OMAP_DSP_MBCMD_KFUNC_FBCTL:
487 mbx1_kfunc_fbctl(mb->data);
489 case OMAP_DSP_MBCMD_KFUNC_AUDIO_PWR:
490 mbx1_kfunc_audio_pwr(mb->data);
494 "mailbox: Unknown kfunc from DSP: 0x%02x\n", mb->cmd_l);
499 * mailbox interrupt handler
501 static irqreturn_t mbx1_interrupt(int irq, void *dev_id, struct pt_regs *regs)
506 } *mb = (void *)&mbq.mb[mbq.wp];
508 #if (INT_D2A_MB1 == INT_DSP_MAILBOX1)
509 mb->hw.data = omap_readw(MAILBOX_DSP2ARM1);
510 mb->hw.cmd = omap_readw(MAILBOX_DSP2ARM1b);
511 #elif (INT_D2A_MB1 == INT_DSP_MAILBOX2)
512 mb->hw.data = omap_readw(MAILBOX_DSP2ARM2);
513 mb->hw.cmd = omap_readw(MAILBOX_DSP2ARM2b);
516 /* if mbx1 has not been validated yet, discard. */
520 if (mb->sw.seq != (*mbseq_expect & 1)) {
521 switch (mbseq_check_level) {
522 case MBSEQ_CHECK_NONE:
524 case MBSEQ_CHECK_VERBOSE:
526 "mbx: illegal seq bit!!! ignoring this command."
527 " (%04x:%04x)\n", mb->hw.cmd, mb->hw.data);
529 case MBSEQ_CHECK_SILENT:
537 if (mbq.wp == mbq.rp) { /* mbq is full */
539 disable_irq(INT_D2A_MB1);
541 schedule_work(&mbx1_work);
546 static irqreturn_t mbx2_interrupt(int irq, void *dev_id, struct pt_regs *regs)
548 unsigned short cmd, data;
550 #if (INT_D2A_MB1 == INT_DSP_MAILBOX1)
551 data = omap_readw(MAILBOX_DSP2ARM2);
552 cmd = omap_readw(MAILBOX_DSP2ARM2b);
553 #elif (INT_D2A_MB1 == INT_DSP_MAILBOX2)
554 data = omap_readw(MAILBOX_DSP2ARM1);
555 cmd = omap_readw(MAILBOX_DSP2ARM1b);
558 "mailbox2 interrupt! cmd=%04x, data=%04x\n", cmd, data);
564 static void mpuio_interrupt(int irq, void *dev_id, struct pt_regs *regs)
566 printk(KERN_INFO "MPUIO interrupt!\n");
570 #ifdef CONFIG_PROC_FS
571 struct proc_dir_entry *procdir_dsp = NULL;
573 static void dsp_create_procdir_dsp(void)
575 procdir_dsp = proc_mkdir("dsp", 0);
576 if (procdir_dsp == NULL) {
578 "omapdsp: failed to register proc directory: dsp\n");
582 static void dsp_remove_procdir_dsp(void)
585 remove_proc_entry("dsp", 0);
587 #else /* CONFIG_PROC_FS */
588 #define dsp_create_procdir_dsp() do { } while (0)
589 #define dsp_remove_procdir_dsp() do { } while (0)
590 #endif /* CONFIG_PROC_FS */
592 extern irqreturn_t dsp_mmu_interrupt(int irq, void *dev_id,
593 struct pt_regs *regs);
595 extern int dsp_ctl_core_init(void);
596 extern void dsp_ctl_core_exit(void);
597 extern void dsp_ctl_init(void);
598 extern void dsp_ctl_exit(void);
599 extern int dsp_mem_init(void);
600 extern void dsp_mem_exit(void);
601 extern void mblog_init(void);
602 extern void mblog_exit(void);
603 extern int dsp_taskmod_init(void);
604 extern void dsp_taskmod_exit(void);
609 static void dsp_dev_release(struct device *dev)
616 #if (INT_D2A_MB1 == INT_DSP_MAILBOX1)
617 # define INT_D2A_MB2 INT_DSP_MAILBOX2
618 #elif(INT_D2A_MB1 == INT_DSP_MAILBOX2) /* swap MB1 and MB2 */
619 # define INT_D2A_MB2 INT_DSP_MAILBOX1
622 static int __init dsp_drv_probe(struct platform_device *pdev)
626 printk(KERN_INFO "OMAP DSP driver initialization\n");
628 //__dsp_enable(); // XXX
630 dsp_create_procdir_dsp();
632 if ((ret = dsp_ctl_core_init()) < 0)
634 if ((ret = dsp_mem_init()) < 0)
638 if ((ret = dsp_taskmod_init()) < 0)
642 * mailbox interrupt handlers registration
644 ret = request_irq(INT_D2A_MB1, mbx1_interrupt, SA_INTERRUPT, "dsp",
648 "failed to register mailbox1 interrupt: %d\n", ret);
652 ret = request_irq(INT_D2A_MB2, mbx2_interrupt, SA_INTERRUPT, "dsp",
656 "failed to register mailbox2 interrupt: %d\n", ret);
660 ret = request_irq(INT_DSP_MMU, dsp_mmu_interrupt, SA_INTERRUPT, "dsp",
664 "failed to register DSP MMU interrupt: %d\n", ret);
668 /* MMU interrupt is not enabled until DSP runs */
669 disable_irq(INT_DSP_MMU);
672 ret = request_irq(INT_MPUIO, mpuio_interrupt, SA_INTERRUPT, "dsp", dev);
675 "failed to register MPUIO interrupt: %d\n", ret);
683 free_irq(INT_D2A_MB2, &pdev->dev);
685 free_irq(INT_D2A_MB1, &pdev->dev);
695 dsp_remove_procdir_dsp();
697 //__dsp_disable(); // XXX
701 static int dsp_drv_remove(struct platform_device *pdev)
703 dsp_cpustat_request(CPUSTAT_RESET);
706 free_irq(INT_MPUIO, dev);
708 free_irq(INT_DSP_MMU, &pdev->dev);
709 free_irq(INT_D2A_MB2, &pdev->dev);
710 free_irq(INT_D2A_MB1, &pdev->dev);
712 /* recover disable_depth */
713 enable_irq(INT_DSP_MMU);
722 dsp_remove_procdir_dsp();
724 //__dsp_disable(); // XXX
730 static int dsp_drv_suspend(struct platform_device *pdev, pm_message_t state)
737 static int dsp_drv_resume(struct platform_device *pdev)
744 #define dsp_drv_suspend NULL
745 #define dsp_drv_resume NULL
746 #endif /* CONFIG_PM */
748 static struct resource dsp_resources[] = {
750 .start = INT_DSP_MAILBOX1,
751 .flags = IORESOURCE_IRQ,
754 .start = INT_DSP_MAILBOX2,
755 .flags = IORESOURCE_IRQ,
758 .start = INT_DSP_MMU,
759 .flags = IORESOURCE_IRQ,
763 struct platform_device dsp_device = {
767 .release = dsp_dev_release,
769 .num_resources = ARRAY_SIZE(&dsp_resources),
770 .resource = dsp_resources,
773 static struct platform_driver dsp_driver = {
774 .probe = dsp_drv_probe,
775 .remove = dsp_drv_remove,
776 .suspend = dsp_drv_suspend,
777 .resume = dsp_drv_resume,
783 static int __init omap_dsp_mod_init(void)
787 ret = platform_device_register(&dsp_device);
789 printk(KERN_ERR "failed to register the DSP device: %d\n", ret);
793 ret = platform_driver_register(&dsp_driver);
795 printk(KERN_ERR "failed to register the DSP driver: %d\n", ret);
802 platform_device_unregister(&dsp_device);
807 static void __exit omap_dsp_mod_exit(void)
809 platform_driver_unregister(&dsp_driver);
810 platform_device_unregister(&dsp_device);
813 module_init(omap_dsp_mod_init);
814 module_exit(omap_dsp_mod_exit);