📄 hmp4d.c
字号:
/* * Decoder device driver (kernel module) * * Copyright (C) 2005 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. * */#include <linux/kernel.h>#include <linux/module.h>/* needed for __init,__exit directives */#include <linux/init.h>/* needed for remap_page_range */#include <linux/mm.h>/* obviously, for kmalloc */#include <linux/slab.h>/* for struct file_operations, register_chrdev() */#include <linux/fs.h>/* standard error codes */#include <linux/errno.h>/* this header files wraps some common module-space operations ... here we use mem_map_reserve() macro */#include <linux/wrapper.h>/* needed for virt_to_phys() */#include <asm/io.h>/* devfs */#include <linux/devfs_fs_kernel.h>#include <asm/uaccess.h>#include <linux/ioport.h>#define NON_PAGE_ALIGNED 1 /* non page aligned */#define CONS_ALLOC 1 /* use consistent alloc */#if CONS_ALLOCu32 hmp4d_phys;#endif/* our own stuff */#include "hmp4d.h"#ifdef MV #include "asm/arch/mx2-regs.h"#else/* motorola bsp */ #include "asm/arch/mx2.h" #include <linux/pm.h> #include <asm/arch/apmc.h> #define APM_HMP4D 1 /* use apm for hmp4e driver */#endif//#define NODEVFS/* module description */MODULE_AUTHOR("Hantro Products Oy");MODULE_DESCRIPTION("Device driver for Hantro's MPEG4 decoder HW");MODULE_SUPPORTED_DEVICE("4400 MPEG4 Decoder");/* these could be module params in the future */#define DEC_IO_BASE 0x10026800#define DEC_IO_SIZE (8*4) /* bytes */#define DEC_IRQ 50#define HMP4D_BUF_SIZE 700000 /* bytes */#define DEC_HW_ID 0x00001842unsigned long base_port = DEC_IO_BASE;unsigned int irq = DEC_IRQ;MODULE_PARM(base_port, "l");MODULE_PARM(irq, "i");/* and this is our MAJOR; use 0 for dynamic allocation (recommended)*/static int hmp4d_major = 0;/* here's all the must remember stuff */typedef struct{ char *buffer; unsigned int buffsize; unsigned long iobaseaddr; unsigned int iosize; u32 *hwregs; unsigned int irq; struct fasync_struct *async_queue;}hmp4d_t;static hmp4d_t hmp4d_data; /* dynamic allocation? */static int AllocMemory(void);static void FreeMemory(void);static int ReserveIO(void);static void ReleaseIO(void);static int MapBuffers(struct file *filp, struct vm_area_struct *vma);static int MapHwRegs(struct file *filp, struct vm_area_struct *vma);static void ResetAsic(hmp4d_t * dev);/* local */#ifdef APM_HMP4D static int hmp4d_pm_handler(struct pm_dev *pmdev, pm_request_t rqst, void *data);#endif/* global static */static devfs_handle_t devfs_handle = NULL;#ifdef APM_HMP4D static struct pm_dev *pmdev; static struct apmc_user *g_hmp4d_apmc;#endifstatic int g_hmp4d_busy = 0; /* readable by other modules through ipc */void hmp4d_isr(int irq, void *dev_id, struct pt_regs *regs);/* VM operations */static struct page *hmp4d_vm_nopage(struct vm_area_struct *vma, unsigned long address, int write_access){ PDEBUG("hmp4d_vm_nopage: problem with mem access\n"); return NOPAGE_SIGBUS; /* send a SIGBUS */}static void hmp4d_vm_open(struct vm_area_struct *vma){ MOD_INC_USE_COUNT; /* maintain HCLK = 88MHz at doze mode */#ifdef APM_HMP4D apmc_set_level(g_hmp4d_apmc, APMC_LEVEL_LOW);#endif PDEBUG("hmp4d_vm_open:\n");}static void hmp4d_vm_close(struct vm_area_struct *vma){ MOD_DEC_USE_COUNT; PDEBUG("hmp4d_vm_close:\n");}static struct vm_operations_struct hmp4d_vm_ops = { open:hmp4d_vm_open, close:hmp4d_vm_close, nopage:hmp4d_vm_nopage,};/* the device's mmap method. The VFS has kindly prepared the process's * vm_area_struct for us, so we examine this to see what was requested. */static int hmp4d_mmap(struct file *filp, struct vm_area_struct *vma){ int result; unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;#if NON_PAGE_ALIGNED int ofs; ofs = hmp4d_data.iobaseaddr & (PAGE_SIZE - 1);#endif PDEBUG("hmp4d_mmap: size = %lu off = 0x%08lx\n", vma->vm_end - vma->vm_start, offset); if(offset == 0) result = MapBuffers(filp, vma);#if NON_PAGE_ALIGNED else if(offset == hmp4d_data.iobaseaddr - ofs)#else else if(offset == hmp4d_data.iobaseaddr)#endif result = MapHwRegs(filp, vma); else result = -EINVAL; if(result == 0) { vma->vm_ops = &hmp4d_vm_ops; /* open is not implicitly called when mmap is called */ hmp4d_vm_open(vma); } return result;}static int hmp4d_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg){ int err = 0; 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) != HMP4D_IOC_MAGIC) return -ENOTTY; if(_IOC_NR(cmd) > HMP4D_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 HMP4D_IOCHARDRESET: /* * reset the counter to 1, to allow unloading in case * of problems. Use 1, not 0, because the invoking * process has the device open. */ while(MOD_IN_USE) MOD_DEC_USE_COUNT; MOD_INC_USE_COUNT; break; case HMP4D_IOCGBUFBUSADDR:#if !CONS_ALLOC __put_user(virt_to_bus(hmp4d_data.buffer), (unsigned long *) arg);#else __put_user(hmp4d_phys, (unsigned long *) arg);#endif break; case HMP4D_IOCGBUFSIZE: __put_user(hmp4d_data.buffsize, (unsigned int *) arg); break; case HMP4D_IOCGHWOFFSET: __put_user(hmp4d_data.iobaseaddr, (unsigned long *) arg); break; case HMP4D_IOCGHWIOSIZE: __put_user(hmp4d_data.iosize, (unsigned int *) arg); break; } return 0;}static int hmp4d_open(struct inode *inode, struct file *filp){ int result; hmp4d_t *dev = &hmp4d_data; filp->private_data = (void *) dev;#if 1 if(MOD_IN_USE) /* just single access */ return -EBUSY;#endif writel(0, dev->hwregs); /* disable first */#if 0 /* no-word write to any reg & write to reg7 causes AHB error */ memset_io(dev->hwregs, 0, hmp4d_data.iosize); /* reset the HW interface */#else{ int i; for (i = 0; i < hmp4d_data.iosize / 4 - 1; i++) { if(i == 2 || i == 6) { /*do not write register 0x08 & 0x1C are read only */ } else { writel(0, dev->hwregs + i); } }}#endif result = request_irq(dev->irq, hmp4d_isr, SA_INTERRUPT, "hmp4d", (void *) dev); if(result == -EINVAL) { printk(KERN_ERR "hmp4d: Bad irq number or handler\n"); return result; } else if(result == -EBUSY) { printk(KERN_ERR "hmp4d: IRQ %d busy, change your config\n", dev->irq); return result; } MOD_INC_USE_COUNT; PDEBUG("dev opened\n"); return 0;}static int hmp4d_fasync(int fd, struct file *filp, int mode){ hmp4d_t *dev = (hmp4d_t *) filp->private_data; PDEBUG("fasync called\n"); return fasync_helper(fd, filp, mode, &dev->async_queue);}static int hmp4d_release(struct inode *inode, struct file *filp){ hmp4d_t *dev = (hmp4d_t *) filp->private_data; /* free the encoder IRQ */{#include <asm/irq.h>/* disable_irq(dev->irq); */} free_irq(dev->irq, (void *) dev); ResetAsic(dev); /* remove this filp from the asynchronusly notified filp's */ hmp4d_fasync(-1, filp, 0); g_hmp4d_busy = 0; MOD_DEC_USE_COUNT; PDEBUG("dev closed\n"); return 0;}/* VFS methods */static struct file_operations hmp4d_fops = { mmap:hmp4d_mmap, open:hmp4d_open, release:hmp4d_release, ioctl:hmp4d_ioctl, fasync:hmp4d_fasync,};/* changed to devfs */int __init hmp4d_init(void){ int result; /* if you want to test the module, you obviously need to "mknod". */ PDEBUG("module init\n"); printk(KERN_INFO "hmp4d: base_port=0x%08lx irq=%i\n", base_port, irq);#ifdef MV MAX_MST_MGPCR(4) = 0x1; /* allow AHB burst to be breakable by other masters */ AIPI_PAR(2) &= ~(0x1 << 6); /* Grant access to eMMa region on AIPI2 bus */ CRM_PCCR0 |= 0x08008000;#else _reg_MAX_MST_MGPCR(4) = 0x1;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -