Chinaunix首页 | 论坛 | 博客
  • 博客访问: 318087
  • 博文数量: 68
  • 博客积分: 1501
  • 博客等级: 上尉
  • 技术积分: 1010
  • 用 户 组: 普通用户
  • 注册时间: 2008-12-30 09:52
文章分类

全部博文(68)

文章存档

2010年(1)

2009年(67)

我的朋友

分类:

2009-01-07 13:24:33

S3C2410-UDA1341声卡驱动主要用到的资源有IIS音频总线,GPIO,DMA通道.S3C24104通道的DMA控制器,支持存储器到存储器,IO到存储器,存储器到IOIOIO的传输,采用猝发传输模式加快传输速率.

IIS功能描述:

IISDI:串行数据输入线.

IISDO:串行数据输出线.

SCLK:串行数据提供位时钟.

LRCK:切换左右声道数据贞(字段选择)

MCLK:系统提供的同步时钟,又称CDCLK(编解码器时钟)

S3C2410-IIS总线接口有3种数据传输模式(具体见手册)

1.正常模式.

2.DMA模式.

3.传输/接受模式.

  UDA1341和S3C2410的连接方式就是这种模式.

IIS 数据线将通过双通道DMA同时接受和发送音频数据.在这个体系中,为实现全双工,数据传输使用2个DMA通道,与IIS交互的数据经过IIS控制器写入 FIFO寄存器组.当FIFO被添满后,DMA控制器一次性将数据写入预先分配的内存区,UDA1341芯片除了提供IIS接口和麦克风接口,还提供L3 接口控制音量等,IIS总线接口信号线包括位时钟输入SCK,字段选择WS,数据输入DATAI,数据输出DATAO,系统时钟SYSCLK,L3接口由 3个I/O口控制.

IISCON:IIS控制寄存器.(具体看手册).

IISMOD:IIS模式寄存器.

IISPSR:IIS分频寄存器.

音频设备的基础知识:

OSS标准中有2个基础设备:mixer(混音器)和dsp(数字信号处理器).

 

 

 

/*  GPB2L3MODE; GPB3L3DATA; GPB4L3CLOCK*/

 

static void uda1341_l3_address(u8 data)  /*address mode*/

{

int i;

unsigned long flags;

 

local_irq_save(flags);

/*这部分是根据手册的控制时钟写的*/

s3c2410_gpio_setpin(S3C2410_GPB2,0);/*S3C2410_GPB2输出0*/

s3c2410_gpio_setpin(S3C2410_GPB4,1);/*S3C2410_GPB4输出1*/

udelay(1);

/*判断8BITdata1S3C2410——GPB31否则0*/

for (i = 0; i < 8; i++) {

if (data & 0x1) {

s3c2410_gpio_setpin(S3C2410_GPB4,0);

s3c2410_gpio_setpin(S3C2410_GPB3,1);

udelay(1);

s3c2410_gpio_setpin(S3C2410_GPB4,1);

} else {

s3c2410_gpio_setpin(S3C2410_GPB4,0);

s3c2410_gpio_setpin(S3C2410_GPB3,0);

udelay(1);

s3c2410_gpio_setpin(S3C2410_GPB4,1);

}

data >>= 1;  /*右移1*/

}

 

s3c2410_gpio_setpin(S3C2410_GPB2,1);

s3c2410_gpio_setpin(S3C2410_GPB4,1);

local_irq_restore(flags);

}

 

static void uda1341_l3_data(u8 data)  /*data transfer mode*/

{

int i;

unsigned long flags;

 

local_irq_save(flags);

udelay(1);

 

for (i = 0; i < 8; i++) {

if (data & 0x1) {

s3c2410_gpio_setpin(S3C2410_GPB4,0);

s3c2410_gpio_setpin(S3C2410_GPB3,1);

udelay(1);

s3c2410_gpio_setpin(S3C2410_GPB4,1);

} else {

s3c2410_gpio_setpin(S3C2410_GPB4,0);

s3c2410_gpio_setpin(S3C2410_GPB3,0);

udelay(1);

s3c2410_gpio_setpin(S3C2410_GPB4,1);

}

 

data >>= 1;

}

 

local_irq_restore(flags);

}

/*清空音频的DMA缓冲区*/

static void audio_clear_buf(audio_stream_t * s)

{

DPRINTK("audio_clear_buf\n");

 

if(s->dma_ok) s3c2410_dma_ctrl(s->dma_ch, S3C2410_DMAOP_FLUSH);

 

if (s->buffers) {

int frag;

 

for (frag = 0; frag < s->nbfrags; frag++) {

if (!s->buffers[frag].master)

continue;

dma_free_coherent(NULL,

s->buffers[frag].master,

s->buffers[frag].start,

s->buffers[frag].dma_addr);

}

kfree(s->buffers);

s->buffers = NULL;

}

 

s->buf_idx = 0;

s->buf = NULL;

}

/*设置DMA缓冲区*/

static int audio_setup_buf(audio_stream_t * s)

{

int frag;

int dmasize = 0;

char *dmabuf = 0;

dma_addr_t dmaphys = 0;

 

if (s->buffers)

return -EBUSY;

 

s->nbfrags = audio_nbfrags;

s->fragsize = audio_fragsize;

 

s->buffers = (audio_buf_t *)

kmalloc(sizeof(audio_buf_t) * s->nbfrags, GFP_KERNEL);

if (!s->buffers)

goto err;

memset(s->buffers, 0, sizeof(audio_buf_t) * s->nbfrags);

 

for (frag = 0; frag < s->nbfrags; frag++) {

audio_buf_t *b = &s->buffers[frag];

 

if (!dmasize) {

dmasize = (s->nbfrags - frag) * s->fragsize;

do {

dmabuf = dma_alloc_coherent(NULL, dmasize, &dmaphys, GFP_KERNEL|GFP_DMA);

if (!dmabuf)

dmasize -= s->fragsize;

} while (!dmabuf && dmasize);

if (!dmabuf)

goto err;

b->master = dmasize;

}

 

b->start = dmabuf;

b->dma_addr = dmaphys;

sema_init(&b->sem, 1);

DPRINTK("buf %d: start %p dma %d\n", frag, b->start, b->dma_addr);

 

dmabuf += s->fragsize;

dmaphys += s->fragsize;

dmasize -= s->fragsize;

}

 

s->buf_idx = 0;

s->buf = &s->buffers[0];

return 0;

 

err:

printk(AUDIO_NAME ": unable to allocate audio memory\n ");

audio_clear_buf(s);

return -ENOMEM;

}

/*传递DMA的输出数据缓冲*/

static void audio_dmaout_done_callback(s3c2410_dma_chan_t *ch, void *buf, int size,

s3c2410_dma_buffresult_t result)

{

audio_buf_t *b = (audio_buf_t *) buf;

up(&b->sem);

wake_up(&b->sem.wait);

}

/*传递DMA的输入数据缓冲*/

static void audio_dmain_done_callback(s3c2410_dma_chan_t *ch, void *buf, int size,

s3c2410_dma_buffresult_t result)

{

audio_buf_t *b = (audio_buf_t *) buf;

b->size = size;

up(&b->sem);

wake_up(&b->sem.wait);

}

/* using when write */

static int audio_sync(struct file *file)

{

audio_stream_t *s = &output_stream;

audio_buf_t *b = s->buf;

 

DPRINTK("audio_sync\n");

 

if (!s->buffers)

return 0;

 

if (b->size != 0) {

down(&b->sem);

s3c2410_dma_enqueue(s->dma_ch, (void *) b, b->dma_addr, b->size);

b->size = 0;

NEXT_BUF(s, buf);

}

 

b = s->buffers + ((s->nbfrags + s->buf_idx - 1) % s->nbfrags);

if (down_interruptible(&b->sem))

return -EINTR;

up(&b->sem);

 

return 0;

}

 /*从用户态传入的录音*/

static inline int copy_from_user_mono_stereo(char *to, const char *from, int count)

{

u_int *dst = (u_int *)to;

const char *end = from + count;

 

if (access_ok(VERIFY_READ, from, count))

return -EFAULT;

 

if ((int)from & 0x2) {

u_int v;

__get_user(v, (const u_short *)from); from += 2;

*dst++ = v | (v << 16);

}

 

while (from < end-2) {

u_int v, x, y;

__get_user(v, (const u_int *)from); from += 4;

x = v << 16;

x |= x >> 16;

y = v >> 16;

y |= y << 16;

*dst++ = x;

*dst++ = y;

}

 

if (from < end) {

u_int v;

__get_user(v, (const u_short *)from);

*dst = v | (v << 16);

}

 

return 0;

}

static ssize_t smdk2410_audio_write(struct file *file, const char *buffer,

size_t count, loff_t * ppos)

{

    const char *buffer0 = buffer;

    audio_stream_t *s = &output_stream;

    int chunksize, ret = 0;

 

    DPRINTK("audio_write : start count=%d\n", count);

 

    switch (file->f_flags & O_ACCMODE) {

       case O_WRONLY:

       case O_RDWR:

        break;

       default:

           return -EPERM;

    }

 

    if (!s->buffers && audio_setup_buf(s))

       return -ENOMEM;

 

    count &= ~0x03;

 

    while (count > 0) {

       audio_buf_t *b = s->buf;

 

       if (file->f_flags & O_NONBLOCK) {

           ret = -EAGAIN;

           if (down_trylock(&b->sem))

              break;

       } else {

           ret = -ERESTARTSYS;

           if (down_interruptible(&b->sem))

           break;

       }

 

       if (audio_channels == 2) {

           chunksize = s->fragsize - b->size;

           if (chunksize > count)

           chunksize = count;

           DPRINTK("write %d to %d\n", chunksize, s->buf_idx);

           if (copy_from_user(b->start + b->size, buffer, chunksize)) {

              up(&b->sem);

              return -EFAULT;

           }

           b->size += chunksize;

       } else {

           chunksize = (s->fragsize - b->size) >> 1;

 

           if (chunksize > count)

           chunksize = count;

           DPRINTK("write %d to %d\n", chunksize*2, s->buf_idx);

           if (copy_from_user_mono_stereo(b->start + b->size,

buffer, chunksize)) {

           up(&b->sem);

           return -EFAULT;

           }

 

           b->size += chunksize*2;

       }

 

       buffer += chunksize;

       count -= chunksize;

       if (b->size < s->fragsize) {

       up(&b->sem);

       break;

       }

 

       if((ret = s3c2410_dma_enqueue(s->dma_ch, (void *) b, b->dma_addr, b->size))) {

           printk(PFX"dma enqueue failed.\n");

           return ret;

       }

       b->size = 0;

       NEXT_BUF(s, buf);

    }

 

    if ((buffer - buffer0))

    ret = buffer - buffer0;

 

    DPRINTK("audio_write : end count=%d\n\n", ret);

 

    return ret;

}

 

 

static ssize_t smdk2410_audio_read(struct file *file, char *buffer,

size_t count, loff_t * ppos)

{

    const char *buffer0 = buffer;

    audio_stream_t *s = &input_stream;

    int chunksize, ret = 0;

 

    DPRINTK("audio_read: count=%d\n", count);

/*

    if (ppos != &file->f_pos)

    return -ESPIPE;

*/

    if (!s->buffers) {

       int i;

 

       if (audio_setup_buf(s))

           return -ENOMEM;

 

       for (i = 0; i < s->nbfrags; i++) {

           audio_buf_t *b = s->buf;

           down(&b->sem);

           s3c2410_dma_enqueue(s->dma_ch, (void *) b, b->dma_addr, s->fragsize);

           NEXT_BUF(s, buf);

       }

    }

 

    while (count > 0) {

       audio_buf_t *b = s->buf;

 

/* Wait for a buffer to become full */

       if (file->f_flags & O_NONBLOCK) {

           ret = -EAGAIN;

           if (down_trylock(&b->sem))

              break;

       } else {

              ret = -ERESTARTSYS;

              if (down_interruptible(&b->sem))

              break;

       }

 

       chunksize = b->size;

       if (chunksize > count)

           chunksize = count;

       DPRINTK("read %d from %d\n", chunksize, s->buf_idx);

       if (copy_to_user(buffer, b->start + s->fragsize - b->size,

chunksize)) {

           up(&b->sem);

           return -EFAULT;

       }

 

       b->size -= chunksize;

 

       buffer += chunksize;

       count -= chunksize;

       if (b->size > 0) {

           up(&b->sem);

           break;

       }

 

/* Make current buffer available for DMA again */

       s3c2410_dma_enqueue(s->dma_ch, (void *) b, b->dma_addr, s->fragsize);

 

       NEXT_BUF(s, buf);

    }

 

    if ((buffer - buffer0))

    ret = buffer - buffer0;

 

    // DPRINTK("audio_read: return=%d\n", ret);

 

    return ret;

}

 

 /*输入输出缓存的状态监控*/

static unsigned int smdk2410_audio_poll(struct file *file,

struct poll_table_struct *wait)

{

unsigned int mask = 0;

int i;

 

DPRINTK("audio_poll(): mode=%s\n",

(file->f_mode & FMODE_WRITE) ? "w" : "");

 

if (file->f_mode & FMODE_READ) {

if (!input_stream.buffers && audio_setup_buf(&input_stream))

return -ENOMEM;

poll_wait(file, &input_stream.buf->sem.wait, wait);

 

for (i = 0; i < input_stream.nbfrags; i++) {

if (atomic_read(&input_stream.buffers[i].sem.count) > 0)

mask |= POLLIN | POLLWRNORM;

break;

}

}

if (file->f_mode & FMODE_WRITE) {

if (!output_stream.buffers && audio_setup_buf(&output_stream))

return -ENOMEM;

poll_wait(file, &output_stream.buf->sem.wait, wait);

 

for (i = 0; i < output_stream.nbfrags; i++) {

if (atomic_read(&output_stream.buffers[i].sem.count) > 0)

mask |= POLLOUT | POLLWRNORM;

break;

}

}

 

DPRINTK("audio_poll() returned mask of %s\n",

(mask & POLLOUT) ? "w" : "");

 

return mask;

}

 

 

static loff_t smdk2410_audio_llseek(struct file *file, loff_t offset,

int origin)

{

return -ESPIPE;

阅读(1226) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~