can anyone help me to test my ac97 driver

silicom silicom at 163.com
Fri Jul 13 15:22:54 EST 2007


 Hi
    I have a simple oss ac97 playback driver for xilinx ml403 and linux2.6.17 kernel,but when I test it with a *.wav file with sample rate 44.1k, there is much noisy, and I want to know whether there's problem with my ml403 board or ac97 driver,could anyone be kind to help me test it on your board or point out my problem?
 
thanks
below is my code "xilinx_ac97_adapter.c"
#include <linux/module.h>
#include <linux/version.h>
#include <linux/string.h>
#include <linux/ctype.h>
#include <linux/ioport.h>
#include <linux/sched.h>
#include <linux/delay.h>
#include <linux/sound.h>
#include <linux/slab.h>
#include <linux/soundcard.h>
#include <asm/io.h>
#include <asm/dma.h>
#include <linux/init.h>
#include <linux/poll.h>
#include <linux/spinlock.h>
#include <linux/smp_lock.h>
#include <linux/sound.h>
#include <linux/ac97_codec.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/signal.h>
//#include <linux/wrapper.h>
#include <asm/uaccess.h>
#include <asm/hardirq.h>
#include "xparameters.h"
#include "xac97_l.h"
#include "xio.h"
#define XILINX_AC97
#define DRIVER_VERSION "1.00a"
#define AC97_BASEADDR XPAR_OPB_AC97_CONTROLLER_REF_0_BASEADDR
#define AC97_HIGHADDR XPAR_OPB_AC97_CONTROLLER_REF_0_HIGHADDR
/* AC97 codec initialisation. */
static struct ac97_codec *xilinx_ac97_codec = NULL;
static int dev_audio = -1;
#define BUFFER_SIZE 32768
#define XILINX_AC97_PLAYBACK_INTERRUPT 7
struct xilinx_ac97_state {
    struct semaphore    open_sem;     /* Device access */
    struct semaphore    sem;             /* File access */
    spinlock_t        lock;        /* State */
    spinlock_t        ac97_lock;
    struct ac97_codec    *ac97;
    char            *buffer;
    int            multichannel;
    int            dsp;         /* OSS handle */
    int                     trigger;    /* mmap I/O trigger */
    int            halfFull;
    //    struct forte_channel    play;
    //    struct forte_channel    rec;
};
static struct xilinx_ac97_state *state;
static int halfEmpty;
static DECLARE_WAIT_QUEUE_HEAD(ac97_queue);
static int emptyTime;
static u16 xilinx_ac97_get(struct ac97_codec *dev, u8 reg);
static void xilinx_ac97_set(struct ac97_codec *dev, u8 reg, u16 data);
static void xilinx_ac97_codec_wait(struct ac97_codec *dev);
static irqreturn_t xilinx_ac97_interrupt(int irq, void * dev_id, struct pt_regs *regs)
{
    disable_irq(XILINX_AC97_PLAYBACK_INTERRUPT);
    halfEmpty = 1;
    wake_up_interruptible(&ac97_queue);
    return IRQ_HANDLED;  
}
static u16 xilinx_ac97_get(struct ac97_codec *dev, u8 reg)
{
        return XAC97_ReadReg((u32)dev->private_data, reg);
}
static void xilinx_ac97_set(struct ac97_codec *dev, u8 reg, u16 data) {
        XAC97_WriteReg((u32)dev->private_data, reg, data);
}
static void xilinx_ac97_codec_wait(struct ac97_codec *dev) {
        XAC97_AwaitCodecReady((u32)dev->private_data);
}
/* OSS /dev/mixer file operation methods */
static int xilinx_ac97_open_mixdev(struct inode *inode, struct file *file)
{
    int minor = MINOR(inode->i_rdev);
    if (xilinx_ac97_codec && xilinx_ac97_codec->dev_mixer == minor) {
        file->private_data = xilinx_ac97_codec;
        return 0;
    }
    return -ENODEV;
}
static int xilinx_ac97_ioctl_mixdev(struct inode *inode,
                    struct file *file,
                    unsigned int cmd, unsigned long arg)
{
    struct ac97_codec *codec = (struct ac97_codec *) file->private_data;
    return codec->mixer_ioctl(codec, cmd, arg);
}
static /*const */ struct file_operations xilinx_ac97_mixer_fops = {
    owner:THIS_MODULE,
    llseek:no_llseek,
    ioctl:xilinx_ac97_ioctl_mixdev,
    open:xilinx_ac97_open_mixdev,
};
static int xilinx_ac97_open(struct inode *inode, struct file *file)
{
    u32 baseAddress;
    if (!state)
        BUG();
    if (!xilinx_ac97_codec)
        BUG();
  
    baseAddress = (u32)xilinx_ac97_codec->private_data;
    if (file->f_flags & O_NONBLOCK) {
        if (down_trylock (&state->open_sem)) {
            printk ("%s: returning -EAGAIN\n", __FUNCTION__);
            return -EAGAIN;
        }
    }
    else {
        if (down_interruptible (&state->open_sem)) {
            printk ("%s: returning -ERESTARTSYS\n", __FUNCTION__);
            return -ERESTARTSYS;
        }
    }
    file->private_data = state;
    printk ("%s: dsp opened by %d\n", __FUNCTION__, current->pid);
    state->buffer = (char *)kmalloc(BUFFER_SIZE, GFP_KERNEL);
    /** Reset AC97 **/                     //added by myself
    XAC97_WriteReg(baseAddress, AC97_Reset, 0);
    XAC97_Delay(1000);
  
    /** Wait for the ready signal **/
    XAC97_AwaitCodecReady(baseAddress);
    XAC97_WriteReg(baseAddress, AC97_PCM_DAC_Rate0, AC97_PCM_RATE_48000_HZ);
    XAC97_WriteReg(baseAddress, AC97_PCM_DAC_Rate1, AC97_PCM_RATE_48000_HZ);
    /** Clear FIFOs **/
    XAC97_ClearFifos(baseAddress);      
    XAC97_WriteReg(baseAddress, AC97_GeneralPurpose, 0x0000);
    XAC97_WriteReg(baseAddress, AC97_SerialConfig, 0x7000);
    /** Enable VRA Mode **/
    XAC97_WriteReg(baseAddress, AC97_ExtendedAudioStat, AC97_EXTENDED_AUDIO_CONTROL_VRA);
  
    if (file->f_mode & FMODE_WRITE) {
        printk(KERN_INFO "setting volume\n");
        /** Play Volume Settings **/
        XAC97_WriteReg(baseAddress, AC97_MasterVol, AC97_VOL_MAX);
        XAC97_WriteReg(baseAddress, AC97_AuxOutVol, AC97_VOL_MAX);
        XAC97_WriteReg(baseAddress, AC97_MasterVolMono, AC97_VOL_MAX);
        XAC97_WriteReg(baseAddress, AC97_PCBeepVol, AC97_VOL_MAX);
        XAC97_WriteReg(baseAddress, AC97_PCMOutVol, AC97_VOL_MAX);
         }
    //firstly disable interrupt
    disable_irq(XILINX_AC97_PLAYBACK_INTERRUPT);
    if(request_irq(XILINX_AC97_PLAYBACK_INTERRUPT,xilinx_ac97_interrupt,SA_INTERRUPT,"ac97",NULL))
    {
        printk(KERN_ALERT "cannot register interrupt handler\n");
    }  
    return 0;
}
static ssize_t
xilinx_ac97_write (struct file *file, const char *buffer, size_t bytes,
         loff_t *ppos)
{
    struct xilinx_ac97_state *state;
    unsigned int i = 0;// sz = 0;
    u32 baseAddress;
    char* sound_ptr;
    size_t words;
    ssize_t ret;
    int j;
     if (!access_ok (VERIFY_READ, buffer, bytes))
         return -EFAULT;
    state = (struct xilinx_ac97_state *) file->private_data;
    if (!state)
        BUG();
    if (!xilinx_ac97_codec)
        BUG();
    if (down_interruptible(&state->sem))
        return -ERESTARTSYS;
    baseAddress = (u32)xilinx_ac97_codec->private_data;
    size_t buffer_size =(size_t)BUFFER_SIZE;
    size_t tmp = bytes;
    bytes = min(tmp,buffer_size);
    if(XAC97_isInFIFOEmpty(baseAddress))
    emptyTime++;//there's some time when playback FIFO is empty,I don't know how to fix it
    if (copy_from_user(state->buffer, buffer, bytes)) {
        ret = -EFAULT;
        goto out;
    }
    words = bytes >> 1;
    sound_ptr = (char *)state->buffer;
    i = 0;
    while(i < words) {
        j = *sound_ptr;
        sound_ptr++;
        j |= (*sound_ptr)<<8;
        if(!XAC97_isInFIFOFull(baseAddress))
            XAC97_WriteFifo(baseAddress, j);
        else
        {
            halfEmpty = 0;
            enable_irq(XILINX_AC97_PLAYBACK_INTERRUPT);
            wait_event_interruptible(ac97_queue,halfEmpty != 0);
            XAC97_WriteFifo(baseAddress,j);
        }
        sound_ptr++;
        i++;
        j = 0;
    }
    ret = i << 1;
 out:
    up(&state->sem);
    return ret;
}
  
static ssize_t
xilinx_ac97_read (struct file *file, const char *buffer, size_t bytes,
         loff_t *ppos)
{
    struct xilinx_ac97_state *state;
    unsigned int i = 0;// sz = 0;
    u32 baseAddress;
    u32* sound_ptr;
    size_t words;
    if (ppos != &file->f_pos)
        return -ESPIPE;
    if (!access_ok (VERIFY_WRITE, buffer, bytes))
        return -EFAULT;
    state = (struct xilinx_ac97_state *) file->private_data;
    if (!state)
        BUG();
    if (!xilinx_ac97_codec)
        BUG();
    baseAddress = (u32)xilinx_ac97_codec->private_data;
    // Check if already opened for read?
    // Compute the number of words to transfer.
    words = bytes >> 2;
    sound_ptr = (u32*)buffer;
    i = 0;
    while(i < words && !XAC97_isOutFIFOEmpty(baseAddress)) {
        *sound_ptr = XAC97_mGetOutFifoData(baseAddress);
        sound_ptr++;
        i++;
    }
    // Return the number of bytes transferred.
    return i << 2;
}
         
static int
xilinx_ac97_release (struct inode *inode, struct file *file)
{
    u32 baseAddress;
    kfree(state->buffer);
    baseAddress = (u32)xilinx_ac97_codec->private_data;
    XAC97_ClearFifos(baseAddress);      
    XAC97_SoftReset(baseAddress);
    free_irq(XILINX_AC97_PLAYBACK_INTERRUPT,NULL);
    up (&state->open_sem);
    printk(KERN_ALERT "Empyt time is %d\n",emptyTime);
    emptyTime = 0;
    return 0;
}
static int
xilinx_ac97_ioctl (struct inode *inode, struct file *file, unsigned int cmd,
         unsigned long arg)
{
    int ival=0, rd, wr;//ret, count, rval=0;
    struct xilinx_ac97_state *state;
    u32 baseAddress = (u32)xilinx_ac97_codec->private_data;
    state = (struct xilinx_ac97_state *)file->private_data;  
  
    if (file->f_mode & FMODE_WRITE)
        wr = 1;
    else
        wr = 0;
    if (file->f_mode & FMODE_READ)
        rd = 1;
    else
        rd = 0;
    switch(cmd){
    default:break;
    }
    return -EINVAL;
}
  
static /*const */ struct file_operations xilinx_ac97_audio_fops = {
    owner:THIS_MODULE,
    llseek:no_llseek,
    read:xilinx_ac97_read,
    write:xilinx_ac97_write,
    //    poll:xilinx_ac97_poll,
    ioctl:xilinx_ac97_ioctl,
    //    mmap:xilinx_ac97_mmap,
    open:xilinx_ac97_open,
    release:xilinx_ac97_release,
};
MODULE_AUTHOR("Xilinx");
MODULE_DESCRIPTION("Xilinx AC97 driver");
MODULE_LICENSE("GPL");
static int __init xilinx_ac97_init_module(void)
{
    struct ac97_codec *codec;
        u32 baseAddress;
    printk(KERN_INFO "Xilinx AC97 Audio, version "
           DRIVER_VERSION ", " __TIME__ " " __DATE__ "\n");
        baseAddress = (u32)ioremap(AC97_BASEADDR,AC97_HIGHADDR-AC97_BASEADDR);
        printk(KERN_INFO "XAC97_HardReset\n");
    XAC97_HardReset(baseAddress);
        if ((codec = ac97_alloc_codec()) == NULL)
                return -ENOMEM;
        codec->private_data = (void *)baseAddress;
        codec->id = 0;
        codec->codec_read = xilinx_ac97_get;
        codec->codec_write = xilinx_ac97_set;
        codec->codec_wait = xilinx_ac97_codec_wait;
    if (!ac97_probe_codec(codec)) {
                printk(KERN_ERR "Failed to init Xilinx AC97");
                kfree(codec);
                return -ENODEV;    /* it didn't work */
        }
        XAC97_InitAudio((u32)codec->private_data,0);
    if ((codec->dev_mixer = register_sound_mixer(&xilinx_ac97_mixer_fops, -1)) < 0) {
        printk(KERN_ERR "xilinx_ac97_audio: couldn't register mixer!\n");
        kfree(codec);
        return -ENODEV;
    }
    if ((dev_audio = register_sound_dsp(&xilinx_ac97_audio_fops, -1)) < 0) {
        printk(KERN_ERR "xilinx_ac97_audio: couldn't register DSP device!\n");
        unregister_sound_mixer(xilinx_ac97_codec->dev_mixer);
        kfree(xilinx_ac97_codec);
        return -ENODEV;
    }
    xilinx_ac97_codec = codec;
    state = (struct xilinx_ac97_state *) kmalloc(sizeof(struct xilinx_ac97_state), GFP_KERNEL);
    init_MUTEX(&state->open_sem);
    init_MUTEX(&state->sem);
    return 0;
}
static void __exit xilinx_ac97_cleanup_module(void)
{
    unregister_sound_mixer(xilinx_ac97_codec->dev_mixer);
    ac97_release_codec(xilinx_ac97_codec);
    xilinx_ac97_codec = NULL;
    unregister_sound_dsp(dev_audio);
    dev_audio = -1;
}
module_init(xilinx_ac97_init_module);
module_exit(xilinx_ac97_cleanup_module);
/*
Local Variables:
c-basic-offset: 8
End:
*/
below is the test file "test_oss.c"
#include <fcntl.h>
#include <stdlib.h>
#include <linux/soundcard.h>
#include <sys/ioctl.h>
#define BUFFER_SIZE 1024
static int audio_fd;
void init_audio_device()
{
    int fmts;
      int init_channels;
      int speed;
      if ( (audio_fd = open("/dev/dsp", O_WRONLY)) == -1)
    {
            perror("open error\n");
            printf("soundcard open error");
            exit(1);
      }
    printf("successfully init soundcard\n\n");
}
int audio_close(void)
{
     if (audio_fd)
    {
        close(audio_fd);
             printf("the device has been closed!\n");
      }    
      return 0;
}
int audio_play(char *buf, int len)
{
    int temp;
    temp = write(audio_fd, buf, len);
    return temp;
}
int main()
{  
    init_audio_device();
    int fd = open("/root/********.wav",O_RDONLY);
    int len = 0;
    char buffer[BUFFER_SIZE];
    int i=0;
    char tempt;
    while(1)
    {
        len = read(fd,buffer,sizeof(buffer));
        if(len == 0)
        {
        printf("reach the end\n");
        break;
        }
        audio_play(buffer,len); 
    }
    audio_close();
    close(fd);
    return 0;
}
 
in the write process,firstly check whether playback FIFO is full,if not, then send PCM data to FIFO;else sleep until FIFO half-empty interrupt,in the interrupt handler,wake up write process,then go on send data to playback FIFO until full. 
  
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://ozlabs.org/pipermail/linuxppc-embedded/attachments/20070713/ebdecf62/attachment.htm 


More information about the Linuxppc-embedded mailing list