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(&reg, (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(&reg, (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 + -
显示快捷键?