]> pilppa.com Git - linux-2.6-omap-h63xx.git/commitdiff
ARM: OMAP: Add DSP gateway
authorToshihiro Kobayashi <toshihiro.kobayashi@nokia.com>
Mon, 9 May 2005 20:16:48 +0000 (13:16 -0700)
committerTony Lindgren <tony@atomide.com>
Mon, 9 May 2005 20:16:48 +0000 (13:16 -0700)
Adds support for integrated DSP on OMAP processors.

Signed-off-by: Toshihiro Kobayashi <toshihiro.kobayashi@nokia.com>
Signed-off-by: Tony Lindgren <tony@atomide.com>
24 files changed:
arch/arm/mach-omap/Makefile
arch/arm/mach-omap/dsp/Kconfig [new file with mode: 0644]
arch/arm/mach-omap/dsp/Makefile [new file with mode: 0644]
arch/arm/mach-omap/dsp/dsp.h [new file with mode: 0644]
arch/arm/mach-omap/dsp/dsp_common.c [new file with mode: 0644]
arch/arm/mach-omap/dsp/dsp_common.h [new file with mode: 0644]
arch/arm/mach-omap/dsp/dsp_core.c [new file with mode: 0644]
arch/arm/mach-omap/dsp/dsp_ctl.c [new file with mode: 0644]
arch/arm/mach-omap/dsp/dsp_ctl_core.c [new file with mode: 0644]
arch/arm/mach-omap/dsp/dsp_mem.c [new file with mode: 0644]
arch/arm/mach-omap/dsp/error.c [new file with mode: 0644]
arch/arm/mach-omap/dsp/fifo.h [new file with mode: 0644]
arch/arm/mach-omap/dsp/hardware_dsp.h [new file with mode: 0644]
arch/arm/mach-omap/dsp/ipbuf.c [new file with mode: 0644]
arch/arm/mach-omap/dsp/ipbuf.h [new file with mode: 0644]
arch/arm/mach-omap/dsp/mblog.c [new file with mode: 0644]
arch/arm/mach-omap/dsp/proclist.h [new file with mode: 0644]
arch/arm/mach-omap/dsp/task.c [new file with mode: 0644]
arch/arm/mach-omap/dsp/taskwatch.c [new file with mode: 0644]
arch/arm/mach-omap/dsp/uaccess_dsp.S [new file with mode: 0644]
arch/arm/mach-omap/dsp/uaccess_dsp.h [new file with mode: 0644]
arch/arm/mach-omap/omap1/Kconfig
include/asm-arm/arch-omap/dsp.h [new file with mode: 0644]
include/asm-arm/arch-omap/dsp_common.h [new file with mode: 0644]

index bcbbfb1fe47b61878af0e992184a9929805a91d7..503345f2ee3a48a5478f00804f0f6247c084f65b 100644 (file)
@@ -21,3 +21,6 @@ obj-$(CONFIG_CPU_FREQ) += cpu-omap.o
 obj-$(CONFIG_OMAP_DM_TIMER) += dmtimer.o
 obj-$(CONFIG_OMAP_BOOT_REASON) += bootreason.o
 obj-$(CONFIG_OMAP_GPIO_SWITCH) += gpio-switch.o
+
+# DSP subsystem
+obj-y += dsp/
diff --git a/arch/arm/mach-omap/dsp/Kconfig b/arch/arm/mach-omap/dsp/Kconfig
new file mode 100644 (file)
index 0000000..fc43908
--- /dev/null
@@ -0,0 +1,29 @@
+
+config OMAP_DSP
+       tristate "OMAP DSP driver (DSP Gateway)"
+       depends on ARCH_OMAP1510 || ARCH_OMAP16XX
+       help
+         This enables OMAP DSP driver, DSP Gateway.
+
+config OMAP_DSP_MBCMD_VERBOSE
+       bool "Mailbox Command Verbose LOG"
+       depends on OMAP_DSP
+       help
+          This enables kernel log output in the Mailbox command exchanges
+         in the DSP Gateway driver.
+
+config OMAP_DSP_TASK_MULTIOPEN
+       bool "DSP Task Multiopen Capability"
+       depends on OMAP_DSP
+       help
+          This enables DSP tasks to be opened by multiple times at a time.
+         Otherwise, they can be opened only once at a time.
+
+config OMAP_DSP_FBEXPORT
+       bool "Framebuffer export to DSP"
+       depends on OMAP_DSP
+       help
+          This enables to map the frame buffer to DSP.
+         By doing this, DSP can access the frame buffer directly without
+         bothering ARM.
+
diff --git a/arch/arm/mach-omap/dsp/Makefile b/arch/arm/mach-omap/dsp/Makefile
new file mode 100644 (file)
index 0000000..c7d86f3
--- /dev/null
@@ -0,0 +1,15 @@
+#
+# Makefile for the OMAP DSP driver.
+#
+
+# The target object and module list name.
+
+obj-y := dsp_common.o
+
+obj-$(CONFIG_OMAP_DSP) += dsp.o
+
+# Declare multi-part drivers
+
+dsp-objs       := dsp_core.o ipbuf.o mblog.o task.o \
+                  dsp_ctl_core.o dsp_ctl.o taskwatch.o error.o dsp_mem.o \
+                  uaccess_dsp.o
diff --git a/arch/arm/mach-omap/dsp/dsp.h b/arch/arm/mach-omap/dsp/dsp.h
new file mode 100644 (file)
index 0000000..6282a28
--- /dev/null
@@ -0,0 +1,184 @@
+/*
+ * linux/arch/arm/mach-omap/dsp/dsp.h
+ *
+ * Header for OMAP DSP driver
+ *
+ * Copyright (C) 2002-2005 Nokia Corporation
+ *
+ * Written by Toshihiro Kobayashi <toshihiro.kobayashi@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * 2005/01/21:  DSP Gateway version 3.2
+ */
+
+#include "hardware_dsp.h"
+#include "dsp_common.h"
+
+#define OLD_BINARY_SUPPORT     y
+
+#ifdef OLD_BINARY_SUPPORT
+#define MBREV_3_0      0x0017
+#endif
+
+#define DSP_INIT_PAGE  0xfff000
+/* idle program will be placed at IDLEPG_BASE. */
+#define IDLEPG_BASE    0xfffe00
+#define IDLEPG_SIZE    0x100
+
+/*
+ * INT_D2A_MB value definition
+ *   INT_DSP_MAILBOX1: use Mailbox 1 (INT 10) for DSP->ARM mailbox
+ *   INT_DSP_MAILBOX2: use Mailbox 2 (INT 11) for DSP->ARM mailbox
+ */
+#define INT_D2A_MB1    INT_DSP_MAILBOX1
+
+/* keep 2 entries for OMAP_DSP_TID_FREE and OMAP_DSP_TID_ANON */
+#define TASKDEV_MAX    254
+
+#define MKLONG(uw,lw)  (((unsigned long)(uw)) << 16 | (lw))
+#define MBCMD(nm)      OMAP_DSP_MBCMD_##nm
+
+/* struct mbcmd and struct mbcmd_hw must be compatible */
+struct mbcmd {
+       unsigned short cmd_l:8;
+       unsigned short cmd_h:7;
+       unsigned short seq:1;
+       unsigned short data;
+};
+
+struct mbcmd_hw {
+       unsigned short cmd;
+       unsigned short data;
+};
+
+#define mbcmd_set(mb, h, l, d) \
+       do { \
+               (mb).cmd_h = (h); \
+               (mb).cmd_l = (l); \
+               (mb).data  = (d); \
+       } while(0)
+
+struct mb_exarg {
+       unsigned char tid;
+       int argc;
+       unsigned short *argv;
+};
+
+extern void dsp_mb_start(void);
+extern void dsp_mb_stop(void);
+extern void dsp_mb_config(void *sync_seq_adr);
+extern int sync_with_dsp(unsigned short *syncwd, unsigned short tid,
+                        int try_cnt);
+extern int __mbsend(struct mbcmd *mb);
+extern int __dsp_mbsend(struct mbcmd *mb, struct mb_exarg *arg,
+                       int recovery_flag);
+#define dsp_mbsend(mb)                 __dsp_mbsend(mb, NULL, 0)
+#define dsp_mbsend_recovery(mb)                __dsp_mbsend(mb, NULL, 1)
+#define dsp_mbsend_exarg(mb, arg)      __dsp_mbsend(mb, arg, 0)
+extern int __dsp_mbsend_and_wait(struct mbcmd *mb, struct mb_exarg *arg,
+                                wait_queue_head_t *q);
+#define dsp_mbsend_and_wait(mb, q) \
+       __dsp_mbsend_and_wait(mb, NULL, q)
+#define dsp_mbsend_and_wait_exarg(mb, arg, q) \
+       __dsp_mbsend_and_wait(mb, arg, q)
+
+extern void ipbuf_start(void);
+extern void ipbuf_stop(void);
+extern int ipbuf_config(unsigned short ln, unsigned short lsz,
+                       unsigned long adr);
+extern unsigned short get_free_ipbuf(unsigned char tid);
+extern void unuse_ipbuf_nowait(unsigned short bid);
+extern void unuse_ipbuf(unsigned short bid);
+extern void release_ipbuf(unsigned short bid);
+extern void balance_ipbuf(void);
+
+#define release_ipbuf_pvt(ipbuf_pvt) \
+       do { \
+               (ipbuf_pvt)->s = OMAP_DSP_TID_FREE; \
+       } while(0)
+
+extern int dsp_is_ready(void);
+extern int dspuncfg(void);
+extern void dsp_runlevel(unsigned char level);
+extern int dsp_suspend(void);
+extern int dsp_resume(void);
+
+extern int dsp_task_config_all(unsigned char n);
+extern void dsp_task_unconfig_all(void);
+extern unsigned char dsp_task_count(void);
+extern int dsp_taskmod_busy(void);
+extern int dsp_mkdev(char *name);
+extern int dsp_rmdev(char *name);
+extern int dsp_tadd(unsigned char minor, unsigned long adr);
+extern int dsp_tdel(unsigned char minor);
+extern int dsp_tkill(unsigned char minor);
+extern long taskdev_state(unsigned char minor);
+
+extern int ipbuf_is_held(unsigned char tid, unsigned short bid);
+
+extern int dsp_mem_enable(void *adr);
+extern int dsp_mem_disable(void *adr);
+extern int __dsp_mem_enable(void *adr);
+extern int __dsp_mem_disable(void *adr);
+extern unsigned long dsp_virt_to_phys(void *vadr, size_t *len);
+extern void dsp_mem_start(void);
+
+extern void dsp_twch_start(void);
+extern void dsp_twch_stop(void);
+extern void dsp_twch_touch(void);
+
+extern void dsp_err_start(void);
+extern void dsp_err_stop(void);
+extern void dsp_err_mmu_set(unsigned long adr);
+extern void dsp_err_mmu_clear(void);
+extern int dsp_err_mmu_isset(void);
+extern void dsp_err_wdt_clear(void);
+extern int dsp_err_wdt_isset(void);
+
+enum cmd_l_type {
+       CMD_L_TYPE_NULL,
+       CMD_L_TYPE_TID,
+       CMD_L_TYPE_SUBCMD,
+};
+
+struct cmdinfo {
+       char *name;
+       enum cmd_l_type cmd_l_type;
+       void (*handler)(struct mbcmd *mb);
+};
+
+extern const struct cmdinfo *cmdinfo[];
+
+#define cmd_name(mb)   (cmdinfo[(mb).cmd_h]->name)
+extern char *subcmd_name(struct mbcmd *mb);
+
+enum mblog_dir {
+       MBLOG_DIR_AD,
+       MBLOG_DIR_DA,
+};
+
+extern void mblog_add(struct mbcmd *mb, enum mblog_dir dir);
+#ifdef CONFIG_OMAP_DSP_MBCMD_VERBOSE
+extern void mblog_printcmd(struct mbcmd *mb, enum mblog_dir dir);
+#else /* CONFIG_OMAP_DSP_MBCMD_VERBOSE */
+#define mblog_printcmd(mb, dir)        do {} while(0)
+#endif /* CONFIG_OMAP_DSP_MBCMD_VERBOSE */
+
+#ifdef CONFIG_PROC_FS
+extern struct proc_dir_entry *procdir_dsp;
+#endif /* CONFIG_PROC_FS */
+
+extern struct platform_device dsp_device;
diff --git a/arch/arm/mach-omap/dsp/dsp_common.c b/arch/arm/mach-omap/dsp/dsp_common.c
new file mode 100644 (file)
index 0000000..175cb98
--- /dev/null
@@ -0,0 +1,233 @@
+/*
+ * linux/arch/arm/mach-omap/dsp/dsp_common.c
+ *
+ * OMAP DSP driver static part
+ *
+ * Copyright (C) 2002-2005 Nokia Corporation
+ *
+ * Written by Toshihiro Kobayashi <toshihiro.kobayashi@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * 2004/11/19:  DSP Gateway version 3.2
+ */
+
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/mm.h>
+#include <asm/tlbflush.h>
+#include <asm/irq.h>
+#include <asm/arch/dsp.h>
+#include <asm/arch/tc.h>
+#include <asm/hardware/clock.h>
+#include "dsp_common.h"
+
+struct clk *dsp_ck_handle;
+struct clk *api_ck_handle;
+unsigned long dspmem_base, dspmem_size;
+int dsp_runstat = RUNSTAT_RESET;
+unsigned short dsp_icrmask = DSPREG_ICR_EMIF_IDLE_DOMAIN |
+                            DSPREG_ICR_DPLL_IDLE_DOMAIN |
+                            DSPREG_ICR_PER_IDLE_DOMAIN |
+                            DSPREG_ICR_CACHE_IDLE_DOMAIN |
+                            DSPREG_ICR_DMA_IDLE_DOMAIN |
+                            DSPREG_ICR_CPU_IDLE_DOMAIN;
+
+int dsp_set_rstvect(unsigned long adr)
+{
+       unsigned long *dst_adr;
+
+       if (adr >= DSPSPACE_SIZE)
+               return -EINVAL;
+
+       dst_adr = dspbyte_to_virt(DSP_BOOT_ADR_DIRECT);
+       /* word swap */
+       *dst_adr = ((adr & 0xffff) << 16) | (adr >> 16);
+       /* fill 8 bytes! */
+       *(dst_adr+1) = 0;
+       /* direct boot */
+       omap_writew(MPUI_DSP_BOOT_CONFIG_DIRECT, MPUI_DSP_BOOT_CONFIG);
+
+       return 0;
+}
+
+static void simple_load_code(unsigned char *src_c, unsigned short *dst, int len)
+{
+       int i;
+       unsigned short *src = (unsigned short *)src_c;
+       int len_w;
+
+       /* len must be multiple of 2. */
+       if (len & 1)
+               BUG();
+
+       len_w = len / 2;
+       for (i = 0; i < len_w; i++) {
+               /* byte swap copy */
+               *dst = ((*src & 0x00ff) << 8) |
+                      ((*src & 0xff00) >> 8);
+               src++;
+               dst++;
+       }
+}
+
+/* program size must be multiple of 2 */
+#define IDLE_TEXT_SIZE 28
+#define IDLE_TEXT(icr) { \
+       /* disable WDT */ \
+       0x76, 0x34, 0x04, 0xb8,         /* 0x763404b8: mov AR3 0x3404 */ \
+       0xfb, 0x61, 0x00, 0xf5,         /* 0xfb6100f5: mov *AR3 0x00f5 */ \
+       0x9a,                           /* 0x9a:       port */ \
+       0xfb, 0x61, 0x00, 0xa0,         /* 0xfb6100a0: mov *AR3 0x00a0 */ \
+       0x9a,                           /* 0x9a:       port */ \
+       /* set ICR = icr */ \
+       0x3c, 0x1b,                     /* 0x3c1b:     mov AR3 0x1 */ \
+       0xe6, 0x61, (icr),              /* 0xe661**:   mov *AR3, icr */ \
+       0x9a,                           /* 0x9a:       port */ \
+       /* idle and loop forever */ \
+       0x7a, 0x00, 0x00, 0x0c,         /* 0x7a00000c: idle */ \
+       0x4a, 0x7a,                     /* 0x4a7a:     b -6 (infinite loop) */ \
+       0x20, 0x20                      /* 0x20: nop */ \
+}
+
+/*
+ * idle_boot base:
+ * Initialized with DSP_BOOT_ADR_MPUI (=0x010000).
+ * This value is used before DSP Gateway driver is initialized.
+ * DSP Gateway driver will overwrite this value with other value,
+ * to avoid confliction with the user program.
+ */
+static unsigned long idle_boot_base = DSP_BOOT_ADR_MPUI;
+
+void dsp_idle(void)
+{
+       unsigned char icr;
+
+       disable_irq(INT_DSP_MMU);
+       preempt_disable();
+       __dsp_reset();
+       clk_use(api_ck_handle);
+
+       /*
+        * icr settings:
+        * DMA should not sleep for DARAM/SARAM access
+        * DPLL should not sleep for DMA.
+        */
+       icr = dsp_icrmask &
+             ~(DSPREG_ICR_DMA_IDLE_DOMAIN | DSPREG_ICR_DPLL_IDLE_DOMAIN) &
+             0xff;
+       {
+               unsigned char idle_text[IDLE_TEXT_SIZE] = IDLE_TEXT(icr);
+               simple_load_code(idle_text, dspbyte_to_virt(idle_boot_base),
+                                IDLE_TEXT_SIZE);
+       }
+       if (idle_boot_base == DSP_BOOT_ADR_MPUI)
+               omap_writew(MPUI_DSP_BOOT_CONFIG_MPUI, MPUI_DSP_BOOT_CONFIG);
+       else
+               dsp_set_rstvect(idle_boot_base);
+       clk_unuse(api_ck_handle);
+       udelay(10);     /* to make things stable */
+       __dsp_run();
+       dsp_runstat = RUNSTAT_IDLE;
+       preempt_enable();
+       enable_irq(INT_DSP_MMU);
+}
+
+void dsp_set_idle_boot_base(unsigned long adr, size_t size)
+{
+       if (adr == idle_boot_base)
+               return;
+       idle_boot_base = adr;
+       if (size < IDLE_TEXT_SIZE) {
+               printk(KERN_ERR
+                      "omapdsp: size for idle program is not enough!\n");
+               BUG();
+       }
+       if (dsp_runstat == RUNSTAT_IDLE)
+               dsp_idle();
+}
+
+static int init_done;
+
+static int __init omap_dsp_init(void)
+{
+       dspmem_size = 0;
+#ifdef CONFIG_ARCH_OMAP1510
+       if (cpu_is_omap1510()) {
+               dspmem_base = OMAP1510_DSP_BASE;
+               dspmem_size = OMAP1510_DSP_SIZE;
+       }
+#endif
+#ifdef CONFIG_ARCH_OMAP16XX
+       if (cpu_is_omap16xx()) {
+               dspmem_base = OMAP16XX_DSP_BASE;
+               dspmem_size = OMAP16XX_DSP_SIZE;
+       }
+#endif
+       if (dspmem_size == 0) {
+               printk(KERN_ERR "omapdsp: unsupported omap architecture.\n");
+               return -ENODEV;
+       }
+
+       dsp_ck_handle = clk_get(0, "dsp_ck");
+       if (IS_ERR(dsp_ck_handle)) {
+               printk(KERN_ERR "omapdsp: could not acquire dsp_ck handle.\n");
+               return PTR_ERR(dsp_ck_handle);
+       }
+
+       api_ck_handle = clk_get(0, "api_ck");
+       if (IS_ERR(api_ck_handle)) {
+               printk(KERN_ERR "omapdsp: could not acquire api_ck handle.\n");
+               return PTR_ERR(api_ck_handle);
+       }
+
+       __dsp_enable();
+       mpui_byteswap_off();
+       mpui_wordswap_on();
+       tc_wordswap();
+
+       init_done = 1;
+       return 0;
+}
+
+void omap_dsp_request_idle(void)
+{
+       if (dsp_runstat == RUNSTAT_RESET) {
+               if (!init_done)
+                       omap_dsp_init();
+               dsp_idle();
+       }
+}
+
+arch_initcall(omap_dsp_init);
+
+EXPORT_SYMBOL(omap_dsp_request_idle);
+
+#ifdef CONFIG_OMAP_DSP_MODULE
+EXPORT_SYMBOL(dsp_ck_handle);
+EXPORT_SYMBOL(api_ck_handle);
+EXPORT_SYMBOL(dspmem_base);
+EXPORT_SYMBOL(dspmem_size);
+EXPORT_SYMBOL(dsp_runstat);
+EXPORT_SYMBOL(dsp_icrmask);
+EXPORT_SYMBOL(dsp_set_rstvect);
+EXPORT_SYMBOL(dsp_idle);
+EXPORT_SYMBOL(dsp_set_idle_boot_base);
+
+EXPORT_SYMBOL(__cpu_flush_kern_tlb_range);
+#endif
diff --git a/arch/arm/mach-omap/dsp/dsp_common.h b/arch/arm/mach-omap/dsp/dsp_common.h
new file mode 100644 (file)
index 0000000..a4b2610
--- /dev/null
@@ -0,0 +1,117 @@
+/*
+ * linux/arch/arm/mach-omap/dsp/dsp_common.h
+ *
+ * Header for OMAP DSP driver static part
+ *
+ * Copyright (C) 2002-2005 Nokia Corporation
+ *
+ * Written by Toshihiro Kobayashi <toshihiro.kobayashi@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * 2004/11/16:  DSP Gateway version 3.2
+ */
+
+#include "hardware_dsp.h"
+
+#define DSPSPACE_SIZE  0x1000000
+
+#define omap_set_bit_regw(b,r) \
+       do { omap_writew(omap_readw(r) | (b), (r)); } while(0)
+#define omap_clr_bit_regw(b,r) \
+       do { omap_writew(omap_readw(r) & ~(b), (r)); } while(0)
+#define omap_set_bit_regl(b,r) \
+       do { omap_writel(omap_readl(r) | (b), (r)); } while(0)
+#define omap_clr_bit_regl(b,r) \
+       do { omap_writel(omap_readl(r) & ~(b), (r)); } while(0)
+
+#define dspword_to_virt(dw)    ((void *)(dspmem_base + ((dw) << 1)))
+#define dspbyte_to_virt(db)    ((void *)(dspmem_base + (db)))
+#define virt_to_dspword(va)    (((unsigned long)(va) - dspmem_base) >> 1)
+#define virt_to_dspbyte(va)    ((unsigned long)(va) - dspmem_base)
+#define is_dsp_internal_mem(va) \
+       (((unsigned long)(va) >= dspmem_base) &&  \
+        ((unsigned long)(va) < dspmem_base + dspmem_size))
+#define is_dspbyte_internal_mem(db)    ((db) < dspmem_size)
+#define is_dspword_internal_mem(dw)    (((dw) << 1) < dspmem_size)
+
+/*
+ * MPUI byteswap/wordswap on/off
+ *   default setting: wordswap = all, byteswap = APIMEM only
+ */
+#define mpui_wordswap_on() \
+       { \
+               omap_writel( \
+                       (omap_readl(MPUI_CTRL) & ~MPUI_CTRL_WORDSWAP_MASK) | \
+                       MPUI_CTRL_WORDSWAP_ALL, MPUI_CTRL); \
+       } while(0)
+
+#define mpui_wordswap_off() \
+       { \
+               omap_writel( \
+                       (omap_readl(MPUI_CTRL) & ~MPUI_CTRL_WORDSWAP_MASK) | \
+                       MPUI_CTRL_WORDSWAP_NONE, MPUI_CTRL); \
+       } while(0)
+
+#define mpui_byteswap_on() \
+       { \
+               omap_writel( \
+                       (omap_readl(MPUI_CTRL) & ~MPUI_CTRL_BYTESWAP_MASK) | \
+                       MPUI_CTRL_BYTESWAP_API, MPUI_CTRL); \
+       } while(0)
+
+#define mpui_byteswap_off() \
+       { \
+               omap_writel( \
+                       (omap_readl(MPUI_CTRL) & ~MPUI_CTRL_BYTESWAP_MASK) | \
+                       MPUI_CTRL_BYTESWAP_NONE, MPUI_CTRL); \
+       } while(0)
+
+/*
+ * TC wordswap on / off
+ */
+#define tc_wordswap() \
+       { \
+               omap_writel(TC_ENDIANISM_SWAP_WORD | TC_ENDIANISM_EN, \
+                           TC_ENDIANISM); \
+       } while(0)
+
+#define tc_noswap() \
+       {  \
+               omap_writel(omap_readl(TC_ENDIANISM) & ~TC_ENDIANISM_EN, \
+                           TC_ENDIANISM); \
+       } while(0)
+
+/*
+ * enable priority registers, EMIF, MPUI control logic
+ */
+#define __dsp_enable() omap_set_bit_regw(ARM_RSTCT1_DSP_RST, ARM_RSTCT1)
+#define __dsp_disable()        omap_clr_bit_regw(ARM_RSTCT1_DSP_RST, ARM_RSTCT1)
+#define __dsp_run()    omap_set_bit_regw(ARM_RSTCT1_DSP_EN, ARM_RSTCT1)
+#define __dsp_reset()  omap_clr_bit_regw(ARM_RSTCT1_DSP_EN, ARM_RSTCT1)
+
+#define        RUNSTAT_RESET   0
+#define        RUNSTAT_IDLE    1
+#define        RUNSTAT_RUN     2
+
+extern struct clk *dsp_ck_handle;
+extern struct clk *api_ck_handle;
+extern unsigned long dspmem_base, dspmem_size;
+extern int dsp_runstat;
+extern unsigned short dsp_icrmask;
+
+int dsp_set_rstvect(unsigned long adr);
+void dsp_idle(void);
+void dsp_set_idle_boot_base(unsigned long adr, size_t size);
diff --git a/arch/arm/mach-omap/dsp/dsp_core.c b/arch/arm/mach-omap/dsp/dsp_core.c
new file mode 100644 (file)
index 0000000..e86d359
--- /dev/null
@@ -0,0 +1,741 @@
+/*
+ * linux/arch/arm/mach-omap/dsp/dsp_core.c
+ *
+ * OMAP DSP driver
+ *
+ * Copyright (C) 2002-2005 Nokia Corporation
+ *
+ * Written by Toshihiro Kobayashi <toshihiro.kobayashi@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * 2005/02/15:  DSP Gateway version 3.2
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/devfs_fs_kernel.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/proc_fs.h>
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <asm/signal.h>
+#include <asm/delay.h>
+#include <asm/irq.h>
+#include <asm/arch/dsp.h>
+#include "hardware_dsp.h"
+#include "dsp.h"
+#include "ipbuf.h"
+
+
+MODULE_AUTHOR("Toshihiro Kobayashi <toshihiro.kobayashi@nokia.com>");
+MODULE_DESCRIPTION("OMAP DSP driver module");
+MODULE_LICENSE("GPL");
+
+enum mbseq_check_level {
+       MBSEQ_CHECK_NONE,       /* no check */
+       MBSEQ_CHECK_VERBOSE,    /* discard the illegal command and
+                                  error report */
+       MBSEQ_CHECK_SILENT,     /* discard the illegal command */
+};
+
+static enum mbseq_check_level mbseq_check_level = MBSEQ_CHECK_VERBOSE;
+static unsigned short mbseq_send;
+static unsigned short mbseq_expect;
+
+struct sync_seq {
+       unsigned short da_dsp;
+       unsigned short da_arm;
+       unsigned short ad_dsp;
+       unsigned short ad_arm;
+};
+
+static struct sync_seq *sync_seq;
+
+/*
+ * mailbox commands
+ */
+extern void mbx1_wdsnd(struct mbcmd *mb);
+extern void mbx1_wdreq(struct mbcmd *mb);
+extern void mbx1_bksnd(struct mbcmd *mb);
+extern void mbx1_bkreq(struct mbcmd *mb);
+extern void mbx1_bkyld(struct mbcmd *mb);
+extern void mbx1_bksndp(struct mbcmd *mb);
+extern void mbx1_bkreqp(struct mbcmd *mb);
+extern void mbx1_tctl(struct mbcmd *mb);
+extern void mbx1_wdt(struct mbcmd *mb);
+extern void mbx1_suspend(struct mbcmd *mb);
+static void mbx1_kfunc(struct mbcmd *mb);
+extern void mbx1_tcfg(struct mbcmd *mb);
+extern void mbx1_tadd(struct mbcmd *mb);
+extern void mbx1_tdel(struct mbcmd *mb);
+extern void mbx1_dspcfg(struct mbcmd *mb);
+extern void mbx1_regrw(struct mbcmd *mb);
+extern void mbx1_getvar(struct mbcmd *mb);
+extern void mbx1_err(struct mbcmd *mb);
+extern void mbx1_dbg(struct mbcmd *mb);
+
+static const struct cmdinfo
+       cif_null     = { "Unknown",  CMD_L_TYPE_NULL,   NULL         },
+       cif_wdsnd    = { "WDSND",    CMD_L_TYPE_TID,    mbx1_wdsnd   },
+       cif_wdreq    = { "WDREQ",    CMD_L_TYPE_TID,    mbx1_wdreq   },
+       cif_bksnd    = { "BKSND",    CMD_L_TYPE_TID,    mbx1_bksnd   },
+       cif_bkreq    = { "BKREQ",    CMD_L_TYPE_TID,    mbx1_bkreq   },
+       cif_bkyld    = { "BKYLD",    CMD_L_TYPE_NULL,   mbx1_bkyld   },
+       cif_bksndp   = { "BKSNDP",   CMD_L_TYPE_TID,    mbx1_bksndp  },
+       cif_bkreqp   = { "BKREQP",   CMD_L_TYPE_TID,    mbx1_bkreqp  },
+       cif_tctl     = { "TCTL",     CMD_L_TYPE_TID,    mbx1_tctl    },
+       cif_wdt      = { "WDT",      CMD_L_TYPE_NULL,   mbx1_wdt     },
+       cif_runlevel = { "RUNLEVEL", CMD_L_TYPE_SUBCMD, NULL         },
+       cif_pm       = { "PM",       CMD_L_TYPE_SUBCMD, NULL         },
+       cif_suspend  = { "SUSPEND",  CMD_L_TYPE_NULL,   mbx1_suspend },
+       cif_kfunc    = { "KFUNC",    CMD_L_TYPE_SUBCMD, mbx1_kfunc   },
+       cif_tcfg     = { "TCFG",     CMD_L_TYPE_TID,    mbx1_tcfg    },
+       cif_tadd     = { "TADD",     CMD_L_TYPE_TID,    mbx1_tadd    },
+       cif_tdel     = { "TDEL",     CMD_L_TYPE_TID,    mbx1_tdel    },
+       cif_tstop    = { "TSTOP",    CMD_L_TYPE_TID,    NULL         },
+       cif_dspcfg   = { "DSPCFG",   CMD_L_TYPE_SUBCMD, mbx1_dspcfg  },
+       cif_regrw    = { "REGRW",    CMD_L_TYPE_SUBCMD, mbx1_regrw   },
+       cif_getvar   = { "GETVAR",   CMD_L_TYPE_SUBCMD, mbx1_getvar  },
+       cif_setvar   = { "SETVAR",   CMD_L_TYPE_SUBCMD, NULL         },
+       cif_err      = { "ERR",      CMD_L_TYPE_SUBCMD, mbx1_err     },
+       cif_dbg      = { "DBG",      CMD_L_TYPE_NULL,   mbx1_dbg     };
+
+const struct cmdinfo *cmdinfo[128] = {
+/*00*/ &cif_null, &cif_null, &cif_null, &cif_null,
+       &cif_null, &cif_null, &cif_null, &cif_null,
+       &cif_null, &cif_null, &cif_null, &cif_null,
+       &cif_null, &cif_null, &cif_null, &cif_null,
+/*10*/ &cif_wdsnd, &cif_wdreq, &cif_null, &cif_null,
+       &cif_null, &cif_null, &cif_null, &cif_null,
+       &cif_null, &cif_null, &cif_null, &cif_null,
+       &cif_null, &cif_null, &cif_null, &cif_null,
+/*20*/ &cif_bksnd, &cif_bkreq, &cif_null, &cif_bkyld,
+       &cif_bksndp, &cif_bkreqp, &cif_null, &cif_null,
+       &cif_null, &cif_null, &cif_null, &cif_null,
+       &cif_null, &cif_null, &cif_null, &cif_null,
+/*30*/ &cif_tctl, &cif_null, &cif_null, &cif_null,
+       &cif_null, &cif_null, &cif_null, &cif_null,
+       &cif_null, &cif_null, &cif_null, &cif_null,
+       &cif_null, &cif_null, &cif_null, &cif_null,
+/*40*/ &cif_null, &cif_null, &cif_null, &cif_null,
+       &cif_null, &cif_null, &cif_null, &cif_null,
+       &cif_null, &cif_null, &cif_null, &cif_null,
+       &cif_null, &cif_null, &cif_null, &cif_null,
+/*50*/ &cif_wdt, &cif_runlevel, &cif_pm, &cif_suspend,
+       &cif_kfunc, &cif_null, &cif_null, &cif_null,
+       &cif_null, &cif_null, &cif_null, &cif_null,
+       &cif_null, &cif_null, &cif_null, &cif_null,
+/*60*/ &cif_tcfg, &cif_null, &cif_tadd, &cif_tdel,
+       &cif_null, &cif_tstop, &cif_null, &cif_null,
+       &cif_null, &cif_null, &cif_null, &cif_null,
+       &cif_null, &cif_null, &cif_null, &cif_null,
+/*70*/ &cif_dspcfg, &cif_null, &cif_regrw, &cif_null,
+       &cif_getvar, &cif_setvar, &cif_null, &cif_null,
+       &cif_err, &cif_dbg, &cif_null, &cif_null,
+       &cif_null, &cif_null, &cif_null, &cif_null
+};
+
+int sync_with_dsp(unsigned short *syncwd, unsigned short tid, int try_cnt)
+{
+       int try;
+
+       if (*(volatile unsigned short *)syncwd == tid)
+               return 0;
+
+       for (try = 0; try < try_cnt; try++) {
+               udelay(1);
+               if (*(volatile unsigned short *)syncwd == tid) {
+                       /* success! */
+                       printk(KERN_INFO
+                              "omapdsp: sync_with_dsp(): try = %d\n", try);
+                       return 0;
+               }
+       }
+
+       /* fail! */
+       return -1;
+}
+
+static __inline__ int mbsync_irq_save(unsigned long *flags, int try_cnt)
+{
+       int cnt;
+
+       local_irq_save(*flags);
+       if (omap_readw(MAILBOX_ARM2DSP1_Flag) == 0)
+               return 0;
+       /*
+        * mailbox is busy. wait for some usecs...
+        */
+       local_irq_restore(*flags);
+       for (cnt = 0; cnt < try_cnt; cnt++) {
+               udelay(1);
+               local_irq_save(*flags);
+               if (omap_readw(MAILBOX_ARM2DSP1_Flag) == 0)     /* success! */
+                       return 0;
+               local_irq_restore(*flags);
+       }
+
+       /* fail! */
+       return -1;
+}
+
+#ifdef CONFIG_OMAP_DSP_MBCMD_VERBOSE
+#define print_mb_busy_abort(mb) \
+       printk(KERN_DEBUG \
+              "mbx: mailbox is busy. %s is aborting.\n", cmd_name(*mb))
+#define print_mb_mmu_abort(mb) \
+       printk(KERN_DEBUG \
+              "mbx: mmu interrupt is set. %s is aborting.\n", cmd_name(*mb))
+#else /* CONFIG_OMAP_DSP_MBCMD_VERBOSE */
+#define print_mb_busy_abort(mb)        do {} while(0)
+#define print_mb_mmu_abort(mb) do {} while(0)
+#endif /* !CONFIG_OMAP_DSP_MBCMD_VERBOSE */
+
+int __mbsend(struct mbcmd *mb)
+{
+       struct mbcmd_hw *mb_hw = (struct mbcmd_hw *)mb;
+       unsigned long flags;
+
+       /*
+        * DSP mailbox interrupt latency must be less than 1ms.
+        */
+       if (mbsync_irq_save(&flags, 1000) < 0) {
+               print_mb_busy_abort(mb);
+               return -1;
+       }
+
+       mb->seq = mbseq_send & 1;
+       mbseq_send++;
+       if (sync_seq)
+               sync_seq->ad_arm = mbseq_send;
+       mblog_add(mb, MBLOG_DIR_AD);
+       mblog_printcmd(mb, MBLOG_DIR_AD);
+
+       omap_writew(mb_hw->data, MAILBOX_ARM2DSP1);
+       omap_writew(mb_hw->cmd, MAILBOX_ARM2DSP1b);
+
+       local_irq_restore(flags);
+       return 0;
+}
+
+/*
+ * __dsp_mbsend(): mailbox dispatcher
+ */
+int __dsp_mbsend(struct mbcmd *mb, struct mb_exarg *arg, int recovery_flag)
+{
+       static DECLARE_MUTEX(mbsend_sem);
+       int ret = 0;
+
+       /*
+        * while MMU fault is set,
+        * only recovery command can be executed
+        */
+       if (dsp_err_mmu_isset() && !recovery_flag) {
+               print_mb_mmu_abort(mb);
+               return -1;
+       }
+
+       if (down_interruptible(&mbsend_sem) < 0)
+               return -1;
+
+       if (arg) {      /* we have extra argument */
+               int i;
+
+               if (__dsp_mem_enable(ipbuf_sys_ad) < 0)
+                       goto out;
+               if (sync_with_dsp(&ipbuf_sys_ad->s, OMAP_DSP_TID_FREE, 10) < 0) {
+                       printk(KERN_ERR "omapdsp: ipbuf_sys_ad is busy.\n");
+                       ret = -EBUSY;
+                       goto out;
+               }
+               for (i = 0; i < arg->argc; i++) {
+                       ipbuf_sys_ad->d[i] = arg->argv[i];
+               }
+               ipbuf_sys_ad->s = arg->tid;
+               if (__dsp_mem_disable(ipbuf_sys_ad) < 0)
+                       goto out;
+       }
+
+       ret = __mbsend(mb);
+
+out:
+       up(&mbsend_sem);
+       return ret;
+}
+
+int __dsp_mbsend_and_wait(struct mbcmd *mb, struct mb_exarg *arg,
+                         wait_queue_head_t *q)
+{
+       long current_state;
+       DECLARE_WAITQUEUE(wait, current);
+
+       add_wait_queue(q, &wait);
+       current_state = current->state;
+       set_current_state(TASK_INTERRUPTIBLE);
+       if (dsp_mbsend_exarg(mb, arg) < 0) {
+               set_current_state(current_state);
+               remove_wait_queue(q, &wait);
+               return -1;
+       }
+       schedule();
+       set_current_state(current_state);
+       remove_wait_queue(q, &wait);
+
+       return 0;
+}
+
+void dsp_mb_start(void)
+{
+       mbseq_send = 0;
+       mbseq_expect = 0;
+}
+
+void dsp_mb_stop(void)
+{
+       sync_seq = NULL;
+}
+
+/*
+ * dsp_mb_config() is called from mbx1 workqueue
+ */
+void dsp_mb_config(void *sync_seq_adr)
+{
+       sync_seq = sync_seq_adr;
+       sync_seq->da_arm = mbseq_expect;
+}
+
+/*
+ * mbq: mailbox queue
+ */
+#define MBQ_DEPTH      16
+struct mbq {
+       struct mbcmd mb[MBQ_DEPTH];
+       int rp, wp, full;
+} mbq = {
+       .rp = 0,
+       .wp = 0,
+};
+
+#define mbq_inc(p)     do { if (++(p) == MBQ_DEPTH) (p) = 0; } while(0)
+
+/*
+ * workqueue for mbx1
+ */
+static void do_mbx1(void)
+{
+       int empty = 0;
+
+       disable_irq(INT_D2A_MB1);
+       if ((mbq.rp == mbq.wp) && !mbq.full)
+               empty = 1;
+       enable_irq(INT_D2A_MB1);
+
+       while (!empty) {
+               struct mbcmd *mb;
+
+               mb = &mbq.mb[mbq.rp];
+
+               mblog_add(mb, MBLOG_DIR_DA);
+               mblog_printcmd(mb, MBLOG_DIR_DA);
+
+               /*
+                * call handler for each command
+                */
+               if (cmdinfo[mb->cmd_h]->handler)
+                       cmdinfo[mb->cmd_h]->handler(mb);
+               else if (cmdinfo[mb->cmd_h] != &cif_null)
+                       printk(KERN_ERR "mbx: %s is not allowed from DSP.\n",
+                              cmd_name(*mb));
+               else
+                       printk(KERN_ERR
+                              "mbx: Unrecognized command: "
+                              "cmd=0x%04x, data=0x%04x\n",
+                              ((struct mbcmd_hw *)mb)->cmd & 0x7fff, mb->data);
+
+               disable_irq(INT_D2A_MB1);
+               mbq_inc(mbq.rp);
+               if (mbq.rp == mbq.wp)
+                       empty = 1;
+               /* if mbq has been full, now we have a room. */
+               if (mbq.full) {
+                       mbq.full = 0;
+                       enable_irq(INT_D2A_MB1);
+               }
+               enable_irq(INT_D2A_MB1);
+       }
+}
+
+static DECLARE_WORK(mbx1_work, (void (*)(void *))do_mbx1, NULL);
+
+/*
+ * kernel function dispatcher
+ */
+#ifdef CONFIG_FB_OMAP_EXTERNAL_LCDC
+extern void mbx1_fbctl_disable(void);
+
+static void mbx1_kfunc_fbctl(unsigned short data)
+{
+       switch (data) {
+       case OMAP_DSP_MBCMD_FBCTL_DISABLE:
+               mbx1_fbctl_disable();
+               break;
+       default:
+               printk(KERN_ERR
+                      "mailbox: Unknown FBCTL from DSP: 0x%04x\n", data);
+       }
+}
+#endif
+
+static void mbx1_kfunc(struct mbcmd *mb)
+{
+       switch (mb->cmd_l) {
+#ifdef CONFIG_FB_OMAP_EXTERNAL_LCDC
+       case OMAP_DSP_MBCMD_KFUNC_FBCTL:
+               mbx1_kfunc_fbctl(mb->data);
+               break;
+#endif
+
+       default:
+               printk(KERN_ERR
+                      "mailbox: Unknown kfunc from DSP: 0x%02x\n", mb->cmd_l);
+       }
+}
+
+/*
+ * mailbox interrupt handler
+ */
+static irqreturn_t mbx1_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+       union {
+               struct mbcmd sw;
+               struct mbcmd_hw hw;
+       } *mb = (void *)&mbq.mb[mbq.wp];
+
+#if (INT_D2A_MB1 == INT_DSP_MAILBOX1)
+       mb->hw.data = omap_readw(MAILBOX_DSP2ARM1);
+       mb->hw.cmd  = omap_readw(MAILBOX_DSP2ARM1b);
+#elif (INT_D2A_MB1 == INT_DSP_MAILBOX2)
+       mb->hw.data = omap_readw(MAILBOX_DSP2ARM2);
+       mb->hw.cmd  = omap_readw(MAILBOX_DSP2ARM2b);
+#endif
+
+       if (mb->sw.seq != (mbseq_expect & 1)) {
+               switch (mbseq_check_level) {
+               case MBSEQ_CHECK_NONE:
+                       break;
+               case MBSEQ_CHECK_VERBOSE:
+                       printk(KERN_INFO
+                              "mbx: illegal seq bit!!!  ignoring this command."
+                              " (%04x:%04x)\n", mb->hw.cmd, mb->hw.data);
+                       return IRQ_HANDLED;
+               case MBSEQ_CHECK_SILENT:
+                       return IRQ_HANDLED;
+               }
+       }
+
+       mbseq_expect++;
+       if (sync_seq)
+               sync_seq->da_arm = mbseq_expect;
+
+       mbq_inc(mbq.wp);
+       if (mbq.wp == mbq.rp) { /* mbq is full */
+               mbq.full = 1;
+               disable_irq(INT_D2A_MB1);
+       }
+       schedule_work(&mbx1_work);
+
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t mbx2_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+       unsigned short cmd, data;
+
+#if (INT_D2A_MB1 == INT_DSP_MAILBOX1)
+       data = omap_readw(MAILBOX_DSP2ARM2);
+       cmd  = omap_readw(MAILBOX_DSP2ARM2b);
+#elif (INT_D2A_MB1 == INT_DSP_MAILBOX2)
+       data = omap_readw(MAILBOX_DSP2ARM1);
+       cmd  = omap_readw(MAILBOX_DSP2ARM1b);
+#endif
+       printk(KERN_DEBUG
+              "mailbox2 interrupt!  cmd=%04x, data=%04x\n", cmd, data);
+
+       return IRQ_HANDLED;
+}
+
+#if 0
+static void mpuio_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+       printk(KERN_INFO "MPUIO interrupt!\n");
+}
+#endif
+
+#ifdef CONFIG_PROC_FS
+struct proc_dir_entry *procdir_dsp = NULL;
+
+static void dsp_create_procdir_dsp(void)
+{
+       procdir_dsp = proc_mkdir("dsp", 0);
+       if (procdir_dsp == NULL) {
+               printk(KERN_ERR
+                      "omapdsp: failed to register proc directory: dsp\n");
+       }
+}
+
+static void dsp_remove_procdir_dsp(void)
+{
+       procdir_dsp = NULL;
+       remove_proc_entry("dsp", 0);
+}
+#else /* CONFIG_PROC_FS */
+#define dsp_create_procdir_dsp()       do { } while (0)
+#define dsp_remove_procdir_dsp()       do { } while (0)
+#endif /* CONFIG_PROC_FS */
+
+extern irqreturn_t dsp_mmu_interrupt(int irq, void *dev_id,
+                                    struct pt_regs *regs);
+
+extern int  dsp_ctl_core_init(void);
+extern void dsp_ctl_core_exit(void);
+extern void dsp_ctl_init(void);
+extern void dsp_ctl_exit(void);
+extern int  dsp_mem_init(void);
+extern void dsp_mem_exit(void);
+extern void mblog_init(void);
+extern void mblog_exit(void);
+extern int  dsp_taskmod_init(void);
+extern void dsp_taskmod_exit(void);
+
+/*
+ * device functions
+ */
+static void dsp_dev_release(struct device *dev)
+{
+}
+
+/*
+ * driver functions
+ */
+#if (INT_D2A_MB1 == INT_DSP_MAILBOX1)
+#      define INT_D2A_MB2 INT_DSP_MAILBOX2
+#elif(INT_D2A_MB1 == INT_DSP_MAILBOX2) /* swap MB1 and MB2 */
+#      define INT_D2A_MB2 INT_DSP_MAILBOX1
+#endif
+
+static int __init dsp_drv_probe(struct device *dev)
+{
+       int ret;
+
+       printk(KERN_INFO "OMAP DSP driver initialization\n");
+
+       //__dsp_enable(); // XXX
+
+       dsp_create_procdir_dsp();
+
+       if ((ret = dsp_ctl_core_init()) < 0)
+               goto fail1;
+       if ((ret = dsp_mem_init()) < 0)
+               goto fail2;
+       dsp_ctl_init();
+       mblog_init();
+       if ((ret = dsp_taskmod_init()) < 0)
+               goto fail3;
+
+       /*
+        * mailbox interrupt handlers registration
+        */
+       ret = request_irq(INT_D2A_MB1, mbx1_interrupt, SA_INTERRUPT, "dsp",
+                         dev);
+       if (ret) {
+               printk(KERN_ERR
+                      "failed to register mailbox1 interrupt: %d\n", ret);
+               goto fail4;
+       }
+
+       ret = request_irq(INT_D2A_MB2, mbx2_interrupt, SA_INTERRUPT, "dsp",
+                         dev);
+       if (ret) {
+               printk(KERN_ERR
+                      "failed to register mailbox2 interrupt: %d\n", ret);
+               goto fail5;
+       }
+
+       ret = request_irq(INT_DSP_MMU, dsp_mmu_interrupt, SA_INTERRUPT, "dsp",
+                         dev);
+       if (ret) {
+               printk(KERN_ERR
+                      "failed to register DSP MMU interrupt: %d\n", ret);
+               goto fail6;
+       }
+
+#if 0
+       ret = request_irq(INT_MPUIO, mpuio_interrupt, SA_INTERRUPT, "dsp", dev);
+       if (ret) {
+               printk(KERN_ERR
+                      "failed to register MPUIO interrupt: %d\n", ret);
+               goto fail7;
+       }
+#endif
+
+       return 0;
+
+fail6:
+       free_irq(INT_D2A_MB2, dev);
+fail5:
+       free_irq(INT_D2A_MB1, dev);
+fail4:
+       dsp_taskmod_exit();
+fail3:
+       mblog_exit();
+       dsp_ctl_exit();
+       dsp_mem_exit();
+fail2:
+       dsp_ctl_core_exit();
+fail1:
+       dsp_remove_procdir_dsp();
+
+       //__dsp_disable(); // XXX
+       return ret;
+}
+
+static int dsp_drv_remove(struct device *dev)
+{
+       __dsp_reset();
+
+#if 0
+       free_irq(INT_MPUIO, dev);
+#endif
+       free_irq(INT_DSP_MMU, dev);
+       free_irq(INT_D2A_MB2, dev);
+       free_irq(INT_D2A_MB1, dev);
+
+       dspuncfg();
+       dsp_taskmod_exit();
+       mblog_exit();
+       dsp_ctl_exit();
+       dsp_mem_exit();
+
+       dsp_ctl_core_exit();
+       dsp_remove_procdir_dsp();
+
+       //__dsp_disable(); // XXX
+
+       return 0;
+}
+
+#ifdef CONFIG_PM
+static int dsp_drv_suspend(struct device *dev, u32 state, u32 level)
+{
+       switch(level) {
+       case SUSPEND_NOTIFY:
+       case SUSPEND_DISABLE:
+       case SUSPEND_SAVE_STATE:
+               break;
+       case SUSPEND_POWER_DOWN:
+               dsp_suspend();
+               break;
+       }
+
+       return 0;
+}
+
+static int dsp_drv_resume(struct device *dev, u32 level)
+{
+       switch(level) {
+       case RESUME_POWER_ON:
+               dsp_resume();
+               break;
+       case RESUME_RESTORE_STATE:
+       case RESUME_ENABLE:
+               break;
+       }
+
+       return 0;
+}
+#endif /* CONFIG_PM */
+
+static struct resource dsp_resources[] = {
+       {
+               .start = INT_DSP_MAILBOX1,
+               .flags = IORESOURCE_IRQ,
+       },
+       {
+               .start = INT_DSP_MAILBOX2,
+               .flags = IORESOURCE_IRQ,
+       },
+       {
+               .start = INT_DSP_MMU,
+               .flags = IORESOURCE_IRQ,
+       },
+};
+
+struct platform_device dsp_device = {
+       .name           = "dsp",
+       .id             = -1,
+       .dev = {
+               .release        = dsp_dev_release,
+       },
+       .num_resources  = ARRAY_SIZE(&dsp_resources),
+       .resource       = dsp_resources,
+};
+
+static struct device_driver dsp_driver = {
+       .name           = "dsp",
+       .bus            = &platform_bus_type,
+       .probe          = dsp_drv_probe,
+       .remove         = dsp_drv_remove,
+#ifdef CONFIG_PM
+       .suspend        = dsp_drv_suspend,
+       .resume         = dsp_drv_resume,
+#endif
+};
+
+static int __init omap_dsp_mod_init(void)
+{
+       int ret;
+
+       ret = platform_device_register(&dsp_device);
+       if (ret) {
+               printk(KERN_ERR "failed to register the DSP device: %d\n", ret);
+               goto fail1;
+       }
+
+       ret = driver_register(&dsp_driver);
+       if (ret) {
+               printk(KERN_ERR "failed to register the DSP driver: %d\n", ret);
+               goto fail2;
+       }
+
+       return 0;
+
+fail2:
+       platform_device_unregister(&dsp_device);
+fail1:
+       return -ENODEV;
+}
+
+static void __exit omap_dsp_mod_exit(void)
+{
+       driver_unregister(&dsp_driver);
+       platform_device_unregister(&dsp_device);
+}
+
+module_init(omap_dsp_mod_init);
+module_exit(omap_dsp_mod_exit);
diff --git a/arch/arm/mach-omap/dsp/dsp_ctl.c b/arch/arm/mach-omap/dsp/dsp_ctl.c
new file mode 100644 (file)
index 0000000..3c65bf0
--- /dev/null
@@ -0,0 +1,913 @@
+/*
+ * linux/arch/arm/mach-omap/dsp/dsp_ctl.c
+ *
+ * OMAP DSP control device driver
+ *
+ * Copyright (C) 2002-2005 Nokia Corporation
+ *
+ * Written by Toshihiro Kobayashi <toshihiro.kobayashi@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * 2005/02/17:  DSP Gateway version 3.2
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/major.h>
+#include <linux/fs.h>
+#include <linux/device.h>
+#include <linux/proc_fs.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <asm/ioctls.h>
+#include <asm/hardware/clock.h>
+#include <asm/arch/dsp.h>
+#include "hardware_dsp.h"
+#include "dsp.h"
+#include "ipbuf.h"
+
+static ssize_t loadinfo_show(struct device *dev, char *buf);
+static struct device_attribute dev_attr_loadinfo = __ATTR_RO(loadinfo);
+extern struct device_attribute dev_attr_ipbuf;
+
+static enum {
+       CFG_ERR,
+       CFG_READY,
+       CFG_SUSPEND
+} cfgstat;
+static int mbx_revision;
+static DECLARE_WAIT_QUEUE_HEAD(ioctl_wait_q);
+static unsigned short ioctl_wait_cmd;
+static DECLARE_MUTEX(ioctl_sem);
+
+static unsigned char n_stask;
+
+/*
+ * control functions
+ */
+static void dsp_run(void)
+{
+       disable_irq(INT_DSP_MMU);
+       preempt_disable();
+       if (dsp_runstat == RUNSTAT_RESET) {
+               clk_use(api_ck_handle);
+               __dsp_run();
+               dsp_runstat = RUNSTAT_RUN;
+       }
+       preempt_enable();
+       enable_irq(INT_DSP_MMU);
+}
+
+static void dsp_reset(void)
+{
+       disable_irq(INT_DSP_MMU);
+       preempt_disable();
+       if (dsp_runstat > RUNSTAT_RESET) {
+               __dsp_reset();
+               if (dsp_runstat == RUNSTAT_RUN)
+                       clk_unuse(api_ck_handle);
+               dsp_runstat = RUNSTAT_RESET;
+       }
+       preempt_enable();
+       enable_irq(INT_DSP_MMU);
+}
+
+static short varread_val[5]; /* maximum */
+
+static int dsp_regread(unsigned short cmd_l, unsigned short adr,
+                      unsigned short *val)
+{
+       struct mbcmd mb;
+       int ret = 0;
+
+       if (down_interruptible(&ioctl_sem))
+               return -ERESTARTSYS;
+
+       ioctl_wait_cmd = MBCMD(REGRW);
+       mbcmd_set(mb, MBCMD(REGRW), cmd_l, adr);
+       dsp_mbsend_and_wait(&mb, &ioctl_wait_q);
+
+       if (ioctl_wait_cmd != 0) {
+               printk(KERN_ERR "omapdsp: register read error!\n");
+               ret = -EINVAL;
+               goto up_out;
+       }
+
+       *val = varread_val[0];
+
+up_out:
+       up(&ioctl_sem);
+       return ret;
+}
+
+static int dsp_regwrite(unsigned short cmd_l, unsigned short adr,
+                       unsigned short val)
+{
+       struct mbcmd mb;
+       struct mb_exarg arg = {
+               .tid  = OMAP_DSP_TID_ANON,
+               .argc = 1,
+               .argv = &val,
+       };
+
+       mbcmd_set(mb, MBCMD(REGRW), cmd_l, adr);
+       dsp_mbsend_exarg(&mb, &arg);
+       return 0;
+}
+
+static int dsp_getvar(unsigned char varid, unsigned short *val, int sz)
+{
+       struct mbcmd mb;
+       int ret = 0;
+
+       if (down_interruptible(&ioctl_sem))
+               return -ERESTARTSYS;
+
+       ioctl_wait_cmd = MBCMD(GETVAR);
+       mbcmd_set(mb, MBCMD(GETVAR), varid, 0);
+       dsp_mbsend_and_wait(&mb, &ioctl_wait_q);
+
+       if (ioctl_wait_cmd != 0) {
+               printk(KERN_ERR "omapdsp: variable read error!\n");
+               ret = -EINVAL;
+               goto up_out;
+       }
+
+       memcpy(val, varread_val, sz * sizeof(short));
+
+up_out:
+       up(&ioctl_sem);
+       return ret;
+}
+
+static int dsp_setvar(unsigned char varid, unsigned short val)
+{
+       struct mbcmd mb;
+
+       mbcmd_set(mb, MBCMD(SETVAR), varid, val);
+       dsp_mbsend(&mb);
+       return 0;
+}
+
+static int dspcfg(void)
+{
+       struct mbcmd mb;
+       int ret = 0;
+
+       if (down_interruptible(&ioctl_sem))
+               return -ERESTARTSYS;
+
+       if (cfgstat != CFG_ERR) {
+               printk(KERN_ERR
+                      "omapdsp: DSP has been already configured. "
+                      "do unconfig!\n");
+               ret = -EBUSY;
+               goto up_out;
+       }
+
+       dsp_mb_start();
+       dsp_twch_start();
+       dsp_mem_start();
+       dsp_err_start();
+
+       mbx_revision = -1;
+       ioctl_wait_cmd = MBCMD(DSPCFG);
+       mbcmd_set(mb, MBCMD(DSPCFG), OMAP_DSP_MBCMD_DSPCFG_REQ, 0);
+       dsp_mbsend_and_wait(&mb, &ioctl_wait_q);
+
+       if (ioctl_wait_cmd != 0) {
+               printk(KERN_ERR "omapdsp: configuration error!\n");
+               ret = -EINVAL;
+               cfgstat = CFG_ERR;
+               goto up_out;
+       }
+
+       if ((ret = dsp_task_config_all(n_stask)) < 0) {
+               up(&ioctl_sem);
+               dspuncfg();
+               return -EINVAL;
+       }
+
+       cfgstat = CFG_READY;
+
+       /* send parameter */
+       if ((ret = dsp_setvar(OMAP_DSP_MBCMD_VARID_ICRMASK, dsp_icrmask)) < 0)
+               goto up_out;
+
+       /* create runtime sysfs entries */
+       device_create_file(&dsp_device.dev, &dev_attr_loadinfo);
+       device_create_file(&dsp_device.dev, &dev_attr_ipbuf);
+
+up_out:
+       up(&ioctl_sem);
+       return ret;
+}
+
+int dspuncfg(void)
+{
+       if (dsp_taskmod_busy()) {
+               printk(KERN_WARNING "omapdsp: tasks are busy.\n");
+               return -EBUSY;
+       }
+
+       if (down_interruptible(&ioctl_sem))
+               return -ERESTARTSYS;
+       
+       /* FIXME: lock task module */
+
+       /* remove runtime sysfs entries */
+       device_remove_file(&dsp_device.dev, &dev_attr_loadinfo);
+       device_remove_file(&dsp_device.dev, &dev_attr_ipbuf);
+
+       dsp_mb_stop();
+       dsp_twch_stop();
+       dsp_err_stop();
+       dsp_task_unconfig_all();
+       ipbuf_stop();
+       cfgstat = CFG_ERR;
+
+       up(&ioctl_sem);
+       return 0;
+}
+
+int dsp_is_ready(void)
+{
+       return (cfgstat == CFG_READY) ? 1 : 0;
+}
+
+void dsp_runlevel(unsigned char level)
+{
+       struct mbcmd mb;
+
+       mbcmd_set(mb, MBCMD(RUNLEVEL), level, 0);
+       if (level == OMAP_DSP_MBCMD_RUNLEVEL_RECOVERY)
+               dsp_mbsend_recovery(&mb);
+       else
+               dsp_mbsend(&mb);
+}
+
+int dsp_suspend(void)
+{
+       struct mbcmd mb;
+       int ret = 0;
+
+       if (down_interruptible(&ioctl_sem))
+               return -ERESTARTSYS;
+
+       if (!dsp_is_ready()) {
+               printk(KERN_WARNING
+                      "omapdsp: DSP is not ready. suspend failed.\n");
+               ret = -EINVAL;
+               goto up_out;
+       }
+
+       ioctl_wait_cmd = MBCMD(SUSPEND);
+       mbcmd_set(mb, MBCMD(SUSPEND), 0, 0);
+       dsp_mbsend_and_wait(&mb, &ioctl_wait_q);
+
+       if (ioctl_wait_cmd != 0) {
+               printk(KERN_ERR "omapdsp: DSP suspend error!\n");
+               ret = -EINVAL;
+               goto up_out;
+       }
+
+       udelay(100);
+       dsp_reset();
+       cfgstat = CFG_SUSPEND;
+up_out:
+       up(&ioctl_sem);
+       return ret;
+}
+
+int dsp_resume(void)
+{
+       if (cfgstat != CFG_SUSPEND)
+               return 0;
+
+       cfgstat = CFG_READY;
+       dsp_run();
+       return 0;
+}
+
+static void dsp_fbctl_enable(void)
+{
+#ifdef CONFIG_FB_OMAP_EXTERNAL_LCDC
+       struct mbcmd mb;
+
+       mbcmd_set(mb, MBCMD(KFUNC), OMAP_DSP_MBCMD_KFUNC_FBCTL,
+                 OMAP_DSP_MBCMD_FBCTL_ENABLE);
+       dsp_mbsend(&mb);
+#endif
+}
+
+static int dsp_fbctl_disable(void)
+{
+       int ret = 0;
+
+#ifdef CONFIG_FB_OMAP_EXTERNAL_LCDC
+       struct mbcmd mb;
+
+       if (down_interruptible(&ioctl_sem))
+               return -ERESTARTSYS;
+
+       ioctl_wait_cmd = MBCMD(KFUNC);
+       mbcmd_set(mb, MBCMD(KFUNC), OMAP_DSP_MBCMD_KFUNC_FBCTL,
+                 OMAP_DSP_MBCMD_FBCTL_DISABLE);
+       dsp_mbsend_and_wait(&mb, &ioctl_wait_q);
+       if (ioctl_wait_cmd != 0) {
+               printk(KERN_ERR "omapdsp: fb disable error!\n");
+               ret = -EINVAL;
+       }
+       up(&ioctl_sem);
+#endif
+
+       return ret;
+}
+
+/*
+ * DSP control device file operations
+ */
+static int dsp_ctl_ioctl(struct inode *inode, struct file *file,
+                        unsigned int cmd, unsigned long arg)
+{
+       int ret = 0;
+
+       switch (cmd) {
+       /*
+        * command level 1: commands which don't need lock
+        */
+       case OMAP_DSP_IOCTL_RUN:
+               dsp_run();
+               break;
+
+       case OMAP_DSP_IOCTL_RESET:
+               dsp_reset();
+               break;
+
+       case OMAP_DSP_IOCTL_SETRSTVECT:
+               ret = dsp_set_rstvect((unsigned long)arg);
+               break;
+
+       case OMAP_DSP_IOCTL_IDLE:
+               dsp_idle();
+               break;
+
+       case OMAP_DSP_IOCTL_MPUI_WORDSWAP_ON:
+               mpui_wordswap_on();
+               break;
+
+       case OMAP_DSP_IOCTL_MPUI_WORDSWAP_OFF:
+               mpui_wordswap_off();
+               break;
+
+       case OMAP_DSP_IOCTL_MPUI_BYTESWAP_ON:
+               mpui_byteswap_on();
+               break;
+
+       case OMAP_DSP_IOCTL_MPUI_BYTESWAP_OFF:
+               mpui_byteswap_off();
+               break;
+
+       case OMAP_DSP_IOCTL_MBSEND:
+               {
+                       struct omap_dsp_mailbox_cmd u_cmd;
+                       struct mbcmd_hw mb;
+                       if (copy_from_user(&u_cmd, (void *)arg, sizeof(u_cmd)))
+                               return -EFAULT;
+                       mb.cmd  = u_cmd.cmd;
+                       mb.data = u_cmd.data;
+                       ret = dsp_mbsend((struct mbcmd *)&mb);
+                       break;
+               }
+
+       case OMAP_DSP_IOCTL_SETVAR:
+               {
+                       struct omap_dsp_varinfo var;
+                       if (copy_from_user(&var, (void *)arg, sizeof(var)))
+                               return -EFAULT;
+                       ret = dsp_setvar(var.varid, var.val[0]);
+                       break;
+               }
+
+       case OMAP_DSP_IOCTL_RUNLEVEL:
+               dsp_runlevel(arg);
+               break;
+
+       case OMAP_DSP_IOCTL_FBEN:
+               dsp_fbctl_enable();
+               return 0;
+
+       /*
+        * command level 2: commands which need lock
+        */
+       case OMAP_DSP_IOCTL_DSPCFG:
+               ret = dspcfg();
+               break;
+
+       case OMAP_DSP_IOCTL_DSPUNCFG:
+               ret = dspuncfg();
+               break;
+
+       case OMAP_DSP_IOCTL_TASKCNT:
+               ret = dsp_task_count();
+               break;
+
+       case OMAP_DSP_IOCTL_FBDIS:
+               ret = dsp_fbctl_disable();
+               break;
+
+       case OMAP_DSP_IOCTL_SUSPEND:
+               ret = dsp_suspend();
+               break;
+
+       case OMAP_DSP_IOCTL_RESUME:
+               ret = dsp_resume();
+               break;
+
+       case OMAP_DSP_IOCTL_REGMEMR:
+               {
+                       struct omap_dsp_reginfo *u_reg = (void *)arg;
+                       unsigned short adr, val;
+
+                       if (copy_from_user(&adr, &u_reg->adr, sizeof(short)))
+                               return -EFAULT;
+                       if ((ret = dsp_regread(OMAP_DSP_MBCMD_REGRW_MEMR,
+                                              adr, &val)) < 0)
+                               return ret;
+                       if (copy_to_user(&u_reg->val, &val, sizeof(short)))
+                               return -EFAULT;
+                       break;
+               }
+
+       case OMAP_DSP_IOCTL_REGMEMW:
+               {
+                       struct omap_dsp_reginfo reg;
+
+                       if (copy_from_user(&reg, (void *)arg, sizeof(reg)))
+                               return -EFAULT;
+                       ret = dsp_regwrite(OMAP_DSP_MBCMD_REGRW_MEMW,
+                                          reg.adr, reg.val);
+                       break;
+               }
+
+       case OMAP_DSP_IOCTL_REGIOR:
+               {
+                       struct omap_dsp_reginfo *u_reg = (void *)arg;
+                       unsigned short adr, val;
+
+                       if (copy_from_user(&adr, &u_reg->adr, sizeof(short)))
+                               return -EFAULT;
+                       if ((ret = dsp_regread(OMAP_DSP_MBCMD_REGRW_IOR,
+                                              adr, &val)) < 0)
+                               return ret;
+                       if (copy_to_user(&u_reg->val, &val, sizeof(short)))
+                               return -EFAULT;
+                       break;
+               }
+
+       case OMAP_DSP_IOCTL_REGIOW:
+               {
+                       struct omap_dsp_reginfo reg;
+
+                       if (copy_from_user(&reg, (void *)arg, sizeof(reg)))
+                               return -EFAULT;
+                       ret = dsp_regwrite(OMAP_DSP_MBCMD_REGRW_IOW,
+                                          reg.adr, reg.val);
+                       break;
+               }
+
+       case OMAP_DSP_IOCTL_GETVAR:
+               {
+                       struct omap_dsp_varinfo *u_var = (void *)arg;
+                       unsigned char varid;
+                       unsigned short val[5]; /* maximum */
+                       int argc;
+
+                       if (copy_from_user(&varid, &u_var->varid, sizeof(char)))
+                               return -EFAULT;
+                       switch (varid) {
+                       case OMAP_DSP_MBCMD_VARID_ICRMASK:
+                               argc = 1;
+                               break;
+                       case OMAP_DSP_MBCMD_VARID_LOADINFO:
+                               argc = 5;
+                               break;
+                       default:
+                               return -EINVAL;
+                       }
+                       if ((ret = dsp_getvar(varid, val, argc)) < 0)
+                               return ret;
+                       if (copy_to_user(&u_var->val, val, sizeof(short) * argc))
+                               return -EFAULT;
+                       break;
+               }
+
+       default:
+               return -ENOIOCTLCMD;
+       }
+
+       return ret;
+}
+
+/*
+ * functions called from mailbox1 interrupt routine
+ */
+void mbx1_suspend(struct mbcmd *mb)
+{
+       if (!waitqueue_active(&ioctl_wait_q) ||
+           (ioctl_wait_cmd != MBCMD(SUSPEND))) {
+               printk(KERN_WARNING
+                      "mbx: SUSPEND command received, "
+                      "but nobody is waiting for it...\n");
+               return;
+       }
+
+       ioctl_wait_cmd = 0;
+       wake_up_interruptible(&ioctl_wait_q);
+}
+
+void mbx1_dspcfg(struct mbcmd *mb)
+{
+       unsigned char last   = mb->cmd_l & 0x80;
+       unsigned char cfgcmd = mb->cmd_l & 0x7f;
+       static unsigned long tmp_ipbuf_sys_da;
+
+       /* mailbox protocol check */
+       if (cfgcmd == OMAP_DSP_MBCMD_DSPCFG_PROTREV) {
+               if (!waitqueue_active(&ioctl_wait_q) ||
+                   (ioctl_wait_cmd != MBCMD(DSPCFG))) {
+                       printk(KERN_WARNING
+                              "mbx: DSPCFG command received, "
+                              "but nobody is waiting for it...\n");
+                       return;
+               }
+
+               mbx_revision = mb->data;
+               if (mbx_revision == OMAP_DSP_MBPROT_REVISION)
+                       return;
+#ifdef OLD_BINARY_SUPPORT
+               else if (mbx_revision == MBREV_3_0) {
+                       printk(KERN_WARNING
+                              "mbx: ***** old DSP binary *****\n"
+                              "  Please update your DSP application.\n");
+                       return;
+               }
+#endif
+               else {
+                       printk(KERN_ERR
+                              "mbx: protocol revision check error!\n"
+                              "  expected=0x%04x, received=0x%04x\n",
+                              OMAP_DSP_MBPROT_REVISION, mb->data);
+                       mbx_revision = -1;
+                       goto abort;
+               }
+       }
+
+       /*
+        * following commands are accepted only after
+        * revision check has been passed.
+        */
+       if (!mbx_revision < 0) {
+               printk(KERN_INFO
+                      "mbx: DSPCFG command received, "
+                      "but revision check has not been passed.\n");
+               return;
+       }
+
+       if (!waitqueue_active(&ioctl_wait_q) ||
+           (ioctl_wait_cmd != MBCMD(DSPCFG))) {
+               printk(KERN_WARNING
+                      "mbx: DSPCFG command received, "
+                      "but nobody is waiting for it...\n");
+               return;
+       }
+
+       switch (cfgcmd) {
+       case OMAP_DSP_MBCMD_DSPCFG_SYSADRH:
+               tmp_ipbuf_sys_da = (unsigned long)mb->data << 16;
+               break;
+
+       case OMAP_DSP_MBCMD_DSPCFG_SYSADRL:
+               tmp_ipbuf_sys_da |= mb->data;
+               break;
+
+       case OMAP_DSP_MBCMD_DSPCFG_ABORT:
+               goto abort;
+
+       default:
+               printk(KERN_ERR
+                      "mbx: Unknown CFG command: cmd_l=0x%02x, data=0x%04x\n",
+                      mb->cmd_l, mb->data);
+               return;
+       }
+
+       if (last) {
+               unsigned long badr;
+               unsigned short bln;
+               unsigned short bsz;
+               volatile unsigned short *buf;
+               void *sync_seq;
+
+               /* system IPBUF initialization */
+               if (tmp_ipbuf_sys_da & 0x1) {
+                       printk(KERN_ERR
+                              "mbx: system ipbuf address (0x%lx) "
+                              "is odd number!\n", tmp_ipbuf_sys_da);
+                       goto abort;
+               }
+               ipbuf_sys_da = dspword_to_virt(tmp_ipbuf_sys_da);
+
+               if (sync_with_dsp(&ipbuf_sys_da->s, OMAP_DSP_TID_ANON, 10) < 0) {
+                       printk(KERN_ERR "mbx: DSPCFG - IPBUF sync failed!\n");
+                       return;
+               }
+               /*
+                * read configuration data on system IPBUF
+                * we must read with 16bit-access
+                */
+#ifdef OLD_BINARY_SUPPORT
+               if (mbx_revision == OMAP_DSP_MBPROT_REVISION) {
+#endif
+                       buf = ipbuf_sys_da->d;
+                       n_stask = buf[0];
+                       bln     = buf[1];
+                       bsz     = buf[2];
+                       badr    = MKLONG(buf[3], buf[4]);
+                       /*ipbuf_sys_da = dspword_to_virt(MKLONG(buf[5], buf[6])); */
+                       ipbuf_sys_ad = dspword_to_virt(MKLONG(buf[7], buf[8]));
+                       sync_seq = dspword_to_virt(MKLONG(buf[9], buf[10]));
+#ifdef OLD_BINARY_SUPPORT
+               } else if (mbx_revision == MBREV_3_0) {
+                       buf = ipbuf_sys_da->d;
+                       n_stask = buf[0];
+                       bln     = buf[1];
+                       bsz     = buf[2];
+                       badr    = MKLONG(buf[3], buf[4]);
+                       /* bkeep   = buf[5]; */
+                       /*ipbuf_sys_da = dspword_to_virt(MKLONG(buf[6], buf[67)); */
+                       ipbuf_sys_ad = dspword_to_virt(MKLONG(buf[8], buf[9]));
+                       sync_seq = dspword_to_virt(MKLONG(buf[10], buf[11]));
+               } else /* should not occur */
+                       goto abort;
+#endif
+
+               /* ipbuf_config() should be done in interrupt routine. */
+               if (ipbuf_config(bln, bsz, badr) < 0)
+                       goto abort;
+
+               ipbuf_sys_da->s = OMAP_DSP_TID_FREE;
+
+               /* mb_config() should be done in interrupt routine. */
+               dsp_mb_config(sync_seq);
+
+               ioctl_wait_cmd = 0;
+               wake_up_interruptible(&ioctl_wait_q);
+       }
+       return;
+
+abort:
+       wake_up_interruptible(&ioctl_wait_q);
+       return;
+}
+
+void mbx1_regrw(struct mbcmd *mb)
+{
+       if (!waitqueue_active(&ioctl_wait_q) ||
+           (ioctl_wait_cmd != MBCMD(REGRW))) {
+               printk(KERN_WARNING
+                      "mbx: REGRW command received, "
+                      "but nobody is waiting for it...\n");
+               return;
+       }
+
+       switch (mb->cmd_l) {
+       case OMAP_DSP_MBCMD_REGRW_DATA:
+               ioctl_wait_cmd = 0;
+               varread_val[0] = mb->data;
+               wake_up_interruptible(&ioctl_wait_q);
+               return;
+
+       default:
+               printk(KERN_ERR
+                      "mbx: Illegal REGRW command: "
+                      "cmd_l=0x%02x, data=0x%04x\n", mb->cmd_l, mb->data);
+               return;
+       }
+}
+
+void mbx1_getvar(struct mbcmd *mb)
+{
+       unsigned char varid = mb->cmd_l;
+       int i;
+       volatile unsigned short *buf;
+
+       if (!waitqueue_active(&ioctl_wait_q) ||
+           (ioctl_wait_cmd != MBCMD(GETVAR))) {
+               printk(KERN_WARNING
+                      "mbx: GETVAR command received, "
+                      "but nobody is waiting for it...\n");
+               return;
+       }
+
+       ioctl_wait_cmd = 0;
+       switch (varid) {
+       case OMAP_DSP_MBCMD_VARID_ICRMASK:
+               varread_val[0] = mb->data;
+               break;
+       case OMAP_DSP_MBCMD_VARID_LOADINFO:
+               {
+                       if (sync_with_dsp(&ipbuf_sys_da->s, OMAP_DSP_TID_ANON, 10) < 0) {
+                               printk(KERN_ERR
+                                      "mbx: GETVAR - IPBUF sync failed!\n");
+                               return;
+                       }
+                       /* need word access. do not use memcpy. */
+                       buf = ipbuf_sys_da->d;
+                       for (i = 0; i < 5; i++) {
+                               varread_val[i] = buf[i];
+                       }
+                       ipbuf_sys_da->s = OMAP_DSP_TID_FREE;
+                       break;
+               }
+       }
+       wake_up_interruptible(&ioctl_wait_q);
+
+       return;
+}
+
+/*
+ * sysfs files
+ */
+static ssize_t ifver_show(struct device *dev, char *buf)
+{
+       int len = 0;
+
+       /*
+        * I/F VERSION descriptions:
+        *
+        * 3.2: sysfs / udev support
+        *      KMEM_RESERVE / KMEM_RELEASE ioctls for mem device
+        */
+
+       /*
+        * print all supporting I/F VERSIONs, like followings.
+        *
+        * len += sprintf(buf, "3.1\n");
+        * len += sprintf(buf, "3.2\n");
+        */
+       len += sprintf(buf + len, "3.2\n");
+
+       return len;
+}
+
+static struct device_attribute dev_attr_ifver = __ATTR_RO(ifver);
+
+static ssize_t icrmask_show(struct device *dev, char *buf)
+{
+#if 0
+       if (dsp_is_ready()) {
+               int ret;
+               unsigned short val;
+
+               if ((ret = dsp_getvar(OMAP_DSP_MBCMD_VARID_ICRMASK, &val, 1)) < 0)
+                       return ret;
+               if (val != dsp_icrmask)
+                       printk(KERN_WARNING
+                              "omapdsp: icrmask value is inconsistent!\n");
+       }
+#endif
+       return sprintf(buf, "0x%04x\n", dsp_icrmask);
+}
+
+static ssize_t icrmask_store(struct device *dev, const char *buf, size_t count)
+{
+       int ret;
+
+       if (!capable(CAP_SYS_ADMIN))
+               return -EPERM;
+
+       dsp_icrmask = simple_strtol(buf, NULL, 16);
+
+       if (dsp_is_ready()) {
+               ret = dsp_setvar(OMAP_DSP_MBCMD_VARID_ICRMASK, dsp_icrmask);
+               if (ret < 0)
+                       return ret;
+       }
+
+       return strlen(buf);
+}
+
+static struct device_attribute dev_attr_icrmask = 
+       __ATTR(icrmask, S_IWUSR | S_IRUGO, icrmask_show, icrmask_store);
+
+static ssize_t loadinfo_show(struct device *dev, char *buf)
+{
+       int len;
+       int ret;
+       static unsigned short val[5];
+
+       if ((ret = dsp_getvar(OMAP_DSP_MBCMD_VARID_LOADINFO, val, 5)) < 0)
+               return ret;
+
+       /* load info value range is 0(free) - 10000(busy) */
+       len = sprintf(buf,
+                     "DSP load info:\n"
+                     "  10ms average = %3d.%02d%%\n"
+                     "  1sec average = %3d.%02d%%  busiest 10ms = %3d.%02d%%\n"
+                     "  1min average = %3d.%02d%%  busiest 1s   = %3d.%02d%%\n",
+                     val[0]/100, val[0]%100,
+                     val[1]/100, val[1]%100, val[2]/100, val[2]%100,
+                     val[3]/100, val[3]%100, val[4]/100, val[4]%100);
+       return len;
+}
+
+/*
+ * This is declared at the top of this file.
+ *
+ * static struct device_attribute dev_attr_loadinfo = __ATTR_RO(loadinfo);
+ */
+
+#ifdef CONFIG_FB_OMAP_EXTERNAL_LCDC
+void mbx1_fbctl_disable(void)
+{
+       if (!waitqueue_active(&ioctl_wait_q) ||
+           (ioctl_wait_cmd != MBCMD(KFUNC))) {
+               printk(KERN_WARNING
+                      "mbx: KFUNC:FBCTL command received, "
+                      "but nobody is waiting for it...\n");
+               return;
+       }
+       ioctl_wait_cmd = 0;
+       wake_up_interruptible(&ioctl_wait_q);
+}
+#endif
+
+#ifdef CONFIG_PROC_FS
+/* for backward compatibility */
+static int version_read_proc(char *page, char **start, off_t off, int count,
+                            int *eof, void *data)
+{
+       /*
+        * This entry is read by 3.1 tools only, so leave it as is.
+        * 3.2 and later will read from sysfs file.
+        */
+       return sprintf(page, "3.1\n");
+}
+
+static void __init dsp_ctl_create_proc(void)
+{
+       struct proc_dir_entry *ent;
+
+       /* version */
+       ent = create_proc_read_entry("version", 0, procdir_dsp,
+                                    version_read_proc, NULL);
+       if (ent == NULL) {
+               printk(KERN_ERR
+                      "omapdsp: failed to register proc device: version\n");
+       }
+}
+
+static void dsp_ctl_remove_proc(void)
+{
+       remove_proc_entry("version", procdir_dsp);
+}
+#endif /* CONFIG_PROC_FS */
+
+struct file_operations dsp_ctl_fops = {
+       .owner   = THIS_MODULE,
+       .ioctl   = dsp_ctl_ioctl,
+};
+
+void __init dsp_ctl_init(void)
+{
+       device_create_file(&dsp_device.dev, &dev_attr_ifver);
+       device_create_file(&dsp_device.dev, &dev_attr_icrmask);
+#ifdef CONFIG_PROC_FS
+       dsp_ctl_create_proc();
+#endif
+}
+
+void dsp_ctl_exit(void)
+{
+       device_remove_file(&dsp_device.dev, &dev_attr_ifver);
+       device_remove_file(&dsp_device.dev, &dev_attr_icrmask);
+#ifdef CONFIG_PROC_FS
+       dsp_ctl_remove_proc();
+#endif
+}
diff --git a/arch/arm/mach-omap/dsp/dsp_ctl_core.c b/arch/arm/mach-omap/dsp/dsp_ctl_core.c
new file mode 100644 (file)
index 0000000..729ad9f
--- /dev/null
@@ -0,0 +1,129 @@
+/*
+ * linux/arch/arm/mach-omap/dsp/dsp_ctl_core.c
+ *
+ * OMAP DSP control devices core driver
+ *
+ * Copyright (C) 2004,2005 Nokia Corporation
+ *
+ * Written by Toshihiro Kobayashi <toshihiro.kobayashi@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * 2005/02/10:  DSP Gateway version 3.2
+ */
+
+#include <linux/module.h>
+#include <linux/major.h>
+#include <linux/fs.h>
+#include <linux/device.h>
+#include <linux/devfs_fs_kernel.h>
+#include <linux/init.h>
+#include <asm/arch/dsp.h>
+#include "hardware_dsp.h"
+
+#define CTL_MINOR      0
+#define MEM_MINOR      1
+#define TWCH_MINOR     2
+#define ERR_MINOR      3
+
+static struct class_simple *dsp_ctl_class;
+extern struct file_operations dsp_ctl_fops,
+                             dsp_mem_fops,
+                             dsp_twch_fops,
+                             dsp_err_fops;
+
+static int dsp_ctl_core_open(struct inode *inode, struct file *file)
+{
+       switch (iminor(inode)) {
+       case CTL_MINOR:
+               file->f_op = &dsp_ctl_fops;
+               break;
+       case MEM_MINOR:
+               file->f_op = &dsp_mem_fops;
+               break;
+       case TWCH_MINOR:
+               file->f_op = &dsp_twch_fops;
+               break;
+       case ERR_MINOR:
+               file->f_op = &dsp_err_fops;
+               break;
+       default:
+               return -ENXIO;
+       }
+       if (file->f_op && file->f_op->open)
+               return file->f_op->open(inode, file);
+       return 0;
+}
+
+static struct file_operations dsp_ctl_core_fops = {
+       .owner = THIS_MODULE,
+       .open  = dsp_ctl_core_open,
+};
+
+static const struct dev_list {
+       unsigned int    minor;
+       char            *devname;
+       char            *devfs_name;
+       umode_t         mode;
+} dev_list[] = {
+       {CTL_MINOR,  "dspctl",  "dspctl/ctl",  S_IRUSR | S_IWUSR},
+       {MEM_MINOR,  "dspmem",  "dspctl/mem",  S_IRUSR | S_IWUSR | S_IRGRP},
+       {TWCH_MINOR, "dsptwch", "dspctl/twch", S_IRUSR | S_IWUSR | S_IRGRP},
+       {ERR_MINOR,  "dsperr",  "dspctl/err",  S_IRUSR | S_IRGRP},
+};
+
+int __init dsp_ctl_core_init(void)
+{
+       int retval;
+       int i;
+
+       retval = register_chrdev(OMAP_DSP_CTL_MAJOR, "dspctl",
+                                &dsp_ctl_core_fops);
+       if (retval < 0) {
+               printk(KERN_ERR
+                      "omapdsp: failed to register dspctl device: %d\n",
+                      retval);
+               return retval;
+       }
+
+       dsp_ctl_class = class_simple_create(THIS_MODULE, "dspctl");
+       devfs_mk_dir("dspctl");
+       for (i = 0; i < ARRAY_SIZE(dev_list); i++) {
+               class_simple_device_add(dsp_ctl_class,
+                                       MKDEV(OMAP_DSP_CTL_MAJOR,
+                                             dev_list[i].minor),
+                                       NULL, dev_list[i].devname);
+               devfs_mk_cdev(MKDEV(OMAP_DSP_CTL_MAJOR, dev_list[i].minor),
+                             S_IFCHR | dev_list[i].mode,
+                             dev_list[i].devfs_name);
+       }
+
+       return 0;
+}
+
+void dsp_ctl_core_exit(void)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(dev_list); i++) {
+               devfs_remove(dev_list[i].devfs_name);
+               class_simple_device_remove(MKDEV(OMAP_DSP_CTL_MAJOR,
+                                                dev_list[i].minor));
+       }
+       devfs_remove("dspctl");
+       class_simple_destroy(dsp_ctl_class);
+
+       unregister_chrdev(OMAP_DSP_CTL_MAJOR, "dspctl");
+}
diff --git a/arch/arm/mach-omap/dsp/dsp_mem.c b/arch/arm/mach-omap/dsp/dsp_mem.c
new file mode 100644 (file)
index 0000000..1074179
--- /dev/null
@@ -0,0 +1,1597 @@
+/*
+ * linux/arch/arm/mach-omap/dsp/dsp_mem.c
+ *
+ * OMAP DSP memory driver
+ *
+ * Copyright (C) 2002-2005 Nokia Corporation
+ *
+ * Written by Toshihiro Kobayashi <toshihiro.kobayashi@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Toshihiro Kobayashi <toshihiro.kobayashi@nokia.com>
+ * 2005/02/17:  DSP Gateway version 3.2
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/major.h>
+#include <linux/fs.h>
+#include <linux/bootmem.h>
+#include <linux/fb.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <asm/ioctls.h>
+#include <asm/irq.h>
+#include <asm/pgalloc.h>
+#include <asm/pgtable.h>
+#include <asm/hardware/clock.h>
+#include <asm/arch/tc.h>
+#include <asm/arch/dsp.h>
+#include "uaccess_dsp.h"
+#include "ipbuf.h"
+#include "dsp.h"
+
+#define SZ_1MB 0x100000
+#define SZ_64KB        0x10000
+#define SZ_4KB 0x1000
+#define SZ_1KB 0x400
+#define is_aligned(adr,align)  (!((adr)&((align)-1)))
+#define ORDER_1MB      (20 - PAGE_SHIFT)
+#define ORDER_64KB     (16 - PAGE_SHIFT)
+#define ORDER_4KB      (12 - PAGE_SHIFT)
+
+#define PGDIR_MASK             (~(PGDIR_SIZE-1))
+#define PGDIR_ALIGN(addr)      (((addr)+PGDIR_SIZE-1)&(PGDIR_MASK))
+
+#define dsp_mmu_enable() \
+       do { \
+               omap_writew(DSPMMU_CNTL_MMU_EN | DSPMMU_CNTL_RESET_SW, \
+                           DSPMMU_CNTL); \
+       } while(0)
+#define dsp_mmu_disable() \
+       do { omap_writew(0, DSPMMU_CNTL); } while(0)
+#define dsp_mmu_flush() \
+       do { \
+               omap_writew(DSPMMU_FLUSH_ENTRY_FLUSH_ENTRY, \
+                           DSPMMU_FLUSH_ENTRY); \
+       } while(0)
+#define __dsp_mmu_gflush() \
+       do { omap_writew(DSPMMU_GFLUSH_GFLUSH, DSPMMU_GFLUSH); } while(0)
+#define __dsp_mmu_itack() \
+       do { omap_writew(DSPMMU_IT_ACK_IT_ACK, DSPMMU_IT_ACK); } while(0)
+
+#define EMIF_PRIO_LB_MASK      0x0000f000
+#define EMIF_PRIO_LB_SHIFT     12
+#define EMIF_PRIO_DMA_MASK     0x00000f00
+#define EMIF_PRIO_DMA_SHIFT    8
+#define EMIF_PRIO_DSP_MASK     0x00000070
+#define EMIF_PRIO_DSP_SHIFT    4
+#define EMIF_PRIO_MPU_MASK     0x00000007
+#define EMIF_PRIO_MPU_SHIFT    0
+#define set_emiff_dma_prio(prio) \
+       do { \
+               omap_writel((omap_readl(OMAP_TC_OCPT1_PRIOR) & \
+       ~EMIF_PRIO_DMA_MASK) | \
+                           ((prio) << EMIF_PRIO_DMA_SHIFT), \
+                           OMAP_TC_OCPT1_PRIOR); \
+       } while(0)
+
+enum exmap_type {
+       EXMAP_TYPE_MEM,
+       EXMAP_TYPE_FB
+};
+
+struct exmap_tbl {
+       unsigned int valid:1;
+       unsigned int cntnu:1;   /* grouping */
+       enum exmap_type type;
+       void *buf;
+       void *vadr;
+       unsigned int order;
+};
+#define DSPMMU_TLB_LINES       32
+static struct exmap_tbl exmap_tbl[DSPMMU_TLB_LINES];
+static DECLARE_RWSEM(exmap_sem);
+
+static int dsp_exunmap(unsigned long dspadr);
+
+static void *dspvect_page;
+static unsigned long dsp_fault_adr;
+
+static __inline__ unsigned long lineup_offset(unsigned long adr,
+                                             unsigned long ref,
+                                             unsigned long mask)
+{
+       unsigned long newadr;
+
+       newadr = (adr & ~mask) | (ref & mask);
+       if (newadr < adr)
+               newadr += mask + 1;
+       return newadr;
+}
+
+/*
+ * kmem_reserve(), kmem_release():
+ * reserve or release kernel memory for exmap().
+ *
+ * exmap() might request consecutive 1MB or 64kB,
+ * but it will be difficult after memory pages are fragmented.
+ * So, user can reserve such memory blocks in the early phase
+ * through kmem_reserve().
+ */
+struct kmem_pool {
+       struct semaphore sem;
+       unsigned long buf[16];
+       int count;
+};
+
+#define KMEM_POOL_INIT(name) \
+{ \
+       .sem = __MUTEX_INITIALIZER((name).sem), \
+}
+#define DECLARE_KMEM_POOL(name) \
+       struct kmem_pool name = KMEM_POOL_INIT(name)
+
+DECLARE_KMEM_POOL(kmem_pool_1M);
+DECLARE_KMEM_POOL(kmem_pool_64K);
+
+static void dsp_kmem_release(void)
+{
+       int i;
+
+       down(&kmem_pool_1M.sem);
+       for (i = 0; i < kmem_pool_1M.count; i++) {
+               if (kmem_pool_1M.buf[i])
+                       free_pages(kmem_pool_1M.buf[i], ORDER_1MB);
+       }
+       kmem_pool_1M.count = 0;
+       up(&kmem_pool_1M.sem);
+
+       down(&kmem_pool_64K.sem);
+       for (i = 0; i < kmem_pool_64K.count; i++) {
+               if (kmem_pool_64K.buf[i])
+                       free_pages(kmem_pool_64K.buf[i], ORDER_64KB);
+       }
+       kmem_pool_64K.count = 0;
+       up(&kmem_pool_1M.sem);
+}
+
+static int dsp_kmem_reserve(unsigned long size)
+{
+       unsigned long buf;
+       unsigned int order;
+       unsigned long unit;
+       unsigned long _size;
+       struct kmem_pool *pool;
+       int i;
+
+       /* alignment check */
+       if (!is_aligned(size, SZ_64KB)) {
+               printk(KERN_ERR
+                      "omapdsp: size(0x%lx) is not multiple of 64KB.\n", size);
+               return -EINVAL;
+       }
+       if (size > DSPSPACE_SIZE) {
+               printk(KERN_ERR
+                      "omapdsp: size(0x%lx) is larger than DSP memory space "
+                      "size (0x%x.\n", size, DSPSPACE_SIZE);
+               return -EINVAL;
+       }
+
+       for (_size = size; _size; _size -= unit) {
+               if (_size >= SZ_1MB) {
+                       unit = SZ_1MB;
+                       order = ORDER_1MB;
+                       pool = &kmem_pool_1M;
+               } else {
+                       unit = SZ_64KB;
+                       order = ORDER_64KB;
+                       pool = &kmem_pool_64K;
+               }
+
+               buf = __get_dma_pages(GFP_KERNEL, order);
+               if (!buf)
+                       return size - _size;
+               down(&pool->sem);
+               for (i = 0; i < 16; i++) {
+                       if (!pool->buf[i]) {
+                               pool->buf[i] = buf;
+                               pool->count++;
+                               buf = 0;
+                               break;
+                       }
+               }
+               up(&pool->sem);
+
+               if (buf) {      /* pool is full */
+                       free_pages(buf, order);
+                       return size - _size;
+               }
+       }
+
+       return size;
+}
+
+static unsigned long dsp_mem_get_dma_pages(unsigned int order)
+{
+       struct kmem_pool *pool;
+       unsigned long buf = 0;
+       int i;
+
+       switch (order) {
+               case ORDER_1MB:
+                       pool = &kmem_pool_1M;
+                       break;
+               case ORDER_64KB:
+                       pool = &kmem_pool_64K;
+                       break;
+               default:
+                       pool = NULL;
+       }
+
+       if (pool) {
+               down(&pool->sem);
+               for (i = 0; i < pool->count; i++) {
+                       if (pool->buf[i]) {
+                               buf = pool->buf[i];
+                               pool->buf[i] = 0;
+                               break;
+                       }
+               }
+               up(&pool->sem);
+               if (buf)
+                       return buf;
+       }
+
+       /* other size or not found in pool */
+       return __get_dma_pages(GFP_KERNEL, order);
+}
+
+static void dsp_mem_free_pages(unsigned int buf, unsigned int order)
+{
+       struct kmem_pool *pool;
+       int i;
+
+       switch (order) {
+               case ORDER_1MB:
+                       pool = &kmem_pool_1M;
+                       break;
+               case ORDER_64KB:
+                       pool = &kmem_pool_64K;
+                       break;
+               default:
+                       pool = NULL;
+       }
+
+       if (pool) {
+               down(&pool->sem);
+               for (i = 0; i < pool->count; i++) {
+                       if (!pool->buf[i]) {
+                               pool->buf[i] = buf;
+                               buf = 0;
+                       }
+               }
+               up(&pool->sem);
+       }
+
+       /* other size or pool is filled */
+       if (buf)
+               free_pages(buf, order);
+}
+
+/*
+ * ARM MMU operations
+ */
+static int exmap_set_armmmu(unsigned long virt, unsigned long phys,
+                           unsigned long size)
+{
+       long off;
+       unsigned long sz_left;
+       pmd_t *pmdp;
+       pte_t *ptep;
+       int prot_pmd, prot_pte;
+
+       printk(KERN_DEBUG
+              "omapdsp: mapping in ARM MMU, v=0x%08lx, p=0x%08lx, sz=0x%lx\n",
+              virt, phys, size);
+
+       prot_pmd = PMD_TYPE_TABLE | PMD_DOMAIN(DOMAIN_IO);
+       prot_pte = L_PTE_PRESENT | L_PTE_YOUNG | L_PTE_DIRTY | L_PTE_WRITE;
+
+       pmdp = pmd_offset(pgd_offset_k(virt), virt);
+       if (pmd_none(*pmdp)) {
+               ptep = pte_alloc_one_kernel(&init_mm, 0);
+               if (ptep == NULL)
+                       return -ENOMEM;
+               /* note: two PMDs will be set  */
+               pmd_populate_kernel(&init_mm, pmdp, ptep);
+       }
+
+       off = phys - virt;
+       for (sz_left = size;
+            sz_left >= PAGE_SIZE;
+            sz_left -= PAGE_SIZE, virt += PAGE_SIZE) {
+               ptep = pte_offset_kernel(pmdp, virt);
+               set_pte(ptep, __pte((virt + off) | prot_pte));
+       }
+       if (sz_left)
+               BUG();
+
+       return 0;
+}
+
+static void exmap_clear_armmmu(unsigned long virt, unsigned long size)
+{
+       unsigned long sz_left;
+       pmd_t *pmdp;
+       pte_t *ptep;
+
+       printk(KERN_DEBUG
+              "omapdsp: unmapping in ARM MMU, v=0x%08lx, sz=0x%lx\n",
+              virt, size);
+
+       for (sz_left = size;
+            sz_left >= PAGE_SIZE;
+            sz_left -= PAGE_SIZE, virt += PAGE_SIZE) {
+               pmdp = pmd_offset(pgd_offset_k(virt), virt);
+               ptep = pte_offset_kernel(pmdp, virt);
+               pte_clear(&init_mm, virt, ptep);
+       }
+       if (sz_left)
+               BUG();
+}
+
+static int exmap_valid(void *vadr, size_t len)
+{
+       int i;
+
+start:
+       for (i = 0; i < DSPMMU_TLB_LINES; i++) {
+               void *mapadr;
+               unsigned long mapsize;
+               struct exmap_tbl *ent = &exmap_tbl[i];
+
+               if (!ent->valid)
+                       continue;
+               mapadr = (void *)ent->vadr;
+               mapsize = 1 << (ent->order + PAGE_SHIFT);
+               if ((vadr >= mapadr) && (vadr < mapadr + mapsize)) {
+                       if (vadr + len <= mapadr + mapsize) {
+                               /* this map covers whole address. */
+                               return 1;
+                       } else {
+                               /*
+                                * this map covers partially.
+                                * check rest portion.
+                                */
+                               len -= mapadr + mapsize - vadr;
+                               vadr = mapadr + mapsize;
+                               goto start;
+                       }
+               }
+       }
+
+       return 0;
+}
+
+/*
+ * dsp_virt_to_phys()
+ * returns physical address, and sets len to valid length
+ */
+unsigned long dsp_virt_to_phys(void *vadr, size_t *len)
+{
+       int i;
+
+       if (is_dsp_internal_mem(vadr)) {
+               /* DSRAM or SARAM */
+               *len = dspmem_base + dspmem_size - (unsigned long)vadr;
+               return (unsigned long)vadr;
+       }
+
+       /* EXRAM */
+       for (i = 0; i < DSPMMU_TLB_LINES; i++) {
+               void *mapadr;
+               unsigned long mapsize;
+               struct exmap_tbl *ent = &exmap_tbl[i];
+
+               if (!ent->valid)
+                       continue;
+               mapadr = (void *)ent->vadr;
+               mapsize = 1 << (ent->order + PAGE_SHIFT);
+               if ((vadr >= mapadr) && (vadr < mapadr + mapsize)) {
+                       *len = mapadr + mapsize - vadr;
+                       return __pa(ent->buf) + vadr - mapadr;
+               }
+       }
+
+       /* valid mapping not found */
+       return 0;
+}
+
+/*
+ * DSP MMU operations
+ */
+static __inline__ unsigned short get_cam_l_va_mask(unsigned short slst)
+{
+       switch (slst) {
+       case DSPMMU_CAM_L_SLST_1MB:
+               return DSPMMU_CAM_L_VA_TAG_L1_MASK |
+                      DSPMMU_CAM_L_VA_TAG_L2_MASK_1MB;
+       case DSPMMU_CAM_L_SLST_64KB:
+               return DSPMMU_CAM_L_VA_TAG_L1_MASK |
+                      DSPMMU_CAM_L_VA_TAG_L2_MASK_64KB;
+       case DSPMMU_CAM_L_SLST_4KB:
+               return DSPMMU_CAM_L_VA_TAG_L1_MASK |
+                      DSPMMU_CAM_L_VA_TAG_L2_MASK_4KB;
+       case DSPMMU_CAM_L_SLST_1KB:
+               return DSPMMU_CAM_L_VA_TAG_L1_MASK |
+                      DSPMMU_CAM_L_VA_TAG_L2_MASK_1KB;
+       }
+       return 0;
+}
+
+static __inline__ void get_tlb_lock(int *base, int *victim)
+{
+       unsigned short lock = omap_readw(DSPMMU_LOCK);
+       if (base != NULL)
+               *base = (lock & DSPMMU_LOCK_BASE_MASK)
+                       >> DSPMMU_LOCK_BASE_SHIFT;
+       if (victim != NULL)
+               *victim = (lock & DSPMMU_LOCK_VICTIM_MASK)
+                         >> DSPMMU_LOCK_VICTIM_SHIFT;
+}
+
+static __inline__ void set_tlb_lock(int base, int victim)
+{
+       omap_writew((base   << DSPMMU_LOCK_BASE_SHIFT) |
+                   (victim << DSPMMU_LOCK_VICTIM_SHIFT), DSPMMU_LOCK);
+}
+
+static __inline__ void __read_tlb(unsigned short lbase, unsigned short victim,
+                                 unsigned short *cam_h, unsigned short *cam_l,
+                                 unsigned short *ram_h, unsigned short *ram_l)
+{
+       /* set victim */
+       set_tlb_lock(lbase, victim);
+
+       /* read a TLB entry */
+       omap_writew(DSPMMU_LD_TLB_RD, DSPMMU_LD_TLB);
+
+       if (cam_h != NULL)
+               *cam_h = omap_readw(DSPMMU_READ_CAM_H);
+       if (cam_l != NULL)
+               *cam_l = omap_readw(DSPMMU_READ_CAM_L);
+       if (ram_h != NULL)
+               *ram_h = omap_readw(DSPMMU_READ_RAM_H);
+       if (ram_l != NULL)
+               *ram_l = omap_readw(DSPMMU_READ_RAM_L);
+}
+
+static __inline__ void __load_tlb(unsigned short cam_h, unsigned short cam_l,
+                                 unsigned short ram_h, unsigned short ram_l)
+{
+       omap_writew(cam_h, DSPMMU_CAM_H);
+       omap_writew(cam_l, DSPMMU_CAM_L);
+       omap_writew(ram_h, DSPMMU_RAM_H);
+       omap_writew(ram_l, DSPMMU_RAM_L);
+
+       /* flush the entry */
+       dsp_mmu_flush();
+
+       /* load a TLB entry */
+       omap_writew(DSPMMU_LD_TLB_LD, DSPMMU_LD_TLB);
+}
+
+static int dsp_mmu_load_tlb(unsigned long vadr, unsigned long padr,
+                           unsigned short slst, unsigned short prsvd,
+                           unsigned short ap)
+{
+       int lbase, victim;
+       unsigned short cam_l_va_mask;
+
+       clk_use(dsp_ck_handle);
+
+       get_tlb_lock(&lbase, NULL);
+       for (victim = 0; victim < lbase; victim++) {
+               unsigned short cam_l;
+
+               /* read a TLB entry */
+               __read_tlb(lbase, victim, NULL, &cam_l, NULL, NULL);
+               if (!(cam_l & DSPMMU_CAM_L_V))
+                       goto found_victim;
+       }
+       set_tlb_lock(lbase, victim);
+
+found_victim:
+       /* The last (31st) entry cannot be locked? */
+       if (victim == 31) {
+               printk(KERN_ERR "omapdsp: TLB is full.\n");
+               return -EBUSY;
+       }
+
+       cam_l_va_mask = get_cam_l_va_mask(slst);
+       if (vadr &
+           ~(DSPMMU_CAM_H_VA_TAG_H_MASK << 22 |
+             (unsigned long)cam_l_va_mask << 6)) {
+               printk(KERN_ERR
+                      "omapdsp: mapping vadr (0x%06lx) is not "
+                      "aligned boundary\n", vadr);
+               return -EINVAL;
+       }
+
+       __load_tlb(vadr >> 22, (vadr >> 6 & cam_l_va_mask) | prsvd | slst,
+                  padr >> 16, (padr & DSPMMU_RAM_L_RAM_LSB_MASK) | ap);
+
+       /* update lock base */
+       if (victim == lbase)
+               lbase++;
+       set_tlb_lock(lbase, lbase);
+
+       clk_unuse(dsp_ck_handle);
+       return 0;
+}
+
+static int dsp_mmu_clear_tlb(unsigned long vadr)
+{
+       int lbase;
+       int i;
+       int max_valid = 0;
+
+       clk_use(dsp_ck_handle);
+
+       get_tlb_lock(&lbase, NULL);
+       for (i = 0; i < lbase; i++) {
+               unsigned short cam_h, cam_l;
+               unsigned short cam_l_va_mask, cam_vld, slst;
+               unsigned long cam_va;
+
+               /* read a TLB entry */
+               __read_tlb(lbase, i, &cam_h, &cam_l, NULL, NULL);
+
+               cam_vld = cam_l & DSPMMU_CAM_L_V;
+               if (!cam_vld)
+                       continue;
+
+               slst = cam_l & DSPMMU_CAM_L_SLST_MASK;
+               cam_l_va_mask = get_cam_l_va_mask(slst);
+               cam_va = (unsigned long)(cam_h & DSPMMU_CAM_H_VA_TAG_H_MASK) << 22 |
+                        (unsigned long)(cam_l & cam_l_va_mask) << 6;
+
+               if (cam_va == vadr)
+                       /* flush the entry */
+                       dsp_mmu_flush();
+               else
+                       max_valid = i;
+       }
+
+       /* set new lock base */
+       set_tlb_lock(max_valid+1, max_valid+1);
+
+       clk_unuse(dsp_ck_handle);
+       return 0;
+}
+
+static void dsp_mmu_gflush(void)
+{
+       clk_use(dsp_ck_handle);
+
+       __dsp_mmu_gflush();
+       set_tlb_lock(1, 1);
+
+       clk_unuse(dsp_ck_handle);
+}
+
+/*
+ * dsp_exmap()
+ *
+ * OMAP_DSP_MEM_IOCTL_EXMAP ioctl calls this function with padr=0.
+ * In this case, the buffer for DSP is allocated in this routine,
+ * then it is mapped.
+ * On the other hand, for example - frame buffer sharing, calls
+ * this function with padr set. It means some known address space
+ * pointed with padr is going to be shared with DSP.
+ */
+static int dsp_exmap(unsigned long dspadr, unsigned long padr,
+                    unsigned long size, enum exmap_type type)
+{
+       unsigned short slst;
+       void *buf;
+       unsigned int order = 0;
+       unsigned long unit;
+       unsigned int cntnu = 0;
+       unsigned long _dspadr = dspadr;
+       unsigned long _padr = padr;
+       void *_vadr = dspbyte_to_virt(dspadr);
+       unsigned long _size = size;
+       struct exmap_tbl *exmap_ent;
+       int status;
+       int i;
+
+#define MINIMUM_PAGESZ SZ_4KB
+       /*
+        * alignment check
+        */
+       if (!is_aligned(size, MINIMUM_PAGESZ)) {
+               printk(KERN_ERR
+                      "omapdsp: size(0x%lx) is not multiple of 4KB.\n", size);
+               return -EINVAL;
+       }
+       if (!is_aligned(dspadr, MINIMUM_PAGESZ)) {
+               printk(KERN_ERR
+                      "omapdsp: DSP address(0x%lx) is not aligned.\n", dspadr);
+               return -EINVAL;
+       }
+       if (!is_aligned(padr, MINIMUM_PAGESZ)) {
+               printk(KERN_ERR
+                      "omapdsp: physical address(0x%lx) is not aligned.\n",
+                      padr);
+               return -EINVAL;
+       }
+
+       /* address validity check */
+       if ((dspadr < dspmem_size) ||
+           (dspadr >= DSPSPACE_SIZE) ||
+           ((dspadr + size > DSP_INIT_PAGE) &&
+            (dspadr < DSP_INIT_PAGE + PAGE_SIZE))) {
+               printk(KERN_ERR
+                      "omapdsp: illegal address/size for dsp_exmap().\n");
+               return -EINVAL;
+       }
+
+       down_write(&exmap_sem);
+
+       /* overlap check */
+       for (i = 0; i < DSPMMU_TLB_LINES; i++) {
+               unsigned long mapsize;
+               struct exmap_tbl *tmp_ent = &exmap_tbl[i];
+
+               if (!tmp_ent->valid)
+                       continue;
+               mapsize = 1 << (tmp_ent->order + PAGE_SHIFT);
+               if ((_vadr + size > tmp_ent->vadr) &&
+                   (_vadr < tmp_ent->vadr + mapsize)) {
+                       printk(KERN_ERR "omapdsp: exmap page overlap!\n");
+                       up_write(&exmap_sem);
+                       return -EINVAL;
+               }
+       }
+
+start:
+       buf = NULL;
+       /* Are there any free TLB lines?  */
+       for (i = 0; i < DSPMMU_TLB_LINES; i++) {
+               if (!exmap_tbl[i].valid)
+                       goto found_free;
+       }
+       printk(KERN_ERR "omapdsp: DSP TLB is full.\n");
+       status = -EBUSY;
+       goto fail;
+
+found_free:
+       exmap_ent = &exmap_tbl[i];
+
+       if ((_size >= SZ_1MB) &&
+           (is_aligned(_padr, SZ_1MB) || (padr == 0)) &&
+           is_aligned(_dspadr, SZ_1MB)) {
+               unit = SZ_1MB;
+               slst = DSPMMU_CAM_L_SLST_1MB;
+               order = ORDER_1MB;
+       } else if ((_size >= SZ_64KB) &&
+                  (is_aligned(_padr, SZ_64KB) || (padr == 0)) &&
+                  is_aligned(_dspadr, SZ_64KB)) {
+               unit = SZ_64KB;
+               slst = DSPMMU_CAM_L_SLST_64KB;
+               order = ORDER_64KB;
+       } else /* if (_size >= SZ_4KB) */ {
+               unit = SZ_4KB;
+               slst = DSPMMU_CAM_L_SLST_4KB;
+               order = ORDER_4KB;
+       }
+#if 0  /* 1KB is not enabled */
+       else if (_size >= SZ_1KB) {
+               unit = SZ_1KB;
+               slst = DSPMMU_CAM_L_SLST_1KB;
+               order = XXX;
+       }
+#endif
+
+       /* buffer allocation */
+       if (type == EXMAP_TYPE_MEM) {
+               struct page *page, *ps, *pe;
+
+               buf = (void *)dsp_mem_get_dma_pages(order);
+               if (buf == NULL) {
+                       status = -ENOMEM;
+                       goto fail;
+               }
+               /* mark the pages as reserved; this is needed for mmap */
+               ps = virt_to_page(buf);
+               pe = virt_to_page(buf + unit);
+               for (page = ps; page < pe; page++) {
+                       SetPageReserved(page);
+               }
+               _padr = __pa(buf);
+       }
+
+       /*
+        * mapping for ARM MMU:
+        * we should not access to the allocated memory through 'buf'
+        * since this area should not be cashed.
+        */
+       status = exmap_set_armmmu((unsigned long)_vadr, _padr, unit);
+       if (status < 0)
+               goto fail;
+
+       /* loading DSP TLB entry */
+       status = dsp_mmu_load_tlb(_dspadr, _padr, slst, 0, DSPMMU_RAM_L_AP_FA);
+       if (status < 0) {
+               exmap_clear_armmmu((unsigned long)_vadr, unit);
+               goto fail;
+       }
+
+       exmap_ent->buf   = buf;
+       exmap_ent->vadr  = _vadr;
+       exmap_ent->order = order;
+       exmap_ent->valid = 1;
+       exmap_ent->cntnu = cntnu;
+       exmap_ent->type  = type;
+
+       if ((_size -= unit) == 0) {     /* normal completion */
+               up_write(&exmap_sem);
+               return size;
+       }
+
+       _dspadr += unit;
+       _vadr   += unit;
+       _padr = padr ? _padr + unit : 0;
+       cntnu = 1;
+       goto start;
+
+fail:
+       up_write(&exmap_sem);
+       if (buf)
+               dsp_mem_free_pages((unsigned long)buf, order);
+       dsp_exunmap(dspadr);
+       return status;
+}
+
+static unsigned long unmap_free_arm(struct exmap_tbl *ent)
+{
+       unsigned long size;
+
+       /* clearing ARM MMU */
+       size = 1 << (ent->order + PAGE_SHIFT);
+       exmap_clear_armmmu((unsigned long)ent->vadr, size);
+
+       /* freeing allocated memory */
+       if (ent->type == EXMAP_TYPE_MEM) {
+               dsp_mem_free_pages((unsigned long)ent->buf, ent->order);
+               printk(KERN_DEBUG
+                      "omapdsp: freeing 0x%lx bytes @ adr 0x%8p\n",
+                      size, ent->buf);
+       }
+
+       return size;
+}
+
+static int dsp_exunmap(unsigned long dspadr)
+{
+       void *vadr;
+       unsigned long size;
+       int total = 0;
+       struct exmap_tbl *ent;
+       int idx;
+
+       vadr = dspbyte_to_virt(dspadr);
+       down_write(&exmap_sem);
+       for (idx = 0; idx < DSPMMU_TLB_LINES; idx++) {
+               ent = &exmap_tbl[idx];
+               if (!ent->valid)
+                       continue;
+               if (ent->vadr == vadr)
+                       goto found_map;
+       }
+       up_write(&exmap_sem);
+       printk(KERN_WARNING
+              "omapdsp: address %06lx not found in exmap_tbl.\n", dspadr);
+       return -EINVAL;
+
+found_map:
+       /* clearing DSP TLB entry */
+       dsp_mmu_clear_tlb(dspadr);
+
+       /* clear ARM MMU and free buffer */
+       size = unmap_free_arm(ent);
+       ent->valid = 0;
+       total += size;
+
+       /* we don't free PTEs */
+
+       /* flush TLB */
+       flush_tlb_kernel_range((unsigned long)vadr, (unsigned long)vadr + size);
+
+       /* check if next mapping is in same group */
+       if (++idx == DSPMMU_TLB_LINES)
+               goto up_out;    /* normal completion */
+       ent = &exmap_tbl[idx];
+       if (!ent->valid || !ent->cntnu)
+               goto up_out;    /* normal completion */
+
+       dspadr += size;
+       vadr   += size;
+       if (ent->vadr == vadr)
+               goto found_map; /* continue */
+
+       printk(KERN_ERR
+              "omapdsp: illegal exmap_tbl grouping!\n"
+              "expected vadr = %p, exmap_tbl[%d].vadr = %p\n",
+              vadr, idx, ent->vadr);
+       up_write(&exmap_sem);
+       return -EINVAL;
+
+up_out:
+       up_write(&exmap_sem);
+       return total;
+}
+
+static void exmap_flush(void)
+{
+       struct exmap_tbl *ent;
+       int i;
+
+       down_write(&exmap_sem);
+
+       /* clearing DSP TLB entry */
+       dsp_mmu_gflush();
+
+       /* exmap_tbl[0] should be preserved */
+       for (i = 1; i < DSPMMU_TLB_LINES; i++) {
+               ent = &exmap_tbl[i];
+               if (ent->valid) {
+                       unmap_free_arm(ent);
+                       ent->valid = 0;
+               }
+       }
+
+       /* flush TLB */
+       flush_tlb_kernel_range(dspmem_base + dspmem_size,
+                              dspmem_base + DSPSPACE_SIZE);
+       /*
+        * we should clear processes' mm as well,
+        * because processes might had accessed to those spaces
+        * with old table in the past.
+        */
+       up_write(&exmap_sem);
+}
+
+#ifdef CONFIG_OMAP_DSP_FBEXPORT
+#ifndef CONFIG_FB
+#error You configured OMAP_DSP_FBEXPORT, but FB was not configured!
+#endif /* CONFIG_FB */
+
+static int dsp_fbexport(unsigned long *dspadr)
+{
+       unsigned long dspadr_actual;
+       unsigned long padr_sys, padr, fbsz_sys, fbsz;
+       int cnt;
+
+       printk(KERN_DEBUG "omapdsp: frame buffer export\n");
+
+       if (num_registered_fb == 0) {
+               printk(KERN_INFO "omapdsp: frame buffer not registered.\n");
+               return -EINVAL;
+       }
+       if (num_registered_fb != 1) {
+               printk(KERN_INFO
+                      "omapdsp: %d frame buffers found. we use first one.\n",
+                      num_registered_fb);
+       }
+       padr_sys = registered_fb[0]->fix.smem_start;
+       fbsz_sys = registered_fb[0]->fix.smem_len;
+       if (fbsz_sys == 0) {
+               printk(KERN_ERR
+                      "omapdsp: framebuffer doesn't seem to be configured "
+                      "correctly! (size=0)\n");
+               return -EINVAL;
+       }
+
+       /*
+        * align padr and fbsz to 4kB boundary
+        * (should be noted to the user afterwards!)
+        */
+       padr = padr_sys & ~(SZ_4KB-1);
+       fbsz = (fbsz_sys + padr_sys - padr + SZ_4KB-1) & ~(SZ_4KB-1);
+
+       /* line up dspadr offset with padr */
+       dspadr_actual =
+               (fbsz > SZ_1MB) ?  lineup_offset(*dspadr, padr, SZ_1MB-1) :
+               (fbsz > SZ_64KB) ? lineup_offset(*dspadr, padr, SZ_64KB-1) :
+               /* (fbsz > SZ_4KB) ? */ *dspadr;
+       if (dspadr_actual != *dspadr)
+               printk(KERN_DEBUG
+                      "omapdsp: actual dspadr for FBEXPORT = %08lx\n",
+                      dspadr_actual);
+       *dspadr = dspadr_actual;
+
+       cnt = dsp_exmap(dspadr_actual, padr, fbsz, EXMAP_TYPE_FB);
+       if (cnt < 0) {
+               printk(KERN_ERR "omapdsp: exmap failure.\n");
+               return cnt;
+       }
+
+       if ((padr != padr_sys) || (fbsz != fbsz_sys)) {
+               printk(KERN_WARNING
+"  !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n"
+"  !!  screen base address or size is not aligned in 4kB:           !!\n"
+"  !!    actual screen  adr = %08lx, size = %08lx             !!\n"
+"  !!    exporting      adr = %08lx, size = %08lx             !!\n"
+"  !!  Make sure that the framebuffer is allocated with 4kB-order!  !!\n"
+"  !!  Otherwise DSP can corrupt the kernel memory.                 !!\n"
+"  !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n",
+                      padr_sys, fbsz_sys, padr, fbsz);
+       }
+
+       /* increase the DMA priority */
+       set_emiff_dma_prio(15);
+
+       return cnt;
+}
+
+#else /* CONFIG_OMAP_DSP_FBEXPORT */
+
+static int dsp_fbexport(unsigned long *dspadr)
+{
+       printk(KERN_ERR "omapdsp: FBEXPORT function is not enabled.\n");
+       return -EINVAL;
+}
+
+#endif /* CONFIG_OMAP_DSP_FBEXPORT */
+
+static int dsp_mmu_itack(void)
+{
+       unsigned long dspadr;
+
+       printk(KERN_INFO "omapdsp: sending DSP MMU interrupt ack.\n");
+       if (!dsp_err_mmu_isset()) {
+               printk(KERN_ERR "omapdsp: DSP MMU error has not been set.\n");
+               return -EINVAL;
+       }
+       dspadr = dsp_fault_adr & ~(SZ_4K-1);
+       dsp_exmap(dspadr, 0, SZ_4K, EXMAP_TYPE_MEM);    /* FIXME: reserve TLB entry for this */
+       printk(KERN_INFO "omapdsp: falling into recovery runlevel...\n");
+       dsp_runlevel(OMAP_DSP_MBCMD_RUNLEVEL_RECOVERY);
+       __dsp_mmu_itack();
+       udelay(100);
+       dsp_exunmap(dspadr);
+       dsp_err_mmu_clear();
+       return 0;
+}
+
+static void dsp_mmu_init(void)
+{
+       unsigned long phys;
+       void *virt;
+
+       clk_use(dsp_ck_handle);
+       down_write(&exmap_sem);
+
+       dsp_mmu_disable();      /* clear all */
+       udelay(100);
+       dsp_mmu_enable();
+
+       /* mapping for ARM MMU */
+       phys = __pa(dspvect_page);
+       virt = dspbyte_to_virt(DSP_INIT_PAGE);  /* 0xe0fff000 */
+       exmap_set_armmmu((unsigned long)virt, phys, PAGE_SIZE);
+       exmap_tbl[0].buf   = dspvect_page;
+       exmap_tbl[0].vadr  = virt;
+       exmap_tbl[0].order = 0;
+       exmap_tbl[0].valid = 1;
+       exmap_tbl[0].cntnu = 0;
+
+       /* DSP TLB initialization */
+       set_tlb_lock(0, 0);
+       /* preserved, full access */
+       dsp_mmu_load_tlb(DSP_INIT_PAGE, phys, DSPMMU_CAM_L_SLST_4KB,
+                        DSPMMU_CAM_L_P, DSPMMU_RAM_L_AP_FA);
+       up_write(&exmap_sem);
+       clk_unuse(dsp_ck_handle);
+}
+
+static void dsp_mmu_shutdown(void)
+{
+       exmap_flush();
+       dsp_mmu_disable();      /* clear all */
+}
+
+/*
+ * dsp_mem_enable() / disable():
+ * if the address is in DSP internal memories,
+ * we send PM mailbox commands so that DSP DMA domain won't go in idle
+ * when ARM is accessing to those memories.
+ * if the address is in external memory, acquire exmap_sem.
+ *
+ * __dsp_mem_enable() / disable() should be called only from __dsp_mbsend().
+ */
+static int dsp_mem_en_count;
+
+int dsp_mem_enable(void *adr)
+{
+       struct mbcmd mb;
+       int ret;
+
+       if (is_dsp_internal_mem(adr)) {
+               if (dsp_is_ready() && (!dsp_mem_en_count) &&
+                   (dsp_icrmask & DSPREG_ICR_DMA_IDLE_DOMAIN)) {
+                       mbcmd_set(mb, MBCMD(PM), OMAP_DSP_MBCMD_PM_ENABLE,
+                                 DSPREG_ICR_DMA_IDLE_DOMAIN);
+                       if ((ret = dsp_mbsend(&mb)) < 0)
+                               return ret;
+                       dsp_mem_en_count++;
+               }
+       } else
+               down_read(&exmap_sem);
+       return 0;
+}
+
+int dsp_mem_disable(void *adr)
+{
+       struct mbcmd mb;
+       int ret;
+
+       if (is_dsp_internal_mem(adr)) {
+               if (dsp_is_ready() && dsp_mem_en_count) {
+                       mbcmd_set(mb, MBCMD(PM), OMAP_DSP_MBCMD_PM_DISABLE,
+                                 DSPREG_ICR_DMA_IDLE_DOMAIN);
+                       if ((ret = dsp_mbsend(&mb)) < 0)
+                               return ret;
+                       dsp_mem_en_count--;
+               }
+       } else
+               up_read(&exmap_sem);
+       return 0;
+}
+
+int __dsp_mem_enable(void *adr)
+{
+       struct mbcmd mb;
+       int ret;
+
+       if (is_dsp_internal_mem(adr)) {
+               if (dsp_is_ready() && (!dsp_mem_en_count) &&
+                   (dsp_icrmask & DSPREG_ICR_DMA_IDLE_DOMAIN)) {
+                       mbcmd_set(mb, MBCMD(PM), OMAP_DSP_MBCMD_PM_ENABLE,
+                                 DSPREG_ICR_DMA_IDLE_DOMAIN);
+                       if ((ret = __mbsend(&mb)) < 0)
+                               return ret;
+                       dsp_mem_en_count++;
+               }
+       } else
+               down_read(&exmap_sem);
+       return 0;
+}
+
+int __dsp_mem_disable(void *adr)
+{
+       struct mbcmd mb;
+       int ret;
+
+       if (is_dsp_internal_mem(adr)) {
+               if (dsp_is_ready() && dsp_mem_en_count) {
+                       mbcmd_set(mb, MBCMD(PM), OMAP_DSP_MBCMD_PM_DISABLE,
+                                 DSPREG_ICR_DMA_IDLE_DOMAIN);
+                       if ((ret = __mbsend(&mb)) < 0)
+                               return ret;
+                       dsp_mem_en_count--;
+               }
+       } else
+               up_read(&exmap_sem);
+       return 0;
+}
+
+/*
+ * dsp_mem file operations
+ */
+static loff_t dsp_mem_lseek(struct file *file, loff_t offset, int orig)
+{
+       loff_t ret;
+
+       down(&file->f_dentry->d_inode->i_sem);
+       switch (orig) {
+       case 0:
+               file->f_pos = offset;
+               ret = file->f_pos;
+               break;
+       case 1:
+               file->f_pos += offset;
+               ret = file->f_pos;
+               break;
+       default:
+               ret = -EINVAL;
+       }
+       up(&file->f_dentry->d_inode->i_sem);
+       return ret;
+}
+
+static ssize_t intmem_read(struct file *file, char *buf, size_t count,
+                          loff_t *ppos)
+{
+       unsigned long p = *ppos;
+       void *vadr = dspbyte_to_virt(p);
+       ssize_t size = dspmem_size;
+       ssize_t read;
+
+       if (p >= size)
+               return 0;
+       clk_use(api_ck_handle);
+       read = count;
+       if (count > size - p)
+               read = size - p;
+       if (copy_to_user(buf, vadr, read)) {
+               read = -EFAULT;
+               goto finish;
+       }
+       *ppos += read;
+finish:
+       clk_unuse(api_ck_handle);
+       return read;
+}
+
+static ssize_t exmem_read(struct file *file, char *buf, size_t count,
+                         loff_t *ppos)
+{
+       unsigned long p = *ppos;
+       void *vadr = dspbyte_to_virt(p);
+       ssize_t ret;
+
+       down_read(&exmap_sem);
+       if (!exmap_valid(vadr, count)) {
+               printk(KERN_ERR
+                      "omapdsp: DSP address %08lx / size %08x "
+                      "is not valid!\n", p, count);
+               ret = -EFAULT;
+               goto up_out;
+       }
+       if (count > DSPSPACE_SIZE - p)
+               count = DSPSPACE_SIZE - p;
+       if (copy_to_user(buf, vadr, count)) {
+               ret = -EFAULT;
+               goto up_out;
+       }
+       *ppos += count;
+
+       up_read(&exmap_sem);
+       return count;
+
+up_out:
+       up_read(&exmap_sem);
+       return ret;
+}
+
+static ssize_t dsp_mem_read(struct file *file, char *buf, size_t count,
+                           loff_t *ppos)
+{
+       if (is_dspbyte_internal_mem(*ppos))
+               return intmem_read(file, buf, count, ppos);
+       else
+               return exmem_read(file, buf, count, ppos);
+}
+
+static ssize_t intmem_write(struct file *file, const char *buf, size_t count,
+                           loff_t *ppos)
+{
+       unsigned long p = *ppos;
+       void *vadr = dspbyte_to_virt(p);
+       ssize_t size = dspmem_size;
+       ssize_t written;
+
+       if (p >= size)
+               return 0;
+       clk_use(api_ck_handle);
+       written = count;
+       if (count > size - p)
+               written = size - p;
+       if (copy_from_user(vadr, buf, written)) {
+               written = -EFAULT;
+               goto finish;
+       }
+       *ppos += written;
+finish:
+       clk_unuse(api_ck_handle);
+       return written;
+}
+
+static ssize_t exmem_write(struct file *file, const char *buf, size_t count,
+                          loff_t *ppos)
+{
+       unsigned long p = *ppos;
+       void *vadr = dspbyte_to_virt(p);
+       ssize_t ret;
+
+       down_read(&exmap_sem);
+       if (!exmap_valid(vadr, count)) {
+               printk(KERN_ERR
+                      "omapdsp: DSP address %08lx / size %08x "
+                      "is not valid!\n", p, count);
+               ret = -EFAULT;
+               goto up_out;
+       }
+       if (count > DSPSPACE_SIZE - p)
+               count = DSPSPACE_SIZE - p;
+       if (copy_from_user(vadr, buf, count)) {
+               ret = -EFAULT;
+               goto up_out;
+       }
+       *ppos += count;
+
+       up_read(&exmap_sem);
+       return count;
+
+up_out:
+       up_read(&exmap_sem);
+       return ret;
+}
+
+static ssize_t dsp_mem_write(struct file *file, const char *buf, size_t count,
+                            loff_t *ppos)
+{
+       if (is_dspbyte_internal_mem(*ppos))
+               return intmem_write(file, buf, count, ppos);
+       else
+               return exmem_write(file, buf, count, ppos);
+}
+
+static int dsp_mem_ioctl(struct inode *inode, struct file *file,
+                        unsigned int cmd, unsigned long arg)
+{
+       switch (cmd) {
+       case OMAP_DSP_MEM_IOCTL_MMUINIT:
+               dsp_mmu_init();
+               return 0;
+
+       case OMAP_DSP_MEM_IOCTL_EXMAP:
+               {
+                       struct omap_dsp_mapinfo mapinfo;
+                       if (copy_from_user(&mapinfo, (void *)arg,
+                                          sizeof(mapinfo)))
+                               return -EFAULT;
+                       return dsp_exmap(mapinfo.dspadr, 0, mapinfo.size,
+                                        EXMAP_TYPE_MEM);
+               }
+
+       case OMAP_DSP_MEM_IOCTL_EXUNMAP:
+               return dsp_exunmap((unsigned long)arg);
+
+       case OMAP_DSP_MEM_IOCTL_EXMAP_FLUSH:
+               exmap_flush();
+               return 0;
+
+       case OMAP_DSP_MEM_IOCTL_FBEXPORT:
+               {
+                       unsigned long dspadr;
+                       int ret;
+                       if (copy_from_user(&dspadr, (void *)arg, sizeof(long)))
+                               return -EFAULT;
+                       ret = dsp_fbexport(&dspadr);
+                       if (copy_to_user((void *)arg, &dspadr, sizeof(long)))
+                               return -EFAULT;
+                       return ret;
+               }
+
+       case OMAP_DSP_MEM_IOCTL_MMUITACK:
+               return dsp_mmu_itack();
+
+       case OMAP_DSP_MEM_IOCTL_KMEM_RESERVE:
+               {
+                       unsigned long size;
+                       if (copy_from_user(&size, (void *)arg, sizeof(long)))
+                               return -EFAULT;
+                       return dsp_kmem_reserve(size);
+               }
+
+       case OMAP_DSP_MEM_IOCTL_KMEM_RELEASE:
+               dsp_kmem_release();
+               return 0;
+
+       default:
+               return -ENOIOCTLCMD;
+       }
+}
+
+static int dsp_mem_mmap(struct file *file, struct vm_area_struct *vma)
+{
+       /*
+        * FIXME
+        */
+       return -ENOSYS;
+}
+
+static int dsp_mem_open(struct inode *inode, struct file *file)
+{
+       if (!capable(CAP_SYS_RAWIO))
+               return -EPERM;
+       if (dsp_mem_enable((void *)dspmem_base) < 0)
+               return -EBUSY;
+
+       return 0;
+}
+
+static int dsp_mem_release(struct inode *inode, struct file *file)
+{
+       dsp_mem_disable((void *)dspmem_base);
+       return 0;
+}
+
+/*
+ * sysfs files
+ */
+static ssize_t mmu_show(struct device *dev, char *buf)
+{
+       int len;
+       int lbase, victim;
+       int i;
+
+       clk_use(dsp_ck_handle);
+       down_read(&exmap_sem);
+
+       get_tlb_lock(&lbase, &victim);
+
+       len = sprintf(buf, "p: preserved,  v: valid\n"
+                          "ety       cam_va     ram_pa   sz ap\n");
+                       /* 00: p v 0x300000 0x10171800 64KB FA */
+       for (i = 0; i < 32; i++) {
+               unsigned short cam_h, cam_l, ram_h, ram_l;
+               unsigned short cam_l_va_mask, prsvd, cam_vld, slst;
+               unsigned long cam_va;
+               unsigned short ram_l_ap;
+               unsigned long ram_pa;
+               char *pgsz_str, *ap_str;
+
+               /* read a TLB entry */
+               __read_tlb(lbase, i, &cam_h, &cam_l, &ram_h, &ram_l);
+
+               slst = cam_l & DSPMMU_CAM_L_SLST_MASK;
+               cam_l_va_mask = get_cam_l_va_mask(slst);
+               pgsz_str = (slst == DSPMMU_CAM_L_SLST_1MB) ? " 1MB":
+                          (slst == DSPMMU_CAM_L_SLST_64KB)? "64KB":
+                          (slst == DSPMMU_CAM_L_SLST_4KB) ? " 4KB":
+                                                            " 1KB";
+               prsvd    = cam_l & DSPMMU_CAM_L_P;
+               cam_vld  = cam_l & DSPMMU_CAM_L_V;
+               ram_l_ap = ram_l & DSPMMU_RAM_L_AP_MASK;
+               ap_str = (ram_l_ap == DSPMMU_RAM_L_AP_RO) ? "RO":
+                        (ram_l_ap == DSPMMU_RAM_L_AP_FA) ? "FA":
+                                                           "NA";
+               cam_va = (unsigned long)(cam_h & DSPMMU_CAM_H_VA_TAG_H_MASK) << 22 |
+                        (unsigned long)(cam_l & cam_l_va_mask) << 6;
+               ram_pa = (unsigned long)ram_h << 16 |
+                        (ram_l & DSPMMU_RAM_L_RAM_LSB_MASK);
+
+               if (i == lbase)
+                       len += sprintf(buf + len, "lock base = %d\n", lbase);
+               if (i == victim)
+                       len += sprintf(buf + len, "victim    = %d\n", victim);
+               /* 00: p v 0x300000 0x10171800 64KB FA */
+               len += sprintf(buf + len,
+                              "%02d: %c %c 0x%06lx 0x%08lx %s %s\n",
+                              i,
+                              prsvd   ? 'p' : ' ',
+                              cam_vld ? 'v' : ' ',
+                              cam_va, ram_pa, pgsz_str, ap_str);
+       }
+
+       /* restore victim entry */
+       set_tlb_lock(lbase, victim);
+
+       up_read(&exmap_sem);
+       clk_unuse(dsp_ck_handle);
+       return len;
+}
+
+static struct device_attribute dev_attr_mmu = __ATTR_RO(mmu);
+
+static ssize_t exmap_show(struct device *dev, char *buf)
+{
+       int len;
+       int i;
+
+       down_read(&exmap_sem);
+       len = sprintf(buf, "v: valid,  c: cntnu\n"
+                          "ety           vadr        buf od\n");
+                        /* 00: v c 0xe0300000 0xc0171800  0 */
+       for (i = 0; i < DSPMMU_TLB_LINES; i++) {
+               struct exmap_tbl *ent = &exmap_tbl[i];
+               /* 00: v c 0xe0300000 0xc0171800  0 */
+               len += sprintf(buf + len, "%02d: %c %c 0x%8p 0x%8p %2d\n",
+                              i,
+                              ent->valid ? 'v' : ' ',
+                              ent->cntnu ? 'c' : ' ',
+                              ent->vadr, ent->buf, ent->order);
+       }
+
+       up_read(&exmap_sem);
+       return len;
+}
+
+static struct device_attribute dev_attr_exmap = __ATTR_RO(exmap);
+
+/*
+ * DSP MMU interrupt handler
+ */
+
+/*
+ * MMU fault mask:
+ * We ignore prefetch err.
+ */
+#define MMUFAULT_MASK \
+       (DSPMMU_FAULT_ST_PERM |\
+        DSPMMU_FAULT_ST_TLB_MISS |\
+        DSPMMU_FAULT_ST_TRANS)
+irqreturn_t dsp_mmu_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+       unsigned short status;
+       unsigned short adh, adl;
+       unsigned short dp;
+
+       status = omap_readw(DSPMMU_FAULT_ST);
+       adh = omap_readw(DSPMMU_FAULT_AD_H);
+       adl = omap_readw(DSPMMU_FAULT_AD_L);
+       dp = adh & DSPMMU_FAULT_AD_H_DP;
+       dsp_fault_adr = MKLONG(adh & DSPMMU_FAULT_AD_H_ADR_MASK, adl);
+       /* if the fault is masked, nothing to do */
+       if ((status & MMUFAULT_MASK) == 0) {
+               printk(KERN_DEBUG "DSP MMU interrupt, but ignoring.\n");
+               /*
+                * note: in OMAP1710,
+                * when CACHE + DMA domain gets out of idle in DSP,
+                * MMU interrupt occurs but DSPMMU_FAULT_ST is not set.
+                * in this case, we just ignore the interrupt.
+                */
+               if (status) {
+                       printk(KERN_DEBUG "%s%s%s%s\n",
+                              (status & DSPMMU_FAULT_ST_PREF)?
+                                       "  (prefetch err)" : "",
+                              (status & DSPMMU_FAULT_ST_PERM)?
+                                       "  (permission fault)" : "",
+                              (status & DSPMMU_FAULT_ST_TLB_MISS)?
+                                       "  (TLB miss)" : "",
+                              (status & DSPMMU_FAULT_ST_TRANS) ?
+                                       "  (translation fault)": "");
+                       printk(KERN_DEBUG
+                              "fault address = %s: 0x%06lx\n",
+                              dp ? "DATA" : "PROGRAM",
+                              dsp_fault_adr);
+               }
+               return IRQ_HANDLED;
+       }
+
+       printk(KERN_INFO "DSP MMU interrupt!\n");
+       printk(KERN_INFO "%s%s%s%s\n",
+              (status & DSPMMU_FAULT_ST_PREF)?
+                       (MMUFAULT_MASK & DSPMMU_FAULT_ST_PREF)?
+                               "  prefetch err":
+                               "  (prefetch err)":
+                               "",
+              (status & DSPMMU_FAULT_ST_PERM)?
+                       (MMUFAULT_MASK & DSPMMU_FAULT_ST_PERM)?
+                               "  permission fault":
+                               "  (permission fault)":
+                               "",
+              (status & DSPMMU_FAULT_ST_TLB_MISS)?
+                       (MMUFAULT_MASK & DSPMMU_FAULT_ST_TLB_MISS)?
+                               "  TLB miss":
+                               "  (TLB miss)":
+                               "",
+              (status & DSPMMU_FAULT_ST_TRANS)?
+                       (MMUFAULT_MASK & DSPMMU_FAULT_ST_TRANS)?
+                               "  translation fault":
+                               "  (translation fault)":
+                               "");
+       printk(KERN_INFO "fault address = %s: 0x%06lx\n",
+              dp ? "DATA" : "PROGRAM",
+              dsp_fault_adr);
+
+       if (dsp_is_ready()) {
+               /*
+                * If we call dsp_exmap() here,
+                * "kernel BUG at slab.c" occurs.
+                */
+               /* FIXME */
+               dsp_err_mmu_set(dsp_fault_adr);
+       } else {
+               printk(KERN_INFO "Resetting DSP...\n");
+               __dsp_reset();
+               clk_unuse(api_ck_handle);
+               /*
+                * if we enable followings, semaphore lock should be avoided.
+                *
+               printk(KERN_INFO "Flushing DSP MMU...\n");
+               exmap_flush();
+               dsp_mmu_init();
+                */
+       }
+
+       return IRQ_HANDLED;
+}
+
+/*
+ *
+ */
+struct file_operations dsp_mem_fops = {
+       .owner   = THIS_MODULE,
+       .llseek  = dsp_mem_lseek,
+       .read    = dsp_mem_read,
+       .write   = dsp_mem_write,
+       .ioctl   = dsp_mem_ioctl,
+       .mmap    = dsp_mem_mmap,
+       .open    = dsp_mem_open,
+       .release = dsp_mem_release,
+};
+
+void dsp_mem_start(void)
+{
+       dsp_mem_en_count = 0;
+}
+
+int __init dsp_mem_init(void)
+{
+       int i;
+
+       for (i = 0; i < DSPMMU_TLB_LINES; i++) {
+               exmap_tbl[i].valid = 0;
+       }
+
+       dspvect_page = (void *)__get_dma_pages(GFP_KERNEL, 0);
+       if (dspvect_page == NULL) {
+               printk(KERN_ERR
+                      "omapdsp: failed to allocate memory "
+                      "for dsp vector table\n");
+               return -ENOMEM;
+       }
+       dsp_mmu_init();
+       dsp_set_idle_boot_base(IDLEPG_BASE, IDLEPG_SIZE);
+
+       device_create_file(&dsp_device.dev, &dev_attr_mmu);
+       device_create_file(&dsp_device.dev, &dev_attr_exmap);
+
+       return 0;
+}
+
+void dsp_mem_exit(void)
+{
+       dsp_mmu_shutdown();
+       dsp_kmem_release();
+
+       if (dspvect_page != NULL) {
+               unsigned long virt;
+               pmd_t *pmdp;
+               pte_t *ptep;
+
+               free_page((unsigned long)dspvect_page);
+               dspvect_page = NULL;
+
+               virt = (unsigned long)dspbyte_to_virt(DSP_INIT_PAGE);
+               pmdp = pmd_offset(pgd_offset_k(virt), virt);
+               ptep = pte_offset_kernel(pmdp, 0);
+               pmd_clear(pmdp);
+               pte_free_kernel(ptep);
+       }
+
+       device_remove_file(&dsp_device.dev, &dev_attr_mmu);
+       device_remove_file(&dsp_device.dev, &dev_attr_exmap);
+}
diff --git a/arch/arm/mach-omap/dsp/error.c b/arch/arm/mach-omap/dsp/error.c
new file mode 100644 (file)
index 0000000..ac6d8d8
--- /dev/null
@@ -0,0 +1,195 @@
+/*
+ * linux/arch/arm/mach-omap/dsp/error.c
+ *
+ * OMAP DSP error detection I/F device driver
+ *
+ * Copyright (C) 2002-2005 Nokia Corporation
+ *
+ * Written by Toshihiro Kobayashi <toshihiro.kobayashi@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * 2004/11/22:  DSP Gateway version 3.2
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/major.h>
+#include <linux/fs.h>
+#include <linux/poll.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <asm/ioctls.h>
+#include <asm/arch/dsp.h>
+#include "dsp.h"
+
+static DECLARE_WAIT_QUEUE_HEAD(err_wait_q);
+static unsigned long errcode;
+static int errcnt;
+static unsigned short wdtval;  /* FIXME: read through ioctl */
+static unsigned long mmu_fadr; /* FIXME: read through ioctl */
+
+/*
+ * DSP error detection device file operations
+ */
+static ssize_t dsp_err_read(struct file *file, char *buf, size_t count,
+                           loff_t *ppos)
+{
+       unsigned long flags;
+       int status;
+
+       if (count < 4)
+               return 0;
+
+       if (errcnt == 0) {
+               long current_state;
+               DECLARE_WAITQUEUE(wait, current);
+
+               add_wait_queue(&err_wait_q, &wait);
+               current_state = current->state;
+               set_current_state(TASK_INTERRUPTIBLE);
+               if (errcnt == 0)        /* last check */
+                       schedule();
+               set_current_state(current_state);
+               remove_wait_queue(&err_wait_q, &wait);
+               if (signal_pending(current))
+                       return -EINTR;
+       }
+
+       local_irq_save(flags);
+       status = copy_to_user(buf, &errcode, 4);
+       if (status) {
+               local_irq_restore(flags);
+               return -EFAULT;
+       }
+       errcnt = 0;
+       local_irq_restore(flags);
+
+       return 4;
+}
+
+static unsigned int dsp_err_poll(struct file *file, poll_table *wait)
+{
+       unsigned int mask = 0;
+
+       poll_wait(file, &err_wait_q, wait);
+       if (errcnt != 0)
+               mask |= POLLIN | POLLRDNORM;
+
+       return mask;
+}
+
+struct file_operations dsp_err_fops = {
+       .owner = THIS_MODULE,
+       .poll  = dsp_err_poll,
+       .read  = dsp_err_read,
+};
+
+/*
+ * DSP MMU
+ */
+void dsp_err_mmu_set(unsigned long adr)
+{
+       disable_irq(INT_DSP_MMU);
+       errcode |= OMAP_DSP_ERRDT_MMU;
+       errcnt++;
+       mmu_fadr = adr;
+       wake_up_interruptible(&err_wait_q);
+}
+
+void dsp_err_mmu_clear(void)
+{
+       errcode &= ~OMAP_DSP_ERRDT_MMU;
+       enable_irq(INT_DSP_MMU);
+}
+
+int dsp_err_mmu_isset(void)
+{
+       return (errcode & OMAP_DSP_ERRDT_MMU) ? 1 : 0;
+}
+
+/*
+ * WDT
+ */
+void dsp_err_wdt_clear(void)
+{
+       errcode &= ~OMAP_DSP_ERRDT_WDT;
+}
+
+int dsp_err_wdt_isset(void)
+{
+       return (errcode & OMAP_DSP_ERRDT_WDT) ? 1 : 0;
+}
+
+/*
+ * functions called from mailbox1 interrupt routine
+ */
+void mbx1_wdt(struct mbcmd *mb)
+{
+       printk(KERN_WARNING "omapdsp: DSP WDT expired!\n");
+       errcode |= OMAP_DSP_ERRDT_WDT;
+       errcnt++;
+       wdtval = mb->data;
+       wake_up_interruptible(&err_wait_q);
+}
+
+extern void mbx1_err_ipbfull(void);
+extern void mbx1_err_fatal(unsigned char tid);
+
+void mbx1_err(struct mbcmd *mb)
+{
+       unsigned char eid = mb->cmd_l;
+       char *eidnm = subcmd_name(mb);
+       unsigned char tid;
+
+       if (eidnm) {
+               printk(KERN_WARNING
+                      "mbx: ERR from DSP (%s): 0x%04x\n", eidnm, mb->data);
+       } else {
+               printk(KERN_WARNING
+                      "mbx: ERR from DSP (unknown EID=%02x): %04x\n",
+                      eid, mb->data);
+       }
+
+       switch (eid) {
+       case OMAP_DSP_EID_IPBFULL:
+               mbx1_err_ipbfull();
+               break;
+
+       case OMAP_DSP_EID_FATAL:
+               tid = mb->data & 0x00ff;
+               mbx1_err_fatal(tid);
+               break;
+       }
+}
+
+/*
+ *
+ */
+void dsp_err_start(void)
+{
+       errcnt = 0;
+       if (dsp_err_wdt_isset())
+               dsp_err_wdt_clear();
+       if (dsp_err_mmu_isset())
+               dsp_err_mmu_clear();
+}
+
+void dsp_err_stop(void)
+{
+       wake_up_interruptible(&err_wait_q);
+}
diff --git a/arch/arm/mach-omap/dsp/fifo.h b/arch/arm/mach-omap/dsp/fifo.h
new file mode 100644 (file)
index 0000000..0f89069
--- /dev/null
@@ -0,0 +1,179 @@
+/*
+ * linux/arch/arm/mach-omap/dsp/fifo.h
+ *
+ * FIFO buffer operators
+ *
+ * Copyright (C) 2002-2005 Nokia Corporation
+ *
+ * Written by Toshihiro Kobayashi <toshihiro.kobayashi@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * 2005/02/24:  DSP Gateway version 3.2
+ */
+
+struct fifo_struct {
+       spinlock_t lock;
+       char *buf;
+       size_t sz;
+       size_t cnt;
+       unsigned int wp;
+};
+
+static inline int alloc_fifo(struct fifo_struct *fifo, size_t sz)
+{
+       if ((fifo->buf = kmalloc(sz, GFP_KERNEL)) == NULL) {
+               fifo->sz = 0;
+               return -ENOMEM;
+       }
+       fifo->sz = sz;
+       fifo->cnt = 0;
+       fifo->wp = 0;
+       return 0;
+}
+
+static inline int init_fifo(struct fifo_struct *fifo, size_t sz)
+{
+       spin_lock_init(&fifo->lock);
+       return alloc_fifo(fifo, sz);
+}
+
+static inline void free_fifo(struct fifo_struct *fifo)
+{
+       spin_lock(&fifo->lock);
+       if (fifo->buf == NULL)
+               return;
+
+       kfree(fifo->buf);
+       fifo->buf = NULL;
+       fifo->sz = 0;
+       spin_unlock(&fifo->lock);
+}
+
+static inline void flush_fifo(struct fifo_struct *fifo)
+{
+       spin_lock(&fifo->lock);
+       fifo->cnt = 0;
+       fifo->wp = 0;
+       spin_unlock(&fifo->lock);
+}
+
+#define fifo_empty(fifo)       ((fifo)->cnt == 0)
+
+static inline int realloc_fifo(struct fifo_struct *fifo, size_t sz)
+{
+       int ret = sz;
+
+       spin_lock(&fifo->lock);
+       if (!fifo_empty(fifo)) {
+               ret = -EBUSY;
+               goto out;
+       }
+
+       /* free */
+       if (fifo->buf)
+               kfree(fifo->buf);
+
+       /* alloc */
+       if ((fifo->buf = kmalloc(sz, GFP_KERNEL)) == NULL) {
+               fifo->sz = 0;
+               ret = -ENOMEM;
+               goto out;
+       }
+       fifo->sz = sz;
+       fifo->cnt = 0;
+       fifo->wp = 0;
+
+out:
+       spin_unlock(&fifo->lock);
+       return ret;
+}
+
+static inline void write_word_to_fifo(struct fifo_struct *fifo,
+                                     unsigned short word)
+{
+       spin_lock(&fifo->lock);
+       *(unsigned short *)&fifo->buf[fifo->wp] = word;
+       if ((fifo->wp += 2) == fifo->sz)
+               fifo->wp = 0;
+       if ((fifo->cnt += 2) > fifo->sz)
+               fifo->cnt = fifo->sz;
+       spin_unlock(&fifo->lock);
+}
+
+/*
+ * (before)
+ *
+ * [*******----------*************]
+ *         ^wp
+ *  <---------------------------->  sz = 30
+ *  <----->          <----------->  cnt = 20
+ *
+ * (read: count=16)
+ *  <->              <----------->  count = 16
+ *                   <----------->  cnt1 = 13
+ *                   ^rp
+ *
+ * (after)
+ * [---****-----------------------]
+ *         ^wp
+ */
+static inline ssize_t copy_to_user_fm_fifo(char *dst, struct fifo_struct *fifo,
+                                          size_t count)
+{
+       int rp;
+       ssize_t ret;
+
+       /* fifo size can be zero */
+       if (fifo->sz == 0)
+               return 0;
+
+       spin_lock(&fifo->lock);
+       if (count > fifo->cnt)
+               count = fifo->cnt;
+
+       if ((rp = fifo->wp - fifo->cnt) >= 0) {
+               /* valid area is straight */
+               if (copy_to_user(dst, &fifo->buf[rp], count)) {
+                       ret = -EFAULT;
+                       goto out;
+               }
+       } else {
+               int cnt1 = -rp;
+               rp += fifo->sz;
+               if (cnt1 >= count) {
+                       /* requested area is straight */
+                       if (copy_to_user(dst, &fifo->buf[rp], count)) {
+                               ret = -EFAULT;
+                               goto out;
+                       }
+               } else {
+                       if (copy_to_user(dst, &fifo->buf[rp], cnt1)) {
+                               ret = -EFAULT;
+                               goto out;
+                       }
+                       if (copy_to_user(dst+cnt1, fifo->buf, count-cnt1)) {
+                               ret = -EFAULT;
+                               goto out;
+                       }
+               }
+       }
+       fifo->cnt -= count;
+       ret = count;
+
+out:
+       spin_unlock(&fifo->lock);
+       return ret;
+}
diff --git a/arch/arm/mach-omap/dsp/hardware_dsp.h b/arch/arm/mach-omap/dsp/hardware_dsp.h
new file mode 100644 (file)
index 0000000..4c70dd1
--- /dev/null
@@ -0,0 +1,196 @@
+/*
+ * linux/arch/arm/mach-omap/dsp/hardware_dsp.h
+ *
+ * Register bit definitions for DSP driver
+ *
+ * Copyright (C) 2002-2005 Nokia Corporation
+ *
+ * Written by Toshihiro Kobayashi <toshihiro.kobayashi@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * 2004/09/30:  DSP Gateway version 3.2
+ */
+
+#ifndef __OMAP_DSP_HARDWARE_DSP_H
+#define __OMAP_DSP_HARDWARE_DSP_H
+
+/*
+ * MAJOR device number: !! allocated arbitrary !!
+ */
+#define OMAP_DSP_CTL_MAJOR             96
+#define OMAP_DSP_TASK_MAJOR            97
+
+/*
+ * Reset Control
+ */
+#define ARM_RSTCT1_SW_RST              0x0008
+#define ARM_RSTCT1_DSP_RST             0x0004
+#define ARM_RSTCT1_DSP_EN              0x0002
+#define ARM_RSTCT1_ARM_RST             0x0001
+
+/*
+ * MPUI
+ */
+#define MPUI_CTRL_WORDSWAP_MASK                0x00600000
+#define MPUI_CTRL_WORDSWAP_ALL         0x00000000
+#define MPUI_CTRL_WORDSWAP_NONAPI      0x00200000
+#define MPUI_CTRL_WORDSWAP_API         0x00400000
+#define MPUI_CTRL_WORDSWAP_NONE                0x00600000
+#define MPUI_CTRL_AP_MASK              0x001c0000
+#define MPUI_CTRL_AP_MDH               0x00000000
+#define MPUI_CTRL_AP_MHD               0x00040000
+#define MPUI_CTRL_AP_DMH               0x00080000
+#define MPUI_CTRL_AP_HMD               0x000c0000
+#define MPUI_CTRL_AP_DHM               0x00100000
+#define MPUI_CTRL_AP_HDM               0x00140000
+#define MPUI_CTRL_BYTESWAP_MASK                0x00030000
+#define MPUI_CTRL_BYTESWAP_NONE                0x00000000
+#define MPUI_CTRL_BYTESWAP_NONAPI      0x00010000
+#define MPUI_CTRL_BYTESWAP_ALL         0x00020000
+#define MPUI_CTRL_BYTESWAP_API         0x00030000
+#define MPUI_CTRL_TIMEOUT_MASK         0x0000ff00
+#define MPUI_CTRL_APIF_HNSTB_DIV_MASK  0x000000f0
+#define MPUI_CTRL_S_NABORT_GL          0x00000008
+#define MPUI_CTRL_S_NABORT_32BIT       0x00000004
+#define MPUI_CTRL_EN_TIMEOUT           0x00000002
+#define MPUI_CTRL_HF_MCUCLK            0x00000001
+#define MPUI_DSP_BOOT_CONFIG_DIRECT    0x00000000
+#define MPUI_DSP_BOOT_CONFIG_PSD_DIRECT        0x00000001
+#define MPUI_DSP_BOOT_CONFIG_IDLE      0x00000002
+#define MPUI_DSP_BOOT_CONFIG_DL16      0x00000003
+#define MPUI_DSP_BOOT_CONFIG_DL32      0x00000004
+#define MPUI_DSP_BOOT_CONFIG_MPUI      0x00000005
+#define MPUI_DSP_BOOT_CONFIG_INTERNAL  0x00000006
+
+/*
+ * DSP boot mode
+ *   direct:        0xffff00
+ *   pseudo direct: 0x080000
+ *   MPUI:          branch 0x010000
+ *   internel:      branch 0x024000
+ */
+#define DSP_BOOT_ADR_DIRECT            0xffff00
+#define DSP_BOOT_ADR_PSD_DIRECT                0x080000
+#define DSP_BOOT_ADR_MPUI              0x010000
+#define DSP_BOOT_ADR_INTERNAL          0x024000
+
+/*
+ * TC
+ */
+#define TC_ENDIANISM_SWAP              0x00000002
+#define TC_ENDIANISM_SWAP_WORD         0x00000002
+#define TC_ENDIANISM_SWAP_BYTE         0x00000000
+#define TC_ENDIANISM_EN                        0x00000001
+
+/*
+ * DSP MMU
+ */
+#define DSPMMU_BASE                    (0xfffed200)
+#define DSPMMU_PREFETCH                        (DSPMMU_BASE + 0x00)
+#define DSPMMU_WALKING_ST              (DSPMMU_BASE + 0x04)
+#define DSPMMU_CNTL                    (DSPMMU_BASE + 0x08)
+#define DSPMMU_FAULT_AD_H              (DSPMMU_BASE + 0x0c)
+#define DSPMMU_FAULT_AD_L              (DSPMMU_BASE + 0x10)
+#define DSPMMU_FAULT_ST                        (DSPMMU_BASE + 0x14)
+#define DSPMMU_IT_ACK                  (DSPMMU_BASE + 0x18)
+#define DSPMMU_TTB_H                   (DSPMMU_BASE + 0x1c)
+#define DSPMMU_TTB_L                   (DSPMMU_BASE + 0x20)
+#define DSPMMU_LOCK                    (DSPMMU_BASE + 0x24)
+#define DSPMMU_LD_TLB                  (DSPMMU_BASE + 0x28)
+#define DSPMMU_CAM_H                   (DSPMMU_BASE + 0x2c)
+#define DSPMMU_CAM_L                   (DSPMMU_BASE + 0x30)
+#define DSPMMU_RAM_H                   (DSPMMU_BASE + 0x34)
+#define DSPMMU_RAM_L                   (DSPMMU_BASE + 0x38)
+#define DSPMMU_GFLUSH                  (DSPMMU_BASE + 0x3c)
+#define DSPMMU_FLUSH_ENTRY             (DSPMMU_BASE + 0x40)
+#define DSPMMU_READ_CAM_H              (DSPMMU_BASE + 0x44)
+#define DSPMMU_READ_CAM_L              (DSPMMU_BASE + 0x48)
+#define DSPMMU_READ_RAM_H              (DSPMMU_BASE + 0x4c)
+#define DSPMMU_READ_RAM_L              (DSPMMU_BASE + 0x50)
+
+#define DSPMMU_CNTL_BURST_16MNGT_EN    0x0020
+#define DSPMMU_CNTL_WTL_EN             0x0004
+#define DSPMMU_CNTL_MMU_EN             0x0002
+#define DSPMMU_CNTL_RESET_SW           0x0001
+
+#define DSPMMU_FAULT_AD_H_DP           0x0100
+#define DSPMMU_FAULT_AD_H_ADR_MASK     0x00ff
+
+#define DSPMMU_FAULT_ST_PREF           0x0008
+#define DSPMMU_FAULT_ST_PERM           0x0004
+#define DSPMMU_FAULT_ST_TLB_MISS       0x0002
+#define DSPMMU_FAULT_ST_TRANS          0x0001
+
+#define DSPMMU_IT_ACK_IT_ACK           0x0001
+
+#define DSPMMU_LOCK_BASE_MASK          0xfc00
+#define DSPMMU_LOCK_BASE_SHIFT         10
+#define DSPMMU_LOCK_VICTIM_MASK                0x03f0
+#define DSPMMU_LOCK_VICTIM_SHIFT       4
+
+#define DSPMMU_CAM_H_VA_TAG_H_MASK             0x0003
+
+#define DSPMMU_CAM_L_VA_TAG_L1_MASK            0xc000
+#define DSPMMU_CAM_L_VA_TAG_L2_MASK_1MB                0x0000
+#define DSPMMU_CAM_L_VA_TAG_L2_MASK_64KB       0x3c00
+#define DSPMMU_CAM_L_VA_TAG_L2_MASK_4KB                0x3fc0
+#define DSPMMU_CAM_L_VA_TAG_L2_MASK_1KB                0x3ff0
+#define DSPMMU_CAM_L_P                         0x0008
+#define DSPMMU_CAM_L_V                         0x0004
+#define DSPMMU_CAM_L_SLST_MASK                 0x0003
+#define DSPMMU_CAM_L_SLST_1MB                  0x0000
+#define DSPMMU_CAM_L_SLST_64KB                 0x0001
+#define DSPMMU_CAM_L_SLST_4KB                  0x0002
+#define DSPMMU_CAM_L_SLST_1KB                  0x0003
+
+#define DSPMMU_RAM_L_RAM_LSB_MASK      0xfc00
+#define DSPMMU_RAM_L_AP_MASK           0x0300
+#define DSPMMU_RAM_L_AP_NA             0x0000
+#define DSPMMU_RAM_L_AP_RO             0x0200
+#define DSPMMU_RAM_L_AP_FA             0x0300
+
+#define DSPMMU_GFLUSH_GFLUSH           0x0001
+
+#define DSPMMU_FLUSH_ENTRY_FLUSH_ENTRY 0x0001
+
+#define DSPMMU_LD_TLB_RD               0x0002
+#define DSPMMU_LD_TLB_LD               0x0001
+
+/*
+ * Mailbox
+ */
+#define MAILBOX_BASE                   (0xfffcf000)
+#define MAILBOX_ARM2DSP1               (MAILBOX_BASE + 0x00)
+#define MAILBOX_ARM2DSP1b              (MAILBOX_BASE + 0x04)
+#define MAILBOX_DSP2ARM1               (MAILBOX_BASE + 0x08)
+#define MAILBOX_DSP2ARM1b              (MAILBOX_BASE + 0x0c)
+#define MAILBOX_DSP2ARM2               (MAILBOX_BASE + 0x10)
+#define MAILBOX_DSP2ARM2b              (MAILBOX_BASE + 0x14)
+#define MAILBOX_ARM2DSP1_Flag          (MAILBOX_BASE + 0x18)
+#define MAILBOX_DSP2ARM1_Flag          (MAILBOX_BASE + 0x1c)
+#define MAILBOX_DSP2ARM2_Flag          (MAILBOX_BASE + 0x20)
+
+/*
+ * DSP ICR
+ */
+#define DSPREG_ICR_EMIF_IDLE_DOMAIN    0x0020
+#define DSPREG_ICR_DPLL_IDLE_DOMAIN    0x0010
+#define DSPREG_ICR_PER_IDLE_DOMAIN     0x0008
+#define DSPREG_ICR_CACHE_IDLE_DOMAIN   0x0004
+#define DSPREG_ICR_DMA_IDLE_DOMAIN     0x0002
+#define DSPREG_ICR_CPU_IDLE_DOMAIN     0x0001
+
+#endif /* __OMAP_DSP_HARDWARE_DSP_H */
diff --git a/arch/arm/mach-omap/dsp/ipbuf.c b/arch/arm/mach-omap/dsp/ipbuf.c
new file mode 100644 (file)
index 0000000..9926dad
--- /dev/null
@@ -0,0 +1,291 @@
+/*
+ * linux/arch/arm/mach-omap/dsp/ipbuf.c
+ *
+ * IPBUF handler
+ *
+ * Copyright (C) 2002-2005 Nokia Corporation
+ *
+ * Written by Toshihiro Kobayashi <toshihiro.kobayashi@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * 2005/02/17:  DSP Gateway version 3.2
+ */
+
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/workqueue.h>
+#include <linux/device.h>
+#include <asm/signal.h>
+#include <asm/arch/dsp.h>
+#include "dsp.h"
+#include "ipbuf.h"
+
+struct ipbuf **ipbuf;
+struct ipbcfg ipbcfg;
+struct ipbuf_sys *ipbuf_sys_da, *ipbuf_sys_ad;
+static struct ipblink ipb_free = IPBLINK_INIT;
+
+void ipbuf_stop(void)
+{
+       ipbcfg.ln = 0;
+       if (ipbuf) {
+               kfree(ipbuf);
+               ipbuf = NULL;
+       }
+}
+
+/*
+ * ipbuf_config() is called by mailbox workqueue
+ */
+int ipbuf_config(unsigned short ln, unsigned short lsz, unsigned long adr)
+{
+       void *base;
+       unsigned long lsz_byte = ((unsigned long)lsz) << 1;
+       size_t size;
+       int ret = 0;
+       int i;
+
+       spin_lock(&ipb_free.lock);
+       INIT_IPBLINK(&ipb_free);
+       spin_unlock(&ipb_free.lock);
+
+       /*
+        * global IPBUF
+        */
+       if (adr & 0x1) {
+               printk(KERN_ERR
+                      "mbx: global ipbuf address (0x%08lx) is odd number!\n",
+                      adr);
+               return -EINVAL;
+       }
+       size = lsz_byte * ln;
+       if (adr + size > DSPSPACE_SIZE) {
+               printk(KERN_ERR
+                      "mbx: ipbuf address (0x%08lx) and size (0x%08x) is "
+                      "illegal!\n", adr, size);
+               return -EINVAL;
+       }
+       base = dspword_to_virt(adr);
+       ipbuf = kmalloc(sizeof(void *) * ln, GFP_KERNEL);
+       if (ipbuf == NULL) {
+               printk(KERN_ERR "mbx: memory allocation for ipbuf failed.\n");
+               return -ENOMEM;
+       }
+       for (i = 0; i < ln; i++) {
+               void *top, *btm;
+
+               top = base + (sizeof(struct ipbuf) + lsz_byte) * i;
+               btm = base + (sizeof(struct ipbuf) + lsz_byte) * (i+1) - 1;
+               ipbuf[i] = (struct ipbuf *)top;
+               if (((unsigned long)top & 0xfffe0000) !=
+                   ((unsigned long)btm & 0xfffe0000)) {
+                       /*
+                        * an ipbuf line should not cross
+                        * 64k-word boundary.
+                        */
+                       printk(KERN_ERR
+                              "omapdsp: ipbuf[%d] crosses 64k-word boundary!\n"
+                              "  @0x%p, size=0x%08lx\n", i, top, lsz_byte);
+                       ret = -EINVAL;
+               }
+       }
+       ipbcfg.ln       = ln;
+       ipbcfg.lsz      = lsz;
+       ipbcfg.adr      = adr;
+       ipbcfg.bsycnt   = ln;   /* DSP holds all ipbufs initially. */
+       ipbcfg.cnt_full = 0;
+
+       printk(KERN_INFO
+              "omapdsp: IPBUF configuration\n"
+              "           %d words * %d lines at 0x%p.\n",
+              ipbcfg.lsz, ipbcfg.ln, dspword_to_virt(ipbcfg.adr));
+
+       return ret;
+}
+
+/*
+ * Global IPBUF operations
+ */
+unsigned short get_free_ipbuf(unsigned char tid)
+{
+       unsigned short bid;
+
+       if (ipblink_empty(&ipb_free)) {
+               /* FIXME: wait on queue when not available.  */
+               return OMAP_DSP_BID_NULL;
+       }
+
+       /*
+        * FIXME: dsp_enable_dspmem!
+        */
+       spin_lock(&ipb_free.lock);
+       bid = ipb_free.top;
+       ipbuf[bid]->la = tid;   /* lock */
+       ipblink_del_top(&ipb_free, ipbuf);
+       spin_unlock(&ipb_free.lock);
+
+       return bid;
+}
+
+void release_ipbuf(unsigned short bid)
+{
+       if (ipbuf[bid]->la == OMAP_DSP_TID_FREE) {
+               printk(KERN_WARNING
+                      "omapdsp: attempt to release unlocked IPBUF[%d].\n",
+                      bid);
+               /*
+                * FIXME: re-calc bsycnt
+                */
+               return;
+       }
+       ipbuf[bid]->la = OMAP_DSP_TID_FREE;
+       ipbuf[bid]->sa = OMAP_DSP_TID_FREE;
+       spin_lock(&ipb_free.lock);
+       ipblink_add_tail(&ipb_free, bid, ipbuf);
+       spin_unlock(&ipb_free.lock);
+}
+
+static int try_yld(unsigned short bid)
+{
+       struct mbcmd mb;
+       int status;
+
+       ipbuf[bid]->sa = OMAP_DSP_TID_ANON;
+       mbcmd_set(mb, MBCMD(BKYLD), 0, bid);
+       status = dsp_mbsend(&mb);
+       if (status < 0) {
+               /* DSP is busy and ARM keeps this line. */
+               release_ipbuf(bid);
+               return status;
+       }
+
+       ipb_bsycnt_inc(&ipbcfg);
+       return 0;
+}
+
+/*
+ * balancing ipbuf lines with DSP
+ */
+static void do_balance_ipbuf(void)
+{
+       while (ipbcfg.bsycnt <= ipbcfg.ln / 4) {
+               unsigned short bid;
+
+               bid = get_free_ipbuf(OMAP_DSP_TID_ANON);
+               if (bid == OMAP_DSP_BID_NULL)
+                       return;
+               if (try_yld(bid) < 0)
+                       return;
+       }
+}
+
+static DECLARE_WORK(balance_ipbuf_work, (void (*)(void *))do_balance_ipbuf,
+                   NULL);
+
+void balance_ipbuf(void)
+{
+       schedule_work(&balance_ipbuf_work);
+}
+
+/* for process context */
+void unuse_ipbuf(unsigned short bid)
+{
+       if (ipbcfg.bsycnt > ipbcfg.ln / 4) {
+               /* we don't have enough IPBUF lines. let's keep it. */
+               release_ipbuf(bid);
+       } else {
+               /* we have enough IPBUF lines. let's return this line to DSP. */
+               ipbuf[bid]->la = OMAP_DSP_TID_ANON;
+               try_yld(bid);
+               balance_ipbuf();
+       }
+}
+
+/* for interrupt context */
+void unuse_ipbuf_nowait(unsigned short bid)
+{
+       release_ipbuf(bid);
+       balance_ipbuf();
+}
+
+/*
+ * functions called from mailbox1 interrupt routine
+ */
+
+void mbx1_err_ipbfull(void)
+{
+       ipbcfg.cnt_full++;
+}
+
+/*
+ * sysfs files
+ */
+static ssize_t ipbuf_show(struct device *dev, char *buf)
+{
+       int len = 0;
+       unsigned short bid;
+
+       for (bid = 0; bid < ipbcfg.ln; bid++) {
+               unsigned short la = ipbuf[bid]->la;
+               unsigned short ld = ipbuf[bid]->ld;
+               unsigned short c  = ipbuf[bid]->c;
+
+               if (len > PAGE_SIZE - 100) {
+                       len += sprintf(buf + len, "out of buffer.\n");
+                       goto finish;
+               }
+
+               len += sprintf(buf + len, "ipbuf[%d]: adr = 0x%p\n",
+                              bid, ipbuf[bid]);
+               if (la == OMAP_DSP_TID_FREE) {
+                       len += sprintf(buf + len,
+                                      "  DSPtask[%d]->Linux "
+                                      "(already read and now free for Linux)\n",
+                                      ld);
+               } else if (ld == OMAP_DSP_TID_FREE) {
+                       len += sprintf(buf + len,
+                                      "  Linux->DSPtask[%d] "
+                                      "(already read and now free for DSP)\n",
+                                      la);
+               } else if (ipbuf_is_held(ld, bid)) {
+                       len += sprintf(buf + len,
+                                      "  DSPtask[%d]->Linux "
+                                      "(waiting to be read)\n"
+                                      "  count = %d\n", ld, c);
+               } else {
+                       len += sprintf(buf + len,
+                                      "  Linux->DSPtask[%d] "
+                                      "(waiting to be read)\n"
+                                      "  count = %d\n", la, c);
+               }
+       }
+
+       len += sprintf(buf + len, "\nFree IPBUF link: ");
+       spin_lock(&ipb_free.lock);
+       ipblink_for_each(bid, &ipb_free, ipbuf) {
+               len += sprintf(buf + len, "%d ", bid);
+       }
+       spin_unlock(&ipb_free.lock);
+       len += sprintf(buf + len, "\n");
+       len += sprintf(buf + len, "IPBFULL error count: %ld\n",
+                      ipbcfg.cnt_full);
+
+finish:
+       return len;
+}
+
+struct device_attribute dev_attr_ipbuf = __ATTR_RO(ipbuf);
diff --git a/arch/arm/mach-omap/dsp/ipbuf.h b/arch/arm/mach-omap/dsp/ipbuf.h
new file mode 100644 (file)
index 0000000..c70f796
--- /dev/null
@@ -0,0 +1,133 @@
+/*
+ * linux/arch/arm/mach-omap/dsp/ipbuf.h
+ *
+ * Header for IPBUF
+ *
+ * Copyright (C) 2002-2005 Nokia Corporation
+ *
+ * Written by Toshihiro Kobayashi <toshihiro.kobayashi@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * 2005/02/17:  DSP Gateway version 3.2
+ */
+
+struct ipbuf {
+       unsigned short c;       /* count */
+       unsigned short next;    /* link */
+       unsigned short la;      /* lock owner (ARM side) */
+       unsigned short sa;      /* sync word (ARM->DSP) */
+       unsigned short ld;      /* lock owner (DSP side) */
+       unsigned short sd;      /* sync word (DSP->ARM) */
+       unsigned char d[0];     /* data */
+};
+
+struct ipbuf_p {
+       unsigned short c;       /* count */
+       unsigned short s;       /* sync word */
+       unsigned short al;      /* data address lower */
+       unsigned short ah;      /* data address upper */
+};
+
+struct ipbuf_sys {
+       unsigned short s;       /* sync word */
+       unsigned short d[15];   /* data */
+};
+
+struct ipbcfg {
+       unsigned short ln;
+       unsigned short lsz;
+       unsigned long adr;
+       unsigned short bsycnt;
+       unsigned long cnt_full; /* count of IPBFULL error */
+};
+
+extern struct ipbuf **ipbuf;
+extern struct ipbcfg ipbcfg;
+extern struct ipbuf_sys *ipbuf_sys_da, *ipbuf_sys_ad;
+
+#define ipb_bsycnt_inc(ipbcfg) \
+       do { \
+               disable_irq(INT_D2A_MB1); \
+               (ipbcfg)->bsycnt++; \
+               enable_irq(INT_D2A_MB1); \
+       } while(0)
+
+#define ipb_bsycnt_dec(ipbcfg) \
+       do { \
+               disable_irq(INT_D2A_MB1); \
+               (ipbcfg)->bsycnt--; \
+               enable_irq(INT_D2A_MB1); \
+       } while(0)
+
+#define dsp_mem_enable_ipbuf() dsp_mem_enable(dspword_to_virt(ipbcfg.adr))
+#define dsp_mem_disable_ipbuf()        dsp_mem_disable(dspword_to_virt(ipbcfg.adr))
+
+struct ipblink {
+       spinlock_t lock;
+       unsigned short top;
+       unsigned short tail;
+};
+
+#define IPBLINK_INIT { \
+               .lock = SPIN_LOCK_UNLOCKED, \
+               .top  = OMAP_DSP_BID_NULL, \
+               .tail = OMAP_DSP_BID_NULL, \
+       }
+
+#define INIT_IPBLINK(link) \
+       do { \
+               (link)->top  = OMAP_DSP_BID_NULL; \
+               (link)->tail = OMAP_DSP_BID_NULL; \
+       } while(0)
+
+#define ipblink_empty(link)    ((link)->top == OMAP_DSP_BID_NULL)
+
+static __inline__ void ipblink_del_top(struct ipblink *link,
+                                      struct ipbuf **ipbuf)
+{
+       struct ipbuf *bufp = ipbuf[link->top];
+
+       if ((link->top = bufp->next) == OMAP_DSP_BID_NULL)
+               link->tail = OMAP_DSP_BID_NULL;
+       else
+               bufp->next = OMAP_DSP_BID_NULL;
+}
+
+static __inline__ void ipblink_add_tail(struct ipblink *link,
+                                       unsigned short bid,
+                                       struct ipbuf **ipbuf)
+{
+       if (ipblink_empty(link))
+               link->top = bid;
+       else
+               ipbuf[link->tail]->next = bid;
+       link->tail = bid;
+}
+
+static __inline__ void ipblink_add_pvt(struct ipblink *link)
+{
+       link->top  = OMAP_DSP_BID_PVT;
+       link->tail = OMAP_DSP_BID_PVT;
+}
+
+static __inline__ void ipblink_del_pvt(struct ipblink *link)
+{
+       link->top  = OMAP_DSP_BID_NULL;
+       link->tail = OMAP_DSP_BID_NULL;
+}
+
+#define ipblink_for_each(bid, link, ipbuf) \
+       for (bid = (link)->top; bid != OMAP_DSP_BID_NULL; bid = ipbuf[bid]->next)
diff --git a/arch/arm/mach-omap/dsp/mblog.c b/arch/arm/mach-omap/dsp/mblog.c
new file mode 100644 (file)
index 0000000..3671c43
--- /dev/null
@@ -0,0 +1,273 @@
+/*
+ * linux/arch/arm/mach-omap/dsp/mblog.c
+ *
+ * OMAP DSP driver Mailbox log module
+ *
+ * Copyright (C) 2003-2005 Nokia Corporation
+ *
+ * Written by Toshihiro Kobayashi <toshihiro.kobayashi@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * 2005/02/17:  DSP Gateway version 3.2
+ */
+
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/device.h>
+#include <linux/init.h>
+#include <asm/irq.h>
+#include <asm/arch/dsp.h>
+#include "dsp.h"
+
+#define RLCMD(nm)      OMAP_DSP_MBCMD_RUNLEVEL_##nm
+#define KFUNCCMD(nm)   OMAP_DSP_MBCMD_KFUNC_##nm
+#define PMCMD(nm)      OMAP_DSP_MBCMD_PM_##nm
+#define CFGCMD(nm)     OMAP_DSP_MBCMD_DSPCFG_##nm
+#define REGCMD(nm)     OMAP_DSP_MBCMD_REGRW_##nm
+#define VICMD(nm)      OMAP_DSP_MBCMD_VARID_##nm
+#define EID(nm)                OMAP_DSP_EID_##nm
+
+char *subcmd_name(struct mbcmd *mb)
+{
+       unsigned char cmd_h = mb->cmd_h;
+       unsigned char cmd_l = mb->cmd_l;
+       char *s;
+
+       switch (cmd_h) {
+       case MBCMD(RUNLEVEL):
+               s = (cmd_l == RLCMD(USER))     ? "USER":
+                   (cmd_l == RLCMD(SUPER))    ? "SUPER":
+                   (cmd_l == RLCMD(RECOVERY)) ? "RECOVERY":
+                   NULL;
+               break;
+       case MBCMD(PM):
+               s = (cmd_l == PMCMD(DISABLE)) ? "DISABLE":
+                   (cmd_l == PMCMD(ENABLE))  ? "ENABLE":
+                   NULL;
+               break;
+       case MBCMD(KFUNC):
+               s = (cmd_l == KFUNCCMD(FBCTL)) ? "FBCTL":
+                   NULL;
+               break;
+       case MBCMD(DSPCFG):
+               {
+                       unsigned char cfgc = cmd_l & 0x7f;
+                       s = (cfgc == CFGCMD(REQ))     ? "REQ":
+                           (cfgc == CFGCMD(SYSADRH)) ? "SYSADRH":
+                           (cfgc == CFGCMD(SYSADRL)) ? "SYSADRL":
+                           (cfgc == CFGCMD(ABORT))   ? "ABORT":
+                           (cfgc == CFGCMD(PROTREV)) ? "PROTREV":
+                           NULL;
+                       break;
+               }
+       case MBCMD(REGRW):
+               s = (cmd_l == REGCMD(MEMR)) ? "MEMR":
+                   (cmd_l == REGCMD(MEMW)) ? "MEMW":
+                   (cmd_l == REGCMD(IOR))  ? "IOR":
+                   (cmd_l == REGCMD(IOW))  ? "IOW":
+                   (cmd_l == REGCMD(DATA)) ? "DATA":
+                   NULL;
+               break;
+       case MBCMD(GETVAR):
+       case MBCMD(SETVAR):
+               s = (cmd_l == VICMD(ICRMASK))  ? "ICRMASK":
+                   (cmd_l == VICMD(LOADINFO)) ? "LOADINFO":
+                   NULL;
+               break;
+       case MBCMD(ERR):
+               s = (cmd_l == EID(BADTID))     ? "BADTID":
+                   (cmd_l == EID(BADTCN))     ? "BADTCN":
+                   (cmd_l == EID(BADBID))     ? "BADBID":
+                   (cmd_l == EID(BADCNT))     ? "BADCNT":
+                   (cmd_l == EID(NOTLOCKED))  ? "NOTLOCKED":
+                   (cmd_l == EID(STVBUF))     ? "STVBUF":
+                   (cmd_l == EID(BADADR))     ? "BADADR":
+                   (cmd_l == EID(BADTCTL))    ? "BADTCTL":
+                   (cmd_l == EID(BADPARAM))   ? "BADPARAM":
+                   (cmd_l == EID(FATAL))      ? "FATAL":
+                   (cmd_l == EID(NOMEM))      ? "NOMEM":
+                   (cmd_l == EID(NORES))      ? "NORES":
+                   (cmd_l == EID(IPBFULL))    ? "IPBFULL":
+                   (cmd_l == EID(TASKNOTRDY)) ? "TASKNOTRDY":
+                   (cmd_l == EID(TASKBSY))    ? "TASKBSY":
+                   (cmd_l == EID(TASKERR))    ? "TASKERR":
+                   (cmd_l == EID(BADCFGTYP))  ? "BADCFGTYP":
+                   (cmd_l == EID(DEBUG))      ? "DEBUG":
+                   (cmd_l == EID(BADSEQ))     ? "BADSEQ":
+                   (cmd_l == EID(BADCMD))     ? "BADCMD":
+                   NULL;
+               break;
+       default:
+               s = NULL;
+       }
+
+       return s;
+}
+
+/* output of show() method should fit to PAGE_SIZE */
+#define MBLOG_DEPTH    64
+
+struct mblogent {
+       unsigned long jiffies;
+       unsigned short cmd;
+       unsigned short data;
+       enum mblog_dir dir;
+};
+
+static struct {
+       spinlock_t lock;
+       int wp;
+       unsigned long cnt, cnt_ad, cnt_da;
+       struct mblogent ent[MBLOG_DEPTH];
+} mblog;
+
+void mblog_add(struct mbcmd *mb, enum mblog_dir dir)
+{
+       struct mbcmd_hw *mb_hw = (struct mbcmd_hw *)mb;
+       struct mblogent *ent;
+
+       spin_lock(&mblog.lock);
+       ent = &mblog.ent[mblog.wp];
+       ent->jiffies = jiffies;
+       ent->cmd     = mb_hw->cmd;
+       ent->data    = mb_hw->data;
+       ent->dir     = dir;
+       if (mblog.cnt < 0xffffffff)
+               mblog.cnt++;
+       switch (dir) {
+       case MBLOG_DIR_AD:
+               if (mblog.cnt_ad < 0xffffffff)
+                       mblog.cnt_ad++;
+               break;
+       case MBLOG_DIR_DA:
+               if (mblog.cnt_da < 0xffffffff)
+                       mblog.cnt_da++;
+               break;
+       }
+       if (++mblog.wp == MBLOG_DEPTH)
+               mblog.wp = 0;
+       spin_unlock(&mblog.lock);
+}
+
+/*
+ * sysfs file
+ */
+static ssize_t mblog_show(struct device *dev, char *buf)
+{
+       int len = 0;
+       int wp;
+       int i;
+
+       spin_lock(&mblog.lock);
+
+       wp = mblog.wp;
+       len += sprintf(buf + len,
+                      "log count:%ld / ARM->DSP:%ld, DSP->ARM:%ld\n",
+                      mblog.cnt, mblog.cnt_ad, mblog.cnt_da);
+       if (mblog.cnt == 0)
+               goto done;
+
+       len += sprintf(buf + len, "          ARM -> DSP   ARM <- DSP\n");
+       len += sprintf(buf + len, "jiffies  q cmd  data q cmd  data\n");
+       i = (mblog.cnt >= MBLOG_DEPTH) ? wp : 0;
+       do {
+               struct mblogent *ent = &mblog.ent[i];
+               union {
+                       struct mbcmd sw;
+                       struct mbcmd_hw hw;
+               } mb = {
+                       .hw.cmd  = ent->cmd,
+                       .hw.data = ent->data
+               };
+               char *subname;
+               const struct cmdinfo *ci = cmdinfo[mb.sw.cmd_h];
+
+               len += sprintf(buf + len,
+                              (ent->dir == MBLOG_DIR_AD) ?
+                               "%08lx %d %04x %04x             ":
+                               "%08lx             %d %04x %04x ",
+                              ent->jiffies, mb.sw.seq, ent->cmd, ent->data);
+               switch (ci->cmd_l_type) {
+               case CMD_L_TYPE_SUBCMD:
+                       if ((subname = subcmd_name(&mb.sw)) == NULL)
+                               subname = "Unknown";
+                       len += sprintf(buf + len, "%s:%s\n",
+                                      ci->name, subname);
+                       break;
+               case CMD_L_TYPE_TID:
+                       len += sprintf(buf + len, "%s:task %d\n",
+                                      ci->name, mb.sw.cmd_l);
+                       break;
+               case CMD_L_TYPE_NULL:
+                       len += sprintf(buf + len, "%s\n", ci->name);
+                       break;
+               }
+
+               if (++i == MBLOG_DEPTH)
+                       i = 0;
+       } while (i != wp);
+
+done:
+       spin_unlock(&mblog.lock);
+
+       return len;
+}
+
+static struct device_attribute dev_attr_mblog = __ATTR_RO(mblog);
+
+#ifdef CONFIG_OMAP_DSP_MBCMD_VERBOSE
+void mblog_printcmd(struct mbcmd *mb, enum mblog_dir dir)
+{
+       const struct cmdinfo *ci = cmdinfo[mb->cmd_h];
+       char *dir_str;
+       char *subname;
+
+       dir_str = (dir == MBLOG_DIR_AD) ? "sending" : "receiving";
+       switch (ci->cmd_l_type) {
+       case CMD_L_TYPE_SUBCMD:
+               if ((subname = subcmd_name(mb)) == NULL)
+                       subname = "Unknown";
+               printk(KERN_DEBUG
+                      "mbx: %s seq=%d, cmd=%02x:%02x(%s:%s), data=%04x\n",
+                      dir_str, mb->seq, mb->cmd_h, mb->cmd_l,
+                      ci->name, subname, mb->data);
+               break;
+       case CMD_L_TYPE_TID:
+               printk(KERN_DEBUG
+                      "mbx: %s seq=%d, cmd=%02x:%02x(%s:task %d), data=%04x\n",
+                      dir_str, mb->seq, mb->cmd_h, mb->cmd_l,
+                      ci->name, mb->cmd_l, mb->data);
+               break;
+       case CMD_L_TYPE_NULL:
+               printk(KERN_DEBUG
+                      "mbx: %s seq=%d, cmd=%02x:%02x(%s), data=%04x\n",
+                      dir_str, mb->seq, mb->cmd_h, mb->cmd_l,
+                      ci->name, mb->data);
+               break;
+       }
+}
+#endif /* CONFIG_OMAP_DSP_MBCMD_VERBOSE */
+
+void __init mblog_init(void)
+{
+       spin_lock_init(&mblog.lock);
+       device_create_file(&dsp_device.dev, &dev_attr_mblog);
+}
+
+void mblog_exit(void)
+{
+       device_remove_file(&dsp_device.dev, &dev_attr_mblog);
+}
diff --git a/arch/arm/mach-omap/dsp/proclist.h b/arch/arm/mach-omap/dsp/proclist.h
new file mode 100644 (file)
index 0000000..723e5b0
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * linux/arch/arm/mach-omap/dsp/proclist.h
+ *
+ * Linux task list handler
+ *
+ * Copyright (C) 2004,2005 Nokia Corporation
+ *
+ * Written by Toshihiro Kobayashi <toshihiro.kobayashi@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * 2004/11/22:  DSP Gateway version 3.2
+ */
+
+struct proc_list {
+       struct list_head list_head;
+       struct task_struct *tsk;
+       unsigned int cnt;
+};
+
+static __inline__ void proc_list_add(struct list_head *list,
+                                    struct task_struct *tsk)
+{
+       struct list_head *ptr;
+       struct proc_list *pl;
+       struct proc_list *new;
+
+       list_for_each(ptr, list) {
+               pl = list_entry(ptr, struct proc_list, list_head);
+               if (pl->tsk == tsk) {
+                       /*
+                        * this process has opened DSP devices multi time
+                        */
+                       pl->cnt++;
+                       return;
+               }
+       }
+
+       new = kmalloc(sizeof(struct proc_list), GFP_KERNEL);
+       new->tsk = tsk;
+       new->cnt = 1;
+       list_add_tail(&new->list_head, list);
+}
+
+static __inline__ void proc_list_del(struct list_head *list,
+                                    struct task_struct *tsk)
+{
+       struct list_head *ptr;
+       struct proc_list *pl;
+
+       list_for_each(ptr, list) {
+               pl = list_entry(ptr, struct proc_list, list_head);
+               if (pl->tsk == tsk) {
+                       if (--pl->cnt == 0) {
+                               list_del(&pl->list_head);
+                               kfree(pl);
+                       }
+                       return;
+               }
+       }
+}
+
+static __inline__ void proc_list_flush(struct list_head *list)
+{
+       struct proc_list *pl;
+
+       while (!list_empty(list)) {
+               pl = list_entry(list->next, struct proc_list, list_head);
+               list_del(&pl->list_head);
+               kfree(pl);
+       }
+}
diff --git a/arch/arm/mach-omap/dsp/task.c b/arch/arm/mach-omap/dsp/task.c
new file mode 100644 (file)
index 0000000..118f1a9
--- /dev/null
@@ -0,0 +1,2615 @@
+/*
+ * linux/arch/arm/mach-omap/dsp/task.c
+ *
+ * OMAP DSP task device driver
+ *
+ * Copyright (C) 2002-2005 Nokia Corporation
+ *
+ * Written by Toshihiro Kobayashi <toshihiro.kobayashi@nokia.com>
+ * mmap function by Hiroo Ishikawa <ext-hiroo.ishikawa@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * 2005/02/22:  DSP Gateway version 3.2
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/major.h>
+#include <linux/fs.h>
+#include <linux/poll.h>
+#include <linux/device.h>
+#include <linux/devfs_fs_kernel.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/proc_fs.h>
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <asm/signal.h>
+#include <asm/irq.h>
+#include <asm/ioctls.h>
+#include <asm/arch/dsp.h>
+#include "uaccess_dsp.h"
+#include "dsp.h"
+#include "ipbuf.h"
+#include "fifo.h"
+#include "proclist.h"
+
+/*
+ * device state machine
+ * NOTASK:     task is not attached.
+ * ATTACHED:   task is attached.
+ * GARBAGE:    task is detached. waiting for all processes to close this device.
+ * ADDREQ:     requesting for tadd
+ * DELREQ:     requesting for tdel. no process is opening this device.
+ * KILLREQ:    requesting for tkill.
+ * ADDFAIL:    tadd failed.
+ */
+
+struct taskdev {
+       struct bus_type *bus;
+//     struct device_driver *driver;
+       struct device dev;      /* Generic device interface */
+
+       long state;
+       spinlock_t state_lock;
+       wait_queue_head_t state_wait_q;
+       unsigned int usecount;
+       char name[OMAP_DSP_TNM_LEN];
+       struct file_operations fops;
+       struct list_head proc_list;
+       struct dsptask *task;
+
+       /* read stuff */
+       wait_queue_head_t read_wait_q;
+       struct semaphore read_sem;
+
+       /* write stuff */
+       wait_queue_head_t write_wait_q;
+       struct semaphore write_sem;
+
+       /* ioctl stuff */
+       wait_queue_head_t ioctl_wait_q;
+       struct semaphore ioctl_sem;
+
+       /* device lock */
+       struct semaphore lock_sem;
+       pid_t lock_pid;
+};
+
+#define to_taskdev(n) container_of(n, struct taskdev, dev)
+
+struct rcvdt_bk_struct {
+       struct ipblink link;
+       unsigned int rp;
+       struct ipbuf_p *ipbuf_pvt_r;
+};
+
+struct dsptask {
+       enum {
+               TASK_STATE_ERR = 0,
+               TASK_STATE_READY,
+               TASK_STATE_CFGREQ
+       } state;
+       unsigned char tid;
+       char name[OMAP_DSP_TNM_LEN];
+       unsigned short ttyp;
+       struct taskdev *dev;
+
+       /* read stuff */
+       union {
+               struct fifo_struct fifo;        /* for active word */
+               struct rcvdt_bk_struct bk;
+       } rcvdt;
+
+       /* write stuff */
+       size_t wsz;
+       spinlock_t wsz_lock;
+       struct ipbuf_p *ipbuf_pvt_w;    /* for private block */
+
+       /* tctl stuff */
+       int tctl_stat;
+
+       /* mmap stuff */
+       void *map_base;
+       size_t map_length;
+};
+
+#define sndtyp_acv(ttyp)       ((ttyp) & OMAP_DSP_TTYP_ASND)
+#define sndtyp_psv(ttyp)       (!((ttyp) & OMAP_DSP_TTYP_ASND))
+#define sndtyp_bk(ttyp)                ((ttyp) & OMAP_DSP_TTYP_BKDM)
+#define sndtyp_wd(ttyp)                (!((ttyp) & OMAP_DSP_TTYP_BKDM))
+#define sndtyp_pvt(ttyp)       ((ttyp) & OMAP_DSP_TTYP_PVDM)
+#define sndtyp_gbl(ttyp)       (!((ttyp) & OMAP_DSP_TTYP_PVDM))
+#define rcvtyp_acv(ttyp)       ((ttyp) & OMAP_DSP_TTYP_ARCV)
+#define rcvtyp_psv(ttyp)       (!((ttyp) & OMAP_DSP_TTYP_ARCV))
+#define rcvtyp_bk(ttyp)                ((ttyp) & OMAP_DSP_TTYP_BKMD)
+#define rcvtyp_wd(ttyp)                (!((ttyp) & OMAP_DSP_TTYP_BKMD))
+#define rcvtyp_pvt(ttyp)       ((ttyp) & OMAP_DSP_TTYP_PVMD)
+#define rcvtyp_gbl(ttyp)       (!((ttyp) & OMAP_DSP_TTYP_PVMD))
+
+static int dsp_rmdev_minor(unsigned char minor);
+static int taskdev_init(struct taskdev *dev, char *name, unsigned char minor);
+static void taskdev_delete(unsigned char minor);
+static void taskdev_attach_task(struct taskdev *dev, struct dsptask *task);
+static void taskdev_detach_task(struct taskdev *dev);
+
+static ssize_t devname_show(struct device *d, char *buf);
+static ssize_t devstate_show(struct device *d, char *buf);
+static ssize_t proc_list_show(struct device *d, char *buf);
+static ssize_t taskname_show(struct device *d, char *buf);
+static ssize_t ttyp_show(struct device *d, char *buf);
+static ssize_t fifosz_show(struct device *d, char *buf);
+static int fifosz_store(struct device *d, const char *buf, size_t count);
+static ssize_t fifocnt_show(struct device *d, char *buf);
+static ssize_t ipblink_show(struct device *d, char *buf);
+static ssize_t wsz_show(struct device *d, char *buf);
+static ssize_t mmap_show(struct device *d, char *buf);
+
+static struct device_attribute dev_attr_devname = __ATTR_RO(devname);
+static struct device_attribute dev_attr_devstate = __ATTR_RO(devstate);
+static struct device_attribute dev_attr_proc_list = __ATTR_RO(proc_list);
+static struct device_attribute dev_attr_fifosz = 
+       __ATTR(fifosz, S_IWUGO | S_IRUGO, fifosz_show, fifosz_store);
+static struct device_attribute dev_attr_fifocnt = __ATTR_RO(fifocnt);
+static struct device_attribute dev_attr_taskname = __ATTR_RO(taskname);
+static struct device_attribute dev_attr_ttyp = __ATTR_RO(ttyp);
+static struct device_attribute dev_attr_ipblink = __ATTR_RO(ipblink);
+static struct device_attribute dev_attr_wsz = __ATTR_RO(wsz);
+static struct device_attribute dev_attr_mmap = __ATTR_RO(mmap);
+
+static struct bus_type dsptask_bus = {
+       .name = "dsptask",
+};
+
+static struct class_simple *dsp_task_class;
+static struct taskdev *taskdev[TASKDEV_MAX];
+static struct dsptask *dsptask[TASKDEV_MAX];
+static DECLARE_MUTEX(cfg_sem);
+static unsigned short cfg_cmd;
+static unsigned char cfg_tid;
+static DECLARE_WAIT_QUEUE_HEAD(cfg_wait_q);
+static unsigned char n_task;
+static void *heap;
+
+#define devstate_lock(dev, devstate)   devstate_lock_timeout(dev, devstate, 0)
+
+/*
+ * devstate_lock_timeout():
+ * when called with timeout > 0, dev->state can be diffeent from what you want.
+ */
+static int devstate_lock_timeout(struct taskdev *dev, long devstate,
+                                int timeout)
+{
+       DECLARE_WAITQUEUE(wait, current);
+       long current_state = current->state;
+       int ret = 0;
+
+       spin_lock(&dev->state_lock);
+       add_wait_queue(&dev->state_wait_q, &wait);
+       while (!(dev->state & devstate)) {
+               set_current_state(TASK_INTERRUPTIBLE);
+               spin_unlock(&dev->state_lock);
+               if (timeout) {
+                       if ((timeout = schedule_timeout(timeout)) == 0) {
+                               /* timeout */
+                               spin_lock(&dev->state_lock);
+                               break;
+                       }
+               }
+               else
+                       schedule();
+               if (signal_pending(current)) {
+                       ret = -ERESTARTSYS;
+                       break;
+               }
+               spin_lock(&dev->state_lock);
+       }
+       remove_wait_queue(&dev->state_wait_q, &wait);
+       set_current_state(current_state);
+       return ret;
+}
+
+static __inline__ void devstate_unlock(struct taskdev *dev)
+{
+       spin_unlock(&dev->state_lock);
+}
+
+static __inline__ int down_tasksem_interruptible(struct taskdev *dev,
+                                                struct semaphore *sem)
+{
+       int ret;
+
+       if (dev->lock_pid == current->pid) {
+               /* this process has lock */
+               ret = down_interruptible(sem);
+       } else {
+               if ((ret = down_interruptible(&dev->lock_sem)) != 0)
+                       return ret;
+               ret = down_interruptible(sem);
+               up(&dev->lock_sem);
+       }
+       return ret;
+}
+
+static int dsp_task_flush_buf(struct dsptask *task)
+{
+       unsigned short ttyp = task->ttyp;
+
+       if (sndtyp_wd(ttyp)) {
+               /* word receiving */
+               flush_fifo(&task->rcvdt.fifo);
+       } else {
+               /* block receiving */
+               struct rcvdt_bk_struct *rcvdt = &task->rcvdt.bk;
+
+               spin_lock(&rcvdt->link.lock);
+               if (sndtyp_gbl(ttyp)) {
+                       /* global IPBUF */
+                       while (!ipblink_empty(&rcvdt->link)) {
+                               unsigned short bid = rcvdt->link.top;
+                               ipblink_del_top(&rcvdt->link, ipbuf);
+                               unuse_ipbuf(bid);
+                       }
+               } else {
+                       /* private IPBUF */
+                       if (!ipblink_empty(&rcvdt->link)) {
+                               ipblink_del_pvt(&rcvdt->link);
+                               release_ipbuf_pvt(rcvdt->ipbuf_pvt_r);
+                       }
+               }
+               spin_unlock(&rcvdt->link.lock);
+       }
+
+       return 0;
+}
+
+static int dsp_task_set_fifosz(struct dsptask *task, unsigned long sz)
+{
+       unsigned short ttyp = task->ttyp;
+       int stat;
+
+       if (!(sndtyp_wd(ttyp) && sndtyp_acv(ttyp))) {
+               printk(KERN_ERR
+                      "omapdsp: buffer size can be changed only for "
+                      "active word sending task.\n");
+               return -EINVAL;
+       }
+       if ((sz == 0) || (sz & 1)) {
+               printk(KERN_ERR "omapdsp: illegal buffer size! (%ld)\n"
+                               "it must be even and non-zero value.\n", sz);
+               return -EINVAL;
+       }
+
+       stat = realloc_fifo(&task->rcvdt.fifo, sz);
+       if (stat == -EBUSY) {
+               printk(KERN_ERR "omapdsp: buffer is not empty!\n");
+               return stat;
+       } else if (stat < 0) {
+               printk(KERN_ERR
+                      "omapdsp: unable to change receive buffer size. "
+                      "(%ld bytes for %s)\n", sz, task->name);
+               return stat;
+       }
+
+       return 0;
+}
+
+static int taskdev_lock(struct taskdev *dev)
+{
+       if (down_interruptible(&dev->lock_sem))
+               return -ERESTARTSYS;
+       dev->lock_pid = current->pid;
+       return 0;
+}
+
+static int taskdev_unlock(struct taskdev *dev)
+{
+       if (dev->lock_pid != current->pid) {
+               printk(KERN_ERR
+                      "omapdsp: an illegal process attempted to "
+                      "unlock the dsptask lock!\n");
+               return -EINVAL;
+       }
+       dev->lock_pid = 0;
+       up(&dev->lock_sem);
+       return 0;
+}
+
+static int dsp_task_config(struct dsptask *task, unsigned char tid)
+{
+       unsigned short ttyp;
+       struct mbcmd mb;
+
+       dsptask[tid] = task;
+       task->tid = tid;
+
+       /* TCFG request */
+       task->state = TASK_STATE_CFGREQ;
+       if (down_interruptible(&cfg_sem))
+               return -ERESTARTSYS;
+       cfg_cmd = MBCMD(TCFG);
+       mbcmd_set(mb, MBCMD(TCFG), tid, 0);
+       dsp_mbsend_and_wait(&mb, &cfg_wait_q);
+       cfg_cmd = 0;
+       up(&cfg_sem);
+
+       if (task->state != TASK_STATE_READY) {
+               printk(KERN_ERR "omapdsp: task %d configuration error!\n", tid);
+               return -EINVAL;
+       }
+
+       if (strlen(task->name) <= 1)
+               sprintf(task->name, "%d", tid);
+       printk(KERN_INFO "omapdsp: task %d: name %s\n", tid, task->name);
+
+       ttyp = task->ttyp;
+
+       /* task type check */
+       if (rcvtyp_psv(ttyp) && rcvtyp_pvt(ttyp)) {
+               printk(KERN_ERR "mbx: illegal task type(0x%04x), tid=%d\n",
+                      tid, ttyp);
+       }
+
+       /* private buffer address check */
+       if (sndtyp_pvt(ttyp)) {
+               void *p = task->rcvdt.bk.ipbuf_pvt_r;
+
+               if ((unsigned long)p & 0x1) {
+                       printk(KERN_ERR
+                              "mbx: private ipbuf (DSP->ARM) address (0x%p) "
+                              "is odd number!\n", p);
+                       return -EINVAL;
+               }
+       }
+
+       if (rcvtyp_pvt(ttyp)) {
+               void *p = task->ipbuf_pvt_w;
+
+               if ((unsigned long)p & 0x1) {
+                       printk(KERN_ERR
+                              "mbx: private ipbuf (ARM->DSP) address (0x%p) "
+                              "is odd number!\n", p);
+                       return -EINVAL;
+               }
+       }
+
+       /* read initialization */
+       if (sndtyp_wd(ttyp)) {
+               /* word */
+               size_t fifosz;
+
+               fifosz = sndtyp_psv(ttyp) ? 2 : /* passive */
+                                           32; /* active */
+               if (init_fifo(&task->rcvdt.fifo, fifosz) < 0) {
+                       printk(KERN_ERR
+                              "omapdsp: unable to allocate receive buffer. "
+                              "(%d bytes for %s)\n", fifosz, task->name);
+                       return -ENOMEM;
+               }
+       } else {
+               /* block */
+               spin_lock_init(&task->rcvdt.bk.link.lock);
+               INIT_IPBLINK(&task->rcvdt.bk.link);
+               task->rcvdt.bk.rp = 0;
+       }
+
+       /* write initialization */
+       spin_lock_init(&task->wsz_lock);
+       task->wsz = rcvtyp_acv(ttyp) ? 0 :              /* active */
+                   rcvtyp_wd(ttyp)  ? 2 :              /* passive word */
+                                      ipbcfg.lsz*2;    /* passive block */
+
+       return 0;
+}
+
+static void dsp_task_init(struct dsptask *task)
+{
+       struct mbcmd mb;
+
+       mbcmd_set(mb, MBCMD(TCTL), task->tid, OMAP_DSP_MBCMD_TCTL_TINIT);
+       dsp_mbsend(&mb);
+}
+
+int dsp_task_config_all(unsigned char n)
+{
+       int i, ret;
+       struct taskdev *devheap;
+       struct dsptask *taskheap;
+       size_t devheapsz, taskheapsz;
+
+       memset(taskdev, 0, sizeof(void *) * TASKDEV_MAX);
+       memset(dsptask, 0, sizeof(void *) * TASKDEV_MAX);
+
+       n_task = n;
+       printk(KERN_INFO "omapdsp: found %d task(s)\n", n_task);
+       if (n_task == 0)
+               return 0;
+
+       /*
+        * reducing kmalloc!
+        */
+       devheapsz  = sizeof(struct taskdev) * n_task;
+       taskheapsz = sizeof(struct dsptask) * n_task;
+       heap = kmalloc(devheapsz + taskheapsz, GFP_KERNEL);
+       if (heap == NULL) {
+               n_task = 0;
+               return -ENOMEM;
+       }
+       memset(heap, 0, devheapsz + taskheapsz);
+       devheap  = heap;
+       taskheap = heap + devheapsz;
+
+       for (i = 0; i < n_task; i++) {
+               struct taskdev *dev  = &devheap[i];
+               struct dsptask *task = &taskheap[i];
+
+               if ((ret = dsp_task_config(task, i)) < 0)
+                       return ret;
+               if ((ret = taskdev_init(dev, task->name, i)) < 0)
+                       return ret;
+               taskdev_attach_task(dev, task);
+               dsp_task_init(task);
+               printk(KERN_INFO "omapdsp: taskdev %s enabled.\n", dev->name);
+       }
+
+       return 0;
+}
+
+static void dsp_task_unconfig(struct dsptask *task)
+{
+       unsigned char tid = task->tid;
+
+       preempt_disable();
+       dsp_task_flush_buf(task);
+       if (sndtyp_wd(task->ttyp) && (task->state == TASK_STATE_READY))
+               free_fifo(&task->rcvdt.fifo);
+       dsptask[tid] = NULL;
+       preempt_enable();
+}
+
+void dsp_task_unconfig_all(void)
+{
+       unsigned char minor;
+       unsigned char tid;
+       struct dsptask *task;
+
+       for (minor = 0; minor < n_task; minor++) {
+               /*
+                * taskdev[minor] can be NULL in case of
+                * configuration failure
+                */
+               if (taskdev[minor])
+                       taskdev_delete(minor);
+       }
+       for (; minor < TASKDEV_MAX; minor++) {
+               if (taskdev[minor])
+                       dsp_rmdev_minor(minor);
+       }
+
+       for (tid = 0; tid < n_task; tid++) {
+               /*
+                * dsptask[tid] can be NULL in case of
+                * configuration failure
+                */
+               task = dsptask[tid];
+               if (task)
+                       dsp_task_unconfig(task);
+       }
+       for (; tid < TASKDEV_MAX; tid++) {
+               task = dsptask[tid];
+               if (task) {
+                       /*
+                        * on-demand tasks should be deleted in
+                        * rmdev_minor(), but just in case.
+                        */
+                       dsp_task_unconfig(task);
+                       kfree(task);
+               }
+       }
+
+       if (heap) {
+               kfree(heap);
+               heap = NULL;
+       }
+
+       n_task = 0;
+}
+
+static struct device_driver dsptask_driver = {
+       .name   = "dsptask",
+       .bus    = &dsptask_bus,
+};
+
+unsigned char dsp_task_count(void)
+{
+       return n_task;
+}
+
+int dsp_taskmod_busy(void)
+{
+       struct taskdev *dev;
+       unsigned char minor;
+
+       for (minor = 0; minor < TASKDEV_MAX; minor++) {
+               dev = taskdev[minor];
+               if (dev &&
+                   ((dev->usecount > 0) ||
+                    (dev->state == OMAP_DSP_DEVSTATE_ADDREQ) ||
+                    (dev->state == OMAP_DSP_DEVSTATE_DELREQ)))
+                       return 1;
+       }
+       return 0;
+}
+
+/*
+ * DSP task device file operations
+ */
+static ssize_t dsp_task_read_wd_acv(struct file *file, char *buf, size_t count,
+                                   loff_t *ppos)
+{
+       unsigned int minor = MINOR(file->f_dentry->d_inode->i_rdev);
+       struct taskdev *dev = taskdev[minor];
+       int have_devstate_lock = 0;
+       int ret = 0;
+
+       if (count == 0) {
+               return 0;
+       } else if (count & 0x1) {
+               printk(KERN_ERR
+                      "omapdsp: odd count is illegal for DSP task device.\n");
+               return -EINVAL;
+       }
+
+       if (down_tasksem_interruptible(dev, &dev->read_sem))
+               return -ERESTARTSYS;
+       if (devstate_lock(dev, OMAP_DSP_DEVSTATE_ATTACHED) < 0) {
+               ret = -ERESTARTSYS;
+               goto up_out;
+       }
+       have_devstate_lock = 1;
+
+       if (fifo_empty(&dev->task->rcvdt.fifo)) {
+               long current_state = current->state;
+               DECLARE_WAITQUEUE(wait, current);
+
+               set_current_state(TASK_INTERRUPTIBLE);
+               add_wait_queue(&dev->read_wait_q, &wait);
+               if (fifo_empty(&dev->task->rcvdt.fifo)) {       /* last check */
+                       devstate_unlock(dev);
+                       have_devstate_lock = 0;
+                       schedule();
+               }
+               set_current_state(current_state);
+               remove_wait_queue(&dev->read_wait_q, &wait);
+               if (signal_pending(current)) {
+                       ret = -ERESTARTSYS;
+                       goto up_out;
+               }
+               if (!have_devstate_lock) {
+                       if (devstate_lock(dev, OMAP_DSP_DEVSTATE_ATTACHED) < 0) {
+                               ret = -ERESTARTSYS;
+                               goto up_out;
+                       }
+                       have_devstate_lock = 1;
+               }
+               if (fifo_empty(&dev->task->rcvdt.fifo)) /* should not occur */
+                       goto up_out;
+       }
+
+       ret = copy_to_user_fm_fifo(buf, &dev->task->rcvdt.fifo, count);
+
+up_out:
+       if (have_devstate_lock)
+               devstate_unlock(dev);
+       up(&dev->read_sem);
+       return ret;
+}
+
+static ssize_t dsp_task_read_bk_acv(struct file *file, char *buf, size_t count,
+                                   loff_t *ppos)
+{
+       unsigned int minor = MINOR(file->f_dentry->d_inode->i_rdev);
+       struct taskdev *dev = taskdev[minor];
+       struct rcvdt_bk_struct *rcvdt;
+       int have_devstate_lock = 0;
+       ssize_t ret = 0;
+
+       if (count == 0) {
+               return 0;
+       } else if (count & 0x1) {
+               printk(KERN_ERR
+                      "omapdsp: odd count is illegal for DSP task device.\n");
+               return -EINVAL;
+       } else if ((int)buf & 0x1) {
+               printk(KERN_ERR
+                      "omapdsp: buf should be word aligned for "
+                      "dsp_task_read().\n");
+               return -EINVAL;
+       }
+
+       if (down_tasksem_interruptible(dev, &dev->read_sem))
+               return -ERESTARTSYS;
+       if (devstate_lock(dev, OMAP_DSP_DEVSTATE_ATTACHED) < 0) {
+               ret = -ERESTARTSYS;
+               goto up_out;
+       }
+       have_devstate_lock = 1;
+
+       if (ipblink_empty(&dev->task->rcvdt.bk.link)) {
+               long current_state;
+               DECLARE_WAITQUEUE(wait, current);
+
+               add_wait_queue(&dev->read_wait_q, &wait);
+               current_state = current->state;
+               set_current_state(TASK_INTERRUPTIBLE);
+               if (ipblink_empty(&dev->task->rcvdt.bk.link)) { /* last check */
+                       devstate_unlock(dev);
+                       have_devstate_lock = 0;
+                       schedule();
+               }
+               set_current_state(current_state);
+               remove_wait_queue(&dev->read_wait_q, &wait);
+               if (signal_pending(current)) {
+                       ret = -ERESTARTSYS;
+                       goto up_out;
+               }
+               if (!have_devstate_lock) {
+                       if (devstate_lock(dev, OMAP_DSP_DEVSTATE_ATTACHED) < 0) {
+                               ret = -ERESTARTSYS;
+                               goto up_out;
+                       }
+                       have_devstate_lock = 1;
+               }
+               /* signal or 0-byte send from DSP */
+               if (ipblink_empty(&dev->task->rcvdt.bk.link))
+                       goto up_out;
+       }
+
+       rcvdt = &dev->task->rcvdt.bk;
+       /* copy from delayed IPBUF */
+       if (sndtyp_pvt(dev->task->ttyp)) {
+               /* private */
+               if (!ipblink_empty(&rcvdt->link)) {
+                       struct ipbuf_p *ipbp = rcvdt->ipbuf_pvt_r;
+                       unsigned char *base, *src;
+                       size_t bkcnt;
+
+                       if (dsp_mem_enable(ipbp) < 0) {
+                               ret = -ERESTARTSYS;
+                               goto up_out;
+                       }
+                       base = dspword_to_virt(MKLONG(ipbp->ah, ipbp->al));
+                       src = base + rcvdt->rp;
+                       if (dsp_mem_enable(base) < 0) {
+                               ret = -ERESTARTSYS;
+                               goto pv_out1;
+                       }
+                       bkcnt = ((unsigned long)ipbp->c) * 2 - rcvdt->rp;
+                       if (bkcnt > count) {
+                               if (copy_to_user_dsp(buf, src, count)) {
+                                       ret = -EFAULT;
+                                       goto pv_out2;
+                               }
+                               ret = count;
+                               rcvdt->rp += count;
+                       } else {
+                               if (copy_to_user_dsp(buf, src, bkcnt)) {
+                                       ret = -EFAULT;
+                                       goto pv_out2;
+                               }
+                               ret = bkcnt;
+                               spin_lock(&rcvdt->link.lock);
+                               ipblink_del_pvt(&rcvdt->link);
+                               spin_unlock(&rcvdt->link.lock);
+                               release_ipbuf_pvt(ipbp);
+                               rcvdt->rp = 0;
+                       }
+pv_out2:
+                       dsp_mem_disable(src);
+pv_out1:
+                       dsp_mem_disable(ipbp);
+               }
+       } else {
+               /* global */
+               if (dsp_mem_enable_ipbuf() < 0) {
+                       ret = -ERESTARTSYS;
+                       goto up_out;
+               }
+               while (!ipblink_empty(&rcvdt->link)) {
+                       unsigned char *src;
+                       size_t bkcnt;
+                       unsigned short bid = rcvdt->link.top;
+                       struct ipbuf *ipbp = ipbuf[bid];
+
+                       src = ipbp->d + rcvdt->rp;
+                       bkcnt = ((unsigned long)ipbp->c) * 2 - rcvdt->rp;
+                       if (bkcnt > count) {
+                               if (copy_to_user_dsp(buf, src, count)) {
+                                       ret = -EFAULT;
+                                       goto gb_out;
+                               }
+                               ret += count;
+                               rcvdt->rp += count;
+                               break;
+                       } else {
+                               if (copy_to_user_dsp(buf, src, bkcnt)) {
+                                       ret = -EFAULT;
+                                       goto gb_out;
+                               }
+                               ret += bkcnt;
+                               buf += bkcnt;
+                               count -= bkcnt;
+                               spin_lock(&rcvdt->link.lock);
+                               ipblink_del_top(&rcvdt->link, ipbuf);
+                               spin_unlock(&rcvdt->link.lock);
+                               unuse_ipbuf(bid);
+                               rcvdt->rp = 0;
+                       }
+               }
+gb_out:
+               dsp_mem_disable_ipbuf();
+       }
+
+up_out:
+       if (have_devstate_lock)
+               devstate_unlock(dev);
+       up(&dev->read_sem);
+       return ret;
+}
+
+static ssize_t dsp_task_read_wd_psv(struct file *file, char *buf, size_t count,
+                                   loff_t *ppos)
+{
+       unsigned int minor = MINOR(file->f_dentry->d_inode->i_rdev);
+       struct taskdev *dev = taskdev[minor];
+       struct mbcmd mb;
+       unsigned char tid;
+       int ret = 0;
+
+       if (count == 0) {
+               return 0;
+       } else if (count & 0x1) {
+               printk(KERN_ERR
+                      "omapdsp: odd count is illegal for DSP task device.\n");
+               return -EINVAL;
+       } else {
+               /* force! */
+               count = 2;
+       }
+
+       if (down_tasksem_interruptible(dev, &dev->read_sem))
+               return -ERESTARTSYS;
+       if (devstate_lock(dev, OMAP_DSP_DEVSTATE_ATTACHED) < 0) {
+               ret = -ERESTARTSYS;
+               goto up_out;
+       }
+       tid = dev->task->tid;
+       devstate_unlock(dev);
+
+       mbcmd_set(mb, MBCMD(WDREQ), tid, 0);
+       dsp_mbsend_and_wait(&mb, &dev->read_wait_q);
+
+       if (signal_pending(current)) {
+               ret = -ERESTARTSYS;
+               goto up_out;
+       }
+       if (devstate_lock(dev, OMAP_DSP_DEVSTATE_ATTACHED) < 0) {
+               ret = -ERESTARTSYS;
+               goto up_out;
+       }
+       if (fifo_empty(&dev->task->rcvdt.fifo)) /* should not occur */
+               goto unlock_out;
+
+       ret = copy_to_user_fm_fifo(buf, &dev->task->rcvdt.fifo, count);
+
+unlock_out:
+       devstate_unlock(dev);
+up_out:
+       up(&dev->read_sem);
+       return ret;
+}
+
+static ssize_t dsp_task_read_bk_psv(struct file *file, char *buf, size_t count,
+                                   loff_t *ppos)
+{
+       unsigned int minor = MINOR(file->f_dentry->d_inode->i_rdev);
+       struct taskdev *dev = taskdev[minor];
+       struct rcvdt_bk_struct *rcvdt;
+       struct mbcmd mb;
+       unsigned char tid;
+       int ret = 0;
+
+       if (count == 0) {
+               return 0;
+       } else if (count & 0x1) {
+               printk(KERN_ERR
+                      "omapdsp: odd count is illegal for DSP task device.\n");
+               return -EINVAL;
+       } else if ((int)buf & 0x1) {
+               printk(KERN_ERR
+                      "omapdsp: buf should be word aligned for "
+                      "dsp_task_read().\n");
+               return -EINVAL;
+       }
+
+       if (down_tasksem_interruptible(dev, &dev->read_sem))
+               return -ERESTARTSYS;
+       if (devstate_lock(dev, OMAP_DSP_DEVSTATE_ATTACHED) < 0) {
+               ret = -ERESTARTSYS;
+               goto up_out;
+       }
+       tid = dev->task->tid;
+       devstate_unlock(dev);
+
+       mbcmd_set(mb, MBCMD(BKREQ), tid, count/2);
+       dsp_mbsend_and_wait(&mb, &dev->read_wait_q);
+
+       if (signal_pending(current)) {
+               ret = -ERESTARTSYS;
+               goto up_out;
+       }
+       if (devstate_lock(dev, OMAP_DSP_DEVSTATE_ATTACHED) < 0) {
+               ret = -ERESTARTSYS;
+               goto up_out;
+       }
+       rcvdt = &dev->task->rcvdt.bk;
+       /* signal or 0-byte send from DSP */
+       if (ipblink_empty(&rcvdt->link))
+               goto unlock_out;
+
+       /*
+        * We will not receive more than requested count.
+        */
+       if (sndtyp_pvt(dev->task->ttyp)) {
+               /* private */
+               struct ipbuf_p *ipbp = rcvdt->ipbuf_pvt_r;
+               size_t rcvcnt;
+               void *src;
+
+               if (dsp_mem_enable(ipbp) < 0) {
+                       ret = -ERESTARTSYS;
+                       goto unlock_out;
+               }
+               src = dspword_to_virt(MKLONG(ipbp->ah, ipbp->al));
+               if (dsp_mem_enable(src) < 0) {
+                       ret = -ERESTARTSYS;
+                       goto pv_out1;
+               }
+               rcvcnt = ((unsigned long)ipbp->c) * 2;
+               if (count > rcvcnt)
+                       count = rcvcnt;
+               if (copy_to_user_dsp(buf, src, count)) {
+                       ret = -EFAULT;
+                       goto pv_out2;
+               }
+               spin_lock(&rcvdt->link.lock);
+               ipblink_del_pvt(&rcvdt->link);
+               spin_unlock(&rcvdt->link.lock);
+               release_ipbuf_pvt(ipbp);
+               ret = count;
+pv_out2:
+               dsp_mem_disable(src);
+pv_out1:
+               dsp_mem_disable(ipbp);
+       } else {
+               /* global */
+               unsigned short bid = rcvdt->link.top;
+               struct ipbuf *ipbp = ipbuf[bid];
+               size_t rcvcnt;
+
+               if (dsp_mem_enable_ipbuf() < 0) {
+                       ret = -ERESTARTSYS;
+                       goto unlock_out;
+               }
+               rcvcnt = ((unsigned long)ipbp->c) * 2;
+               if (count > rcvcnt)
+                       count = rcvcnt;
+               if (copy_to_user_dsp(buf, ipbp->d, count)) {
+                       ret = -EFAULT;
+                       goto gb_out;
+               }
+               spin_lock(&rcvdt->link.lock);
+               ipblink_del_top(&rcvdt->link, ipbuf);
+               spin_unlock(&rcvdt->link.lock);
+               unuse_ipbuf(bid);
+               ret = count;
+gb_out:
+               dsp_mem_disable_ipbuf();
+       }
+
+unlock_out:
+       devstate_unlock(dev);
+up_out:
+       up(&dev->read_sem);
+       return ret;
+}
+
+static ssize_t dsp_task_write_wd(struct file *file, const char *buf,
+                                size_t count, loff_t *ppos)
+{
+       unsigned int minor = MINOR(file->f_dentry->d_inode->i_rdev);
+       struct taskdev *dev = taskdev[minor];
+       struct mbcmd mb;
+       unsigned short wd;
+       int have_devstate_lock = 0;
+       int ret = 0;
+
+       if (count == 0) {
+               return 0;
+       } else if (count & 0x1) {
+               printk(KERN_ERR
+                      "omapdsp: odd count is illegal for DSP task device.\n");
+               return -EINVAL;
+       } else {
+               /* force! */
+               count = 2;
+       }
+
+       if (down_tasksem_interruptible(dev, &dev->write_sem))
+               return -ERESTARTSYS;
+       if (devstate_lock(dev, OMAP_DSP_DEVSTATE_ATTACHED) < 0) {
+               ret = -ERESTARTSYS;
+               goto up_out;
+       }
+       have_devstate_lock = 1;
+
+       if (dev->task->wsz == 0) {
+               long current_state;
+               DECLARE_WAITQUEUE(wait, current);
+
+               add_wait_queue(&dev->write_wait_q, &wait);
+               current_state = current->state;
+               set_current_state(TASK_INTERRUPTIBLE);
+               if (dev->task->wsz == 0) {      /* last check */
+                       devstate_unlock(dev);
+                       have_devstate_lock = 0;
+                       schedule();
+               }
+               set_current_state(current_state);
+               remove_wait_queue(&dev->write_wait_q, &wait);
+               if (signal_pending(current)) {
+                       ret = -ERESTARTSYS;
+                       goto up_out;
+               }
+               if (!have_devstate_lock) {
+                       if (devstate_lock(dev, OMAP_DSP_DEVSTATE_ATTACHED) < 0) {
+                               ret = -ERESTARTSYS;
+                               goto up_out;
+                       }
+                       have_devstate_lock = 1;
+               }
+               if (dev->task->wsz == 0)        /* should not occur */
+                       goto up_out;
+       }
+
+       if (copy_from_user(&wd, buf, count)) {
+               ret = -EFAULT;
+               goto up_out;
+       }
+
+       mbcmd_set(mb, MBCMD(WDSND), dev->task->tid, wd);
+       spin_lock(&dev->task->wsz_lock);
+       if (dsp_mbsend(&mb) < 0) {
+               spin_unlock(&dev->task->wsz_lock);
+               goto up_out;
+       }
+       ret = count;
+       if (rcvtyp_acv(dev->task->ttyp))
+               dev->task->wsz = 0;
+       spin_unlock(&dev->task->wsz_lock);
+
+up_out:
+       if (have_devstate_lock)
+               devstate_unlock(dev);
+       up(&dev->write_sem);
+       return ret;
+}
+
+static ssize_t dsp_task_write_bk(struct file *file, const char *buf,
+                                size_t count, loff_t *ppos)
+{
+       unsigned int minor = MINOR(file->f_dentry->d_inode->i_rdev);
+       struct taskdev *dev = taskdev[minor];
+       struct mbcmd mb;
+       int have_devstate_lock = 0;
+       int ret = 0;
+
+       if (count == 0) {
+               return 0;
+       } else if (count & 0x1) {
+               printk(KERN_ERR
+                      "omapdsp: odd count is illegal for DSP task device.\n");
+               return -EINVAL;
+       } else if ((int)buf & 0x1) {
+               printk(KERN_ERR
+                      "omapdsp: buf should be word aligned for "
+                      "dsp_task_write().\n");
+               return -EINVAL;
+       }
+
+       if (down_tasksem_interruptible(dev, &dev->write_sem))
+               return -ERESTARTSYS;
+       if (devstate_lock(dev, OMAP_DSP_DEVSTATE_ATTACHED) < 0) {
+               ret = -ERESTARTSYS;
+               goto up_out;
+       }
+       have_devstate_lock = 1;
+
+       if (dev->task->wsz == 0) {
+               long current_state;
+               DECLARE_WAITQUEUE(wait, current);
+
+               add_wait_queue(&dev->write_wait_q, &wait);
+               current_state = current->state;
+               set_current_state(TASK_INTERRUPTIBLE);
+               if (dev->task->wsz == 0) {      /* last check */
+                       devstate_unlock(dev);
+                       have_devstate_lock = 0;
+                       schedule();
+               }
+               set_current_state(current_state);
+               remove_wait_queue(&dev->write_wait_q, &wait);
+               if (signal_pending(current)) {
+                       ret = -ERESTARTSYS;
+                       goto up_out;
+               }
+               if (!have_devstate_lock) {
+                       if (devstate_lock(dev, OMAP_DSP_DEVSTATE_ATTACHED) < 0) {
+                               ret = -ERESTARTSYS;
+                               goto up_out;
+                       }
+                       have_devstate_lock = 1;
+               }
+               if (dev->task->wsz == 0)        /* should not occur */
+                       goto up_out;
+       }
+
+       if (count > dev->task->wsz)
+               count = dev->task->wsz;
+
+       if (rcvtyp_pvt(dev->task->ttyp)) {
+               /* private */
+               struct ipbuf_p *ipbp = dev->task->ipbuf_pvt_w;
+               unsigned char *dst;
+
+               if (dsp_mem_enable(ipbp) < 0) {
+                       ret = -ERESTARTSYS;
+                       goto up_out;
+               }
+               dst = dspword_to_virt(MKLONG(ipbp->ah, ipbp->al));
+               if (dsp_mem_enable(dst) < 0) {
+                       ret = -ERESTARTSYS;
+                       goto pv_out1;
+               }
+               if (copy_from_user_dsp(dst, buf, count)) {
+                       ret = -EFAULT;
+                       goto pv_out2;
+               }
+               ipbp->c = count/2;
+               ipbp->s = dev->task->tid;
+               mbcmd_set(mb, MBCMD(BKSNDP), dev->task->tid, 0);
+               spin_lock(&dev->task->wsz_lock);
+               if (dsp_mbsend(&mb) == 0) {
+                       if (rcvtyp_acv(dev->task->ttyp))
+                               dev->task->wsz = 0;
+                       ret = count;
+               }
+               spin_unlock(&dev->task->wsz_lock);
+pv_out2:
+               dsp_mem_disable(dst);
+pv_out1:
+               dsp_mem_disable(ipbp);
+       } else {
+               /* global */
+               struct ipbuf *ipbp;
+               unsigned short bid;
+
+               if (dsp_mem_enable_ipbuf() < 0) {
+                       ret = -ERESTARTSYS;
+                       goto up_out;
+               }
+               bid = get_free_ipbuf(dev->task->tid);
+               if (bid == OMAP_DSP_BID_NULL)
+                       goto gb_out;
+               ipbp = ipbuf[bid];
+               if (copy_from_user_dsp(ipbp->d, buf, count)) {
+                       release_ipbuf(bid);
+                       ret = -EFAULT;
+                       goto gb_out;
+               }
+               ipbp->c  = count/2;
+               ipbp->sa = dev->task->tid;
+               mbcmd_set(mb, MBCMD(BKSND), dev->task->tid, bid);
+               spin_lock(&dev->task->wsz_lock);
+               if (dsp_mbsend(&mb) == 0) {
+                       if (rcvtyp_acv(dev->task->ttyp))
+                               dev->task->wsz = 0;
+                       ret = count;
+                       ipb_bsycnt_inc(&ipbcfg);
+               } else
+                       release_ipbuf(bid);
+               spin_unlock(&dev->task->wsz_lock);
+gb_out:
+               dsp_mem_disable_ipbuf();
+       }
+
+up_out:
+       if (have_devstate_lock)
+               devstate_unlock(dev);
+       up(&dev->write_sem);
+       return ret;
+}
+
+static unsigned int dsp_task_poll(struct file * file, poll_table * wait)
+{
+       unsigned int minor = MINOR(file->f_dentry->d_inode->i_rdev);
+       struct taskdev *dev = taskdev[minor];
+       struct dsptask *task = dev->task;
+       unsigned int mask = 0;
+
+       poll_wait(file, &dev->read_wait_q, wait);
+       poll_wait(file, &dev->write_wait_q, wait);
+       if (devstate_lock(dev, OMAP_DSP_DEVSTATE_ATTACHED) < 0)
+               return 0;
+       if (sndtyp_psv(task->ttyp) ||
+           (sndtyp_wd(task->ttyp) && !fifo_empty(&task->rcvdt.fifo)) ||
+           (sndtyp_bk(task->ttyp) && !ipblink_empty(&task->rcvdt.bk.link)))
+               mask |= POLLIN | POLLRDNORM;
+       if (task->wsz)
+               mask |= POLLOUT | POLLWRNORM;
+       devstate_unlock(dev);
+
+       return mask;
+}
+
+static int dsp_task_ioctl(struct inode *inode, struct file *file,
+                         unsigned int cmd, unsigned long arg)
+{
+       unsigned int minor = MINOR(inode->i_rdev);
+       struct taskdev *dev = taskdev[minor];
+       struct mbcmd mb;
+       unsigned char tid;
+       struct mb_exarg mbarg, *mbargp;
+       int mbargc;
+       unsigned short mbargv[1];
+       int interactive;
+       int ret;
+
+       /* LOCK / UNLOCK operations */
+       switch (cmd) {
+       case OMAP_DSP_TASK_IOCTL_LOCK:
+               return taskdev_lock(dev);
+       case OMAP_DSP_TASK_IOCTL_UNLOCK:
+               return taskdev_unlock(dev);
+       }
+
+       /*
+        * actually only interractive commands need to lock
+        * the semaphore, but here all commands do it for simplicity.
+        */
+       if (down_tasksem_interruptible(dev, &dev->ioctl_sem))
+               return -ERESTARTSYS;
+       if (devstate_lock(dev, OMAP_DSP_DEVSTATE_ATTACHED) < 0) {
+               ret = -ERESTARTSYS;
+               goto up_out;
+       }
+
+       if ((cmd >= 0x0080) && (cmd < 0x0100)) {
+               /*
+                * 0x0080 - 0x00ff
+                * reserved for backward compatibility
+                * user-defined TCTL commands: no arg, non-interactive
+                */
+               mbargc = 0;
+               interactive = 0;
+       } else if (cmd < 0x8000) {
+               /*
+                * 0x0000 - 0x7fff (except 0x0080 - 0x00ff)
+                * system reserved TCTL commands
+                */
+               switch (cmd) {
+               case OMAP_DSP_MBCMD_TCTL_TEN:
+               case OMAP_DSP_MBCMD_TCTL_TDIS:
+                       mbargc = 0;
+                       interactive = 0;
+                       break;
+               default:
+                       ret = -ENOIOCTLCMD;
+                       goto unlock_out;
+               }
+       }
+       /*
+        * 0x8000 - 0xffff
+        * user-defined TCTL commands
+        */
+       else if (cmd < 0x8100) {
+               /* 0x8000-0x80ff: no arg, non-interactive */
+               mbargc = 0;
+               interactive = 0;
+       } else if (cmd < 0x8200) {
+               /* 0x8100-0x81ff: 1 arg, non-interactive */
+               mbargc = 1;
+               mbargv[0] = arg & 0xffff;
+               interactive = 0;
+       } else if (cmd < 0x9000) {
+               /* 0x8200-0x8fff: reserved */
+               ret = -ENOIOCTLCMD;
+               goto unlock_out;
+       } else if (cmd < 0x9100) {
+               /* 0x9000-0x90ff: no arg, interactive */
+               mbargc = 0;
+               interactive = 1;
+       } else if (cmd < 0x9200) {
+               /* 0x9100-0x91ff: 1 arg, interactive */
+               mbargc = 1;
+               mbargv[0] = arg & 0xffff;
+               interactive = 1;
+       } else if (cmd < 0x10000) {
+               /* 0x9200-0xffff: reserved */
+               ret =  -ENOIOCTLCMD;
+               goto unlock_out;
+       } else {
+               /*
+                * 0x10000 -
+                * non TCTL ioctls
+                */
+               switch (cmd) {
+               case OMAP_DSP_TASK_IOCTL_BFLSH:
+                       ret = dsp_task_flush_buf(dev->task);
+                       break;
+               case OMAP_DSP_TASK_IOCTL_SETBSZ:
+                       ret = dsp_task_set_fifosz(dev->task, arg);
+                       break;
+               case OMAP_DSP_TASK_IOCTL_GETNAME:
+                       ret = 0;
+                       if (copy_to_user((void *)arg, dev->name,
+                                        strlen(dev->name) + 1))
+                               ret = -EFAULT;
+                       break;
+               default:
+                       ret = -ENOIOCTLCMD;
+               }
+               goto unlock_out;
+       }
+
+       /*
+        * issue TCTL
+        */
+       tid = dev->task->tid;
+       mbcmd_set(mb, MBCMD(TCTL), tid, cmd);
+       if (mbargc > 0) {
+               mbarg.argc = mbargc;
+               mbarg.tid  = tid;
+               mbarg.argv = mbargv;
+               mbargp = &mbarg;
+       } else
+               mbargp = NULL;
+
+       if (interactive) {
+               dev->task->tctl_stat = -ERESTARTSYS;
+               devstate_unlock(dev);
+
+               dsp_mbsend_and_wait_exarg(&mb, mbargp, &dev->ioctl_wait_q);
+               if (signal_pending(current)) {
+                       ret = -ERESTARTSYS;
+                       goto up_out;
+               }
+               if (devstate_lock(dev, OMAP_DSP_DEVSTATE_ATTACHED) < 0) {
+                       ret = -ERESTARTSYS;
+                       goto up_out;
+               }
+               ret = dev->task->tctl_stat;
+               if (ret < 0) {
+                       printk(KERN_ERR "omapdsp: TCTL not responding.\n");
+                       goto unlock_out;
+               }
+       } else {
+               dsp_mbsend_exarg(&mb, mbargp);
+               ret = 0;
+       }
+
+unlock_out:
+       devstate_unlock(dev);
+up_out:
+       up(&dev->ioctl_sem);
+       return ret;
+}
+
+/**
+ * On demand page allocation is not allowed. The mapping area is defined by
+ * corresponding DSP tasks.
+ */
+static struct page *dsp_task_nopage_mmap(struct vm_area_struct *vma,
+                                        unsigned long address, int *type)
+{
+       return NOPAGE_SIGBUS;
+}
+
+static struct vm_operations_struct dsp_task_vm_ops = {
+       .nopage = dsp_task_nopage_mmap,
+};
+
+static int dsp_task_mmap(struct file *filp, struct vm_area_struct *vma)
+{
+       void *tmp_vadr;
+       unsigned long tmp_padr, tmp_vmadr, off;
+       size_t req_len, tmp_len;
+       unsigned int minor = MINOR(filp->f_dentry->d_inode->i_rdev);
+       struct taskdev *dev = taskdev[minor];
+       struct dsptask *task;
+       int ret = 0;
+
+       if (devstate_lock(dev, OMAP_DSP_DEVSTATE_ATTACHED) < 0)
+               return -ERESTARTSYS;
+       task = dev->task;
+       if (task->map_length == 0) {
+               printk(KERN_ERR
+                      "omapdsp: task %s doesn't have mmap buffer.\n",
+                      task->name);
+               ret = -EINVAL;
+               goto unlock_out;
+       }
+       if (is_dsp_internal_mem(task->map_base)) {
+               printk(KERN_ERR
+                      "omapdsp: task %s: map_base = %p\n"
+                      "    DARAM/SARAM can't be used as mmap buffer.\n",
+                      task->name, task->map_base);
+               ret = -EINVAL;
+               goto unlock_out;
+       }
+
+       /*
+        * Don't swap this area out
+        * Don't dump this area to a core file
+        */
+       vma->vm_flags |= VM_RESERVED | VM_IO;
+
+       /* Do not cache this area */
+       vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+
+       req_len = vma->vm_end - vma->vm_start;
+       off = vma->vm_pgoff << PAGE_SHIFT;
+       tmp_vmadr = vma->vm_start;
+       tmp_vadr = task->map_base + off;
+       do {
+               tmp_padr = dsp_virt_to_phys(tmp_vadr, &tmp_len);
+               if (tmp_padr == 0) {
+                       printk(KERN_ERR
+                              "omapdsp: task %s: illegal address "
+                              "for mmap: %p", task->name, tmp_vadr);
+                       /* partial mapping will be cleared in upper layer */
+                       ret = -EINVAL;
+                       goto unlock_out;
+               }
+               if (tmp_len > req_len)
+                       tmp_len = req_len;
+
+               printk(KERN_DEBUG
+                      "omapdsp: mmap info: "
+                      "vmadr = %08lx, padr = %08lx, len = %x\n",
+                      tmp_vmadr, tmp_padr, tmp_len);
+               if (remap_pfn_range(vma, tmp_vmadr, tmp_padr >> PAGE_SHIFT,
+                                   tmp_len, vma->vm_page_prot) != 0) {
+                       printk(KERN_ERR
+                              "omapdsp: task %s: remap_page_range() failed.\n",
+                              task->name);
+                       /* partial mapping will be cleared in upper layer */
+                       ret = -EINVAL;
+                       goto unlock_out;
+               }
+
+               req_len   -= tmp_len;
+               tmp_vmadr += tmp_len;
+               tmp_vadr  += tmp_len;
+       } while (req_len);
+
+       vma->vm_ops = &dsp_task_vm_ops;
+unlock_out:
+       devstate_unlock(dev);
+       return ret;
+}
+
+static int dsp_task_open(struct inode *inode, struct file *file)
+{
+       unsigned int minor = MINOR(inode->i_rdev);
+       struct taskdev *dev;
+       int ret = 0;
+
+       if (minor >= TASKDEV_MAX)
+               return -ENODEV;
+       dev = taskdev[minor];
+       if (dev == NULL)
+               return -ENODEV;
+
+       if (devstate_lock(dev, OMAP_DSP_DEVSTATE_NOTASK |
+                              OMAP_DSP_DEVSTATE_ATTACHED) < 0)
+               return -ERESTARTSYS;
+#ifndef CONFIG_OMAP_DSP_TASK_MULTIOPEN
+       if (dev->usecount > 0) {
+               ret = -EBUSY;
+               goto unlock_out;
+       }
+#endif
+
+       if (dev->state == OMAP_DSP_DEVSTATE_NOTASK) {
+               dev->state = OMAP_DSP_DEVSTATE_ADDREQ;
+               /* wake up twch daemon for tadd */
+               dsp_twch_touch();
+               devstate_unlock(dev);
+               if (devstate_lock(dev, OMAP_DSP_DEVSTATE_ATTACHED |
+                                      OMAP_DSP_DEVSTATE_ADDFAIL) < 0)
+                       return -ERESTARTSYS;
+               if (dev->state == OMAP_DSP_DEVSTATE_ADDFAIL) {
+                       printk(KERN_ERR "omapdsp: task attach failed for %s!\n",
+                              dev->name);
+                       ret = -EBUSY;
+                       dev->state = OMAP_DSP_DEVSTATE_NOTASK;
+                       wake_up_interruptible_all(&dev->state_wait_q);
+                       goto unlock_out;
+               }
+       }
+
+       /* state_lock covers usecount, proc_list as well. */
+       dev->usecount++;
+       proc_list_add(&dev->proc_list, current);
+       file->f_op = &dev->fops;
+       devstate_unlock(dev);
+
+       return 0;
+
+unlock_out:
+       devstate_unlock(dev);
+       return ret;
+}
+
+static int dsp_task_release(struct inode *inode, struct file *file)
+{
+       unsigned int minor = MINOR(inode->i_rdev);
+       struct taskdev *dev = taskdev[minor];
+
+       /* state_lock covers usecount, proc_list as well. */
+       spin_lock(&dev->state_lock);
+
+       /* state can be ATTACHED, KILLREQ or GARBAGE here. */
+       switch (dev->state) {
+
+       case OMAP_DSP_DEVSTATE_KILLREQ:
+               dev->usecount--;
+               break;
+
+       case OMAP_DSP_DEVSTATE_GARBAGE:
+               if(--dev->usecount == 0) {
+                       dev->state = OMAP_DSP_DEVSTATE_NOTASK;
+                       wake_up_interruptible_all(&dev->state_wait_q);
+               }
+               break;
+
+       case OMAP_DSP_DEVSTATE_ATTACHED:
+               if (dev->lock_pid == current->pid)
+                       taskdev_unlock(dev);
+               proc_list_del(&dev->proc_list, current);
+               if (--dev->usecount == 0) {
+                       if (minor >= n_task) {  /* dynamic task */
+                               dev->state = OMAP_DSP_DEVSTATE_DELREQ;
+                               /* wake up twch daemon for tdel */
+                               dsp_twch_touch();
+                       }
+               }
+               break;
+
+       }
+
+       spin_unlock(&dev->state_lock);
+       return 0;
+}
+
+/*
+ * mkdev / rmdev
+ */
+int dsp_mkdev(char *name)
+{
+       struct taskdev *dev;
+       int status;
+       unsigned char minor;
+
+       if (!dsp_is_ready()) {
+               printk(KERN_ERR "omapdsp: dsp has not been configured.\n");
+               return -EINVAL;
+       }
+       for (minor = n_task; minor < TASKDEV_MAX; minor++) {
+               if (taskdev[minor] == NULL)
+                       goto do_make;
+       }
+       printk(KERN_ERR "omapdsp: Too many task devices.\n");
+       return -EBUSY;
+
+do_make:
+       if ((dev = kmalloc(sizeof(struct taskdev), GFP_KERNEL)) == NULL)
+               return -ENOMEM;
+       memset(dev, 0, sizeof(struct taskdev));
+       if ((status = taskdev_init(dev, name, minor)) < 0) {
+               kfree(dev);
+               return status;
+       }
+       return minor;
+}
+
+int dsp_rmdev(char *name)
+{
+       unsigned char minor;
+       int ret;
+
+       if (!dsp_is_ready()) {
+               printk(KERN_ERR "omapdsp: dsp has not been configured.\n");
+               return -EINVAL;
+       }
+       for (minor = n_task; minor < TASKDEV_MAX; minor++) {
+               if (taskdev[minor] && !strcmp(taskdev[minor]->name, name)) {
+                       if ((ret = dsp_rmdev_minor(minor)) < 0)
+                               return ret;
+                       return minor;
+               }
+       }
+       return -EINVAL;
+}
+
+static int dsp_rmdev_minor(unsigned char minor)
+{
+       struct taskdev *dev = taskdev[minor];
+       struct dsptask *task = dev->task;
+
+       spin_lock(&dev->state_lock);
+
+       switch (dev->state) {
+
+       case OMAP_DSP_DEVSTATE_NOTASK:
+               /* fine */
+               break;
+
+       case OMAP_DSP_DEVSTATE_ATTACHED:
+               /* task is working. kill it. */
+               {
+                       siginfo_t info;
+                       struct proc_list *pl;
+
+                       info.si_signo = SIGBUS;
+                       info.si_errno = 0;
+                       info.si_code = SI_KERNEL;
+                       info._sifields._sigfault._addr = NULL;
+                       list_for_each_entry(pl, &dev->proc_list, list_head) {
+                               send_sig_info(SIGBUS, &info, pl->tsk);
+                       }
+                       taskdev_detach_task(dev);
+                       dsp_task_unconfig(task);
+                       kfree(task);
+                       dev->state = OMAP_DSP_DEVSTATE_GARBAGE;
+               }
+               break;
+
+       case OMAP_DSP_DEVSTATE_ADDREQ:
+               /* open() is waiting. drain it. */
+               dev->state = OMAP_DSP_DEVSTATE_ADDFAIL;
+               wake_up_interruptible_all(&dev->state_wait_q);
+               break;
+
+       case OMAP_DSP_DEVSTATE_DELREQ:
+               /* nobody is waiting. */
+               dev->state = OMAP_DSP_DEVSTATE_NOTASK;
+               wake_up_interruptible_all(&dev->state_wait_q);
+               break;
+
+       case OMAP_DSP_DEVSTATE_KILLREQ:
+       case OMAP_DSP_DEVSTATE_GARBAGE:
+       case OMAP_DSP_DEVSTATE_ADDFAIL:
+               /* transient state. wait for a moment. */
+               break;
+
+       }
+
+       spin_unlock(&dev->state_lock);
+
+       /* wait for some time and hope the state is settled */
+       devstate_lock_timeout(dev, OMAP_DSP_DEVSTATE_NOTASK, HZ);
+       if (dev->state != OMAP_DSP_DEVSTATE_NOTASK) {
+               printk(KERN_WARNING
+                      "omapdsp: illegal device state on rmdev %s.\n",
+                      dev->name);
+       }
+       dev->state = OMAP_DSP_DEVSTATE_INVALID;
+       devstate_unlock(dev);
+
+       taskdev_delete(minor);
+       kfree(dev);
+
+       return 0;
+}
+
+struct file_operations dsp_task_fops = {
+       .owner   = THIS_MODULE,
+       .poll    = dsp_task_poll,
+       .ioctl   = dsp_task_ioctl,
+       .open    = dsp_task_open,
+       .release = dsp_task_release,
+       .mmap    = dsp_task_mmap,
+};
+
+static void dsptask_dev_release(struct device *dev)
+{
+}
+
+static int taskdev_init(struct taskdev *dev, char *name, unsigned char minor)
+{
+       taskdev[minor] = dev;
+
+       INIT_LIST_HEAD(&dev->proc_list);
+       init_waitqueue_head(&dev->read_wait_q);
+       init_waitqueue_head(&dev->write_wait_q);
+       init_waitqueue_head(&dev->ioctl_wait_q);
+       init_MUTEX(&dev->read_sem);
+       init_MUTEX(&dev->write_sem);
+       init_MUTEX(&dev->ioctl_sem);
+       init_MUTEX(&dev->lock_sem);
+       dev->lock_pid = 0;
+
+       strncpy(dev->name, name, OMAP_DSP_TNM_LEN);
+       dev->name[OMAP_DSP_TNM_LEN-1] = '\0';
+       dev->state = (minor < n_task) ? OMAP_DSP_DEVSTATE_ATTACHED :
+                                       OMAP_DSP_DEVSTATE_NOTASK;
+       dev->usecount = 0;
+       memcpy(&dev->fops, &dsp_task_fops, sizeof(struct file_operations));
+
+       dev->dev.parent = &dsp_device.dev;
+       dev->dev.bus = &dsptask_bus;
+       sprintf(dev->dev.bus_id, "dsptask%d", minor);
+       dev->dev.release = dsptask_dev_release;
+       device_register(&dev->dev);
+       device_create_file(&dev->dev, &dev_attr_devname);
+       device_create_file(&dev->dev, &dev_attr_devstate);
+       device_create_file(&dev->dev, &dev_attr_proc_list);
+       class_simple_device_add(dsp_task_class,
+                               MKDEV(OMAP_DSP_TASK_MAJOR, minor), NULL,
+                               "dsptask%d", minor);
+       devfs_mk_cdev(MKDEV(OMAP_DSP_TASK_MAJOR, minor),
+                     S_IFCHR | S_IRUGO | S_IWUGO, "dsptask%d", minor);
+
+       init_waitqueue_head(&dev->state_wait_q);
+       spin_lock_init(&dev->state_lock);
+
+       return 0;
+}
+
+static void taskdev_delete(unsigned char minor)
+{
+       struct taskdev *dev = taskdev[minor];
+
+       if (!dev)
+               return;
+       device_remove_file(&dev->dev, &dev_attr_devname);
+       device_remove_file(&dev->dev, &dev_attr_devstate);
+       device_remove_file(&dev->dev, &dev_attr_proc_list);
+
+       devfs_remove("dsptask%d", minor);
+       device_unregister(&dev->dev);
+       proc_list_flush(&dev->proc_list);
+       taskdev[minor] = NULL;
+}
+
+static void taskdev_attach_task(struct taskdev *dev, struct dsptask *task)
+{
+       unsigned short ttyp = task->ttyp;
+
+       dev->task = task;
+       task->dev = dev;
+       dev->fops.read =
+               sndtyp_acv(ttyp) ?
+                       sndtyp_wd(ttyp) ? dsp_task_read_wd_acv:
+                       /* sndtyp_bk */   dsp_task_read_bk_acv:
+               /* sndtyp_psv */
+                       sndtyp_wd(ttyp) ? dsp_task_read_wd_psv:
+                       /* sndtyp_bk */   dsp_task_read_bk_psv;
+       dev->fops.write =
+               rcvtyp_wd(ttyp) ? dsp_task_write_wd:
+               /* rcvbyp_bk */   dsp_task_write_bk;
+
+       device_create_file(&dev->dev, &dev_attr_taskname);
+       device_create_file(&dev->dev, &dev_attr_ttyp);
+       if (sndtyp_wd(ttyp)) {
+               device_create_file(&dev->dev, &dev_attr_fifosz);
+               device_create_file(&dev->dev, &dev_attr_fifocnt);
+       } else
+               device_create_file(&dev->dev, &dev_attr_ipblink);
+       device_create_file(&dev->dev, &dev_attr_wsz);
+       if (task->map_length)
+               device_create_file(&dev->dev, &dev_attr_mmap);
+}
+
+static void taskdev_detach_task(struct taskdev *dev)
+{
+       unsigned short ttyp = dev->task->ttyp;
+
+       device_remove_file(&dev->dev, &dev_attr_taskname);
+       device_remove_file(&dev->dev, &dev_attr_ttyp);
+       if (sndtyp_wd(ttyp)) {
+               device_remove_file(&dev->dev, &dev_attr_fifosz);
+               device_remove_file(&dev->dev, &dev_attr_fifocnt);
+       } else
+               device_remove_file(&dev->dev, &dev_attr_ipblink);
+       device_remove_file(&dev->dev, &dev_attr_wsz);
+       if (dev->task->map_length)
+               device_remove_file(&dev->dev, &dev_attr_mmap);
+
+       if (dev->task) {
+               dev->task = NULL;
+               dev->fops.read = NULL;
+               dev->fops.write = NULL;
+               printk(KERN_INFO "omapdsp: taskdev %s disabled.\n", dev->name);
+       }
+}
+
+/*
+ * tadd / tdel / tkill
+ */
+int dsp_tadd(unsigned char minor, unsigned long adr)
+{
+       struct taskdev *dev;
+       struct dsptask *task;
+       struct mbcmd mb;
+       struct mb_exarg arg;
+       unsigned char tid;
+       unsigned short argv[2];
+       int ret = minor;
+
+       if ((minor >= TASKDEV_MAX) || ((dev = taskdev[minor]) == NULL)) {
+               printk(KERN_ERR
+                      "omapdsp: no task device with minor %d\n", minor);
+               return -EINVAL;
+       }
+       /*
+        * we don't need to lock state_lock because
+        * only tadd is allowed when devstate is ADDREQ.
+        */
+       if (dev->state != OMAP_DSP_DEVSTATE_ADDREQ) {
+               printk(KERN_ERR
+                      "omapdsp: taskdev %s is not requesting for tadd.\n",
+                      dev->name);
+               return -EINVAL;
+       }
+
+       if (adr == OMAP_DSP_TADD_ABORTADR) {
+               /* aborting tadd intentionally */
+               printk(KERN_INFO "omapdsp: tadd address is ABORTADR.\n");
+               goto fail_out;
+       }
+       if (adr >= DSPSPACE_SIZE) {
+               printk(KERN_ERR
+                      "omapdsp: illegal address 0x%08lx for tadd\n", adr);
+               ret = -EINVAL;
+               goto fail_out;
+       }
+
+       adr >>= 1;      /* word address */
+       argv[0] = adr >> 16;    /* addrh */
+       argv[1] = adr & 0xffff; /* addrl */
+
+       if (down_interruptible(&cfg_sem)) {
+               ret = -ERESTARTSYS;
+               goto fail_out;
+       }
+       cfg_tid = OMAP_DSP_TID_ANON;
+       cfg_cmd = MBCMD(TADD);
+       mbcmd_set(mb, MBCMD(TADD), 0, 0);
+       arg.tid  = OMAP_DSP_TID_ANON;
+       arg.argc = 2;
+       arg.argv = argv;
+
+       dsp_mbsend_and_wait_exarg(&mb, &arg, &cfg_wait_q);
+
+       tid = cfg_tid;
+       cfg_tid = OMAP_DSP_TID_ANON;
+       cfg_cmd = 0;
+       up(&cfg_sem);
+
+       if (tid == OMAP_DSP_TID_ANON) {
+               printk(KERN_ERR "omapdsp: tadd failed!\n");
+               ret = -EINVAL;
+               goto fail_out;
+       }
+       if ((tid < n_task) || dsptask[tid]) {
+               printk(KERN_ERR "omapdsp: illegal tid (%d)!\n", tid);
+               ret = -EINVAL;
+               goto fail_out;
+       }
+       if ((task = kmalloc(sizeof(struct dsptask), GFP_KERNEL)) == NULL) {
+               ret = -ENOMEM;
+               goto fail_out;
+       }
+       memset(task, 0, sizeof(struct dsptask));
+
+       if ((ret = dsp_task_config(task, tid)) < 0)
+               goto free_out;
+       taskdev_attach_task(dev, task);
+
+       if (strcmp(dev->name, task->name)) {
+               printk(KERN_ERR
+                      "omapdsp: task name (%s) doesn't match with "
+                      "device name (%s).\n", task->name, dev->name);
+               ret = -EINVAL;
+               dev->state = OMAP_DSP_DEVSTATE_DELREQ;
+               dsp_twch_touch();
+               return -EINVAL;
+       }
+
+       dsp_task_init(task);
+       printk(KERN_INFO "omapdsp: taskdev %s enabled.\n", dev->name);
+       dev->state = OMAP_DSP_DEVSTATE_ATTACHED;
+       wake_up_interruptible_all(&dev->state_wait_q);
+       return minor;
+
+free_out:
+       kfree(task);
+fail_out:
+       dev->state = OMAP_DSP_DEVSTATE_ADDFAIL;
+       wake_up_interruptible_all(&dev->state_wait_q);
+       return ret;
+}
+
+int dsp_tdel(unsigned char minor)
+{
+       struct taskdev *dev;
+       struct dsptask *task;
+       struct mbcmd mb;
+       unsigned char tid, tid_response;
+       int ret = minor;
+
+       if ((minor >= TASKDEV_MAX) || ((dev = taskdev[minor]) == NULL)) {
+               printk(KERN_ERR
+                      "omapdsp: no task device with minor %d\n", minor);
+               return -EINVAL;
+       }
+       /*
+        * we don't need to lock state_lock because
+        * only tdel is allowed when devstate is DELREQ.
+        */
+       if (dev->state != OMAP_DSP_DEVSTATE_DELREQ) {
+               printk(KERN_ERR
+                      "omapdsp: taskdev %s is not requesting for tdel.\n",
+                      dev->name);
+               return -EINVAL;
+       }
+
+       task = dev->task;
+       tid = task->tid;
+       if (down_interruptible(&cfg_sem)) {
+               return -ERESTARTSYS;
+       }
+       cfg_tid = OMAP_DSP_TID_ANON;
+       cfg_cmd = MBCMD(TDEL);
+       mbcmd_set(mb, MBCMD(TDEL), tid, OMAP_DSP_MBCMD_TDEL_SAFE);
+       dsp_mbsend_and_wait(&mb, &cfg_wait_q);
+       tid_response = cfg_tid;
+       cfg_tid = OMAP_DSP_TID_ANON;
+       cfg_cmd = 0;
+       up(&cfg_sem);
+
+       taskdev_detach_task(dev);
+       dsp_task_unconfig(task);
+       kfree(task);
+       dev->state = OMAP_DSP_DEVSTATE_NOTASK;
+       wake_up_interruptible_all(&dev->state_wait_q);
+
+       if (tid_response != tid) {
+               printk(KERN_ERR "omapdsp: tdel failed!\n");
+               ret = -EINVAL;
+       }
+
+       return ret;
+}
+
+int dsp_tkill(unsigned char minor)
+{
+       struct taskdev *dev;
+       struct dsptask *task;
+       struct mbcmd mb;
+       unsigned char tid, tid_response;
+       siginfo_t info;
+       struct proc_list *pl;
+       int ret = minor;
+
+       if ((minor >= TASKDEV_MAX) || ((dev = taskdev[minor]) == NULL)) {
+               printk(KERN_ERR
+                      "omapdsp: no task device with minor %d\n", minor);
+               return -EINVAL;
+       }
+       spin_lock(&dev->state_lock);
+       if (dev->state != OMAP_DSP_DEVSTATE_ATTACHED) {
+               printk(KERN_ERR
+                      "omapdsp: task has not been attached for taskdev %s\n",
+                      dev->name);
+               spin_unlock(&dev->state_lock);
+               return -EINVAL;
+       }
+       dev->state = OMAP_DSP_DEVSTATE_KILLREQ;
+       info.si_signo = SIGBUS;
+       info.si_errno = 0;
+       info.si_code = SI_KERNEL;
+       info._sifields._sigfault._addr = NULL;
+       list_for_each_entry(pl, &dev->proc_list, list_head) {
+               send_sig_info(SIGBUS, &info, pl->tsk);
+       }
+       spin_unlock(&dev->state_lock);
+
+       task = dev->task;
+       tid = task->tid;
+       if (down_interruptible(&cfg_sem)) {
+               tid_response = OMAP_DSP_TID_ANON;
+               ret = -ERESTARTSYS;
+               goto detach_out;
+       }
+       cfg_tid = OMAP_DSP_TID_ANON;
+       cfg_cmd = MBCMD(TDEL);
+       mbcmd_set(mb, MBCMD(TDEL), tid, OMAP_DSP_MBCMD_TDEL_KILL);
+       dsp_mbsend_and_wait(&mb, &cfg_wait_q);
+       tid_response = cfg_tid;
+       cfg_tid = OMAP_DSP_TID_ANON;
+       cfg_cmd = 0;
+       up(&cfg_sem);
+
+detach_out:
+       taskdev_detach_task(dev);
+       dsp_task_unconfig(task);
+       kfree(task);
+
+       if (tid_response != tid)
+               printk(KERN_ERR "omapdsp: tkill failed!\n");
+
+       spin_lock(&dev->state_lock);
+       dev->state = (dev->usecount > 0) ? OMAP_DSP_DEVSTATE_GARBAGE :
+                                          OMAP_DSP_DEVSTATE_NOTASK;
+       wake_up_interruptible_all(&dev->state_wait_q);
+       spin_unlock(&dev->state_lock);
+
+       return ret;
+}
+
+/*
+ * state inquiry
+ */
+long taskdev_state(unsigned char minor)
+{
+       return taskdev[minor] ? taskdev[minor]->state :
+                               OMAP_DSP_DEVSTATE_NOTASK;
+}
+
+/*
+ * functions called from mailbox1 interrupt routine
+ */
+void mbx1_wdsnd(struct mbcmd *mb)
+{
+       unsigned char tid = mb->cmd_l;
+       struct dsptask *task = dsptask[tid];
+
+       if ((tid >= TASKDEV_MAX) || (task == NULL)) {
+               printk(KERN_ERR "mbx: WDSND with illegal tid! %d\n", tid);
+               return;
+       }
+       if (sndtyp_bk(task->ttyp)) {
+               printk(KERN_ERR
+                      "mbx: WDSND from block sending task! (task%d)\n", tid);
+               return;
+       }
+       if (sndtyp_psv(task->ttyp) &&
+           !waitqueue_active(&task->dev->read_wait_q)) {
+               printk(KERN_WARNING
+                      "mbx: WDSND from passive sending task (task%d) "
+                      "without request!\n", tid);
+               return;
+       }
+
+       write_word_to_fifo(&task->rcvdt.fifo, mb->data);
+       wake_up_interruptible(&task->dev->read_wait_q);
+}
+
+void mbx1_wdreq(struct mbcmd *mb)
+{
+       unsigned char tid = mb->cmd_l;
+       struct dsptask *task = dsptask[tid];
+
+       if ((tid >= TASKDEV_MAX) || (task == NULL)) {
+               printk(KERN_ERR "mbx: WDREQ with illegal tid! %d\n", tid);
+               return;
+       }
+       if (rcvtyp_psv(task->ttyp)) {
+               printk(KERN_ERR
+                      "mbx: WDREQ from passive receiving task! (task%d)\n",
+                      tid);
+               return;
+       }
+
+       spin_lock(&task->wsz_lock);
+       task->wsz = 2;
+       spin_unlock(&task->wsz_lock);
+       wake_up_interruptible(&task->dev->write_wait_q);
+}
+
+void mbx1_bksnd(struct mbcmd *mb)
+{
+       unsigned char tid = mb->cmd_l;
+       unsigned short bid = mb->data;
+       struct dsptask *task = dsptask[tid];
+       unsigned short cnt;
+
+       if (bid >= ipbcfg.ln) {
+               printk(KERN_ERR "mbx: BKSND with illegal bid! %d\n", bid);
+               return;
+       }
+       ipb_bsycnt_dec(&ipbcfg);
+       if ((tid >= TASKDEV_MAX) || (task == NULL)) {
+               printk(KERN_ERR "mbx: BKSND with illegal tid! %d\n", tid);
+               goto unuse_ipbuf_out;
+       }
+       if (sndtyp_wd(task->ttyp)) {
+               printk(KERN_ERR
+                      "mbx: BKSND from word sending task! (task%d)\n", tid);
+               goto unuse_ipbuf_out;
+       }
+       if (sndtyp_pvt(task->ttyp)) {
+               printk(KERN_ERR
+                      "mbx: BKSND from private sending task! (task%d)\n", tid);
+               goto unuse_ipbuf_out;
+       }
+       if (sync_with_dsp(&ipbuf[bid]->sd, tid, 10) < 0) {
+               printk(KERN_ERR "mbx: BKSND - IPBUF sync failed!\n");
+               return;
+       }
+
+       /* should be done in DSP, but just in case. */
+       ipbuf[bid]->next = OMAP_DSP_BID_NULL;
+
+       cnt = ipbuf[bid]->c;
+       if (cnt > ipbcfg.lsz) {
+               printk(KERN_ERR "mbx: BKSND cnt(%d) > ipbuf line size(%d)!\n",
+                      cnt, ipbcfg.lsz);
+               goto unuse_ipbuf_out;
+       }
+
+       if (cnt == 0) {
+               /* 0-byte send from DSP */
+               unuse_ipbuf_nowait(bid);
+               goto done;
+       }
+       spin_lock(&task->rcvdt.bk.link.lock);
+       ipblink_add_tail(&task->rcvdt.bk.link, bid, ipbuf);
+       spin_unlock(&task->rcvdt.bk.link.lock);
+       /* we keep coming bid and return alternative line to DSP. */
+       balance_ipbuf();
+
+done:
+       wake_up_interruptible(&task->dev->read_wait_q);
+       return;
+
+unuse_ipbuf_out:
+       unuse_ipbuf_nowait(bid);
+       return;
+}
+
+void mbx1_bkreq(struct mbcmd *mb)
+{
+       unsigned char tid = mb->cmd_l;
+       unsigned short cnt = mb->data;
+       struct dsptask *task = dsptask[tid];
+
+       if ((tid >= TASKDEV_MAX) || (task == NULL)) {
+               printk(KERN_ERR "mbx: BKREQ with illegal tid! %d\n", tid);
+               return;
+       }
+       if (rcvtyp_wd(task->ttyp)) {
+               printk(KERN_ERR
+                      "mbx: BKREQ from word receiving task! (task%d)\n", tid);
+               return;
+       }
+       if (rcvtyp_pvt(task->ttyp)) {
+               printk(KERN_ERR
+                      "mbx: BKREQ from private receiving task! (task%d)\n",
+                      tid);
+               return;
+       }
+       if (rcvtyp_psv(task->ttyp)) {
+               printk(KERN_ERR
+                      "mbx: BKREQ from passive receiving task! (task%d)\n",
+                      tid);
+               return;
+       }
+
+       spin_lock(&task->wsz_lock);
+       task->wsz = cnt*2;
+       spin_unlock(&task->wsz_lock);
+       wake_up_interruptible(&task->dev->write_wait_q);
+}
+
+void mbx1_bkyld(struct mbcmd *mb)
+{
+       unsigned short bid = mb->data;
+
+       if (bid >= ipbcfg.ln) {
+               printk(KERN_ERR "mbx: BKYLD with illegal bid! %d\n", bid);
+               return;
+       }
+
+       /* should be done in DSP, but just in case. */
+       ipbuf[bid]->next = OMAP_DSP_BID_NULL;
+
+       /* we don't need to sync with DSP */
+       ipb_bsycnt_dec(&ipbcfg);
+       release_ipbuf(bid);
+}
+
+void mbx1_bksndp(struct mbcmd *mb)
+{
+       unsigned char tid = mb->cmd_l;
+       struct dsptask *task = dsptask[tid];
+       struct rcvdt_bk_struct *rcvdt = &task->rcvdt.bk;
+       struct ipbuf_p *ipbp = rcvdt->ipbuf_pvt_r;
+
+       if ((tid >= TASKDEV_MAX) || (task == NULL)) {
+               printk(KERN_ERR "mbx: BKSNDP with illegal tid! %d\n", tid);
+               return;
+       }
+       if (sndtyp_wd(task->ttyp)) {
+               printk(KERN_ERR
+                      "mbx: BKSNDP from word sending task! (task%d)\n", tid);
+               return;
+       }
+       if (sndtyp_gbl(task->ttyp)) {
+               printk(KERN_ERR
+                      "mbx: BKSNDP from non-private sending task! (task%d)\n",
+                      tid);
+               return;
+       }
+
+       /*
+        * we should not have delayed block at this point
+        * because read() routine releases the lock of the buffer and
+        * until then DSP can't send next data.
+        */
+
+       if (sync_with_dsp(&ipbp->s, tid, 10) < 0) {
+               printk(KERN_ERR "mbx: BKSNDP - IPBUF sync failed!\n");
+               return;
+       }
+       printk(KERN_DEBUG "mbx: ipbuf_pvt_r->a = 0x%08lx\n",
+              MKLONG(ipbp->ah, ipbp->al));
+       spin_lock(&rcvdt->link.lock);
+       ipblink_add_pvt(&rcvdt->link);
+       spin_unlock(&rcvdt->link.lock);
+       wake_up_interruptible(&task->dev->read_wait_q);
+}
+
+void mbx1_bkreqp(struct mbcmd *mb)
+{
+       unsigned char tid = mb->cmd_l;
+       struct dsptask *task = dsptask[tid];
+       struct ipbuf_p *ipbp = task->ipbuf_pvt_w;
+
+       if ((tid >= TASKDEV_MAX) || (task == NULL)) {
+               printk(KERN_ERR "mbx: BKREQP with illegal tid! %d\n", tid);
+               return;
+       }
+       if (rcvtyp_wd(task->ttyp)) {
+               printk(KERN_ERR
+                      "mbx: BKREQP from word receiving task! (task%d)\n", tid);
+               return;
+       }
+       if (rcvtyp_gbl(task->ttyp)) {
+               printk(KERN_ERR
+                      "mbx: BKREQP from non-private receiving task! (task%d)\n", tid);
+               return;
+       }
+       if (rcvtyp_psv(task->ttyp)) {
+               printk(KERN_ERR
+                      "mbx: BKREQP from passive receiving task! (task%d)\n", tid);
+               return;
+       }
+
+       if (sync_with_dsp(&ipbp->s, OMAP_DSP_TID_FREE, 10) < 0) {
+               printk(KERN_ERR "mbx: BKREQP - IPBUF sync failed!\n");
+               return;
+       }
+       printk(KERN_DEBUG "mbx: ipbuf_pvt_w->a = 0x%08lx\n",
+              MKLONG(ipbp->ah, ipbp->al));
+       spin_lock(&task->wsz_lock);
+       task->wsz = ipbp->c*2;
+       spin_unlock(&task->wsz_lock);
+       wake_up_interruptible(&task->dev->write_wait_q);
+}
+
+void mbx1_tctl(struct mbcmd *mb)
+{
+       unsigned char tid = mb->cmd_l;
+       struct dsptask *task = dsptask[tid];
+
+       if ((tid >= TASKDEV_MAX) || (task == NULL)) {
+               printk(KERN_ERR "mbx: TCTL with illegal tid! %d\n", tid);
+               return;
+       }
+
+       if (!waitqueue_active(&task->dev->ioctl_wait_q)) {
+               printk(KERN_WARNING "mbx: unexpected TCTL from DSP!\n");
+               return;
+       }
+
+       task->tctl_stat = mb->data;
+       wake_up_interruptible(&task->dev->ioctl_wait_q);
+}
+
+void mbx1_tcfg(struct mbcmd *mb)
+{
+       unsigned char tid = mb->cmd_l;
+       struct dsptask *task = dsptask[tid];
+       unsigned long tmp_ipbp_r, tmp_ipbp_w;
+       unsigned long tmp_mapstart, tmp_maplen;
+       unsigned long tmp_tnm;
+       unsigned short *tnm;
+       volatile unsigned short *buf;
+       int i;
+
+       if ((tid >= TASKDEV_MAX) || (task == NULL)) {
+               printk(KERN_ERR "mbx: TCFG with illegal tid! %d\n", tid);
+               return;
+       }
+       if ((task->state != TASK_STATE_CFGREQ) || (cfg_cmd != MBCMD(TCFG))) {
+               printk(KERN_WARNING "mbx: unexpected TCFG from DSP!\n");
+               return;
+       }
+
+       if (sync_with_dsp(&ipbuf_sys_da->s, tid, 10) < 0) {
+               printk(KERN_ERR "mbx: TCFG - IPBUF sync failed!\n");
+               return;
+       }
+
+       /*
+        * read configuration data on system IPBUF
+        */
+       buf = ipbuf_sys_da->d;
+       task->ttyp   = buf[0];
+       tmp_ipbp_r   = MKLONG(buf[1], buf[2]);
+       tmp_ipbp_w   = MKLONG(buf[3], buf[4]);
+       tmp_mapstart = MKLONG(buf[5], buf[6]);
+       tmp_maplen   = MKLONG(buf[7], buf[8]);
+       tmp_tnm      = MKLONG(buf[9], buf[10]);
+
+       task->rcvdt.bk.ipbuf_pvt_r = dspword_to_virt(tmp_ipbp_r);
+       task->ipbuf_pvt_w          = dspword_to_virt(tmp_ipbp_w);
+       task->map_base   = dspword_to_virt(tmp_mapstart);
+       task->map_length = tmp_maplen << 1;     /* word -> byte */
+       tnm = dspword_to_virt(tmp_tnm);
+       for (i = 0; i < OMAP_DSP_TNM_LEN-1; i++) {
+               /* avoiding byte access */
+               unsigned short tmp = tnm[i];
+               task->name[i] = tmp & 0x00ff;
+               if (!tmp)
+                       break;
+       }
+       task->name[OMAP_DSP_TNM_LEN-1] = '\0';
+
+       release_ipbuf_pvt(ipbuf_sys_da);
+       task->state = TASK_STATE_READY;
+       wake_up_interruptible(&cfg_wait_q);
+}
+
+void mbx1_tadd(struct mbcmd *mb)
+{
+       unsigned char tid = mb->cmd_l;
+
+       if ((!waitqueue_active(&cfg_wait_q)) || (cfg_cmd != MBCMD(TADD))) {
+               printk(KERN_WARNING "mbx: unexpected TADD from DSP!\n");
+               return;
+       }
+       cfg_tid = tid;
+       wake_up_interruptible(&cfg_wait_q);
+}
+
+void mbx1_tdel(struct mbcmd *mb)
+{
+       unsigned char tid = mb->cmd_l;
+
+       if ((!waitqueue_active(&cfg_wait_q)) || (cfg_cmd != MBCMD(TDEL))) {
+               printk(KERN_WARNING "mbx: unexpected TDEL from DSP!\n");
+               return;
+       }
+       cfg_tid = tid;
+       wake_up_interruptible(&cfg_wait_q);
+}
+
+void mbx1_err_fatal(unsigned char tid)
+{
+       struct dsptask *task = dsptask[tid];
+       struct proc_list *pl;
+       siginfo_t info;
+
+       if ((tid >= TASKDEV_MAX) || (task == NULL)) {
+               printk(KERN_ERR "mbx: FATAL ERR with illegal tid! %d\n", tid);
+               return;
+       }
+
+       info.si_signo = SIGBUS;
+       info.si_errno = 0;
+       info.si_code = SI_KERNEL;
+       info._sifields._sigfault._addr = NULL;
+       spin_lock(&task->dev->state_lock);
+       list_for_each_entry(pl, &task->dev->proc_list, list_head) {
+               send_sig_info(SIGBUS, &info, pl->tsk);
+       }
+       spin_unlock(&task->dev->state_lock);
+}
+
+void mbx1_dbg(struct mbcmd *mb)
+{
+       unsigned char tid = mb->cmd_l;
+       char s[80], *s_end = &s[79], *p;
+       unsigned short *src;
+       volatile unsigned short *buf;
+       int cnt;
+       int i;
+
+       if (((tid >= TASKDEV_MAX) || (dsptask[tid] == NULL)) &&
+           (tid != OMAP_DSP_TID_ANON)) {
+               printk(KERN_ERR "mbx: DBG with illegal tid! %d\n", tid);
+               return;
+       }
+       if (sync_with_dsp(&ipbuf_sys_da->s, tid, 10) < 0) {
+               printk(KERN_ERR "mbx: DBG - IPBUF sync failed!\n");
+               return;
+       }
+       buf = ipbuf_sys_da->d;
+       cnt = buf[0];
+       src = dspword_to_virt(MKLONG(buf[1], buf[2]));
+       p = s;
+       for (i = 0; i < cnt; i++) {
+               unsigned short tmp;
+               /*
+                * Be carefull that ipbuf should not be read with
+                * 1-byte access since it might be placed in DARAM/SARAM
+                * and it can cause unexpected byteswap.
+                * For example,
+                *   *(p++) = *(src++) & 0xff;
+                * causes 1-byte access!
+                */
+               tmp = *src++;
+               *(p++) = tmp & 0xff;
+               if (*(p-1) == '\n') {
+                       *p = '\0';
+                       printk(KERN_INFO "%s", s);
+                       p = s;
+                       continue;
+               }
+               if (p == s_end) {
+                       *p = '\0';
+                       printk(KERN_INFO "%s\n", s);
+                       p = s;
+                       continue;
+               }
+       }
+       if (p > s) {
+               *p = '\0';
+               printk(KERN_INFO "%s\n", s);
+       }
+
+       release_ipbuf_pvt(ipbuf_sys_da);
+}
+
+
+/*
+ * sysfs files
+ */
+static ssize_t devname_show(struct device *d, char *buf)
+{
+       struct taskdev *dev = to_taskdev(d);
+       return sprintf(buf, "%s\n", dev->name);
+}
+
+#define devstate_name(stat) (\
+       ((stat) == OMAP_DSP_DEVSTATE_NOTASK)   ? "NOTASK" :\
+       ((stat) == OMAP_DSP_DEVSTATE_ATTACHED) ? "ATTACHED" :\
+       ((stat) == OMAP_DSP_DEVSTATE_GARBAGE)  ? "GARBAGE" :\
+       ((stat) == OMAP_DSP_DEVSTATE_INVALID)  ? "INVALID" :\
+       ((stat) == OMAP_DSP_DEVSTATE_ADDREQ)   ? "ADDREQ" :\
+       ((stat) == OMAP_DSP_DEVSTATE_DELREQ)   ? "DELREQ" :\
+       ((stat) == OMAP_DSP_DEVSTATE_KILLREQ)  ? "KILLREQ" :\
+       ((stat) == OMAP_DSP_DEVSTATE_ADDFAIL)  ? "ADDFAIL" :\
+                                                "unknown")
+
+static ssize_t devstate_show(struct device *d, char *buf)
+{
+       struct taskdev *dev = to_taskdev(d);
+       return sprintf(buf, "%s\n", devstate_name(dev->state));
+}
+
+static ssize_t proc_list_show(struct device *d, char *buf)
+{
+       struct taskdev *dev;
+       struct proc_list *pl;
+       int len = 0;
+
+       dev = to_taskdev(d);
+       spin_lock(&dev->state_lock);
+       list_for_each_entry(pl, &dev->proc_list, list_head) {
+               len += sprintf(buf + len, "%d\n", pl->tsk->pid);
+       }
+       spin_unlock(&dev->state_lock);
+
+       return len;
+}
+
+static ssize_t taskname_show(struct device *d, char *buf)
+{
+       struct taskdev *dev = to_taskdev(d);
+       int len;
+
+       len = sprintf(buf, "%s\n", dev->task->name);
+
+       return len;
+}
+
+static ssize_t ttyp_show(struct device *d, char *buf)
+{
+       unsigned short ttyp = to_taskdev(d)->task->ttyp;
+       int len = 0;
+
+       len += sprintf(buf + len, "0x%04x\n", ttyp);
+       len += sprintf(buf + len, "%s %s send\n",
+                       (sndtyp_acv(ttyp)) ? "active" :
+                                            "passive",
+                       (sndtyp_wd(ttyp))  ? "word" :
+                       (sndtyp_pvt(ttyp)) ? "private block" :
+                                            "global block");
+       len += sprintf(buf + len, "%s %s receive\n",
+                       (rcvtyp_acv(ttyp)) ? "active" :
+                                            "passive",
+                       (rcvtyp_wd(ttyp))  ? "word" :
+                       (rcvtyp_pvt(ttyp)) ? "private block" :
+                                            "global block");
+
+       return len;
+}
+
+static ssize_t fifosz_show(struct device *d, char *buf)
+{
+       struct fifo_struct *fifo = &to_taskdev(d)->task->rcvdt.fifo;
+       return sprintf(buf, "%d\n", fifo->sz);
+}
+
+static int fifosz_store(struct device *d, const char *buf, size_t count)
+{
+       struct dsptask *task = to_taskdev(d)->task;
+       unsigned long fifosz;
+       int ret;
+
+       fifosz = simple_strtol(buf, NULL, 10);
+       ret = dsp_task_set_fifosz(task, fifosz);
+
+       return (ret < 0) ? ret : strlen(buf);
+}
+
+static ssize_t fifocnt_show(struct device *d, char *buf)
+{
+       struct fifo_struct *fifo = &to_taskdev(d)->task->rcvdt.fifo;
+       return sprintf(buf, "%d\n", fifo->cnt);
+}
+
+static __inline__ char *bid_name(unsigned short bid)
+{
+       static char s[6];
+
+       switch (bid) {
+       case OMAP_DSP_BID_NULL:
+               return "NULL";
+       case OMAP_DSP_BID_PVT:
+               return "PRIVATE";
+       default:
+               sprintf(s, "%d", bid);
+               return s;
+       }
+}
+
+static ssize_t ipblink_show(struct device *d, char *buf)
+{
+       struct rcvdt_bk_struct *rcvdt = &to_taskdev(d)->task->rcvdt.bk;
+       int len;
+
+       spin_lock(&rcvdt->link.lock);
+       len = sprintf(buf, "top  %s\ntail %s\n",
+                     bid_name(rcvdt->link.top), bid_name(rcvdt->link.tail));
+       spin_unlock(&rcvdt->link.lock);
+
+       return len;
+}
+
+static ssize_t wsz_show(struct device *d, char *buf)
+{
+       return sprintf(buf, "%d\n", to_taskdev(d)->task->wsz);
+}
+
+static ssize_t mmap_show(struct device *d, char *buf)
+{
+       struct dsptask *task = to_taskdev(d)->task;
+       return sprintf(buf, "0x%p 0x%x\n", task->map_base, task->map_length);
+}
+
+/*
+ * called from ipbuf_read_proc()
+ */
+int ipbuf_is_held(unsigned char tid, unsigned short bid)
+{
+       struct dsptask *task = dsptask[tid];
+       unsigned short b;
+       int ret = 0;
+
+       if (task == NULL)
+               return 0;
+
+       spin_lock(&task->rcvdt.bk.link.lock);
+       ipblink_for_each(b, &task->rcvdt.bk.link, ipbuf) {
+               if (b == bid) { /* found */
+                       ret = 1;
+                       break;
+               }
+       }
+       spin_unlock(&task->rcvdt.bk.link.lock);
+
+       return ret;
+}
+
+int __init dsp_taskmod_init(void)
+{
+       int retval;
+
+       memset(taskdev, 0, sizeof(void *) * TASKDEV_MAX);
+       memset(dsptask, 0, sizeof(void *) * TASKDEV_MAX);
+
+       retval = register_chrdev(OMAP_DSP_TASK_MAJOR, "dsptask",
+                                &dsp_task_fops);
+       if (retval < 0) {
+               printk(KERN_ERR
+                      "omapdsp: failed to register task device: %d\n", retval);
+               return retval;
+       }
+
+       bus_register(&dsptask_bus);
+       retval = driver_register(&dsptask_driver);
+       if (retval) {
+               printk(KERN_ERR
+                      "omapdsp: failed to register DSP task driver: %d\n",
+                      retval);
+               bus_unregister(&dsptask_bus);
+               unregister_chrdev(OMAP_DSP_TASK_MAJOR, "dsptask");
+               return -EINVAL;
+       }
+       dsp_task_class = class_simple_create(THIS_MODULE, "dsptask");
+       devfs_mk_dir("dsptask");
+
+       return 0;
+}
+
+void dsp_taskmod_exit(void)
+{
+       devfs_remove("dsptask");
+       class_simple_destroy(dsp_task_class);
+       driver_unregister(&dsptask_driver);
+       bus_unregister(&dsptask_bus);
+       unregister_chrdev(OMAP_DSP_TASK_MAJOR, "dsptask");
+}
diff --git a/arch/arm/mach-omap/dsp/taskwatch.c b/arch/arm/mach-omap/dsp/taskwatch.c
new file mode 100644 (file)
index 0000000..fb96c78
--- /dev/null
@@ -0,0 +1,183 @@
+/*
+ * linux/arch/arm/mach-omap/dsp/taskwatch.c
+ *
+ * OMAP DSP task watch device driver
+ *
+ * Copyright (C) 2002-2005 Nokia Corporation
+ *
+ * Written by Toshihiro Kobayashi <toshihiro.kobayashi@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * 2004/12/01:  DSP Gateway version 3.2
+ */
+
+#include <linux/module.h>
+#include <linux/major.h>
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/poll.h>
+#include <linux/interrupt.h>
+#include <linux/sched.h>
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <asm/arch/dsp.h>
+#include "dsp.h"
+
+static DECLARE_WAIT_QUEUE_HEAD(read_wait_q);
+static unsigned int change_cnt;
+
+void dsp_twch_touch(void)
+{
+       change_cnt++;
+       wake_up_interruptible(&read_wait_q);
+}
+
+/*
+ * @count: represents the device counts of the user's interst
+ */
+static ssize_t dsp_twch_read(struct file *file, char *buf, size_t count,
+                            loff_t *ppos)
+{
+       long taskstat[TASKDEV_MAX];
+       int devcount = count / sizeof(long);
+       int i;
+
+       if (!dsp_is_ready()) {
+               printk(KERN_ERR "omapdsp: dsp has not been configured.\n");
+               return -EINVAL;
+       }
+
+       if (change_cnt == 0) {
+               long current_state;
+               DECLARE_WAITQUEUE(wait, current);
+
+               add_wait_queue(&read_wait_q, &wait);
+               current_state = current->state;
+               set_current_state(TASK_INTERRUPTIBLE);
+               if (change_cnt == 0)    /* last check */
+                       schedule();
+               set_current_state(current_state);
+               remove_wait_queue(&read_wait_q, &wait);
+
+               /* unconfigured while waiting ;-( */
+               if (dsp_is_ready())
+                       return -EINVAL;
+       }
+
+       if (devcount > TASKDEV_MAX)
+               devcount = TASKDEV_MAX;
+
+       count = devcount * sizeof(long);
+       change_cnt = 0;
+       for (i = 0; i < devcount; i++) {
+               taskstat[i] = taskdev_state(i);
+       }
+
+       if (copy_to_user(buf, taskstat, count))
+               return -EFAULT;
+
+       return count;
+}
+
+static unsigned int dsp_twch_poll(struct file *file, poll_table *wait)
+{
+       unsigned int mask = 0;
+
+       poll_wait(file, &read_wait_q, wait);
+       if (change_cnt)
+               mask |= POLLIN | POLLRDNORM;
+
+       return mask;
+}
+
+static int dsp_twch_ioctl(struct inode *inode, struct file *file,
+                         unsigned int cmd, unsigned long arg)
+{
+       static DECLARE_MUTEX(ioctl_sem);
+       int ret;
+
+       if (down_interruptible(&ioctl_sem))
+               return -ERESTARTSYS;
+
+       switch (cmd) {
+       case OMAP_DSP_TWCH_IOCTL_MKDEV:
+               {
+                       char name[OMAP_DSP_TNM_LEN];
+                       if (copy_from_user(name, (void *)arg, OMAP_DSP_TNM_LEN)) {
+                               ret = -EFAULT;
+                               goto up_out;
+                       }
+                       name[OMAP_DSP_TNM_LEN-1] = '\0';
+                       ret = dsp_mkdev(name);
+                       break;
+               }
+
+       case OMAP_DSP_TWCH_IOCTL_RMDEV:
+               {
+                       char name[OMAP_DSP_TNM_LEN];
+                       if (copy_from_user(name, (void *)arg, OMAP_DSP_TNM_LEN)) {
+                               ret = -EFAULT;
+                               goto up_out;
+                       }
+                       name[OMAP_DSP_TNM_LEN-1] = '\0';
+                       ret = dsp_rmdev(name);
+                       break;
+               }
+
+       case OMAP_DSP_TWCH_IOCTL_TADD:
+               {
+                       struct omap_dsp_taddinfo ti;
+                       if (copy_from_user(&ti, (void *)arg, sizeof(ti))) {
+                               ret = -EFAULT;
+                               goto up_out;
+                       }
+                       ret = dsp_tadd(ti.minor, ti.taskadr);
+                       break;
+               }
+
+       case OMAP_DSP_TWCH_IOCTL_TDEL:
+               ret = dsp_tdel(arg);
+               break;
+
+       case OMAP_DSP_TWCH_IOCTL_TKILL:
+               ret = dsp_tkill(arg);
+               break;
+
+       default:
+               ret = -ENOIOCTLCMD;
+       }
+
+up_out:
+       up(&ioctl_sem);
+       return ret;
+}
+
+struct file_operations dsp_twch_fops = {
+       .owner = THIS_MODULE,
+       .read  = dsp_twch_read,
+       .poll  = dsp_twch_poll,
+       .ioctl = dsp_twch_ioctl,
+};
+
+void dsp_twch_start(void)
+{
+       change_cnt = 1;         /* first read will not wait */
+}
+
+void dsp_twch_stop(void)
+{
+       wake_up_interruptible(&read_wait_q);
+}
diff --git a/arch/arm/mach-omap/dsp/uaccess_dsp.S b/arch/arm/mach-omap/dsp/uaccess_dsp.S
new file mode 100644 (file)
index 0000000..0763903
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ * linux/arch/arm/mach-omap/dsp/uaccess_dsp.S
+ *
+ * user memory access functions for DSP driver
+ *
+ * Copyright (C) 2004,2005 Nokia Corporation
+ *
+ * Written by Toshihiro Kobayashi <toshihiro.kobayashi@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * 2004/06/29:  DSP Gateway version 3.2
+ */
+
+#include <linux/linkage.h>
+#include <asm/assembler.h>
+
+               .text
+
+/* Prototype: int __arch_copy_to_user_dsp_2b(void *to, const char *from)
+ * Purpose  : copy 2 bytes to user memory from kernel(DSP) memory
+ *            escaping from unexpected byte swap using __arch_copy_to_user()
+ *            in OMAP architecture.
+ * Params   : to   - user memory
+ *          : from - kernel(DSP) memory
+ * Returns  : success = 0, failure = 2
+ */
+
+ENTRY(__arch_copy_to_user_dsp_2b)
+               stmfd   sp!, {r4, lr}
+               ldrb    r3, [r1], #1
+               ldrb    r4, [r1], #1
+USER(          strbt   r4, [r0], #1)                   @ May fault
+USER(          strbt   r3, [r0], #1)                   @ May fault
+               mov     r0, #0
+               LOADREGS(fd,sp!,{r4, pc})
+
+               .section .fixup,"ax"
+               .align  0
+9001:          mov     r0, #2
+               LOADREGS(fd,sp!, {r4, pc})
+               .previous
+
+/* Prototype: unsigned long __arch_copy_from_user_dsp_2b(void *to, const void *from);
+ * Purpose  : copy 2 bytes from user memory to kernel(DSP) memory
+ *            escaping from unexpected byte swap using __arch_copy_to_user()
+ *            in OMAP architecture.
+ * Params   : to   - kernel (DSP) memory
+ *          : from - user memory
+ * Returns  : success = 0, failure = 2
+ */
+
+ENTRY(__arch_copy_from_user_dsp_2b)
+               stmfd   sp!, {r4, lr}
+USER(          ldrbt   r3, [r1], #1)                   @ May fault
+USER(          ldrbt   r4, [r1], #1)                   @ May fault
+               strb    r4, [r0], #1
+               strb    r3, [r0], #1
+               mov     r0, #0
+               LOADREGS(fd,sp!,{r4, pc})
+
+               .section .fixup,"ax"
+               .align  0
+9001:          mov     r3, #0
+               strh    r3, [r0], #2
+               mov     r0, #2
+               LOADREGS(fd,sp!, {r4, pc})
+               .previous
diff --git a/arch/arm/mach-omap/dsp/uaccess_dsp.h b/arch/arm/mach-omap/dsp/uaccess_dsp.h
new file mode 100644 (file)
index 0000000..d1f4453
--- /dev/null
@@ -0,0 +1,186 @@
+/*
+ * linux/arch/arm/mach-omap/dsp/uaccess_dsp.h
+ *
+ * Header for user access functions for DSP driver
+ *
+ * Copyright (C) 2002-2005 Nokia Corporation
+ *
+ * Modified from linux/include/asm-arm/uaccess.h
+ * by Toshihiro Kobayashi <toshihiro.kobayashi@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * 2004/06/29:  DSP Gateway version 3.2
+ */
+
+#ifndef _OMAP_DSP_UACCESS_DSP_H
+#define _OMAP_DSP_UACCESS_DSP_H
+
+#include <asm/uaccess.h>
+
+#define HAVE_ASM_COPY_FROM_USER_DSP_2B
+
+#ifdef HAVE_ASM_COPY_FROM_USER_DSP_2B
+extern unsigned long __arch_copy_from_user_dsp_2b(void *to,
+                                                 const void __user *from);
+extern unsigned long __arch_copy_to_user_dsp_2b(void __user *to,
+                                               const void *from);
+#endif
+
+extern unsigned long dspmem_base, dspmem_size;
+#define is_dsp_internal_mem(va) \
+       (((unsigned long)(va) >= dspmem_base) &&  \
+        ((unsigned long)(va) < dspmem_base + dspmem_size))
+
+
+#ifndef HAVE_ASM_COPY_FROM_USER_DSP_2B
+static __inline__ unsigned long copy_from_user_dsp_2b(void *to,
+                                                     const void *from)
+{
+       unsigned short tmp;
+
+       if (__arch_copy_from_user(&tmp, from, 2))
+               return 2;
+       /* expecting compiler to generate "strh" instruction */
+       *((unsigned short *)to) = tmp;
+       return 0;
+}
+#endif
+
+/*
+ * @n must be multiple of 2
+ */
+static __inline__ unsigned long copy_from_user_dsp(void *to, const void *from,
+                                                  unsigned long n)
+{
+       if (access_ok(VERIFY_READ, from, n)) {
+               if ((is_dsp_internal_mem(to)) &&
+                   (((unsigned long)to & 2) || (n & 2))) {
+                       /*
+                        * DARAM/SARAM with odd word alignment
+                        */
+                       unsigned long n4;
+                       unsigned long last_n;
+
+                       /* dest not aligned -- copy 2 bytes */
+                       if (((unsigned long)to & 2) && (n >= 2)) {
+#ifdef HAVE_ASM_COPY_FROM_USER_DSP_2B
+                               if (__arch_copy_from_user_dsp_2b(to, from))
+#else
+                               if (copy_from_user_dsp_2b(to, from))
+#endif
+                                       return n;
+                               to += 2;
+                               from += 2;
+                               n -= 2;
+                       }
+                       /* middle 4*n bytes */
+                       last_n = n & 2;
+                       n4 = n - last_n;
+                       if ((n = __arch_copy_from_user(to, from, n4)) != 0)
+                               return n + last_n;
+                       /* last 2 bytes */
+                       if (last_n) {
+                               to += n4;
+                               from += n4;
+#ifdef HAVE_ASM_COPY_FROM_USER_DSP_2B
+                               if (__arch_copy_from_user_dsp_2b(to, from))
+#else
+                               if (copy_from_user_dsp_2b(to, from))
+#endif
+                                       return 2;
+                               n = 0;
+                       }
+               } else {
+                       /*
+                        * DARAM/SARAM with 4-byte alignment or
+                        * external memory
+                        */
+                       n = __arch_copy_from_user(to, from, n);
+               }
+       }
+       else    /* security hole - plug it */
+               memzero(to, n);
+       return n;
+}
+
+#ifndef HAVE_ASM_COPY_FROM_USER_DSP_2B
+static __inline__ unsigned long copy_to_user_dsp_2b(void *to, const void *from)
+{
+       /* expecting compiler to generate "strh" instruction */
+       unsigned short tmp = *(unsigned short *)from;
+
+       return __arch_copy_to_user(to, &tmp, 2);
+}
+#endif
+
+/*
+ * @n must be multiple of 2
+ */
+static __inline__ unsigned long copy_to_user_dsp(void *to, const void *from,
+                                                unsigned long n)
+{
+       if (access_ok(VERIFY_WRITE, to, n)) {
+               if ((is_dsp_internal_mem(from)) &&
+                   (((unsigned long)to & 2) || (n & 2))) {
+                       /*
+                        * DARAM/SARAM with odd word alignment
+                        */
+                       unsigned long n4;
+                       unsigned long last_n;
+
+                       /* dest not aligned -- copy 2 bytes */
+                       if (((unsigned long)to & 2) && (n >= 2)) {
+#ifdef HAVE_ASM_COPY_FROM_USER_DSP_2B
+                               if (__arch_copy_to_user_dsp_2b(to, from))
+#else
+                               if (copy_to_user_dsp_2b(to, from))
+#endif
+                                       return n;
+                               to += 2;
+                               from += 2;
+                               n -= 2;
+                       }
+                       /* middle 4*n bytes */
+                       last_n = n & 2;
+                       n4 = n - last_n;
+                       if ((n = __arch_copy_to_user(to, from, n4)) != 0)
+                               return n + last_n;
+                       /* last 2 bytes */
+                       if (last_n) {
+                               to += n4;
+                               from += n4;
+#ifdef HAVE_ASM_COPY_FROM_USER_DSP_2B
+                               if (__arch_copy_to_user_dsp_2b(to, from))
+#else
+                               if (copy_to_user_dsp_2b(to, from))
+#endif
+                                       return 2;
+                               n = 0;
+                       }
+               } else {
+                       /*
+                        * DARAM/SARAM with 4-byte alignment or
+                        * external memory
+                        */
+                       n = __arch_copy_to_user(to, from, n);
+               }
+       }
+       return n;
+}
+
+#undef is_dsp_internal_mem
+
+#endif /* _OMAP_DSP_UACCESS_DSP_H */
index 7408ac94f771663005b2a52c609aea5c5d7afede..478e0d6a51dcea2e2e3bc79060b683a652af300d 100644 (file)
@@ -142,3 +142,5 @@ config OMAP_ARM_30MHZ
        help
           Enable 30MHz clock for OMAP CPU. If unsure, say N.
 
+source "arch/arm/mach-omap/dsp/Kconfig"
+
diff --git a/include/asm-arm/arch-omap/dsp.h b/include/asm-arm/arch-omap/dsp.h
new file mode 100644 (file)
index 0000000..a4a17ba
--- /dev/null
@@ -0,0 +1,236 @@
+/*
+ * linux/include/asm-arm/arch-omap/dsp.h
+ *
+ * Header for OMAP DSP driver
+ *
+ * Copyright (C) 2002-2005 Nokia Corporation
+ *
+ * Written by Toshihiro Kobayashi <toshihiro.kobayashi@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * 2005/01/17:  DSP Gateway version 3.2
+ */
+
+#ifndef ASM_ARCH_DSP_H
+#define ASM_ARCH_DSP_H
+
+
+/*
+ * for /dev/dspctl/ctl
+ */
+#define OMAP_DSP_IOCTL_RESET                   1
+#define OMAP_DSP_IOCTL_RUN                     2
+#define OMAP_DSP_IOCTL_SETRSTVECT              3
+#define OMAP_DSP_IOCTL_IDLE                    4
+#define OMAP_DSP_IOCTL_MPUI_WORDSWAP_ON                5
+#define OMAP_DSP_IOCTL_MPUI_WORDSWAP_OFF       6
+#define OMAP_DSP_IOCTL_MPUI_BYTESWAP_ON                7
+#define OMAP_DSP_IOCTL_MPUI_BYTESWAP_OFF       8
+#define OMAP_DSP_IOCTL_DSPCFG                  10
+#define OMAP_DSP_IOCTL_DSPUNCFG                        11
+#define OMAP_DSP_IOCTL_TASKCNT                 12
+#define OMAP_DSP_IOCTL_REGMEMR                 40
+#define OMAP_DSP_IOCTL_REGMEMW                 41
+#define OMAP_DSP_IOCTL_REGIOR                  42
+#define OMAP_DSP_IOCTL_REGIOW                  43
+#define OMAP_DSP_IOCTL_GETVAR                  44
+#define OMAP_DSP_IOCTL_SETVAR                  45
+#define OMAP_DSP_IOCTL_RUNLEVEL                        50
+#define OMAP_DSP_IOCTL_SUSPEND                 51
+#define OMAP_DSP_IOCTL_RESUME                  52
+#define OMAP_DSP_IOCTL_FBEN                    53
+#define OMAP_DSP_IOCTL_FBDIS                   54
+#define OMAP_DSP_IOCTL_MBSEND                  99
+
+/*
+ * for taskdev
+ * (ioctls below should be >= 0x10000)
+ */
+#define OMAP_DSP_TASK_IOCTL_BFLSH      0x10000
+#define OMAP_DSP_TASK_IOCTL_SETBSZ     0x10001
+#define OMAP_DSP_TASK_IOCTL_LOCK       0x10002
+#define OMAP_DSP_TASK_IOCTL_UNLOCK     0x10003
+#define OMAP_DSP_TASK_IOCTL_GETNAME    0x10004
+
+/*
+ * for /dev/dspctl/mem
+ */
+#define OMAP_DSP_MEM_IOCTL_EXMAP       1
+#define OMAP_DSP_MEM_IOCTL_EXUNMAP     2
+#define OMAP_DSP_MEM_IOCTL_EXMAP_FLUSH 3
+#define OMAP_DSP_MEM_IOCTL_FBEXPORT    5
+#define OMAP_DSP_MEM_IOCTL_MMUITACK    7
+#define OMAP_DSP_MEM_IOCTL_MMUINIT     9
+#define OMAP_DSP_MEM_IOCTL_KMEM_RESERVE        11
+#define OMAP_DSP_MEM_IOCTL_KMEM_RELEASE        12
+
+struct omap_dsp_mapinfo {
+       unsigned long dspadr;
+       unsigned long size;
+};
+
+/*
+ * for /dev/dspctl/twch
+ */
+#define OMAP_DSP_TWCH_IOCTL_MKDEV      1
+#define OMAP_DSP_TWCH_IOCTL_RMDEV      2
+#define OMAP_DSP_TWCH_IOCTL_TADD       11
+#define OMAP_DSP_TWCH_IOCTL_TDEL       12
+#define OMAP_DSP_TWCH_IOCTL_TKILL      13
+
+#define OMAP_DSP_DEVSTATE_NOTASK       0x00000001
+#define OMAP_DSP_DEVSTATE_ATTACHED     0x00000002
+#define OMAP_DSP_DEVSTATE_GARBAGE      0x00000004
+#define OMAP_DSP_DEVSTATE_INVALID      0x00000008
+#define OMAP_DSP_DEVSTATE_ADDREQ       0x00000100
+#define OMAP_DSP_DEVSTATE_DELREQ       0x00000200
+#define OMAP_DSP_DEVSTATE_KILLREQ      0x00000400
+#define OMAP_DSP_DEVSTATE_ADDFAIL      0x00001000
+
+struct omap_dsp_taddinfo {
+       unsigned char minor;
+       unsigned long taskadr;
+};
+#define OMAP_DSP_TADD_ABORTADR 0xffffffff
+
+
+/*
+ * error cause definition (for error detection device)
+ */
+#define OMAP_DSP_ERRDT_WDT     0x00000001
+#define OMAP_DSP_ERRDT_MMU     0x00000002
+
+
+/*
+ * mailbox protocol definitions
+ */
+
+struct omap_dsp_mailbox_cmd {
+       unsigned short cmd;
+       unsigned short data;
+};
+
+struct omap_dsp_reginfo {
+       unsigned short adr;
+       unsigned short val;
+};
+
+struct omap_dsp_varinfo {
+       unsigned char varid;
+       unsigned short val[0];
+};
+
+#define OMAP_DSP_MBPROT_REVISION       0x0018
+
+#define OMAP_DSP_MBCMD_WDSND   0x10
+#define OMAP_DSP_MBCMD_WDREQ   0x11
+#define OMAP_DSP_MBCMD_BKSND   0x20
+#define OMAP_DSP_MBCMD_BKREQ   0x21
+#define OMAP_DSP_MBCMD_BKYLD   0x23
+#define OMAP_DSP_MBCMD_BKSNDP  0x24
+#define OMAP_DSP_MBCMD_BKREQP  0x25
+#define OMAP_DSP_MBCMD_TCTL    0x30
+#define OMAP_DSP_MBCMD_TCTLDATA        0x31
+#define OMAP_DSP_MBCMD_WDT     0x50
+#define OMAP_DSP_MBCMD_RUNLEVEL        0x51
+#define OMAP_DSP_MBCMD_PM      0x52
+#define OMAP_DSP_MBCMD_SUSPEND 0x53
+#define OMAP_DSP_MBCMD_KFUNC   0x54
+#define OMAP_DSP_MBCMD_TCFG    0x60
+#define OMAP_DSP_MBCMD_TADD    0x62
+#define OMAP_DSP_MBCMD_TDEL    0x63
+#define OMAP_DSP_MBCMD_TSTOP   0x65
+#define OMAP_DSP_MBCMD_DSPCFG  0x70
+#define OMAP_DSP_MBCMD_REGRW   0x72
+#define OMAP_DSP_MBCMD_GETVAR  0x74
+#define OMAP_DSP_MBCMD_SETVAR  0x75
+#define OMAP_DSP_MBCMD_ERR     0x78
+#define OMAP_DSP_MBCMD_DBG     0x79
+
+#define OMAP_DSP_MBCMD_TCTL_TINIT      0x0000
+#define OMAP_DSP_MBCMD_TCTL_TEN                0x0001
+#define OMAP_DSP_MBCMD_TCTL_TDIS       0x0002
+#define OMAP_DSP_MBCMD_TCTL_TCLR       0x0003
+#define OMAP_DSP_MBCMD_TCTL_TCLR_FORCE 0x0004
+
+#define OMAP_DSP_MBCMD_RUNLEVEL_USER           0x01
+#define OMAP_DSP_MBCMD_RUNLEVEL_SUPER          0x0e
+#define OMAP_DSP_MBCMD_RUNLEVEL_RECOVERY       0x10
+
+#define OMAP_DSP_MBCMD_PM_DISABLE      0x00
+#define OMAP_DSP_MBCMD_PM_ENABLE       0x01
+
+#define OMAP_DSP_MBCMD_KFUNC_FBCTL     0x00
+
+#define OMAP_DSP_MBCMD_FBCTL_ENABLE    0x0002
+#define OMAP_DSP_MBCMD_FBCTL_DISABLE   0x0003
+
+#define OMAP_DSP_MBCMD_TDEL_SAFE       0x0000
+#define OMAP_DSP_MBCMD_TDEL_KILL       0x0001
+
+#define OMAP_DSP_MBCMD_DSPCFG_REQ      0x00
+#define OMAP_DSP_MBCMD_DSPCFG_SYSADRH  0x28
+#define OMAP_DSP_MBCMD_DSPCFG_SYSADRL  0x29
+#define OMAP_DSP_MBCMD_DSPCFG_PROTREV  0x70
+#define OMAP_DSP_MBCMD_DSPCFG_ABORT    0x78
+#define OMAP_DSP_MBCMD_DSPCFG_LAST     0x80
+
+#define OMAP_DSP_MBCMD_REGRW_MEMR      0x00
+#define OMAP_DSP_MBCMD_REGRW_MEMW      0x01
+#define OMAP_DSP_MBCMD_REGRW_IOR       0x02
+#define OMAP_DSP_MBCMD_REGRW_IOW       0x03
+#define OMAP_DSP_MBCMD_REGRW_DATA      0x04
+
+#define OMAP_DSP_MBCMD_VARID_ICRMASK   0x00
+#define OMAP_DSP_MBCMD_VARID_LOADINFO  0x01
+
+#define OMAP_DSP_TTYP_ARCV     0x0001
+#define OMAP_DSP_TTYP_ASND     0x0002
+#define OMAP_DSP_TTYP_BKMD     0x0004
+#define OMAP_DSP_TTYP_BKDM     0x0008
+#define OMAP_DSP_TTYP_PVMD     0x0010
+#define OMAP_DSP_TTYP_PVDM     0x0020
+
+#define OMAP_DSP_EID_BADTID    0x10
+#define OMAP_DSP_EID_BADTCN    0x11
+#define OMAP_DSP_EID_BADBID    0x20
+#define OMAP_DSP_EID_BADCNT    0x21
+#define OMAP_DSP_EID_NOTLOCKED 0x22
+#define OMAP_DSP_EID_STVBUF    0x23
+#define OMAP_DSP_EID_BADADR    0x24
+#define OMAP_DSP_EID_BADTCTL   0x30
+#define OMAP_DSP_EID_BADPARAM  0x50
+#define OMAP_DSP_EID_FATAL     0x58
+#define OMAP_DSP_EID_NOMEM     0xc0
+#define OMAP_DSP_EID_NORES     0xc1
+#define OMAP_DSP_EID_IPBFULL   0xc2
+#define OMAP_DSP_EID_TASKNOTRDY        0xe0
+#define OMAP_DSP_EID_TASKBSY   0xe1
+#define OMAP_DSP_EID_TASKERR   0xef
+#define OMAP_DSP_EID_BADCFGTYP 0xf0
+#define OMAP_DSP_EID_DEBUG     0xf8
+#define OMAP_DSP_EID_BADSEQ    0xfe
+#define OMAP_DSP_EID_BADCMD    0xff
+
+#define OMAP_DSP_TNM_LEN       16
+
+#define OMAP_DSP_TID_FREE      0xff
+#define OMAP_DSP_TID_ANON      0xfe
+
+#define OMAP_DSP_BID_NULL      0xffff
+#define OMAP_DSP_BID_PVT       0xfffe
+
+#endif /* ASM_ARCH_DSP_H */
diff --git a/include/asm-arm/arch-omap/dsp_common.h b/include/asm-arm/arch-omap/dsp_common.h
new file mode 100644 (file)
index 0000000..2cad37e
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * linux/include/asm-arm/arch-omap/dsp_common.h
+ *
+ * Header for OMAP DSP subsystem control
+ *
+ * Copyright (C) 2004,2005 Nokia Corporation
+ *
+ * Written by Toshihiro Kobayashi <toshihiro.kobayashi@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * 2004/09/22:  DSP Gateway version 3.2
+ */
+
+#ifndef ASM_ARCH_DSP_COMMON_H
+#define ASM_ARCH_DSP_COMMON_H
+
+void omap_dsp_request_idle(void);
+
+#endif /* ASM_ARCH_DSP_COMMON_H */