- 论坛徽章:
- 0
|
static struct device *this_dev;
typedef struct {
int size; /* buffer size */
char *start; /* point to actual buffer */
dma_addr_t dma_addr; /* physical buffer address */
struct semaphore sem; /* down before touching the buffer */
int master; /* owner for buffer allocation, contain size when true */
int dma_size;
} audio_buf_t;
typedef struct {
audio_buf_t *buffers; /* pointer to audio buffer structures */
audio_buf_t *buf; /* current buffer used by read/write */
u_int buf_idx; /* index for the pointer above */
u_int fragsize; /* fragment i.e. buffer size */
u_int nbfrags; /* nbr of fragments */
// dmach_t dma_ch; /* DMA channel (channel2 for audio) */
int dma_ch;
} audio_stream_t;
static audio_stream_t output_stream;
static audio_stream_t input_stream; /* input */
static u_int rd_buf_tail;
static u_int wr_buf_tail;
static u_int play_dma_running;
#define NEXT_BUF(_s_,_b_) { \
(_s_)->_b_##_idx++; \
(_s_)->_b_##_idx %= (_s_)->nbfrags; \
(_s_)->_b_ = (_s_)->buffers + (_s_)->_b_##_idx; }
static u_int audio_rate;
static int audio_channels;
static int audio_fmt;
static u_int audio_fragsize;
static u_int audio_nbfrags;
static int audio_dev_dsp;
static int audio_dev_mixer;
static int audio_rd_refcount;
static int audio_wr_refcount;
#define audio_active (audio_rd_refcount | audio_wr_refcount)
static int uda1341_volume;
static int uda1341_boost;
static int mixer_igain = 0x4; /* -6db*/
#define L3MOD_HI() __raw_writel(__raw_readl(S3C2410_GPBDAT)|(1<<2), S3C2410_GPBDAT)
#define L3MOD_LOW() __raw_writel(__raw_readl(S3C2410_GPBDAT)&~(1<<2), S3C2410_GPBDAT)
#define L3DAT_HI() __raw_writel(__raw_readl(S3C2410_GPBDAT)|(1<<3), S3C2410_GPBDAT)
#define L3DAT_LOW() __raw_writel(__raw_readl(S3C2410_GPBDAT)&~(1<<3), S3C2410_GPBDAT)
#define L3CLK_HI() __raw_writel(__raw_readl(S3C2410_GPBDAT)|(1<<4), S3C2410_GPBDAT)
#define L3CLK_LOW() __raw_writel(__raw_readl(S3C2410_GPBDAT)&~(1<<4), S3C2410_GPBDAT)
static void uda1341_l3_address(u8 data)
{
int i;
unsigned long flags;
//qsm2007-05-25
#ifdef YLE2440_DEBUG
printk(KERN_ERR "line %d passed!\n",__LINE__);
#endif
local_irq_save(flags);
L3MOD_LOW();//write_gpio_bit(GPIO_L3MODE, 0);
L3DAT_LOW();//write_gpio_bit(GPIO_L3DATA, 0);
L3CLK_HI(); //write_gpio_bit(GPIO_L3CLOCK, 1);
udelay(1);
for (i = 0; i < 8; i++) {
if (data & 0x1) {
L3CLK_LOW();//write_gpio_bit(GPIO_L3CLOCK, 0);
udelay(1);
L3DAT_HI(); //write_gpio_bit(GPIO_L3DATA, 1);
udelay(1);
L3CLK_HI(); //write_gpio_bit(GPIO_L3CLOCK, 1);
udelay(1);
} else {
L3CLK_LOW();//write_gpio_bit(GPIO_L3CLOCK, 0);
udelay(1);
L3DAT_LOW();//write_gpio_bit(GPIO_L3DATA, 0);
udelay(1);
L3CLK_HI(); //write_gpio_bit(GPIO_L3CLOCK, 1);
udelay(1);
}
data >>= 1; /*右移一位*/
}
L3MOD_HI(); //write_gpio_bit(GPIO_L3MODE, 1);
udelay(1);
local_irq_restore(flags);
}
static void uda1341_l3_data(u8 data)
{
int i;
unsigned long flags;
//qsm2007-05-25
#ifdef YLE2440_DEBUG
printk(KERN_ERR "line %d passed!\n",__LINE__);
#endif
local_irq_save(flags);
L3MOD_HI(); //write_gpio_bit(GPIO_L3MODE, 1);
udelay(1);
L3MOD_LOW();//write_gpio_bit(GPIO_L3MODE, 0);
udelay(1);
L3MOD_HI(); //write_gpio_bit(GPIO_L3MODE, 1);
for (i = 0; i < 8; i++) {
if (data & 0x1) {
L3CLK_LOW();//write_gpio_bit(GPIO_L3CLOCK, 0);
udelay(1);
L3DAT_HI(); //write_gpio_bit(GPIO_L3DATA, 1);
udelay(1);
L3CLK_HI(); //write_gpio_bit(GPIO_L3CLOCK, 1);
udelay(1);
} else {
L3CLK_LOW();//write_gpio_bit(GPIO_L3CLOCK, 0);
udelay(1);
L3DAT_LOW();//write_gpio_bit(GPIO_L3DATA, 0);
udelay(1);
L3CLK_HI(); //write_gpio_bit(GPIO_L3CLOCK, 1);
udelay(1);
}
data >>= 1;
}
L3MOD_HI(); //write_gpio_bit(GPIO_L3MODE, 1);
L3MOD_LOW();//write_gpio_bit(GPIO_L3MODE, 0);
udelay(1);
L3MOD_HI(); //write_gpio_bit(GPIO_L3MODE, 1);
local_irq_restore(flags);
}
struct iis_psr_val {
__u32 rate;
__u16 psr;
__u16 fs;
};
/*PLCK/16/fs(384/256)=IISLRCK*/
static struct iis_psr_val iis_psr_para[] = {
{8000, 16, 384},
{11025, 11, 384},
{16000, 8, 384},
{22050, 6, 384},
{24000, 8, 256},
{32000, 4, 384},
{44100, 3, 384},
{48000, 4, 256}
};
static void init_s3c2410_iis_bus_rx(void)
{
//qsm2007-05-25
#ifdef YLE2440_DEBUG
printk(KERN_ERR "line %d passed!\n",__LINE__);
#endif
__raw_writel(0, S3C2410_IISCON);
__raw_writel(0, S3C2410_IISMOD);
__raw_writel(0, S3C2410_IISFCON);
/* 22.05 KHz , 384fs */
__raw_writel(((iis_psr_para[3].psr-1)<<5)|(iis_psr_para[3].psr-1), S3C2410_IISPSR);
__raw_writel(IISCON_RX_DMA /* Transmit DMA service request */
|IISCON_TX_IDLE /* Receive Channel idle */
|IISCON_PRESCALE, /* IIS Prescaler Enable */
S3C2410_IISCON);
__raw_writel(IISMOD_SEL_MA /* Master mode */
| IISMOD_SEL_RX /* Transmit */
| IISMOD_CH_RIGHT /* Low for left channel */
| IISMOD_FMT_MSB /* MSB-justified format */
| IISMOD_BIT_16 /* Serial data bit/channel is 16 bit */
| IISMOD_FREQ_384 /* Master clock freq = 384 fs */
| IISMOD_SFREQ_32, /* 32 fs */
S3C2410_IISMOD);
__raw_writel(IISFCON_RX_DMA /* Transmit FIFO access mode: DMA */
| IISFCON_RX_EN, /* Transmit FIFO enable */
S3C2410_IISFCON);
__raw_writel(__raw_readl(S3C2410_IISCON)|IISCON_EN, S3C2410_IISCON); /* IIS enable(start) */
}
static void init_s3c2410_iis_bus_tx(void)
{
//qsm21cn2007-05-25
//printk(KERN_ERR "line %d passed!\n",__LINE__);
__raw_writel(0, S3C2410_IISCON);
printk(KERN_INFO "have done __raw_ writel() about S3C2410_IISCON-->(VIRTUAL).\n");
printk(KERN_INFO "S3C2410_IISCON=%08x.\n",S3C2410_IISCON);
//printk(KERN_ERR "line %d passed!\n",__LINE__);
__raw_writel(0, S3C2410_IISMOD);
printk(KERN_ERR "S3C2410_IISMOD=%08x.\n",S3C2410_IISMOD);
//printk(KERN_ERR "line %d passed!\n",__LINE__);
__raw_writel(0, S3C2410_IISFCON);
printk(KERN_ERR "S3C2410_IISFCON=%08x.\n",S3C2410_IISFCON);
//printk(KERN_ERR "line %d passed!\n",__LINE__);
/* 22.05 KHz , 384fs */
__raw_writel(((iis_psr_para[3].psr-1)<<5)|(iis_psr_para[3].psr-1), S3C2410_IISPSR);
//printk(KERN_ERR "line %d passed!\n",__LINE__);
__raw_writel(IISCON_TX_DMA /* Transmit DMA service request */
|IISCON_RX_IDLE /* Receive Channel idle */
|IISCON_PRESCALE, /* IIS Prescaler Enable */
S3C2410_IISCON);
//printk(KERN_ERR "line %d passed!\n",__LINE__);
__raw_writel(IISMOD_SEL_MA /* Master mode */
| IISMOD_SEL_TX /* Transmit */
| IISMOD_CH_RIGHT /* Low for left channel */
| IISMOD_FMT_MSB /* MSB-justified format */
| IISMOD_BIT_16 /* Serial data bit/channel is 16 bit */
| IISMOD_FREQ_384 /* Master clock freq = 384 fs */
| IISMOD_SFREQ_32, /* 32 fs */
S3C2410_IISMOD);
//printk(KERN_ERR "line %d passed!\n",__LINE__);
__raw_writel(IISFCON_TX_DMA /* Transmit FIFO access mode: DMA */
| IISFCON_TX_EN, /* Transmit FIFO enable */
S3C2410_IISFCON);
//printk(KERN_ERR "line %d passed!\n",__LINE__);
__raw_writel(__raw_readl(S3C2410_IISCON)|IISCON_EN, S3C2410_IISCON); /* IIS enable(start) */
//printk(KERN_ERR "line %d passed!\n",__LINE__);
}
static void audio_clear_buf(audio_stream_t * s)
{
DPRINTK("audio_clear_buf\n");
//printk(KERN_ERR "line %d passed!\n",__LINE__);
// s3c2410_dma_flush_all(s->dma_ch);
if (s->buffers) {
int frag;
for (frag = 0; frag < s->nbfrags; frag++) {
if (!s->buffers[frag].master) //master = size
continue;
dma_free_writecombine(this_dev, 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;
}
static int audio_setup_buf(audio_stream_t * s)
{
int frag;
int dmasize = 0;
char *dmabuf = 0;
dma_addr_t dmaphys = 0;
//qsm21cn2007-05-25
//printk(KERN_ERR "line %d passed!\n",__LINE__);
if (s->buffers)
{
//printk(KERN_ERR "line %d passed!\n",__LINE__);
return -EBUSY;
}
wr_buf_tail = rd_buf_tail = play_dma_running = 0;
//printk(KERN_ERR "line %d passed!\n",__LINE__);
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)
{
//printk(KERN_ERR "line %d passed!\n",__LINE__);
goto err;
}
memset(s->buffers, 0, sizeof(audio_buf_t) * s->nbfrags);
//printk(KERN_ERR "line %d passed!\n",__LINE__);
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_writecombine(dev, dmasize, &dmaphys, GFP_KERNEL),
if (!dmabuf)
dmasize -= s->fragsize;
} while (!dmabuf && dmasize);
if (!dmabuf)
goto err;
b->master = dmasize;
}*/
dmabuf = dma_alloc_writecombine(this_dev, s->fragsize, &dmaphys, GFP_KERNEL);
if(!dmabuf) {
//printk("Unable to get DMA buffer\n");
{
//printk(KERN_ERR "line %d passed!\n",__LINE__);
goto err;
}
}
b->master = s->fragsize;
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);
//printk(KERN_ERR "line %d passed!\n",__LINE__);
dmabuf += s->fragsize;
dmaphys += s->fragsize;
dmasize -= s->fragsize;
}
s->buf_idx = 0;
s->buf = &s->buffers[0];
return 0;
err:
//printk(KERN_ERR "line %d passed!\n",__LINE__);
printk(AUDIO_NAME ": unable to allocate audio memory\n ");
audio_clear_buf(s);
return -ENOMEM;
}
static irqreturn_t iisin_dma_done_handler(int irq, void *dev_id, struct pt_regs *regs)
{
//qsm21cn2007-05-25
//printk(KERN_ERR "line %d passed!\n",__LINE__);
// printk(KERN_DEBUG"%d\n", input_stream.buf_idx);
audio_stream_t *s = &input_stream;
if(s->buffers) {
audio_buf_t *b = s->buffers+rd_buf_tail;
rd_buf_tail++;
if(rd_buf_tail>=s->nbfrags)
rd_buf_tail = 0;
__raw_writel(s->buffers[rd_buf_tail].dma_addr, DIDST1);
__raw_writel(2, DMTRIG1);
if(s->buf_idx==rd_buf_tail) {
s->buf_idx++;
if(s->buf_idx>=s->nbfrags)
s->buf_idx = 0;
s->buf = s->buffers+s->buf_idx;
}
if(b->size==0)
up(&(b->sem));
b->size = s->fragsize;
}
return IRQ_HANDLED;
}
static irqreturn_t iisout_dma_done_handler(int irq, void *dev_id, struct pt_regs *regs)
{
audio_stream_t *s = &output_stream;
//printk(KERN_ERR "line %d passed! enter iisout_dma_done_handler()\n",__LINE__);
if(s->buffers) {
audio_buf_t *b = s->buffers+wr_buf_tail;
wr_buf_tail++;
if(wr_buf_tail>=s->nbfrags)
wr_buf_tail = 0;
up(&b->sem);
// wake_up(&b->sem.wait);
b = s->buffers+wr_buf_tail;
if(b!=s->buf) {
__raw_writel(b->dma_addr, DISRC2); //DMA src memory
// __raw_writel(0, DISRCC2); //AHB, address increase
// __raw_writel(0x55000010, DIDST2); //IISFIFO phy address
// __raw_writel(3, DIDSTC2); //APB, address fixed
__raw_writel(0xa0d00000|(b->dma_size>>1), DCON2); //set mode and length
__raw_writel(2, DMTRIG2); //set channel on
b->dma_size = 0;
} else
play_dma_running = 0;
}
return IRQ_HANDLED;
}
/* using when write */
static int audio_sync(struct file *file)
{
//printk(KERN_ERR "line %d passed!\n",__LINE__);
audio_stream_t *s = &output_stream;
audio_buf_t *b = s->buf;
unsigned long flags;
DPRINTK("audio_sync\n");
if (!s->buffers)
return 0;
if (b->size != 0) {
down(&b->sem);
//s3c2410_dma_queue_buffer(s->dma_ch, (void *) b,
// b->dma_addr, b->size, DMA_BUF_WR);
local_irq_save(flags);
if(!play_dma_running) {
__raw_writel(b->dma_addr, DISRC2); //DMA src memory
__raw_writel(0, DISRCC2); //AHB, address increase
//__raw_writel(S3C2410_IISFIFO, DIDST2); //IISFIFO address
__raw_writel(0x55000010, DIDST2); //IISFIFO phy address
__raw_writel(3, DIDSTC2); //APB, address fixed
__raw_writel(0xa0d00000|(b->size>>1), DCON2); //set mode and length
__raw_writel(2, DMTRIG2); //set channel on
} else
b->dma_size = b->size;
b->size = 0;
NEXT_BUF(s, buf);
local_irq_restore(flags);
}
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;
// printk(KERN_ERR "line %d passed!\n",__LINE__);
if (verify_area(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;
unsigned long flags;
//qsm2007-05-25
// printk(KERN_ERR "line %d passed!\n",__LINE__);
DPRINTK("audio_write : start count=%d\n", count);
switch (file->f_flags & O_ACCMODE) {
case O_WRONLY:
case O_RDWR:
//printk(KERN_ERR "line %d passed!\n",__LINE__);
break;
default:
//printk(KERN_ERR "line %d passed!\n",__LINE__);
return -EPERM;
}
if (!s->buffers && audio_setup_buf(s))
{
//printk(KERN_ERR "line %d passed!\n",__LINE__);
return -ENOMEM;
}
count &= ~0x03;
while (count > 0) {
//printk(KERN_ERR "line %d passed!\n",__LINE__);
audio_buf_t *b = s->buf;
if (file->f_flags & O_NONBLOCK) {
//printk(KERN_ERR "line %d passed!\n",__LINE__);
ret = -EAGAIN;
if (down_trylock(&b->sem))
{
//printk(KERN_ERR "line %d passed!\n",__LINE__);
break;
}
} else {
ret = -ERESTARTSYS;
//printk(KERN_ERR "line %d passed!\n",__LINE__);
if (down_interruptible(&b->sem))
{
//printk(KERN_ERR "line %d passed!\n",__LINE__);
break;
}
}
if (audio_channels == 2) {
//printk(KERN_ERR "line %d passed!audio_channels == 2\n",__LINE__);
chunksize = s->fragsize - b->size;
if (chunksize > count) //!!!
{
chunksize = count;
//printk(KERN_ERR "line %d passed!\n",__LINE__);
}
DPRINTK("write %d to %d\n", chunksize, s->buf_idx);
if (copy_from_user(b->start + b->size, buffer, chunksize)) {
up(&b->sem);
//printk(KERN_ERR "line %d passed!\n",__LINE__);
return -EFAULT;
}
b->size += chunksize;
//printk(KERN_ERR "line %d passed!\n",__LINE__);
} else {
//printk(KERN_ERR "line %d passed!\n",__LINE__);
chunksize = (s->fragsize - b->size) >> 1;
if (chunksize > count)
{
//printk(KERN_ERR "line %d passed!\n",__LINE__);
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);
//printk(KERN_ERR "line %d passed!\n",__LINE__);
return -EFAULT;
}
b->size += chunksize*2;
//printk(KERN_ERR "line %d passed!\n",__LINE__);
}
//printk(KERN_ERR "line %d passed!\n",__LINE__);
buffer += chunksize;
count -= chunksize;
//printk(KERN_ERR "line %d passed!\n",__LINE__);
if (b->size < s->fragsize) { //!!!
//printk(KERN_ERR "line %d passed!\n",__LINE__);
up(&b->sem);
break; //jump out while
}
//in while
//s3c2410_dma_queue_buffer(s->dma_ch, (void *) b,
// b->dma_addr, b->size, DMA_BUF_WR);
local_irq_save(flags);
//printk(KERN_ERR "line %d passed!\n",__LINE__);
if(!play_dma_running) {
//printk(KERN_ERR "line %d passed!\n",__LINE__);
play_dma_running = 1;
__raw_writel(b->dma_addr, DISRC2); //DMA src memory
__raw_writel(0, DISRCC2); //AHB, address increase
//__raw_writel(S3C2410_IISFIFO, DIDST2); //IISFIFO address
__raw_writel(0x55000010, DIDST2); //IISFIFO phy address
__raw_writel(3, DIDSTC2); //APB, address fixed
__raw_writel(0xa0d00000|(b->size>>1), DCON2); //set mode and length
__raw_writel(2, DMTRIG2); //set channel on |
|