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