diff --git a/Kbuild b/Kbuild new file mode 100644 index 0000000..186ca39 --- /dev/null +++ b/Kbuild @@ -0,0 +1 @@ +obj-m := hx170dec.o memalloc.o diff --git a/hx170dec.c b/hx170dec.c new file mode 100644 index 0000000..dd9004f --- /dev/null +++ b/hx170dec.c @@ -0,0 +1,615 @@ +/* + * Decoder device driver (kernel module) + * + * Copyright (C) 2009 Hantro Products Oy. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * +-------------------------------------------------------------------------------- +-- +-- Version control information, please leave untouched. +-- +-- $RCSfile: hx170dec.c,v $ +-- $Date: 2009/12/22 12:10:38 $ +-- $Revision: 1.1 $ +-- +------------------------------------------------------------------------------*/ + +#include +#include +/* needed for __init,__exit directives */ +#include +/* needed for remap_pfn_range + SetPageReserved + ClearPageReserved +*/ +#include +/* obviously, for kmalloc */ +#include +/* for struct file_operations, register_chrdev() */ +#include +/* standard error codes */ +#include + +#include +/* request_irq(), free_irq() */ +#include + +/* needed for virt_to_phys() */ +#include +#include +#include +#include +#include + +#include +#include + +#include + +/* our own stuff */ +#include "hx170dec.h" + +/* module description */ +MODULE_LICENSE("Proprietary"); +MODULE_AUTHOR("Hantro Products Oy"); +MODULE_DESCRIPTION("driver module for 8170/81990 Hantro decoder/pp"); + +/* Decoder interrupt register */ +#define X170_INTERRUPT_REGISTER_DEC (1*4) +#define X170_INTERRUPT_REGISTER_PP (60*4) + +/* Logic module base address */ +#define HXDEC_LOGIC_MODULE0_BASE AT91SAM9G45_VDEC_BASE + +#define VP_PB_INT_LT AT91SAM9G45_ID_VDEC + +#define INT_EXPINT1 10 +#define INT_EXPINT2 11 +#define INT_EXPINT3 12 + +/* these could be module params in the future */ + +#define DEC_IO_BASE HXDEC_LOGIC_MODULE0_BASE +#define DEC_IO_SIZE ((100+1) * 4) /* bytes */ +#define DEC_IRQ VP_PB_INT_LT + +#define HX_DEC_INTERRUPT_BIT 0x100 +#define HX_PP_INTERRUPT_BIT 0x100 + +static const int DecHwId[] = { 0x8190, 0x8170, 0x9170, 0x9190 }; + +static u32 hx_pp_instance = 0; +static u32 hx_dec_instance = 0; + +unsigned long base_port = HXDEC_LOGIC_MODULE0_BASE; +int irq = DEC_IRQ; + +/* module_param(name, type, perm) */ +module_param(base_port, ulong, 0); +module_param(irq, int, 0); + +/* and this is our MAJOR; use 0 for dynamic allocation (recommended)*/ +static int hx170dec_major = 0; + +/* here's all the must remember stuff */ +typedef struct +{ + char *buffer; + unsigned long iobaseaddr; + unsigned int iosize; + volatile u8 *hwregs; + int irq; + struct fasync_struct *async_queue_dec; + struct fasync_struct *async_queue_pp; +} hx170dec_t; + +static hx170dec_t hx170dec_data; /* dynamic allocation? */ + +#ifdef HW_PERFORMANCE +static struct timeval end_time; +#endif + +static int ReserveIO(void); +static void ReleaseIO(void); + +static void ResetAsic(hx170dec_t * dev); + +#ifdef HX170DEC_DEBUG +static void dump_regs(unsigned long data); +#endif + +/* IRQ handler */ +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18)) +static irqreturn_t hx170dec_isr(int irq, void *dev_id, struct pt_regs *regs); +#else +static irqreturn_t hx170dec_isr(int irq, void *dev_id); +#endif + +/*------------------------------------------------------------------------------ + Function name : hx170dec_ioctl + Description : communication method to/from the user space + + Return type : int +------------------------------------------------------------------------------*/ + +static int hx170dec_ioctl(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + int err = 0; + +#ifdef HW_PERFORMANCE + struct timeval *end_time_arg; +#endif + + PDEBUG("ioctl cmd 0x%08ux\n", cmd); + /* + * extract the type and number bitfields, and don't decode + * wrong cmds: return ENOTTY (inappropriate ioctl) before access_ok() + */ + if(_IOC_TYPE(cmd) != HX170DEC_IOC_MAGIC) + return -ENOTTY; + if(_IOC_NR(cmd) > HX170DEC_IOC_MAXNR) + return -ENOTTY; + + /* + * the direction is a bitmask, and VERIFY_WRITE catches R/W + * transfers. `Type' is user-oriented, while + * access_ok is kernel-oriented, so the concept of "read" and + * "write" is reversed + */ + if(_IOC_DIR(cmd) & _IOC_READ) + err = !access_ok(VERIFY_WRITE, (void *) arg, _IOC_SIZE(cmd)); + else if(_IOC_DIR(cmd) & _IOC_WRITE) + err = !access_ok(VERIFY_READ, (void *) arg, _IOC_SIZE(cmd)); + if(err) + return -EFAULT; + + switch (cmd) + { + case HX170DEC_IOC_CLI: + disable_irq(hx170dec_data.irq); + break; + + case HX170DEC_IOC_STI: + enable_irq(hx170dec_data.irq); + break; + case HX170DEC_IOCGHWOFFSET: + __put_user(hx170dec_data.iobaseaddr, (unsigned long *) arg); + break; + case HX170DEC_IOCGHWIOSIZE: + __put_user(hx170dec_data.iosize, (unsigned int *) arg); + break; + case HX170DEC_PP_INSTANCE: + filp->private_data = &hx_pp_instance; + break; + +#ifdef HW_PERFORMANCE + case HX170DEC_HW_PERFORMANCE: + end_time_arg = (struct timeval *) arg; + end_time_arg->tv_sec = end_time.tv_sec; + end_time_arg->tv_usec = end_time.tv_usec; + break; +#endif + } + return 0; +} + +/*------------------------------------------------------------------------------ + Function name : hx170dec_open + Description : open method + + Return type : int +------------------------------------------------------------------------------*/ + +static int hx170dec_open(struct inode *inode, struct file *filp) +{ + filp->private_data = &hx_dec_instance; + + PDEBUG("dev opened\n"); + return 0; +} + +/*------------------------------------------------------------------------------ + Function name : hx170dec_fasync + Description : Method for signing up for a interrupt + + Return type : int +------------------------------------------------------------------------------*/ + +static int hx170dec_fasync(int fd, struct file *filp, int mode) +{ + + hx170dec_t *dev = &hx170dec_data; + struct fasync_struct **async_queue; + + /* select which interrupt this instance will sign up for */ + + if(((u32 *) filp->private_data) == &hx_dec_instance) + { + /* decoder */ + PDEBUG("decoder fasync called %d %x %d %x\n", + fd, (u32) filp, mode, (u32) & dev->async_queue_dec); + + async_queue = &dev->async_queue_dec; + } + else + { + /* pp */ + PDEBUG("pp fasync called %d %x %d %x\n", + fd, (u32) filp, mode, (u32) & dev->async_queue_pp); + async_queue = &dev->async_queue_pp; + } + + return fasync_helper(fd, filp, mode, async_queue); +} + +/*------------------------------------------------------------------------------ + Function name : hx170dec_release + Description : Release driver + + Return type : int +------------------------------------------------------------------------------*/ + +static int hx170dec_release(struct inode *inode, struct file *filp) +{ + + /* hx170dec_t *dev = &hx170dec_data; */ + + if(filp->f_flags & FASYNC) + { + /* remove this filp from the asynchronusly notified filp's */ + hx170dec_fasync(-1, filp, 0); + } + + PDEBUG("dev closed\n"); + return 0; +} + +/* VFS methods */ +static struct file_operations hx170dec_fops = { + open:hx170dec_open, + release:hx170dec_release, + ioctl:hx170dec_ioctl, + fasync:hx170dec_fasync, +}; + +/*------------------------------------------------------------------------------ + Function name : hx170dec_init + Description : Initialize the driver + + Return type : int +------------------------------------------------------------------------------*/ + +int __init hx170dec_init(void) +{ + int result; + + PDEBUG("module init\n"); + + printk(KERN_INFO "hx170dec: dec/pp kernel module. %s \n", "$Revision: 1.9 $"); + printk(KERN_INFO "hx170dec: supports 8170 and 8190 hardware \n"); + printk(KERN_INFO "hx170dec: base_port=0x%08lx irq=%i\n", base_port, irq); + + hx170dec_data.iobaseaddr = base_port; + hx170dec_data.iosize = DEC_IO_SIZE; + hx170dec_data.irq = irq; + + hx170dec_data.async_queue_dec = NULL; + hx170dec_data.async_queue_pp = NULL; + + result = register_chrdev(hx170dec_major, "hx170dec", &hx170dec_fops); + if(result < 0) + { + printk(KERN_INFO "hx170dec: unable to get major %d\n", hx170dec_major); + goto err; + } + else if(result != 0) /* this is for dynamic major */ + { + hx170dec_major = result; + } + + result = ReserveIO(); + if(result < 0) + { + goto err; + } + + ResetAsic(&hx170dec_data); /* reset hardware */ + /* get the IRQ line */ + if(irq > 0) + { + result = request_irq(irq, hx170dec_isr, +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18)) + SA_INTERRUPT | SA_SHIRQ, +#else + IRQF_DISABLED | IRQF_SHARED, +#endif + "hx170dec", (void *) &hx170dec_data); + if(result != 0) + { + if(result == -EINVAL) + { + printk(KERN_ERR "hx170dec: Bad irq number or handler\n"); + } + else if(result == -EBUSY) + { + printk(KERN_ERR "hx170dec: IRQ <%d> busy, change your config\n", + hx170dec_data.irq); + } + + ReleaseIO(); + goto err; + } + } + else + { + printk(KERN_INFO "hx170dec: IRQ not in use!\n"); + } + + printk(KERN_INFO "hx170dec: module inserted. Major = %d\n", hx170dec_major); + + return 0; + + err: + printk(KERN_INFO "hx170dec: module not inserted\n"); + unregister_chrdev(hx170dec_major, "hx170dec"); + return result; +} + +/*------------------------------------------------------------------------------ + Function name : hx170dec_cleanup + Description : clean up + + Return type : int +------------------------------------------------------------------------------*/ + +void __exit hx170dec_cleanup(void) +{ + hx170dec_t *dev = (hx170dec_t *) & hx170dec_data; + + /* clear dec IRQ */ + writel(0, dev->hwregs + X170_INTERRUPT_REGISTER_DEC); + /* clear pp IRQ */ + writel(0, dev->hwregs + X170_INTERRUPT_REGISTER_PP); + +#ifdef HX170DEC_DEBUG + dump_regs((unsigned long) dev); /* dump the regs */ +#endif + + /* free the IRQ */ + if(dev->irq != -1) + { + free_irq(dev->irq, (void *) dev); + } + + ReleaseIO(); + + unregister_chrdev(hx170dec_major, "hx170dec"); + + printk(KERN_INFO "hx170dec: module removed\n"); + return; +} + +module_init(hx170dec_init); +module_exit(hx170dec_cleanup); + +static int CheckHwId(hx170dec_t * dev) +{ + long int hwid; + + size_t numHw = sizeof(DecHwId) / sizeof(*DecHwId); + + hwid = readl(dev->hwregs); + printk(KERN_INFO "hx170dec: HW ID=0x%08lx\n", hwid); + + hwid = (hwid >> 16) & 0xFFFF; /* product version only */ + + while(numHw--) + { + if(hwid == DecHwId[numHw]) + { + printk(KERN_INFO "hx170dec: Compatible HW found at 0x%08lx\n", + dev->iobaseaddr); + return 1; + } + } + + printk(KERN_INFO "hx170dec: No Compatible HW found at 0x%08lx\n", + dev->iobaseaddr); + return 0; +} + +/*------------------------------------------------------------------------------ + Function name : ReserveIO + Description : IO reserve + + Return type : int +------------------------------------------------------------------------------*/ +static int ReserveIO(void) +{ + set_irq_type(AT91SAM9G45_ID_VDEC, IRQ_TYPE_LEVEL_HIGH); + + clk_enable(clk_get(NULL, "vdec_clk")); + + if(!request_mem_region + (hx170dec_data.iobaseaddr, hx170dec_data.iosize, "hx170dec")) + { + printk(KERN_INFO "hx170dec: failed to reserve HW regs\n"); + return -EBUSY; + } + + hx170dec_data.hwregs = + (volatile u8 *) ioremap_nocache(hx170dec_data.iobaseaddr, + hx170dec_data.iosize); + + if(hx170dec_data.hwregs == NULL) + { + printk(KERN_INFO "hx170dec: failed to ioremap HW regs\n"); + ReleaseIO(); + return -EBUSY; + } + + /* check for correct HW */ + if(!CheckHwId(&hx170dec_data)) + { + ReleaseIO(); + return -EBUSY; + } + + return 0; +} + +/*------------------------------------------------------------------------------ + Function name : releaseIO + Description : release + + Return type : void +------------------------------------------------------------------------------*/ + +static void ReleaseIO(void) +{ + if(hx170dec_data.hwregs) + iounmap((void *) hx170dec_data.hwregs); + release_mem_region(hx170dec_data.iobaseaddr, hx170dec_data.iosize); + + clk_disable(clk_get(NULL, "vdec_clk")); +} + +/*------------------------------------------------------------------------------ + Function name : hx170dec_isr + Description : interrupt handler + + Return type : irqreturn_t +------------------------------------------------------------------------------*/ +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18)) +irqreturn_t hx170dec_isr(int irq, void *dev_id, struct pt_regs *regs) +#else +irqreturn_t hx170dec_isr(int irq, void *dev_id) +#endif +{ + unsigned int handled = 0; + + hx170dec_t *dev = (hx170dec_t *) dev_id; + u32 irq_status_dec; + u32 irq_status_pp; + + handled = 0; + + /* interrupt status register read */ + irq_status_dec = readl(dev->hwregs + X170_INTERRUPT_REGISTER_DEC); + irq_status_pp = readl(dev->hwregs + X170_INTERRUPT_REGISTER_PP); + + if((irq_status_dec & HX_DEC_INTERRUPT_BIT) || + (irq_status_pp & HX_PP_INTERRUPT_BIT)) + { + + if(irq_status_dec & HX_DEC_INTERRUPT_BIT) + { +#ifdef HW_PERFORMANCE + do_gettimeofday(&end_time); +#endif + /* clear dec IRQ */ + writel(irq_status_dec & (~HX_DEC_INTERRUPT_BIT), + dev->hwregs + X170_INTERRUPT_REGISTER_DEC); + /* fasync kill for decoder instances */ + if(dev->async_queue_dec != NULL) + { + kill_fasync(&dev->async_queue_dec, SIGIO, POLL_IN); + } +#if 0 + else + { + printk(KERN_WARNING + "hx170dec: DEC IRQ received w/o anybody waiting for it!\n"); + } +#endif + PDEBUG("decoder IRQ received!\n"); + } + + if(irq_status_pp & HX_PP_INTERRUPT_BIT) + { +#ifdef HW_PERFORMANCE + do_gettimeofday(&end_time); +#endif + /* clear pp IRQ */ + writel(irq_status_pp & (~HX_PP_INTERRUPT_BIT), + dev->hwregs + X170_INTERRUPT_REGISTER_PP); + + /* kill fasync for PP instances */ + if(dev->async_queue_pp != NULL) + { + kill_fasync(&dev->async_queue_pp, SIGIO, POLL_IN); + } +#if 0 + else + { + printk(KERN_WARNING + "hx170dec: PP IRQ received w/o anybody waiting for it!\n"); + } +#endif + PDEBUG("pp IRQ received!\n"); + } + + handled = 1; + } + else + { + PDEBUG("IRQ received, but not x170's!\n"); + } + + return IRQ_RETVAL(handled); +} + +/*------------------------------------------------------------------------------ + Function name : ResetAsic + Description : reset asic + + Return type : +------------------------------------------------------------------------------*/ + +void ResetAsic(hx170dec_t * dev) +{ + int i; + + writel(0, dev->hwregs + 0x04); + + for(i = 4; i < dev->iosize; i += 4) + { + writel(0, dev->hwregs + i); + } +} + +/*------------------------------------------------------------------------------ + Function name : dump_regs + Description : Dump registers + + Return type : +------------------------------------------------------------------------------*/ +#ifdef HX170DEC_DEBUG +void dump_regs(unsigned long data) +{ + hx170dec_t *dev = (hx170dec_t *) data; + int i; + + PDEBUG("Reg Dump Start\n"); + for(i = 0; i < dev->iosize; i += 4) + { + PDEBUG("\toffset %02X = %08X\n", i, readl(dev->hwregs + i)); + } + PDEBUG("Reg Dump End\n"); +} +#endif + diff --git a/hx170dec.h b/hx170dec.h new file mode 100644 index 0000000..76b97f5 --- /dev/null +++ b/hx170dec.h @@ -0,0 +1,76 @@ +/* + * Decoder device driver (kernel module headers) + * + * Copyright (C) 2009 Hantro Products Oy. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * +-------------------------------------------------------------------------------- +-- +-- Version control information, please leave untouched. +-- +-- $RCSfile: hx170dec.h,v $ +-- $Date: 2009/12/22 12:10:59 $ +-- $Revision: 1.1 $ +-- +------------------------------------------------------------------------------*/ + +#ifndef _HX170DEC_H_ +#define _HX170DEC_H_ +#include /* needed for the _IOW etc stuff used later */ + +/* + * Macros to help debugging + */ +#undef PDEBUG /* undef it, just in case */ +#ifdef HX170DEC_DEBUG +# ifdef __KERNEL__ + /* This one if debugging is on, and kernel space */ +# define PDEBUG(fmt, args...) printk( KERN_INFO "hx170dec: " fmt, ## args) +# else + /* This one for user space */ +# define PDEBUG(fmt, args...) printf(__FILE__ ":%d: " fmt, __LINE__ , ## args) +# endif +#else +# define PDEBUG(fmt, args...) /* not debugging: nothing */ +#endif + +/* + * Ioctl definitions + */ + +/* Use 'k' as magic number */ +#define HX170DEC_IOC_MAGIC 'k' +/* + * S means "Set" through a ptr, + * T means "Tell" directly with the argument value + * G means "Get": reply by setting through a pointer + * Q means "Query": response is on the return value + * X means "eXchange": G and S atomically + * H means "sHift": T and Q atomically + */ + +#define HX170DEC_PP_INSTANCE _IO(HX170DEC_IOC_MAGIC, 1) /* the client is pp instance */ +#define HX170DEC_HW_PERFORMANCE _IO(HX170DEC_IOC_MAGIC, 2) /* decode/pp time for HW performance */ +#define HX170DEC_IOCGHWOFFSET _IOR(HX170DEC_IOC_MAGIC, 3, unsigned long *) +#define HX170DEC_IOCGHWIOSIZE _IOR(HX170DEC_IOC_MAGIC, 4, unsigned int *) + +#define HX170DEC_IOC_CLI _IO(HX170DEC_IOC_MAGIC, 5) +#define HX170DEC_IOC_STI _IO(HX170DEC_IOC_MAGIC, 6) + +#define HX170DEC_IOC_MAXNR 6 + +#endif /* !_HX170DEC_H_ */ + diff --git a/memalloc.c b/memalloc.c new file mode 100644 index 0000000..d211ddf --- /dev/null +++ b/memalloc.c @@ -0,0 +1,450 @@ +/* + * Memalloc, encoder memory allocation driver (kernel module) + * + * Copyright (C) 2009 Hantro Products Oy. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * +-------------------------------------------------------------------------------- +-- +-- Abstract : Allocate memory blocks +-- +-------------------------------------------------------------------------------- +-- +-- Version control information, please leave untouched. +-- +-- $RCSfile: memalloc.c,v $ +-- $Date: 2009/12/22 12:19:59 $ +-- $Revision: 1.1 $ +-- +------------------------------------------------------------------------------*/ + +#include +#include +/* needed for __init,__exit directives */ +#include +/* needed for remap_page_range */ +#include +/* obviously, for kmalloc */ +#include +/* for struct file_operations, register_chrdev() */ +#include +/* standard error codes */ +#include +/* this header files wraps some common module-space operations ... + here we use mem_map_reserve() macro */ + +#include +#include +#include +#include +/* for current pid */ +#include + +/* Our header */ +#include "memalloc.h" + +/* module description */ +MODULE_LICENSE("Proprietary"); +MODULE_AUTHOR("Hantro Products Oy"); +MODULE_DESCRIPTION("RAM allocation"); + +#define HLINA_START_ADDRESS 0x74000000 /* 64 MB for Linux, 64 MB for VDEC */ + +#ifndef HLINA_START_ADDRESS +#define HLINA_START_ADDRESS 0x02000000 +#endif + +#define MAX_OPEN 32 +#define ID_UNUSED 0xFF +#define MEMALLOC_BASIC 0 +#define MEMALLOC_MAX_OUTPUT 1 +#define MEMALLOC_BASIC_X2 2 + +/* selects the memory allocation method, i.e. which allocation scheme table is used */ +unsigned int alloc_method = MEMALLOC_BASIC; + +static int memalloc_major = 0; /* dynamic */ + +int id[MAX_OPEN] = { ID_UNUSED }; + +/* module_param(name, type, perm) */ +module_param(alloc_method, uint, 0); + +/* here's all the must remember stuff */ +struct allocation +{ + struct list_head list; + void *buffer; + unsigned int order; + int fid; +}; + +struct list_head heap_list; + +static spinlock_t mem_lock = SPIN_LOCK_UNLOCKED; + +typedef struct hlinc +{ + unsigned int bus_address; + unsigned int used; + unsigned int size; + int file_id; +} hlina_chunk; + +static unsigned int *size_table = NULL; +static size_t chunks = 0; + +unsigned int size_table_0[] = { + 1, 1, 1, 1, 1, 1, 1, 1, 1, + 4, 4, 4, 4, 4, 4, 4, 4, + 10, 10, 10, 10, + 22, 22, 22, 22, + 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, + 50, 50, 50, 50, 50, 50, 50, + 75, 75, 75, 75, 75, + 86, 86, 86, 86, 86, + 113, 113, + 152, 152, + 162, 162, 162, + 270, 270, 270, + 403, 403, 403, 403, + 403, 403, + 450, 450, + 893, 893, + 893, 893, + 1999, + 3997, + 4096, + 8192 +}; + +unsigned int size_table_1[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, + 0, 0, + 0, 0, 0, + 0, 0, 0, + 0, 0, + 0, 64, + 64, 128, + 512, + 3072, + 8448 +}; + +unsigned int size_table_2[] = { + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 10, 10, 10, 10, 10, 10, 10, 10, + 22, 22, 22, 22, 22, 22, 22, 22, + 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, + 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, + 113, 113, 113, 113, + 152, 152, 152, 152, + 162, 162, 162, 162, 162, 162, + 270, 270, 270, 270, 270, 270, + 403, 403, 403, 403, 403, 403, 403, 403, + 403, 403, 403, 403, + 450, 450, 450, 450, + 893, 893, 893, 893, + 893, 893, 893, 893, + 1999, 1999, + 3997, 3997, + 4096, 4096, + 8192, 8192 +}; + +static hlina_chunk hlina_chunks[256]; + +static int AllocMemory(unsigned *busaddr, unsigned int size, struct file *filp); +static int FreeMemory(unsigned long busaddr); +static void ResetMems(void); + +static int memalloc_ioctl(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + int err = 0; + int ret; + + PDEBUG("ioctl cmd 0x%08x\n", cmd); + + if(inode == NULL || filp == NULL || arg == 0) + { + return -EFAULT; + } + /* + * extract the type and number bitfields, and don't decode + * wrong cmds: return ENOTTY (inappropriate ioctl) before access_ok() + */ + if(_IOC_TYPE(cmd) != MEMALLOC_IOC_MAGIC) + return -ENOTTY; + if(_IOC_NR(cmd) > MEMALLOC_IOC_MAXNR) + return -ENOTTY; + + if(_IOC_DIR(cmd) & _IOC_READ) + err = !access_ok(VERIFY_WRITE, (void *) arg, _IOC_SIZE(cmd)); + else if(_IOC_DIR(cmd) & _IOC_WRITE) + err = !access_ok(VERIFY_READ, (void *) arg, _IOC_SIZE(cmd)); + if(err) + return -EFAULT; + + switch (cmd) + { + case MEMALLOC_IOCHARDRESET: + + PDEBUG("HARDRESET\n"); + ResetMems(); + + break; + + case MEMALLOC_IOCXGETBUFFER: + { + int result; + MemallocParams memparams; + + PDEBUG("GETBUFFER\n"); + spin_lock(&mem_lock); + + __copy_from_user(&memparams, (const void *) arg, sizeof(memparams)); + + result = AllocMemory(&memparams.busAddress, memparams.size, filp); + + __copy_to_user((void *) arg, &memparams, sizeof(memparams)); + + spin_unlock(&mem_lock); + + return result; + } + case MEMALLOC_IOCSFREEBUFFER: + { + + unsigned long busaddr; + + PDEBUG("FREEBUFFER\n"); + spin_lock(&mem_lock); + __get_user(busaddr, (unsigned long *) arg); + ret = FreeMemory(busaddr); + + spin_unlock(&mem_lock); + return ret; + } + } + return 0; +} + +static int memalloc_open(struct inode *inode, struct file *filp) +{ + int i = 0; + + for(i = 0; i < MAX_OPEN + 1; i++) + { + + if(i == MAX_OPEN) + return -1; + if(id[i] == ID_UNUSED) + { + id[i] = i; + filp->private_data = id + i; + break; + } + } + PDEBUG("dev opened\n"); + return 0; + +} + +static int memalloc_release(struct inode *inode, struct file *filp) +{ + + int i = 0; + + for(i = 0; i < chunks; i++) + { + if(hlina_chunks[i].file_id == *((int *) (filp->private_data))) + { + hlina_chunks[i].used = 0; + hlina_chunks[i].file_id = ID_UNUSED; + } + } + *((int *) filp->private_data) = ID_UNUSED; + PDEBUG("dev closed\n"); + return 0; +} + +/* VFS methods */ +static struct file_operations memalloc_fops = { + open:memalloc_open, + release:memalloc_release, + ioctl:memalloc_ioctl, +}; + +int __init memalloc_init(void) +{ + int result; + int i = 0; + + PDEBUG("module init\n"); + printk("memalloc: 8190 Linear Memory Allocator, %s \n", "$Revision: 1.1 $"); + printk("memalloc: linear memory base = 0x%08x \n", HLINA_START_ADDRESS); + + switch (alloc_method) + { + + case MEMALLOC_MAX_OUTPUT: + size_table = size_table_1; + chunks = (sizeof(size_table_1) / sizeof(*size_table_1)); + printk(KERN_INFO "memalloc: allocation method: MEMALLOC_MAX_OUTPUT\n"); + break; + case MEMALLOC_BASIC_X2: + size_table = size_table_2; + chunks = (sizeof(size_table_2) / sizeof(*size_table_2)); + printk(KERN_INFO "memalloc: allocation method: MEMALLOC_BASIC x 2\n"); + break; + default: + size_table = size_table_0; + chunks = (sizeof(size_table_0) / sizeof(*size_table_0)); + printk(KERN_INFO "memalloc: allocation method: MEMALLOC_BASIC\n"); + break; + } + + result = register_chrdev(memalloc_major, "memalloc", &memalloc_fops); + if(result < 0) + { + PDEBUG("memalloc: unable to get major %d\n", memalloc_major); + goto err; + } + else if(result != 0) /* this is for dynamic major */ + { + memalloc_major = result; + } + + ResetMems(); + + /* We keep a register of out customers, reset it */ + for(i = 0; i < MAX_OPEN; i++) + { + id[i] = ID_UNUSED; + } + + return 0; + + err: + PDEBUG("memalloc: module not inserted\n"); + unregister_chrdev(memalloc_major, "memalloc"); + return result; +} + +void __exit memalloc_cleanup(void) +{ + + PDEBUG("clenup called\n"); + + unregister_chrdev(memalloc_major, "memalloc"); + + PDEBUG("memalloc: module removed\n"); + return; +} + +module_init(memalloc_init); +module_exit(memalloc_cleanup); + +/* Cycle through the buffers we have, give the first free one */ +static int AllocMemory(unsigned *busaddr, unsigned int size, struct file *filp) +{ + + int i = 0; + + *busaddr = 0; + + for(i = 0; i < chunks; i++) + { + + if(!hlina_chunks[i].used && (hlina_chunks[i].size >= size)) + { + *busaddr = hlina_chunks[i].bus_address; + hlina_chunks[i].used = 1; + hlina_chunks[i].file_id = *((int *) (filp->private_data)); + break; + } + } + + if(*busaddr == 0) + { + printk("memalloc: Allocation FAILED: size = %d\n", size); + } + else + { + PDEBUG("MEMALLOC OK: size: %d, size reserved: %d\n", size, + hlina_chunks[i].size); + } + + return 0; +} + +/* Free a buffer based on bus address */ +static int FreeMemory(unsigned long busaddr) +{ + int i = 0; + + for(i = 0; i < chunks; i++) + { + if(hlina_chunks[i].bus_address == busaddr) + { + hlina_chunks[i].used = 0; + hlina_chunks[i].file_id = ID_UNUSED; + } + } + + return 0; +} + +/* Reset "used" status */ +void ResetMems(void) +{ + int i = 0; + unsigned int ba = HLINA_START_ADDRESS; + + for(i = 0; i < chunks; i++) + { + + hlina_chunks[i].bus_address = ba; + hlina_chunks[i].used = 0; + hlina_chunks[i].file_id = ID_UNUSED; + hlina_chunks[i].size = 4096 * size_table[i]; + + ba += hlina_chunks[i].size; + } + + printk("memalloc: %d bytes (%dMB) configured. Check RAM size!\n", + ba - (unsigned int)(HLINA_START_ADDRESS), + (ba - (unsigned int)(HLINA_START_ADDRESS)) / (1024 * 1024)); + + if(ba - (unsigned int)(HLINA_START_ADDRESS) > 96 * 1024 * 1024) + { + PDEBUG("MEMALLOC ERROR: MEMORY ALLOC BUG\n"); + } + +} + diff --git a/memalloc.h b/memalloc.h new file mode 100644 index 0000000..97feaf3 --- /dev/null +++ b/memalloc.h @@ -0,0 +1,80 @@ +/* + * Memalloc, encoder memory allocation driver (kernel module headers) + * + * Copyright (C) 2009 Hantro Products Oy. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * +-------------------------------------------------------------------------------- +-- +-- Abstract : +-- +-------------------------------------------------------------------------------- +-- +-- Version control information, please leave untouched. +-- +-- $RCSfile: memalloc.h,v $ +-- $Date: 2009/12/22 12:20:20 $ +-- $Revision: 1.1 $ +-- +------------------------------------------------------------------------------*/ + + +#ifndef _HMP4ENC_H_ +#define _HMP4ENC_H_ +#include /* needed for the _IOW etc stuff used later */ +/* + * Macros to help debugging + */ + +#undef PDEBUG /* undef it, just in case */ +#ifdef MEMALLOC_DEBUG +# ifdef __KERNEL__ + /* This one if debugging is on, and kernel space */ +# define PDEBUG(fmt, args...) printk( KERN_INFO "memalloc: " fmt, ## args) +# else + /* This one for user space */ +# define PDEBUG(fmt, args...) fprintf(stderr, fmt, ## args) +# endif +#else +# define PDEBUG(fmt, args...) /* not debugging: nothing */ +#endif +/* + * Ioctl definitions + */ +/* Use 'k' as magic number */ +#define MEMALLOC_IOC_MAGIC 'k' +/* + * S means "Set" through a ptr, + * T means "Tell" directly with the argument value + * G means "Get": reply by setting through a pointer + * Q means "Query": response is on the return value + * X means "eXchange": G and S atomically + * H means "sHift": T and Q atomically + */ +#define MEMALLOC_IOCXGETBUFFER _IOWR(MEMALLOC_IOC_MAGIC, 1, unsigned long) +#define MEMALLOC_IOCSFREEBUFFER _IOW(MEMALLOC_IOC_MAGIC, 2, unsigned long) + +/* ... more to come */ +#define MEMALLOC_IOCHARDRESET _IO(MEMALLOC_IOC_MAGIC, 15) /* debugging tool */ +#define MEMALLOC_IOC_MAXNR 15 + +typedef struct { + unsigned busAddress; + unsigned size; +}MemallocParams; + +#endif /* _HMP4ENC_H_ */ +