?? pwm5272.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);
}
/*是否在寫入緩沖前進(jìn)行精度轉(zhuǎn)換,檢查算法,應(yīng)該在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);
}
/*設(shè)置采樣頻率和精度*/
/*****************************************************************************/
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前應(yīng)該disable pwm-timer3,注意初始化之前應(yīng)該初始化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 */
?? 快捷鍵說明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -