dsp_ctl.c
来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 645 行
C
645 行
/* * linux/arch/arm/mach-omap/dsp/dsp_ctl.c * * OMAP DSP control device driver * * Copyright (C) 2002-2004 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 * * $Id: dsp_ctl.c * $Revision: 3.0.1 * $Date: 2004/10/04 * */#include <linux/module.h>#include <linux/slab.h>#include <linux/major.h>#include <linux/fs.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 int cfgstat = CFGSTAT_ERR;static wait_queue_head_t cfg_wait_q;static unsigned short cfg_wait_cmd = 0;static struct semaphore ioctl_sem;static unsigned char n_stask;/* * control functions */static int varread_val;static int omap_dsp_regread(unsigned short cmd_l, unsigned short adr, unsigned short *val){ varread_val = -1; cfg_wait_cmd = MBCMD(REGRW); omap_dsp_mbsend_and_wait(MBCMD(REGRW), cmd_l, adr, &cfg_wait_q); if (varread_val == -1) { printk(KERN_ERR "omapdsp: register read error!\n"); return -EINVAL; } *val = varread_val; return 0;}static int omap_dsp_regwrite(unsigned short cmd_l, unsigned short adr, unsigned short val){ omap_dsp_mbsend_exarg(MBCMD(REGRW), cmd_l, adr, OMAP_DSP_TID_ANON, 1, &val); return 0;}static int omap_dsp_getvar(unsigned char varid, unsigned short *val){ varread_val = -1; cfg_wait_cmd = MBCMD(GETVAR); omap_dsp_mbsend_and_wait(MBCMD(GETVAR), varid, 0, &cfg_wait_q); if (varread_val == -1) { printk(KERN_ERR "omapdsp: variable read error!\n"); return -EINVAL; } *val = varread_val; return 0;}static int omap_dsp_setvar(unsigned char varid, unsigned short val){ omap_dsp_mbsend(MBCMD(SETVAR), varid, val); return 0;}#ifdef CONFIG_PROC_FSextern void omap_dsp_create_ipbuf_proc(void);extern void omap_dsp_remove_ipbuf_proc(void);#endifstatic int omap_dsp_dspcfg(void){ int ret; if (!((cfgstat == CFGSTAT_ERR) || (cfgstat == CFGSTAT_ABORT))) { printk(KERN_ERR "omapdsp: DSP has been already configured. " "do unconfig!\n"); return -EBUSY; } cfgstat = CFGSTAT_GOING; omap_dsp_mb_config(); cfg_wait_cmd = MBCMD(DSPCFG); omap_dsp_mbsend_and_wait(MBCMD(DSPCFG), OMAP_DSP_MBCMD_DSPCFG_REQ, 0, &cfg_wait_q); if (cfgstat != CFGSTAT_DONE) { printk(KERN_ERR "omapdsp: configuration error!\n"); return -EINVAL; } if ((ret = omap_dsp_task_config_all(n_stask)) < 0) { omap_dsp_dspuncfg(); return ret; } omap_dsp_twch_config(n_stask); omap_dsp_err_config(); /* ipbuf has been configured in interrupt context */ omap_dsp_ipbuf_pcfg(); /* send parameter */ if ((ret = omap_dsp_setvar(OMAP_DSP_MBCMD_VARID_ICRMASK, dsp_icrmask)) < 0) return ret;#ifdef CONFIG_PROC_FS omap_dsp_create_ipbuf_proc();#endif return 0;}int omap_dsp_dspuncfg(void){ if (omap_dsp_taskmod_busy()) { printk(KERN_WARNING "omapdsp: tasks are busy.\n"); return -EBUSY; }#ifdef CONFIG_PROC_FS omap_dsp_remove_ipbuf_proc();#endif omap_dsp_mb_unconfig(); omap_dsp_twch_unconfig(); omap_dsp_err_unconfig(); omap_dsp_task_unconfig_all(); omap_dsp_ipbuf_unconfig(); cfgstat = CFGSTAT_ERR; return 0;}int omap_dsp_is_config_done(void){ return (cfgstat == CFGSTAT_DONE) ? 1 : 0;}void omap_dsp_runlevel(unsigned char level){ if (level == OMAP_DSP_MBCMD_RUNLEVEL_RECOVERY) omap_dsp_mbsend_recovery(MBCMD(RUNLEVEL), level, 0); else omap_dsp_mbsend(MBCMD(RUNLEVEL), level, 0);}/* * DSP control device file operations */static int omap_dsp_ctl_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg){ int sem_st; int ret = 0; /* * command level 1: commands which don't need lock */ switch (cmd) { case OMAP_DSP_IOCTL_RESET: disable_irq(INT_DSP_MMU); preempt_disable(); if (dsp_runstat > RUNSTAT_RESET) { __dsp_reset(); clk_use(dsp_api_ck_handle); dsp_runstat = RUNSTAT_RESET; } preempt_enable(); enable_irq(INT_DSP_MMU); return 0; case OMAP_DSP_IOCTL_UNRESET: disable_irq(INT_DSP_MMU); preempt_disable(); if (dsp_runstat == RUNSTAT_RESET) { clk_unuse(dsp_api_ck_handle); udelay(10); /* to make things stable */ __dsp_unreset(); dsp_runstat = RUNSTAT_RUN; } preempt_enable(); enable_irq(INT_DSP_MMU); return 0; case OMAP_DSP_IOCTL_SETRSTVECT: return dsp_set_rstvect((unsigned long)arg); case OMAP_DSP_IOCTL_IDLE: dsp_idle(); return 0; case OMAP_DSP_IOCTL_MPUI_WORDSWAP_ON: mpui_wordswap_on(); return 0; case OMAP_DSP_IOCTL_MPUI_WORDSWAP_OFF: mpui_wordswap_off(); return 0; case OMAP_DSP_IOCTL_MPUI_BYTESWAP_ON: mpui_byteswap_on(); return 0; case OMAP_DSP_IOCTL_MPUI_BYTESWAP_OFF: mpui_byteswap_off(); return 0; case OMAP_DSP_IOCTL_MBSEND: { struct omap_dsp_mailbox_cmd mb; if (copy_from_user(&mb, (void *)arg, sizeof(mb))) { return -EFAULT; } omap_dsp_mbsend(mb_cmd_h(mb.cmd), mb_cmd_l(mb.cmd), mb.data); return 0; } case OMAP_DSP_IOCTL_SETVAR: { struct omap_dsp_varinfo var; if (copy_from_user(&var, (void *)arg, sizeof(var))) { ret = -EFAULT; break; } ret = omap_dsp_setvar(var.varid, var.val); return 0; } case OMAP_DSP_IOCTL_RUNLEVEL: omap_dsp_runlevel(arg); return 0; case OMAP_DSP_IOCTL_DSPCFG: case OMAP_DSP_IOCTL_DSPUNCFG: case OMAP_DSP_IOCTL_TASKCNT: case OMAP_DSP_IOCTL_REGMEMR: case OMAP_DSP_IOCTL_REGMEMW: case OMAP_DSP_IOCTL_REGIOR: case OMAP_DSP_IOCTL_REGIOW: case OMAP_DSP_IOCTL_GETVAR: break; default: return -ENOIOCTLCMD; } /* * command level 2: commands which need lock */ if ((sem_st = down_interruptible(&ioctl_sem)) < 0) return sem_st; switch (cmd) { case OMAP_DSP_IOCTL_DSPCFG: ret = omap_dsp_dspcfg(); break; case OMAP_DSP_IOCTL_DSPUNCFG: ret = omap_dsp_dspuncfg(); break; case OMAP_DSP_IOCTL_TASKCNT: ret = dsp_task_count(); 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))) { ret = -EFAULT; break; } if ((ret = omap_dsp_regread(OMAP_DSP_MBCMD_REGRW_MEMR, adr, &val)) < 0) break; if (copy_to_user(&u_reg->val, &val, sizeof(short))) { ret = -EFAULT; break; } break; } case OMAP_DSP_IOCTL_REGMEMW: { struct omap_dsp_reginfo reg; if (copy_from_user(®, (void *)arg, sizeof(reg))) { ret = -EFAULT; break; } ret = omap_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))) { ret = -EFAULT; break; } if ((ret = omap_dsp_regread(OMAP_DSP_MBCMD_REGRW_IOR, adr, &val)) < 0) break; if (copy_to_user(&u_reg->val, &val, sizeof(short))) { ret = -EFAULT; break; } break; } case OMAP_DSP_IOCTL_REGIOW: { struct omap_dsp_reginfo reg; if (copy_from_user(®, (void *)arg, sizeof(reg))) { ret = -EFAULT; break; } ret = omap_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; if (copy_from_user(&varid, &u_var->varid, sizeof(char))) { ret = -EFAULT; break; } if ((ret = omap_dsp_getvar(varid, &val)) < 0) break; if (copy_to_user(&u_var->val, &val, sizeof(short))) { ret = -EFAULT; break; } break; } } up(&ioctl_sem); return ret;}static int omap_dsp_ctl_open(struct inode *inode, struct file *file){ omap_dsp_map_update(current); omap_dsp_cur_users_add(current); return 0;}static int omap_dsp_ctl_release(struct inode *inode, struct file *file){ omap_dsp_cur_users_del(current); return 0;}/* * functions called from mailbox1 interrupt routine */void mailbox1_dspcfg(unsigned char cmd_l, unsigned short data){ unsigned char last = cmd_l & 0x80; unsigned char cfgcmd = cmd_l & 0x7f; static unsigned long tmp_ipbuf_sys_da; if (cfgstat == CFGSTAT_ABORT) { /* * We had an error. * surpressing other error messages. */ return; } if ((cfgstat != CFGSTAT_GOING) || (cfg_wait_cmd != MBCMD(DSPCFG))) { printk(KERN_WARNING "mailbox: DSPCFG command received," "but nobody is waiting for it...\n"); return; } switch (cfgcmd) { case OMAP_DSP_MBCMD_DSPCFG_PROTREV: if (data != OMAP_DSP_MBPROT_REVISION) { printk(KERN_ERR "omapdsp: mailbox protocol " "revision check error!\n" " expected=0x%04x, received=0x%04x\n", OMAP_DSP_MBPROT_REVISION, data); goto abort; } break; case OMAP_DSP_MBCMD_DSPCFG_SYSADRH: tmp_ipbuf_sys_da = (unsigned long)data << 16; break; case OMAP_DSP_MBCMD_DSPCFG_SYSADRL: tmp_ipbuf_sys_da |= data; break; default: printk(KERN_ERR "mailbox: Unknown CFG command: " "cmd_l=0x%02x, data=0x%04x\n", cmd_l, data); return; } if (last) { unsigned long badr; unsigned short bln; unsigned short bsz; unsigned short bkeep; volatile unsigned short *buf; /* system IPBUF initialization */ if (tmp_ipbuf_sys_da & 0x1) { printk(KERN_ERR "mailbox: 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 "mailbox: DSPCFG - IPBUF sync failed!\n"); return; } /* * read configuration data on system IPBUF * we must read with 16bit-access */ 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[7])); */ ipbuf_sys_ad = dspword_to_virt(MKLONG(buf[8], buf[9])); sync_seq = dspword_to_virt(MKLONG(buf[10], buf[11])); /* ipbuf_config() should be done in interrupt routine. */ if (omap_dsp_ipbuf_config(bln, bsz, badr, bkeep) < 0) goto abort; ipbuf_sys_da->s = OMAP_DSP_TID_FREE; cfgstat = CFGSTAT_DONE; wake_up_interruptible(&cfg_wait_q); } return;abort: cfgstat = CFGSTAT_ABORT; wake_up_interruptible(&cfg_wait_q);}void mailbox1_regrw(unsigned char cmd_l, unsigned short data){ if (!waitqueue_active(&cfg_wait_q) || (cfg_wait_cmd != MBCMD(REGRW))) { printk(KERN_WARNING "mailbox: REGRW command received," "but nobody is waiting for it...\n"); return; } switch (cmd_l) { case OMAP_DSP_MBCMD_REGRW_DATA: varread_val = data; wake_up_interruptible(&cfg_wait_q); return; default: printk(KERN_ERR "mailbox: Illegal REGRW command: " "cmd_l=0x%02x, data=0x%04x\n", cmd_l, data); return; }}void mailbox1_getvar(unsigned char varid, unsigned short data){ if (!waitqueue_active(&cfg_wait_q) || (cfg_wait_cmd != MBCMD(GETVAR))) { printk(KERN_WARNING "mailbox: GETVAR command received," "but nobody is waiting for it...\n"); return; } varread_val = data; wake_up_interruptible(&cfg_wait_q); return;}#ifdef CONFIG_PROC_FS/* * proc entry */static int omap_dsp_proc_icrmask_read(char *page, char **start, off_t off, int count, int *eof, void *data){ int len;#if 0 if (omap_dsp_is_config_done()) { int sem_st; int ret; unsigned short val; if ((sem_st = down_interruptible(&ioctl_sem)) < 0) return sem_st; ret = omap_dsp_getvar(OMAP_DSP_MBCMD_VARID_ICRMASK, &val); up(&ioctl_sem); if (ret < 0) return ret; if (val != dsp_icrmask) { printk(KERN_WARNING "omapdsp: " "icrmask value is inconsistent!\n"); } }#endif len = sprintf(page, "%04x (hex)\n", dsp_icrmask); return len;}static int omap_dsp_proc_icrmask_write(struct file *file, const char *buffer, unsigned long count, void *data){ int len; char tmp[16]; int ret; if (!capable(CAP_SYS_ADMIN)) return -EPERM; len = (count > 15) ? 15 : count; if (copy_from_user(tmp, buffer, len)) return -EFAULT; tmp[len] = '\0'; dsp_icrmask = simple_strtol(tmp, NULL, 16); if (omap_dsp_is_config_done()) { ret = omap_dsp_setvar(OMAP_DSP_MBCMD_VARID_ICRMASK, dsp_icrmask); if (ret < 0) return ret; } return len;}static void __init omap_dsp_ctl_create_proc(void){ struct proc_dir_entry *ent; /* icrmask */ ent = create_proc_entry("icrmask", S_IFREG | S_IWUSR | S_IRUGO, procdir_dsp); if (ent == NULL) { printk(KERN_ERR "omapdsp: " "failed to register proc device: icrmask\n"); } ent->read_proc = omap_dsp_proc_icrmask_read; ent->write_proc = omap_dsp_proc_icrmask_write;}static void omap_dsp_ctl_remove_proc(void){ remove_proc_entry("icrmask", procdir_dsp);}#endif /* CONFIG_PROC_FS */struct file_operations omap_dsp_ctl_fops = { .owner = THIS_MODULE, .ioctl = omap_dsp_ctl_ioctl, .open = omap_dsp_ctl_open, .release = omap_dsp_ctl_release,};void __init omap_dsp_ctl_init(void){ init_MUTEX(&ioctl_sem); init_waitqueue_head(&cfg_wait_q);#ifdef CONFIG_PROC_FS omap_dsp_ctl_create_proc();#endif}void omap_dsp_ctl_exit(void){#ifdef CONFIG_PROC_FS omap_dsp_ctl_remove_proc();#endif}
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?