summaryrefslogtreecommitdiff
path: root/Source/DirectFB/gfxdrivers/davinci/kernel-module/c64x/c64x.c
diff options
context:
space:
mode:
Diffstat (limited to 'Source/DirectFB/gfxdrivers/davinci/kernel-module/c64x/c64x.c')
-rwxr-xr-xSource/DirectFB/gfxdrivers/davinci/kernel-module/c64x/c64x.c507
1 files changed, 507 insertions, 0 deletions
diff --git a/Source/DirectFB/gfxdrivers/davinci/kernel-module/c64x/c64x.c b/Source/DirectFB/gfxdrivers/davinci/kernel-module/c64x/c64x.c
new file mode 100755
index 0000000..bbbbfe9
--- /dev/null
+++ b/Source/DirectFB/gfxdrivers/davinci/kernel-module/c64x/c64x.c
@@ -0,0 +1,507 @@
+/*
+ TI Davinci driver - C64X+ DSP Kernel Module
+
+ (c) Copyright 2007 Telio AG
+
+ Written by Olaf Dreesen <dreesen@qarx.de>.
+
+ All rights reserved.
+
+ This module is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ version 2 as published by the Free Software Foundation.
+
+ This library 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 library; if not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#include <linux/version.h>
+#include <linux/init.h>
+#include <linux/module.h>
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/cdev.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/fcntl.h>
+#include <linux/firmware.h>
+#include <linux/fs.h>
+#include <linux/interrupt.h>
+#include <linux/mm.h>
+#include <linux/page-flags.h>
+#include <linux/poll.h>
+#include <linux/proc_fs.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+
+#include <asm/cacheflush.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+#include <asm/uaccess.h>
+
+#include <linux/c64x.h>
+
+#define C64X_IRQ
+
+MODULE_LICENSE("GPL v2");
+//MODULE_LICENSE("Propietary");
+MODULE_AUTHOR("Olaf Dreesen <dreesen@qarx.de>");
+MODULE_DESCRIPTION("A little c64+ handling module.");
+
+#define C_MOD_MAJOR 400
+#define C_MOD_NUM_DEV 1
+#define C_MOD_NAME "c64x"
+#define F_NAME "c64x_drv.bin"
+
+#define CODE_BASE 0x00800000
+
+/* DDR2:
+ *
+ * transfer buffer
+ */
+#define R_BASE DAVINCI_C64X_MEM
+#define R_LEN 0x02000000
+
+/* L2RAM:
+ *
+ * 0x00800000 - 0x0080FFFF C64x+
+ * 0x11800000 - 0x1180FFFF ARM
+ */
+#define D_BASE 0x11800000
+#define D_LEN 0x00010000
+
+/* L1DRAM:
+ *
+ * 0x00F04000 - 0x00F0FFFF C64x+
+ * 0x11F04000 - 0x11F0FFFF ARM
+ *
+ * Queue controls @ 0x00F04000 (4096 Bytes)
+ */
+#define Q_BASE 0x11F04000
+#define Q_LEN 0x00001000
+
+#define HQueueDSP (l1dram[0x00>>2])
+#define HQueueARM (l1dram[0x04>>2])
+#define LQueueDSP (l1dram[0x08>>2])
+#define LQueueARM (l1dram[0x0C>>2])
+#define DSPidle (l1dram[0x10>>2])
+
+/* IO Register needed:
+ *
+ * 0x01C40008 DSPBOOTADDR DSP Boot Address
+ * 0x01C40010 INTGEN Interrupt Generator
+ * 0x01C40038 CHP_SHRTSW DSP Power
+ * 0x01C4169C MDCFG39 DSP Module config
+ * 0x01C41A9C MDCTL39 DSP Module control
+ */
+#define IO_BASE 0x01c40000
+#define IO_LEN 0x00010000
+
+#define DSPBOOTADDR (mmr[0x0008>>2])
+#define INTGEN (mmr[0x0010>>2])
+#define CHP_SHRTSW (mmr[0x0038>>2])
+#define MDCFG39 (mmr[0x169C>>2])
+#define MDCTL39 (mmr[0x1A9C>>2])
+
+MODULE_FIRMWARE(F_NAME);
+
+static dev_t dev_major;
+static struct cdev*dev_cdev;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19)
+static struct class*dev_class;
+#else
+static struct class_simple*dev_class;
+#endif
+
+static volatile unsigned int*mmr=0;
+static unsigned char*l2ram=0;
+static volatile unsigned int*l1dram=0;
+static volatile void*dram=0;
+static volatile c64xTaskControl*c64xctl=0;
+static volatile c64xTask*queue_l=0;
+
+#ifdef C64X_IRQ
+static int dev_irq=46;
+static DECLARE_WAIT_QUEUE_HEAD( wait_irq );
+
+/* IRQ */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 21)
+static irqreturn_t dev_irq_handler(int irq,void*dev_id) {
+#else
+static irqreturn_t dev_irq_handler(int irq,void*dev_id,struct pt_regs*regs) {
+#endif
+ wake_up_all( &wait_irq );
+ return IRQ_HANDLED;
+}
+#endif
+
+static u32 opencnt=0;
+
+/* char-dev */
+static int dev_open(struct inode*inode,struct file*filp) {
+ if (opencnt++==0) {
+ DSPidle=0;
+ MDCTL39=0x00000103; /* Go! Go, go Go! */
+ while(DSPidle==0);
+ }
+ return 0;
+}
+static int dev_release(struct inode*inode,struct file*filp) {
+ if (--opencnt==0) {
+ MDCTL39=0x00000000; /* local reset */
+ }
+ return 0;
+}
+
+static ssize_t dev_write(struct file*filp,const char __user*buffer,size_t len,loff_t*off) {
+ long ret=0;
+ unsigned long offset=*off;
+ if (offset<D_LEN) {
+ if ((offset+len)>=D_LEN) { len=D_LEN-offset; }
+// printk(KERN_INFO "c64x+ : read got offset %08lx %08lx\n",offset,(long)len);
+ ret=len;
+ *off+=len;
+ }
+ return ret;
+}
+static ssize_t dev_read(struct file*filp,char __user*buffer,size_t len,loff_t*off) {
+ long ret=0;
+ unsigned long offset=*off;
+ if (offset<D_LEN) {
+ if ((offset+len)>=D_LEN) { len=D_LEN-offset; }
+// printk(KERN_INFO "c64x+ : read got offset %08lx %08lx\n",offset,(long)len);
+ ret=len;
+ ret-=copy_to_user(buffer,(l2ram+offset),len);
+ *off+=len;
+ }
+ return ret;
+}
+
+static int dev_mmap(struct file * file, struct vm_area_struct * vma) {
+ size_t size=vma->vm_end-vma->vm_start;
+ if (vma->vm_pgoff) {
+ if (size!=R_LEN) return -EINVAL;
+#if defined(pgprot_writecombine)
+ vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
+#else
+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+#endif
+ if (remap_pfn_range(vma,
+ vma->vm_start,
+ R_BASE>>PAGE_SHIFT,
+ size,
+ vma->vm_page_prot))
+ return -EAGAIN;
+ }
+ else {
+ if (size!=Q_LEN) return -EINVAL;
+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+ if (remap_pfn_range(vma,
+ vma->vm_start,
+ Q_BASE>>PAGE_SHIFT,
+ size,
+ vma->vm_page_prot))
+ return -EAGAIN;
+ }
+ return 0;
+}
+
+static void
+c64x_dump( const char *condition )
+{
+ static const char *state_names[] = { "DONE", "ERROR", "TODO", "RUNNING" };
+
+ uint32_t ql_dsp = c64xctl->QL_dsp;
+ uint32_t ql_arm = c64xctl->QL_arm;
+ uint32_t tl_dsp = queue_l[ql_dsp & C64X_QUEUE_MASK].c64x_function;
+ uint32_t tl_arm = queue_l[ql_arm & C64X_QUEUE_MASK].c64x_function;
+ int dl;
+
+ dl = ql_arm - ql_dsp;
+ if (dl < 0)
+ dl += C64X_QUEUE_LENGTH;
+
+ printk( "C64X+ Queue: %s\n"
+ " [DSP %d / %d (%s), ARM %d / %d (%s)] <- %d pending\n",
+ condition,
+ ql_dsp, (tl_dsp >> 2) & 0x3fff, state_names[tl_dsp & 3],
+ ql_arm, (tl_arm >> 2) & 0x3fff, state_names[tl_arm & 3],
+ dl );
+}
+
+static int
+c64x_wait_low( void )
+{
+ int ret;
+ int num = 0;
+ /* Keep reference values for comparison. */
+ u32 idle = c64xctl->idlecounter;
+ u32 dsp = c64xctl->QL_dsp;
+
+ /* Wait for equal pointers... */
+ while (dsp != c64xctl->QL_arm) {
+ /* ...each time for a 1/50 second... */
+ ret = wait_event_interruptible_timeout( wait_irq, c64xctl->QL_dsp == c64xctl->QL_arm, HZ/50 );
+ if (ret < 0)
+ return ret;
+
+ /* ...if after that 1/50 second still the same command is running... */
+ if (!ret && c64xctl->QL_dsp == dsp) {
+ /* ...and almost one second elapsed in total, or the DSP felt idle... */
+ if (++num > 42 || c64xctl->idlecounter != idle) {
+ /* ...timeout! */
+ printk( KERN_ERR "c64x+ : timeout waiting for idle queue\n" );
+ c64x_dump( "TIMEOUT!!!" );
+ return -ETIMEDOUT;
+ }
+ }
+ else {
+ /* Different command running, reset total elapsed time. */
+ num = 0;
+ }
+
+ /* Update reference values. */
+ idle = c64xctl->idlecounter;
+ dsp = c64xctl->QL_dsp;
+ }
+
+ return 0;
+}
+
+static int dev_ioctl(struct inode *i, struct file *f, unsigned int cmd, unsigned long arg) {
+ switch (cmd) {
+ case C64X_IOCTL_RESET:
+ MDCTL39=0x00000000; /* local reset */
+ mdelay(10);
+ DSPidle=0;
+ MDCTL39=0x00000103;
+ break;
+ case C64X_IOCTL_WAIT_LOW:
+ return c64x_wait_low();
+ default:
+ printk(KERN_INFO "c64x+ : unknown ioctl : cmd=%08x\n",cmd);
+ return -EAGAIN;
+ break;
+ }
+ return 0;
+}
+static struct file_operations dev_file_ops={
+ .owner = THIS_MODULE,
+ .open = dev_open,
+ .release = dev_release,
+ .read = dev_read,
+ .write = dev_write,
+ .mmap = dev_mmap,
+ .ioctl = dev_ioctl,
+};
+
+/* INIT */
+static __initdata struct device dev_device = {
+ .bus_id = "c64x0",
+};
+static int __init dev_init(void) {
+ int ret=-EIO;
+ u8 *at;
+ const struct firmware*fw = NULL;
+
+ printk(KERN_INFO "c64x+ : module load\n");
+
+ if ((dram=ioremap(R_BASE,R_LEN))==0) {
+ printk(KERN_ERR "c64x+ : module couldn't get memory\n");
+ goto err0;
+ }
+ printk(KERN_INFO "c64x+ : module got memory @ %p\n",dram);
+ queue_l = dram + 0x01e00000;
+
+ /* get the 'device' memory */
+ if ((mmr=ioremap(IO_BASE,IO_LEN))==0) {
+ printk(KERN_ERR "c64x+ : module couldn't get IO-MMR\n");
+ goto err0;
+ }
+ printk(KERN_INFO "c64x+ : DSP bootaddr: %08x\n",DSPBOOTADDR);
+ printk(KERN_INFO "c64x+ : got mmr %p %08x %08x\n",mmr,MDCTL39,MDCFG39);
+
+ printk(KERN_INFO "c64x+ : switch state: %08x\n",CHP_SHRTSW);
+
+ MDCTL39=0x00000000; /* local reset */
+ mdelay(10);
+ DSPBOOTADDR=CODE_BASE; /* set DSP base address */
+
+// printk(KERN_INFO "c64x+ : check0: %p %08x %08x\n",mmr,MDCTL39,MDCFG39);
+
+ /* get the 'device' memory */
+ if ((l1dram=ioremap(Q_BASE,Q_LEN))==0) {
+ printk(KERN_ERR "c64x+ : module couldn't get L1 dsp-memory\n");
+ goto err1;
+ }
+ printk(KERN_INFO "c64x+ : module got L1D @ %p\n",l1dram);
+ c64xctl = (volatile void*)l1dram;
+
+ if ((l2ram=ioremap(D_BASE,D_LEN))==0) {
+ printk(KERN_ERR "c64x+ : module couldn't get L2 dsp-memory\n");
+ goto err2;
+ }
+ printk(KERN_INFO "c64x+ : module got L2 @ %p\n",l2ram);
+
+ /* request firmware */
+ device_initialize(&dev_device);
+ ret=device_add(&dev_device);
+ if (ret) {
+ printk(KERN_ERR "c64x+ : device_add failed\n");
+ goto err3;
+ }
+ printk(KERN_INFO "c64x+ : module requesting firmware '%s'\n",F_NAME);
+ ret=request_firmware(&fw,F_NAME,&dev_device);
+ printk(KERN_INFO "c64x+ : module got fw %p\n",fw);
+ if (ret) {
+ printk(KERN_ERR "c64x+ : no firmware upload (timeout or file not found?)\n");
+ device_del(&dev_device);
+ goto err3;
+ }
+ printk(KERN_INFO "c64x+ : firmware upload %p %zd\n",fw->data,fw->size);
+ if (fw->size>32767) {
+ printk(KERN_ERR "c64x+ : firmware too big! 32767 is maximum (for now)\n");
+ release_firmware(fw);
+ device_del(&dev_device);
+ goto err3;
+ }
+ if (memcmp(fw->data+8,"C64x+DV",8)) {
+ printk(KERN_ERR "c64x+ : firmware signature missing\n");
+ release_firmware(fw);
+ device_del(&dev_device);
+ goto err3;
+ }
+ at = fw->data + fw->size;
+ while ((ulong)--at > (ulong)fw->data) {
+ if (*at == '@')
+ break;
+ }
+ if (at == fw->data) {
+ printk(KERN_ERR "c64x+ : firmware tag missing\n");
+ release_firmware(fw);
+ device_del(&dev_device);
+ goto err3;
+ }
+ printk(KERN_NOTICE "c64x+ : got firmware of length %d at %p with tag '%*s' of length %d at %p+1\n",
+ fw->size, fw->data,
+ (int)((ulong)(fw->data + fw->size) - (ulong)at - 1), at + 1,
+ (int)((ulong)(fw->data + fw->size) - (ulong)at - 1), at );
+ /* move firmware into the hardware buffer here. */
+ memcpy(l2ram,fw->data,fw->size);
+ release_firmware(fw);
+ device_del(&dev_device);
+
+#if 0
+ /* release DSP */
+ printk(KERN_INFO "c64x+ : check1: %p %08x %08x\n",mmr,MDCTL39,MDCFG39);
+ MDCTL39=0x00000103; /* Hopefully run... */
+ printk(KERN_INFO "c64x+ : check2: %p %08x %08x\n",mmr,MDCTL39,MDCFG39);
+ printk(KERN_INFO "c64x+ : check3: %08x\n",DSPBOOTADDR);
+#endif
+
+ /* register char-dev */
+ dev_major=MKDEV(C_MOD_MAJOR,0);
+ ret=register_chrdev_region(dev_major,C_MOD_NUM_DEV,C_MOD_NAME);
+ if (ret) {
+ printk(KERN_ERR "c64x+ : can't get chrdev %d\n",C_MOD_MAJOR);
+ goto err3;
+ }
+
+ /* allocate cdev */
+ dev_cdev=cdev_alloc();
+ dev_cdev->ops=&dev_file_ops;
+ /* cdev_init(&dev_data.cdev,&dev_file_ops); */
+ ret=cdev_add(dev_cdev,dev_major,1);
+ if (ret) {
+ printk(KERN_ERR "c64x+ : can't allocate cdev\n");
+ goto err4;
+ }
+
+#ifdef C64X_IRQ
+ /* allocate interrupt slot */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 21)
+ ret=request_irq(dev_irq,dev_irq_handler,IRQF_DISABLED,C_MOD_NAME,NULL);
+#else
+ ret=request_irq(dev_irq,dev_irq_handler,SA_INTERRUPT ,C_MOD_NAME,NULL);
+#endif
+ if (ret) {
+ printk(KERN_ERR "c64x+ : can't get IRQ %d\n",dev_irq);
+ goto err5;
+ }
+#endif
+
+ /* tell sysfs/udev */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19)
+ dev_class=class_create(THIS_MODULE,C_MOD_NAME);
+#else
+ dev_class=class_simple_create(THIS_MODULE,C_MOD_NAME);
+#endif
+ if (IS_ERR(dev_class)) {
+ ret=PTR_ERR(dev_class);
+ printk(KERN_ERR "c64x+ : can't allocate class\n");
+ goto err6;
+ }
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19)
+ class_device_create(dev_class,NULL,dev_major,NULL,C_MOD_NAME"%d",0);
+#else
+ class_simple_device_add(dev_class,dev_major,NULL,C_MOD_NAME"%d",0);
+#endif
+
+ printk(KERN_INFO "c64x+ : module load finished\n");
+ return 0;
+ /* error out */
+err6:
+#ifdef C64X_IRQ
+ free_irq(dev_irq,0);
+err5:
+#endif
+ cdev_del(dev_cdev);
+err4:
+ unregister_chrdev_region(dev_major,1);
+err3:
+ iounmap(l2ram);
+err2:
+ iounmap((void*)l1dram);
+err1:
+ iounmap((void*)mmr);
+err0:
+ if (dram)
+ iounmap((void*)dram);
+ return ret;
+}
+module_init(dev_init);
+
+/* EXIT */
+static void __exit dev_exit(void) {
+ /* Put the DSP into Reset */
+ MDCTL39=0x00000000;
+ /* release the DSP memory */
+ iounmap((void*)mmr);
+ iounmap((void*)l1dram);
+ iounmap(l2ram);
+ /* release all the other resources */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19)
+ class_device_destroy(dev_class,dev_major);
+ class_destroy(dev_class);
+#else
+ class_simple_device_remove(dev_major);
+ class_simple_destroy(dev_class);
+#endif
+#ifdef C64X_IRQ
+ free_irq(dev_irq,0);
+#endif
+ cdev_del(dev_cdev);
+ unregister_chrdev_region(dev_major,C_MOD_NUM_DEV);
+}
+module_exit(dev_exit);
+