]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blob - arch/arm/plat-omap/dsp/dsp_core.c
ARM: OMAP: DSPGW: cleanup
[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
38 MODULE_AUTHOR("Toshihiro Kobayashi <toshihiro.kobayashi@nokia.com>");
39 MODULE_DESCRIPTION("OMAP DSP driver module");
40 MODULE_LICENSE("GPL");
41
42 struct mbx *mbx_dsp;
43 static struct sync_seq *mbseq;
44 static u16 mbseq_expect_tmp;
45 static u16 *mbseq_expect = &mbseq_expect_tmp;
46
47 /*
48  * mailbox commands
49  */
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
60 /* v3.3 obsolete */
61 extern void mbx_wdt(struct mbcmd *mb);
62 #endif
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);
73
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
85         /* v3.3 obsolete */
86         cif_wdt      = { "WDT",      CMD_L_TYPE_NULL,   mbx_wdt     },
87 #endif
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     };
102
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 */
116 #endif
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,
131 };
132
133 int sync_with_dsp(u16 *adr, u16 val, int try_cnt)
134 {
135         int try;
136
137         if (*(volatile u16 *)adr == val)
138                 return 0;
139
140         for (try = 0; try < try_cnt; try++) {
141                 udelay(1);
142                 if (*(volatile u16 *)adr == val) {
143                         /* success! */
144                         printk(KERN_INFO
145                                "omapdsp: sync_with_dsp(): try = %d\n", try);
146                         return 0;
147                 }
148         }
149
150         /* fail! */
151         return -1;
152 }
153
154 /*
155  * __dsp_mbcmd_send_exarg(): mailbox dispatcher
156  */
157 int __dsp_mbcmd_send_exarg(struct mbcmd *mb, struct mb_exarg *arg,
158                            int recovery_flag)
159 {
160         static DEFINE_MUTEX(mbsend_lock);
161         int ret = 0;
162
163         /*
164          * while MMU fault is set,
165          * only recovery command can be executed
166          */
167         if (dsp_err_isset(ERRCODE_MMU) && !recovery_flag) {
168                 printk(KERN_ERR
169                        "mbx: mmu interrupt is set. %s is aborting.\n",
170                        cmd_name(*mb));
171                 return -1;
172         }
173
174         if (mutex_lock_interruptible(&mbsend_lock) < 0)
175                 return -1;
176
177         if (arg) {      /* we have extra argument */
178                 int i;
179
180                 /*
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)
185                  *
186                  * Therefore, we can call this function here safely.
187                  */
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);
192                         ret = -EBUSY;
193                         goto out;
194                 }
195                 for (i = 0; i < arg->argc; i++) {
196                         ipbuf_sys_ad->d[i] = arg->argv[i];
197                 }
198                 ipbuf_sys_ad->s = arg->tid;
199                 dsp_mem_disable(ipbuf_sys_ad);
200         }
201
202         if (mbseq)
203                 mbseq->ad_arm++;
204
205         mblog_add(mb, DIR_A2D);
206         mblog_printcmd(mb, DIR_A2D);
207
208         ret = mbx_send(mbx_dsp, *(mbx_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(mbx_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         mblog_printcmd(mb, DIR_D2A);
253
254         /* call handler for the command */
255         if (cmdinfo[mb->cmd_h]->handler)
256                 cmdinfo[mb->cmd_h]->handler(mb);
257         else
258                 printk(KERN_ERR "mbx: %s is not allowed from DSP.\n",
259                        cmd_name(*mb));
260 }
261
262 static int mbsync_hold_mem_active;
263
264 void dsp_mbx_start(void)
265 {
266         mbx_init_seq(mbx_dsp);
267         mbseq_expect_tmp = 0;
268 }
269
270 void dsp_mbx_stop(void)
271 {
272         mbseq = NULL;
273         mbseq_expect = &mbseq_expect_tmp;
274 }
275
276 int dsp_mbx_config(void *p)
277 {
278         unsigned long flags;
279
280         if (dsp_address_validate(p, sizeof(struct sync_seq), "mbseq") < 0)
281                 return -1;
282         if (dsp_mem_type(p, sizeof(struct sync_seq)) != MEM_TYPE_EXTERN) {
283                 printk(KERN_WARNING
284                        "omapdsp: mbseq is placed in DSP internal memory.\n"
285                        "         It will prevent DSP from idling.\n");
286                 mbsync_hold_mem_active = 1;
287                 /*
288                  * dsp_mem_enable() never fails because
289                  * it has been already enabled in dspcfg process and
290                  * this will just increment the usecount.
291                  */
292                 dsp_mem_enable((void *)daram_base);
293         }
294
295         local_irq_save(flags);
296         mbseq = p;
297         mbseq->da_arm = mbseq_expect_tmp;
298         mbseq_expect = &mbseq->da_arm;
299         local_irq_restore(flags);
300
301         return 0;
302 }
303
304 static int __init dsp_mbx_init(void)
305 {
306         int i;
307         int ret;
308
309         for (i = 0; i < MBX_CMD_MAX; i++) {
310                 if (cmdinfo[i] != NULL) {
311                         ret = register_mbx_receiver(mbx_dsp, i, mbcmd_receiver);
312                         if (ret)
313                                 goto fail;
314                 }
315         }
316
317         return 0;
318
319 fail:
320         for (i--; i; i--)
321                 unregister_mbx_receiver(mbx_dsp, i, mbcmd_receiver);
322
323         return ret;
324 }
325
326 static void dsp_mbx_exit(void)
327 {
328         int i;
329
330         for (i = 0; i < MBX_CMD_MAX; i++) {
331                 if (cmdinfo[i] != NULL)
332                         unregister_mbx_receiver(mbx_dsp, i, mbcmd_receiver);
333         }
334
335         if (mbsync_hold_mem_active) {
336                 dsp_mem_disable((void *)daram_base);
337                 mbsync_hold_mem_active = 0;
338         }
339 }
340
341 /*
342  * kernel function dispatcher
343  */
344 extern void mbx_fbctl_upd(void);
345 extern void mbx_fbctl_disable(struct mbcmd *mb);
346
347 static void mbx_kfunc_fbctl(struct mbcmd *mb)
348 {
349         switch (mb->data) {
350         case FBCTL_UPD:
351                 mbx_fbctl_upd();
352                 break;
353         case FBCTL_DISABLE:
354                 mbx_fbctl_disable(mb);
355                 break;
356         default:
357                 printk(KERN_ERR
358                        "mbx: Unknown FBCTL from DSP: 0x%04x\n", mb->data);
359         }
360 }
361
362 static void mbx_kfunc_audio_pwr(unsigned short data)
363 {
364         switch (data) {
365         case AUDIO_PWR_UP:
366                 omap_dsp_audio_pwr_up_request(0);
367                 /* send back ack */
368                 mbcompose_send(KFUNC, KFUNC_AUDIO_PWR, AUDIO_PWR_UP);
369                 break;
370         case AUDIO_PWR_DOWN1:
371                 omap_dsp_audio_pwr_down_request(1);
372                 break;
373         case AUDIO_PWR_DOWN2:
374                 omap_dsp_audio_pwr_down_request(2);
375                 break;
376         default:
377                 printk(KERN_ERR
378                        "mailbox: Unknown AUDIO_PWR from DSP: 0x%04x\n", data);
379         }
380 }
381
382 static void mbx_kfunc(struct mbcmd *mb)
383 {
384         switch (mb->cmd_l) {
385         case KFUNC_FBCTL:
386                 mbx_kfunc_fbctl(mb);
387                 break;
388         case KFUNC_AUDIO_PWR:
389                 mbx_kfunc_audio_pwr(mb->data);
390                 break;
391         default:
392                 printk(KERN_ERR
393                        "mbx: Unknown KFUNC from DSP: 0x%02x\n", mb->cmd_l);
394         }
395 }
396
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);
407
408 /*
409  * device functions
410  */
411 static void dsp_dev_release(struct device *dev)
412 {
413 }
414
415 /*
416  * driver functions
417  */
418 static int __init dsp_drv_probe(struct platform_device *pdev)
419 {
420         int ret;
421
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);
426         __dsp_per_enable();
427 #endif
428
429         if ((ret = dsp_ctl_core_init()) < 0)
430                 goto fail1;
431         if ((ret = dsp_mem_init()) < 0)
432                 goto fail2;
433         dsp_ctl_init();
434         mblog_init();
435         if ((ret = dsp_taskmod_init()) < 0)
436                 goto fail3;
437         if ((ret = dsp_mbx_init()) < 0)
438                 goto fail4;
439
440         return 0;
441
442 fail4:
443         dsp_taskmod_exit();
444 fail3:
445         mblog_exit();
446         dsp_ctl_exit();
447         dsp_mem_exit();
448 fail2:
449         dsp_ctl_core_exit();
450 fail1:
451 #ifdef CONFIG_ARCH_OMAP2
452         __dsp_per_disable();
453         clk_disable(dsp_ick_handle);
454         clk_disable(dsp_fck_handle);
455 #endif
456         return ret;
457 }
458
459 static int dsp_drv_remove(struct platform_device *pdev)
460 {
461         dsp_cpustat_request(CPUSTAT_RESET);
462
463         dsp_cfgstat_request(CFGSTAT_CLEAN);
464         dsp_mbx_exit();
465         dsp_taskmod_exit();
466         mblog_exit();
467         dsp_ctl_exit();
468         dsp_mem_exit();
469
470         dsp_ctl_core_exit();
471
472 #ifdef CONFIG_ARCH_OMAP2
473         __dsp_per_disable();
474         clk_disable(dsp_ick_handle);
475         clk_disable(dsp_fck_handle);
476 #endif
477         return 0;
478 }
479
480 #ifdef CONFIG_PM
481 static int dsp_drv_suspend(struct platform_device *pdev, pm_message_t state)
482 {
483         dsp_cfgstat_request(CFGSTAT_SUSPEND);
484
485         return 0;
486 }
487
488 static int dsp_drv_resume(struct platform_device *pdev)
489 {
490         dsp_cfgstat_request(CFGSTAT_RESUME);
491
492         return 0;
493 }
494 #else
495 #define dsp_drv_suspend         NULL
496 #define dsp_drv_resume          NULL
497 #endif /* CONFIG_PM */
498
499 static struct resource dsp_resources[] = {
500         {
501                 .start = INT_DSP_MMU,
502                 .flags = IORESOURCE_IRQ,
503         },
504 };
505
506 struct platform_device dsp_device = {
507         .name           = "dsp",
508         .id             = -1,
509         .dev = {
510                 .release        = dsp_dev_release,
511         },
512         .num_resources  = ARRAY_SIZE(&dsp_resources),
513         .resource       = dsp_resources,
514 };
515
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,
521         .driver         = {
522                 .name   = "dsp",
523         },
524 };
525
526 static int __init omap_dsp_mod_init(void)
527 {
528         int ret;
529
530         mbx_dsp = mbx_get("DSP");
531         if (IS_ERR(mbx_dsp)) {
532                 printk(KERN_ERR "failed to get mailbox handler for DSP.\n");
533                 goto fail1;
534         }
535
536         ret = platform_device_register(&dsp_device);
537         if (ret) {
538                 printk(KERN_ERR "failed to register the DSP device: %d\n", ret);
539                 goto fail1;
540         }
541
542         ret = platform_driver_register(&dsp_driver);
543         if (ret) {
544                 printk(KERN_ERR "failed to register the DSP driver: %d\n", ret);
545                 goto fail2;
546         }
547
548         return 0;
549
550 fail2:
551         platform_device_unregister(&dsp_device);
552 fail1:
553         return -ENODEV;
554 }
555
556 static void __exit omap_dsp_mod_exit(void)
557 {
558         platform_driver_unregister(&dsp_driver);
559         platform_device_unregister(&dsp_device);
560 }
561
562 module_init(omap_dsp_mod_init);
563 module_exit(omap_dsp_mod_exit);