分类:
2009-01-07 13:24:33
S
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(数字信号处理器).
/* GPB2—L3MODE; GPB3—L3DATA; GPB4—L3CLOCK*/
static void uda1341_l3_address(u8 data) /*address mode*/
{
int i;
unsigned long flags;
local_irq_save(flags);
/*这部分是根据手册的控制时钟写的*/
s
s
udelay(1);
/*判断8BIT的data是1则S
for (i = 0; i < 8; i++) {
if (data & 0x1) {
s
s
udelay(1);
s
} else {
s
s
udelay(1);
s
}
data >>= 1; /*右移1位*/
}
s
s
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) {
s
s
udelay(1);
s
} else {
s
s
udelay(1);
s
}
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) s
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(s
s
{
audio_buf_t *b = (audio_buf_t *) buf;
up(&b->sem);
wake_up(&b->sem.wait);
}
/*传递DMA的输入数据缓冲*/
static void audio_dmain_done_callback(s
s
{
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);
s
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 = s
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);
s
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 */
s
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;