⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 pwm5272.c

📁 uclinux下的驱动
💻 C
字号:
/*****************************************************************************/

/*
 *      pwm5272.c -- Driver for MCF5272 PWM audio,using timer0 to produce sample
 *	rate interrupt.It works well if sample rate is less then 24khz when play
 *	mp3 music.Note that only one PWM channel(PWM0) is used!
 *      By floatingg(木头),2003/6/`8
 *
 *      Derived from:
 *
 *	dac0800.c -- driver for NETtel DMA audio
 *
 *	(C) Copyright 1999, greg Ungerer (gerg@moreton.com.au)
 *	
 *	Based loosely on lcddma.c which is:
 *
 *	Copyright (C) 1999 Rob Scott (rscott@mtrob.fdns.net)
 */

/*****************************************************************************/
#include <linux/malloc.h>
#include <linux/signal.h>
#include <linux/sched.h>
#include <asm/ptrace.h>
#include <linux/config.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/mm.h>
#include <linux/wait.h>
#include <asm/irq.h>
#include <asm/param.h>
#include <asm/dma.h>
#include <asm/irq.h>
#include <asm/system.h>
#include <asm/uaccess.h> /* for put_user */
#include <linux/init.h>
#include <asm/coldfire.h>
#include <asm/mcftimer.h>
#include <linux/sound.h>
#include <linux/soundcard.h>

#ifdef MODULE
#include <linux/module.h>
#else
#define MOD_INC_USE_COUNT
#define MOD_DEC_USE_COUNT
#endif

#include "pwm5272.h"

/*****************************************************************************/

#define	PWM_MAJOR	228

#define	PWM_BUFSIZE	(16 * 1024)

#define PWM_BUSY	1
#define PWM_OPEN	2

//#define DEBUG_STARTPWM
//#define DEBUG_PWM_ISR
//#define DEBUG_PWM_BUF
//#define DEBUG_PWM_OPEN

/*****************************************************************************/

/*
 *	Double buffers for storing audio samples.
 */
unsigned char	pwm_buf0[PWM_BUFSIZE];
unsigned char	pwm_buf1[PWM_BUFSIZE];
unsigned int	pwm_bufsize;
unsigned int	pwm_buf0cnt;
unsigned int	pwm_buf1cnt;

unsigned int	pwm_flags;
unsigned int	pwm_curbuf;
unsigned int 	pwm_bufbusy;
unsigned long playbits=8,playsterero=0;

#define	PWM_BUF0		0x1
#define	PWM_BUF1		0x2

#define	PWM_ALLBUFS		0x3

wait_queue_head_t pwm_waitchan;/*the queue is used to implement flow control*/
static unsigned char *pwm_pos;
static unsigned char *pwm_end;

//#define PWMC_CLKSEL	(0<<PWMC_CLKSEL_SHIFT) /* /2 32KHz rate */
//#define PWMC_PRESCALER	(0<<PWMC_PRESCALER_SHIFT) /* voice mode */
//#define PWMC_REPEAT	(2<<PWMC_REPEAT_SHIFT) /* repeat 3 times */

void timer_uninst(void)
{
 	volatile unsigned short *timerp=(volatile unsigned short *) (MCF_MBAR + MCFTIMER_BASE1);
 	volatile unsigned long  *icrp=(volatile unsigned long *) (MCF_MBAR + MCFSIM_ICR1);
 		
	timerp[MCFTIMER_TMR] = MCFTIMER_TMR_DISABLE;
	*icrp &= 0xffff0fff; /* TMR0 with priority 5 */

}

void timer_init(unsigned long freq)
 {
	volatile unsigned long  *icrp= (volatile unsigned long *) (MCF_MBAR + MCFSIM_ICR1);
	volatile unsigned short *tmr_addr = (volatile unsigned short *)(MCF_MBAR + MCFTIMER_BASE1);
	volatile unsigned short *trr_addr = (volatile unsigned short *)(MCF_MBAR + MCFTIMER_BASE1+4);
 	/* Set up TIMER 0  */
//	 *trm_addr = 0xff3a;//lock input by 256/toggle ouput/interrupt on ref value/bus clock by 16
 //	 *trr_addr =  20000; //set period of interrupts
//	 *tcn_addr = 0x0000; //clear timer counter
//	 *ter_addr = 0x0003; //clear timer event register
//	 *tmr_addr |=0x0001; //enable timer 
	 *tmr_addr =  0x0000;//MCFTIMER_TMR_DISABLE;
	 *trr_addr = 66000000/freq;  // SAMPLE_RATE(freq); /*采样频率8kHz--32*/
	 //MCFTIMER_TMR_PREMASK
	 *tmr_addr = MCFTIMER_TMR_ENORI | MCFTIMER_TMR_CLK1 | \
		     MCFTIMER_TMR_RESTART | MCFTIMER_TMR_ENABLE;
//	 *tmr_addr =  TMR_INIT; //0xff5b; //TMR_INIT;
//	timerp[MCFTIMER_TER] = MCFTIMER_TER_CAP | MCFTIMER_TER_REF;
//	 enable_irq(69);
//	 *icrp = (*icrp) &0xffff0fff;
	 *icrp |= 0x0000d000; /* TMR0 with priority 5 */
// 	TRR = SAMPLE_RATE(5); /*采样频率5Hz*/
// 	TMR = TMR_INIT; /*TMR_EN | TMR_CLK_SEL | TMR_FRR | TMR_ORI | TMR_CE | TMR_PS*/	
 	
}
			

/*****************************************************************************/

/*****************************************************************************/

void pwm_startpwm(unsigned char *buf, unsigned int size)
{

#ifdef DEBUG_STARTPWM
	printk("PWM: starting curbuf=%x\n", pwm_curbuf);
#endif
	/* Set the vars */
	pwm_pos = buf;
	pwm_end = buf + size;

	/* mark the PWM BUSY */
	pwm_flags |= PWM_BUSY;

	/* Turn on the hardware */
	PWMC = PWMC_INIT;

}

/*****************************************************************************/

/*
 *	If there is a buffer ready to go then start sending it
 */

void pwm_startbuf(void)
{
	unsigned char	*bufp;
	unsigned int	flag, cnt;

	/* If already sending nothing to do */
	if (pwm_curbuf)
		return;

	/* Simple double buffered arrangement... */
	if (pwm_bufbusy & PWM_BUF0) {
		bufp = pwm_buf0;
		cnt = pwm_buf0cnt;
		flag = PWM_BUF0;
	} else if (pwm_bufbusy & PWM_BUF1) {
		bufp = pwm_buf1;
		cnt = pwm_buf1cnt;
		flag = PWM_BUF1;
	} else {
		return;
	}

	pwm_curbuf = flag;
	pwm_startpwm(bufp, cnt);
}



/*是否在写入缓冲前进行精度转换,检查算法,应该在buffer只有有空就写入*/
/*****************************************************************************/
ssize_t pwm_write(struct file *filp,const char *buf,size_t count,loff_t *f_pos)
{
	unsigned char	*pwmbufp;
	unsigned int	size, bufflag,i;
	unsigned long	flags;

#ifdef DEBUG_PWM_WRITE
	printk("%s(%d): pwm_write(buf=%x,count=%d)\n", __FILE__, __LINE__,
		(int) buf, count);
#endif
	/*pwm-dac is 8 bit resolution.so translate 16bit to 8 bit to play
	 *,and translate sterero to mono channel*/
 
	if(playsterero){
		if(playbits==8)
		{
			for(i=1;i<count/2;i++)
			{
				buf[i] = buf[i*2];
			}
		}
		else
		{/*16bit*/
			 for(i=0;i<count/4;i++)
			 {
				 buf[i] = (buf[i*4 +1])^0x80;
				 
 			 }
		}
		
	}
	else /*mono channel*/
	{
		if(playbits==16)
		{
			 for(i=0;i<count/2;i++)
			 {
				 buf[i] = (buf[i*2 + 1])^0x80;
				 
			  }
		}
		else
			 i = count;
		/*do not need to tranlate when playbits is equal to 8*/
		
	}
	count =i;
	size = (count > pwm_bufsize) ? pwm_bufsize : count;
		
	/* Check for empty buffer, if not wait for one. */
	save_flags(flags); cli();
	while ((pwm_bufbusy & PWM_ALLBUFS) == PWM_ALLBUFS) {
   	/*	if (filp->f_flags &O_NONBLOCK)
      		{
       			return -EAGAIN;
      		}*/
		interruptible_sleep_on(&pwm_waitchan);
		if (signal_pending(current))/*a signal arrived */
			return -ERESTARTSYS;/*tell the fs layer to handle it */
		/*else,loop */
		
					
	}

	if (pwm_bufbusy & PWM_BUF0) {
		bufflag = PWM_BUF1;
		pwmbufp = &pwm_buf1[0];
		pwm_buf1cnt = size;
	} else {
		bufflag = PWM_BUF0;
		pwmbufp = &pwm_buf0[0];
		pwm_buf0cnt = size;
	}
	restore_flags(flags);

#ifdef DEBUG_PWM_BUF
	printk("PWM: filling %d\n", bufflag);
#endif
	/* Copy the buffer local and start sending it. */
	copy_from_user(pwmbufp, buf, size);
	save_flags(flags); cli();
	pwm_bufbusy |= bufflag;
	pwm_startbuf();
	restore_flags(flags);
	return(size);
}

/*设置采样频率和精度*/
/*****************************************************************************/

static int pwm_ioctl(struct inode *inode, struct file *filp,
                                unsigned int cmd, unsigned long arg)

{
	int	rc = 0;
	unsigned long    speed;
        

	switch (cmd) {
	case 1:
		printk("PWM: pwm_curbuf=%x pwm_pos=%x\n", pwm_curbuf, pwm_pos);
		break;
	case SNDCTL_DSP_SPEED:
		speed = *((unsigned long *)arg);
		if(speed >24000)
			{
				printk("desired sample rate is %d Hz,which is too fast!\nso ",speed);
				speed = 24000;
			}
		printk("seting sample rate as %d Hz.\n",speed);
		timer_init(speed);
		break;
	case SNDCTL_DSP_STEREO:
		playsterero = *((unsigned long *)arg);
		printk("setting channel num as %d\n",playsterero+1);
		break;
	case SNDCTL_DSP_SAMPLESIZE:
		playbits = *((unsigned long *)arg);
		if(playbits==32)
			playbits = playbits/2;
		printk("setting sample size as %d bits\n",playbits);
		break;	
	default:
		rc = -EINVAL;
		break;
	}

	return(rc);
}

/*****************************************************************************/

void pwm_isr(int irq, void *dev_id, struct pt_regs *regs)
{
	unsigned long	flags;
//	unsigned int	status;
volatile unsigned short *ter_addr = (volatile unsigned short *)(MCF_MBAR +0x210 );
volatile unsigned char *pwwd_addr = (volatile unsigned char *)PWWD_ADDR;
volatile unsigned char *pwcr_addr = (volatile unsigned char *)PWMC_ADDR;

//	*pwcr_addr = PWMC_INIT ;

	/* read status to clear IRQ */
//	status = PWMC;
//	printk("isr servered!\n");
	*ter_addr &= 0x02;	// clear Timer2 event condition
	if (pwm_curbuf) {

	//	*((volatile unsigned char *) PWWD) = *(pwm_pos++);
			
		if (pwm_pos < pwm_end) 
			//*((volatile unsigned char *) PWWD) = *(pwm_pos++);
			*pwwd_addr= *(pwm_pos++);

		/* Handle end-of-buffer */
		if (pwm_pos >= pwm_end) {
#ifdef DEBUG_PWM_ISR
			printk("PWM: buffer %x done\n", pwm_curbuf);
#endif
			save_flags(flags); cli();

			/* Mark buffer no longer in use */
			pwm_bufbusy &= ~pwm_curbuf;
			pwm_curbuf = 0;

			/* Start other waiting buffers! */
			pwm_startbuf();

			if (!pwm_curbuf) {
				pwm_flags &= ~PWM_BUSY;
//				PWMC = PWMC_RESET;
			}

			/* Wake up writers */
			wake_up_interruptible(&pwm_waitchan);

			restore_flags(flags);

		}
	}
	else {
		pwm_flags &= ~PWM_BUSY;
//		PWMC = PWMC_RESET;
	}

}


/*****************************************************************************/
static int pwm_open(struct inode *inode,struct file *filp)
{
#ifdef DEBUG_PWM_OPEN
	printk("%s(%d): pwm_open() flags=%d\n", __FILE__, __LINE__, pwm_flags);
#endif

	if (pwm_flags)
		return -EBUSY;
	pwm_flags = 0;	
	pwm_flags |= PWM_OPEN;
		
	PWMC = PWMC_RESET;
	pwm_bufsize = PWM_BUFSIZE;
        pwm_curbuf = 0;
        pwm_bufbusy = 0;
	MOD_INC_USE_COUNT;
	/*在这里开timer中断*/
	init_waitqueue_head(&pwm_waitchan);
	timer_init(8000);//8khz
	//enable_irq(PWM_IRQ_NUM);
        if(request_irq(69, pwm_isr, SA_INTERRUPT, "pwm timer", NULL))
        {
                 printk ("PWM: IRQ %d already in use\n", PWM_IRQ_NUM);
                 return -EINVAL;
        }

 	return(0);
}

/*****************************************************************************/

static int pwm_release(struct inode *inode, struct file *filp)
{
#ifdef DEBUG_PWM_RELEASE
	printk("%s(%d): pwm_close()\n", __FILE__, __LINE__);
#endif
	
	pwm_flags = 0;
	PWMC &= ~PWMC_EN;
	MOD_DEC_USE_COUNT;
	timer_uninst();
	free_irq(69,NULL);
	
	return(0);
}

/*****************************************************************************/

/*
 *	Exported file operations structure for driver...
 */
static struct file_operations pwm_fops =
       {
      /*owner:          THIS_MODULE,*/
      /*read:           pwm_read,*/
        write:          pwm_write,
        ioctl:          pwm_ioctl,
        open:           pwm_open,
        release:        pwm_release,};


/*****************************************************************************/
 
 int __init pwm_init(void)
{
	int			result;

	/* Register pwm as character device */
	result = register_chrdev(PWM_MAJOR, "pwm", &pwm_fops);
	if (result < 0) {
		printk(KERN_WARNING "PWM: can't get major %d\n",
			PWM_MAJOR);
		 return result;
	}

	printk ("PWM: Copyright (C) 2003, Yan Hiyong "
		"(yanhy99@mails.tsinghua.edu.cn)\n");

	/* reset the hardware */
	PWMC = PWMC_RESET;
 
	pwm_bufsize = PWM_BUFSIZE;
	pwm_flags = 0;
	pwm_curbuf = 0;
	pwm_bufbusy = 0;
	return(0);
}
/*在退出release前应该disable pwm-timer3,注意初始化之前应该初始化timer3-TMR3&TRR3*/
void __exit pwm_exit(void)
{
 int unreg_result=0;

 PWMC &= ~PWMC_EN;
 /* Unregister the device */
 unreg_result = unregister_chrdev(PWM_MAJOR, "pwm");
 /* If there's an error, report it */
 if (unreg_result < 0) {
 	printk("Error in unregister_chrdev: %d\n", unreg_result);
 	return;
 }
 printk("pwm audio driver succesfully uninstalled.\n");
 return;
	
}
/*****************************************************************************/
module_init(pwm_init);      /*    so driver can compile directly into kernel */
module_exit(pwm_exit);      /*  so driver can compile directly into kernel */

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -