]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blob - arch/arm/plat-omap/dsp/dsp_core.c
f0b303f9c660e6497653693bd601de0e6a5fc42c
[linux-2.6-omap-h63xx.git] / arch / arm / plat-omap / dsp / dsp_core.c
1 /*
2  * This file is part of OMAP DSP driver (DSP Gateway version 3.3.1)
3  *
4  * Copyright (C) 2002-2006 Nokia Corporation. All rights reserved.
5  *
6  * Contact: Toshihiro Kobayashi <toshihiro.kobayashi@nokia.com>
7  *
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.
11  *
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.
16  *
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
20  * 02110-1301 USA
21  *
22  */
23
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"
35 #include "dsp.h"
36 #include "ipbuf.h"
37 #include "dsp_common.h"
38
39 MODULE_AUTHOR("Toshihiro Kobayashi <toshihiro.kobayashi@nokia.com>");
40 MODULE_DESCRIPTION("OMAP DSP driver module");
41 MODULE_LICENSE("GPL");
42
43 struct omap_mbox *mbox_dsp;
44 static struct sync_seq *mbseq;
45 static u16 mbseq_expect_tmp;
46 static u16 *mbseq_expect = &mbseq_expect_tmp;
47
48 /*
49  * mailbox commands
50  */
51 extern void mbox_wdsnd(struct mbcmd *mb);
52 extern void mbox_wdreq(struct mbcmd *mb);
53 extern void mbox_bksnd(struct mbcmd *mb);
54 extern void mbox_bkreq(struct mbcmd *mb);
55 extern void mbox_bkyld(struct mbcmd *mb);
56 extern void mbox_bksndp(struct mbcmd *mb);
57 extern void mbox_bkreqp(struct mbcmd *mb);
58 extern void mbox_tctl(struct mbcmd *mb);
59 extern void mbox_poll(struct mbcmd *mb);
60 #ifdef OLD_BINARY_SUPPORT
61 /* v3.3 obsolete */
62 extern void mbox_wdt(struct mbcmd *mb);
63 #endif
64 extern void mbox_suspend(struct mbcmd *mb);
65 static void mbox_kfunc(struct mbcmd *mb);
66 extern void mbox_tcfg(struct mbcmd *mb);
67 extern void mbox_tadd(struct mbcmd *mb);
68 extern void mbox_tdel(struct mbcmd *mb);
69 extern void mbox_dspcfg(struct mbcmd *mb);
70 extern void mbox_regrw(struct mbcmd *mb);
71 extern void mbox_getvar(struct mbcmd *mb);
72 extern void mbox_err(struct mbcmd *mb);
73 extern void mbox_dbg(struct mbcmd *mb);
74
75 static const struct cmdinfo
76         cif_wdsnd    = { "WDSND",    CMD_L_TYPE_TID,    mbox_wdsnd   },
77         cif_wdreq    = { "WDREQ",    CMD_L_TYPE_TID,    mbox_wdreq   },
78         cif_bksnd    = { "BKSND",    CMD_L_TYPE_TID,    mbox_bksnd   },
79         cif_bkreq    = { "BKREQ",    CMD_L_TYPE_TID,    mbox_bkreq   },
80         cif_bkyld    = { "BKYLD",    CMD_L_TYPE_NULL,   mbox_bkyld   },
81         cif_bksndp   = { "BKSNDP",   CMD_L_TYPE_TID,    mbox_bksndp  },
82         cif_bkreqp   = { "BKREQP",   CMD_L_TYPE_TID,    mbox_bkreqp  },
83         cif_tctl     = { "TCTL",     CMD_L_TYPE_TID,    mbox_tctl    },
84         cif_poll     = { "POLL",     CMD_L_TYPE_NULL,   mbox_poll    },
85 #ifdef OLD_BINARY_SUPPORT
86         /* v3.3 obsolete */
87         cif_wdt      = { "WDT",      CMD_L_TYPE_NULL,   mbox_wdt     },
88 #endif
89         cif_runlevel = { "RUNLEVEL", CMD_L_TYPE_SUBCMD, NULL        },
90         cif_pm       = { "PM",       CMD_L_TYPE_SUBCMD, NULL        },
91         cif_suspend  = { "SUSPEND",  CMD_L_TYPE_NULL,   mbox_suspend },
92         cif_kfunc    = { "KFUNC",    CMD_L_TYPE_SUBCMD, mbox_kfunc   },
93         cif_tcfg     = { "TCFG",     CMD_L_TYPE_TID,    mbox_tcfg    },
94         cif_tadd     = { "TADD",     CMD_L_TYPE_TID,    mbox_tadd    },
95         cif_tdel     = { "TDEL",     CMD_L_TYPE_TID,    mbox_tdel    },
96         cif_tstop    = { "TSTOP",    CMD_L_TYPE_TID,    NULL        },
97         cif_dspcfg   = { "DSPCFG",   CMD_L_TYPE_SUBCMD, mbox_dspcfg  },
98         cif_regrw    = { "REGRW",    CMD_L_TYPE_SUBCMD, mbox_regrw   },
99         cif_getvar   = { "GETVAR",   CMD_L_TYPE_SUBCMD, mbox_getvar  },
100         cif_setvar   = { "SETVAR",   CMD_L_TYPE_SUBCMD, NULL        },
101         cif_err      = { "ERR",      CMD_L_TYPE_SUBCMD, mbox_err     },
102         cif_dbg      = { "DBG",      CMD_L_TYPE_NULL,   mbox_dbg     };
103
104 #define MBOX_CMD_MAX    0x80
105 const struct cmdinfo *cmdinfo[MBOX_CMD_MAX] = {
106         [MBOX_CMD_DSP_WDSND]    = &cif_wdsnd,
107         [MBOX_CMD_DSP_WDREQ]    = &cif_wdreq,
108         [MBOX_CMD_DSP_BKSND]    = &cif_bksnd,
109         [MBOX_CMD_DSP_BKREQ]    = &cif_bkreq,
110         [MBOX_CMD_DSP_BKYLD]    = &cif_bkyld,
111         [MBOX_CMD_DSP_BKSNDP]   = &cif_bksndp,
112         [MBOX_CMD_DSP_BKREQP]   = &cif_bkreqp,
113         [MBOX_CMD_DSP_TCTL]     = &cif_tctl,
114         [MBOX_CMD_DSP_POLL]     = &cif_poll,
115 #ifdef OLD_BINARY_SUPPORT
116         [MBOX_CMD_DSP_WDT]      = &cif_wdt, /* v3.3 obsolete */
117 #endif
118         [MBOX_CMD_DSP_RUNLEVEL] = &cif_runlevel,
119         [MBOX_CMD_DSP_PM]       = &cif_pm,
120         [MBOX_CMD_DSP_SUSPEND]  = &cif_suspend,
121         [MBOX_CMD_DSP_KFUNC]    = &cif_kfunc,
122         [MBOX_CMD_DSP_TCFG]     = &cif_tcfg,
123         [MBOX_CMD_DSP_TADD]     = &cif_tadd,
124         [MBOX_CMD_DSP_TDEL]     = &cif_tdel,
125         [MBOX_CMD_DSP_TSTOP]    = &cif_tstop,
126         [MBOX_CMD_DSP_DSPCFG]   = &cif_dspcfg,
127         [MBOX_CMD_DSP_REGRW]    = &cif_regrw,
128         [MBOX_CMD_DSP_GETVAR]   = &cif_getvar,
129         [MBOX_CMD_DSP_SETVAR]   = &cif_setvar,
130         [MBOX_CMD_DSP_ERR]      = &cif_err,
131         [MBOX_CMD_DSP_DBG]      = &cif_dbg,
132 };
133
134 int sync_with_dsp(u16 *adr, u16 val, int try_cnt)
135 {
136         int try;
137
138         if (*(volatile u16 *)adr == val)
139                 return 0;
140
141         for (try = 0; try < try_cnt; try++) {
142                 udelay(1);
143                 if (*(volatile u16 *)adr == val) {
144                         /* success! */
145                         printk(KERN_INFO
146                                "omapdsp: sync_with_dsp(): try = %d\n", try);
147                         return 0;
148                 }
149         }
150
151         /* fail! */
152         return -1;
153 }
154
155 /*
156  * __dsp_mbcmd_send_exarg(): mailbox dispatcher
157  */
158 int __dsp_mbcmd_send_exarg(struct mbcmd *mb, struct mb_exarg *arg,
159                            int recovery_flag)
160 {
161         static DEFINE_MUTEX(mbsend_lock);
162         int ret = 0;
163
164         /*
165          * while MMU fault is set,
166          * only recovery command can be executed
167          */
168         if (dsp_err_isset(ERRCODE_MMU) && !recovery_flag) {
169                 printk(KERN_ERR
170                        "mbox: mmu interrupt is set. %s is aborting.\n",
171                        cmd_name(*mb));
172                 return -1;
173         }
174
175         if (mutex_lock_interruptible(&mbsend_lock) < 0)
176                 return -1;
177
178         if (arg) {      /* we have extra argument */
179                 int i;
180
181                 /*
182                  * even if ipbuf_sys_ad is in DSP internal memory,
183                  * dsp_mem_enable() never cause to call PM mailbox command
184                  * because in that case DSP memory should be always enabled.
185                  * (see ipbuf_sys_hold_mem_active in ipbuf.c)
186                  *
187                  * Therefore, we can call this function here safely.
188                  */
189                 dsp_mem_enable(ipbuf_sys_ad);
190                 if (sync_with_dsp(&ipbuf_sys_ad->s, TID_FREE, 10) < 0) {
191                         printk(KERN_ERR "omapdsp: ipbuf_sys_ad is busy.\n");
192                         dsp_mem_disable(ipbuf_sys_ad);
193                         ret = -EBUSY;
194                         goto out;
195                 }
196                 for (i = 0; i < arg->argc; i++) {
197                         ipbuf_sys_ad->d[i] = arg->argv[i];
198                 }
199                 ipbuf_sys_ad->s = arg->tid;
200                 dsp_mem_disable(ipbuf_sys_ad);
201         }
202
203         if (mbseq)
204                 mbseq->ad_arm++;
205
206         mblog_add(mb, DIR_A2D);
207
208         ret = omap_mbox_msg_send(mbox_dsp, *(mbox_msg_t *)mb);
209
210 out:
211         mutex_unlock(&mbsend_lock);
212         return ret;
213 }
214
215 int dsp_mbcmd_send_and_wait_exarg(struct mbcmd *mb, struct mb_exarg *arg,
216                                   wait_queue_head_t *q)
217 {
218         long current_state;
219         DECLARE_WAITQUEUE(wait, current);
220
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);
227                 return -1;
228         }
229         schedule_timeout(DSP_TIMEOUT);
230         set_current_state(current_state);
231         remove_wait_queue(q, &wait);
232
233         return 0;
234 }
235
236 /*
237  * mbcmd receiver
238  */
239 static void mbcmd_receiver(mbox_msg_t msg)
240 {
241         struct mbcmd *mb = (struct mbcmd *)&msg;
242
243         if (cmdinfo[mb->cmd_h] == NULL) {
244                 printk(KERN_ERR
245                        "invalid message (%08x) for mbcmd_receiver().\n", msg);
246                 return;
247         }
248
249         (*mbseq_expect)++;
250
251         mblog_add(mb, DIR_D2A);
252
253         /* call handler for the command */
254         if (cmdinfo[mb->cmd_h]->handler)
255                 cmdinfo[mb->cmd_h]->handler(mb);
256         else
257                 printk(KERN_ERR "mbox: %s is not allowed from DSP.\n",
258                        cmd_name(*mb));
259 }
260
261 static int mbsync_hold_mem_active;
262
263 void dsp_mbox_start(void)
264 {
265         omap_mbox_init_seq(mbox_dsp);
266         mbseq_expect_tmp = 0;
267 }
268
269 void dsp_mbox_stop(void)
270 {
271         mbseq = NULL;
272         mbseq_expect = &mbseq_expect_tmp;
273 }
274
275 int dsp_mbox_config(void *p)
276 {
277         unsigned long flags;
278
279         if (dsp_address_validate(p, sizeof(struct sync_seq), "mbseq") < 0)
280                 return -1;
281         if (dsp_mem_type(p, sizeof(struct sync_seq)) != MEM_TYPE_EXTERN) {
282                 printk(KERN_WARNING
283                        "omapdsp: mbseq is placed in DSP internal memory.\n"
284                        "         It will prevent DSP from idling.\n");
285                 mbsync_hold_mem_active = 1;
286                 /*
287                  * dsp_mem_enable() never fails because
288                  * it has been already enabled in dspcfg process and
289                  * this will just increment the usecount.
290                  */
291                 dsp_mem_enable((void *)daram_base);
292         }
293
294         local_irq_save(flags);
295         mbseq = p;
296         mbseq->da_arm = mbseq_expect_tmp;
297         mbseq_expect = &mbseq->da_arm;
298         local_irq_restore(flags);
299
300         return 0;
301 }
302
303 static int __init dsp_mbox_init(void)
304 {
305         mbox_dsp->mbox = omap_mbox_get("dsp");
306         if (IS_ERR(mbox_dsp)) {
307                 printk(KERN_ERR "failed to get mailbox handler for DSP.\n");
308                 return -ENODEV;
309         }
310
311         mbox_dsp->mbox->msg_receive_cb = mbcmd_receiver;
312
313         return 0;
314 }
315
316 static void dsp_mbox_exit(void)
317 {
318         mbox_dsp->mbox->msg_receive_cb = NULL;
319
320         if (mbsync_hold_mem_active) {
321                 dsp_mem_disable((void *)daram_base);
322                 mbsync_hold_mem_active = 0;
323         }
324 }
325
326 /*
327  * kernel function dispatcher
328  */
329 extern void mbox_fbctl_upd(void);
330 extern void mbox_fbctl_disable(struct mbcmd *mb);
331
332 static void mbox_kfunc_fbctl(struct mbcmd *mb)
333 {
334         switch (mb->data) {
335         case FBCTL_UPD:
336                 mbox_fbctl_upd();
337                 break;
338         case FBCTL_DISABLE:
339                 mbox_fbctl_disable(mb);
340                 break;
341         default:
342                 printk(KERN_ERR
343                        "mbox: Unknown FBCTL from DSP: 0x%04x\n", mb->data);
344         }
345 }
346
347 static void mbox_kfunc_audio_pwr(unsigned short data)
348 {
349         switch (data) {
350         case AUDIO_PWR_UP:
351                 omap_dsp_audio_pwr_up_request(0);
352                 /* send back ack */
353                 mbcompose_send(KFUNC, KFUNC_AUDIO_PWR, AUDIO_PWR_UP);
354                 break;
355         case AUDIO_PWR_DOWN1:
356                 omap_dsp_audio_pwr_down_request(1);
357                 break;
358         case AUDIO_PWR_DOWN2:
359                 omap_dsp_audio_pwr_down_request(2);
360                 break;
361         default:
362                 printk(KERN_ERR
363                        "mailbox: Unknown AUDIO_PWR from DSP: 0x%04x\n", data);
364         }
365 }
366
367 static void mbox_kfunc(struct mbcmd *mb)
368 {
369         switch (mb->cmd_l) {
370         case KFUNC_FBCTL:
371                 mbox_kfunc_fbctl(mb);
372                 break;
373         case KFUNC_AUDIO_PWR:
374                 mbox_kfunc_audio_pwr(mb->data);
375                 break;
376         default:
377                 printk(KERN_ERR
378                        "mbox: Unknown KFUNC from DSP: 0x%02x\n", mb->cmd_l);
379         }
380 }
381
382 extern int  dsp_ctl_core_init(void);
383 extern void dsp_ctl_core_exit(void);
384 extern void dsp_ctl_init(void);
385 extern void dsp_ctl_exit(void);
386 extern int  dsp_mem_init(void);
387 extern void dsp_mem_exit(void);
388 extern void mblog_init(void);
389 extern void mblog_exit(void);
390 extern int  dsp_taskmod_init(void);
391 extern void dsp_taskmod_exit(void);
392
393 /*
394  * device functions
395  */
396 static void dsp_dev_release(struct device *dev)
397 {
398 }
399
400 /*
401  * driver functions
402  */
403 static int __init dsp_drv_probe(struct platform_device *pdev)
404 {
405         int ret;
406
407         printk(KERN_INFO "OMAP DSP driver initialization\n");
408 #ifdef CONFIG_ARCH_OMAP2
409         clk_enable(dsp_fck_handle);
410         clk_enable(dsp_ick_handle);
411         __dsp_per_enable();
412 #endif
413
414         if ((ret = dsp_ctl_core_init()) < 0)
415                 goto fail1;
416         if ((ret = dsp_mem_init()) < 0)
417                 goto fail2;
418         dsp_ctl_init();
419         mblog_init();
420         if ((ret = dsp_taskmod_init()) < 0)
421                 goto fail3;
422         if ((ret = dsp_mbox_init()) < 0)
423                 goto fail4;
424
425         return 0;
426
427 fail4:
428         dsp_taskmod_exit();
429 fail3:
430         mblog_exit();
431         dsp_ctl_exit();
432         dsp_mem_exit();
433 fail2:
434         dsp_ctl_core_exit();
435 fail1:
436 #ifdef CONFIG_ARCH_OMAP2
437         __dsp_per_disable();
438         clk_disable(dsp_ick_handle);
439         clk_disable(dsp_fck_handle);
440 #endif
441         return ret;
442 }
443
444 static int dsp_drv_remove(struct platform_device *pdev)
445 {
446         dsp_cpustat_request(CPUSTAT_RESET);
447
448         dsp_cfgstat_request(CFGSTAT_CLEAN);
449         dsp_mbox_exit();
450         dsp_taskmod_exit();
451         mblog_exit();
452         dsp_ctl_exit();
453         dsp_mem_exit();
454
455         dsp_ctl_core_exit();
456
457 #ifdef CONFIG_ARCH_OMAP2
458         __dsp_per_disable();
459         clk_disable(dsp_ick_handle);
460         clk_disable(dsp_fck_handle);
461 #endif
462         return 0;
463 }
464
465 #ifdef CONFIG_PM
466 static int dsp_drv_suspend(struct platform_device *pdev, pm_message_t state)
467 {
468         dsp_cfgstat_request(CFGSTAT_SUSPEND);
469
470         return 0;
471 }
472
473 static int dsp_drv_resume(struct platform_device *pdev)
474 {
475         dsp_cfgstat_request(CFGSTAT_RESUME);
476
477         return 0;
478 }
479 #else
480 #define dsp_drv_suspend         NULL
481 #define dsp_drv_resume          NULL
482 #endif /* CONFIG_PM */
483
484 static struct resource dsp_resources[] = {
485         {
486                 .start = INT_DSP_MMU,
487                 .flags = IORESOURCE_IRQ,
488         },
489 };
490
491 struct platform_device dsp_device = {
492         .name           = "dsp",
493         .id             = -1,
494         .dev = {
495                 .release        = dsp_dev_release,
496         },
497         .num_resources  = ARRAY_SIZE(&dsp_resources),
498         .resource       = dsp_resources,
499 };
500
501 static struct platform_driver dsp_driver = {
502         .probe          = dsp_drv_probe,
503         .remove         = dsp_drv_remove,
504         .suspend        = dsp_drv_suspend,
505         .resume         = dsp_drv_resume,
506         .driver         = {
507                 .name   = "dsp",
508         },
509 };
510
511 static int __init omap_dsp_mod_init(void)
512 {
513         int ret;
514
515         mbox_dsp = mbox_get("DSP");
516         if (IS_ERR(mbox_dsp)) {
517                 printk(KERN_ERR "failed to get mailbox handler for DSP.\n");
518                 goto fail1;
519         }
520
521         ret = platform_device_register(&dsp_device);
522         if (ret) {
523                 printk(KERN_ERR "failed to register the DSP device: %d\n", ret);
524                 goto fail1;
525         }
526
527         ret = platform_driver_register(&dsp_driver);
528         if (ret) {
529                 printk(KERN_ERR "failed to register the DSP driver: %d\n", ret);
530                 goto fail2;
531         }
532
533         return 0;
534
535 fail2:
536         platform_device_unregister(&dsp_device);
537 fail1:
538         return -ENODEV;
539 }
540
541 static void __exit omap_dsp_mod_exit(void)
542 {
543         platform_driver_unregister(&dsp_driver);
544         platform_device_unregister(&dsp_device);
545 }
546
547 module_init(omap_dsp_mod_init);
548 module_exit(omap_dsp_mod_exit);