📄 linux-at91rm9200-i2s.c
字号:
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 void iis_dma_done_handler(int irq, void *dev_id, struct pt_regs *regs)
{
AT91PS_SSC pSSC = (AT91PS_SSC)AT91C_VA_BASE_SSC2;
audio_stream_t *s = &output_stream;
// printk("i");
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)&&(b->dma_size)) {
pSSC->SSC_TPR = b->dma_addr;
pSSC->SSC_TCR = b->dma_size>>1;
b->dma_size = 0;
// printk(KERN_ERR "%d\n", wr_buf_tail);
} else {
play_dma_running = 0;
}
} else {
printk(KERN_DEBUG "buffers is null\n");
//pSSC->SSC_PTCR = (1<<1)|(1<<9); //disable PDC tx and rx
//pSSC->SSC_CR = (1<<1)|(1<<9); //disable tx and rx
}
// disable_irq(AT91C_ID_SSC2);
// ((AT91PS_SYS)AT91C_VA_BASE_SYS)->AIC_ICCR = 1<<AT91C_ID_SSC2;
return;//IRQ_HANDLED;
}
/* using when write */
static int audio_sync(struct file *file)
{
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) {
AT91PS_SSC pSSC = (AT91PS_SSC)AT91C_VA_BASE_SSC2;
play_dma_running = 1;
pSSC->SSC_TPR = b->dma_addr;
pSSC->SSC_TCR = b->size>>1;
pSSC->SSC_CR = 1<<8;
pSSC->SSC_PTCR = 1<<8;
} 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;
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;
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;
}
//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) {
AT91PS_SSC pSSC = (AT91PS_SSC)AT91C_VA_BASE_SSC2;
play_dma_running = 1;
pSSC->SSC_TPR = b->dma_addr;
pSSC->SSC_TCR = b->size>>1;
pSSC->SSC_CR = 1<<8;
pSSC->SSC_PTCR = 1<<8;
} else
b->dma_size = b->size;
b->size = 0;
NEXT_BUF(s, buf);
local_irq_restore(flags);
}
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) {
AT91PS_SSC pSSC = (AT91PS_SSC)AT91C_VA_BASE_SSC2;
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);
NEXT_BUF(s, buf);
}
pSSC->SSC_RPR = s->buf->dma_addr;
pSSC->SSC_RCR = s->fragsize>>1;
pSSC->SSC_CR = 1<<0;
pSSC->SSC_PTCR = 1<<0;
}
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;
}
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;
}
static int audio_set_dsp_speed(__u32 val)
{
AT91PS_SSC pSSC = (AT91PS_SSC)AT91C_VA_BASE_SSC2;
AT91PS_TCB pTCB = (AT91PS_TCB)AT91C_VA_BASE_TCB0;
AT91PS_TC pTC = (AT91PS_TC)&pTCB->TCB_TC1;
int i;
switch (val) {
case 8000:
case 11025:
case 16000:
case 22050:
case 24000:
case 32000:
case 44100:
case 48000:
break;
default:
printk("Invalid value\n");
return -1;
}
for(i=0; i<ARRAY_SIZE(iis_psr_para); i++) {
if(iis_psr_para[i].rate==val)
break;
}
if(i>=ARRAY_SIZE(iis_psr_para)) {
printk("Invalid psr parameters\n");
return -1;
}
pSSC->SSC_CMR = iis_psr_para[i].psr;
// pTC->TC_CCR = 2; //disable tc
pTC->TC_IDR = 0xff; //disable all interrupts
//select TIMER_CLOCK1 = MCK/2, CPCTRG, up mode, Waveform mode
pTC->TC_CMR = 0|(2<<13)|(1<<15)|(1<<16)|(2<<18); //Capture模式下,RC COMPARE也可复位计数器
uda1341_l3_address(UDA1341_REG_STATUS);
if(iis_psr_para[i].fs==384) {
uda1341_l3_data(STAT0_SC_384FS | STAT0_IF_MSB); // set 384 system clock, MSB
pTC->TC_RC = iis_psr_para[i].psr/12; //1/12
} else if(iis_psr_para[i].fs==256) {
uda1341_l3_data(STAT0_SC_256FS | STAT0_IF_MSB); // set 256 system clock, MSB
pTC->TC_RC = iis_psr_para[i].psr>>3; //1/8
} else {
uda1341_l3_data(STAT0_SC_256FS | STAT0_IF_MSB); // set 128 system clock, MSB
pTC->TC_RC = iis_psr_para[i].psr>>2; //1/4
}
pTC->TC_RA = pTC->TC_RC>>1;
pTC->TC_CCR = 5; //enable timer-counter and trig it
DPRINTK("RC_VAL=0x%x\n", pTC->TC_RC);
i = val;
return i;
}
static int smdk2410_mixer_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg)
{
static __u32 audio_mix_modcnt = 0;
int ret;
long val = 0;
audio_mix_modcnt++;
switch (cmd) {
case SOUND_MIXER_INFO:
{
mixer_info info;
memset(&info, 0, sizeof(info));
strncpy(info.id, "UDA1341", sizeof(info.id));
strncpy(info.name,"Philips UDA1341", sizeof(info.name));
info.modify_counter = audio_mix_modcnt;
return copy_to_user((void *)arg, &info, sizeof(info));
}
case SOUND_OLD_MIXER_INFO:
{
_old_mixer_info info;
memset(&info, 0, sizeof(info));
strncpy(info.id, "UDA1341", sizeof(info.id));
strncpy(info.name,"Philips UDA1341", sizeof(info.name));
return copy_to_user((void *)arg, &info, sizeof(info));
}
case SOUND_MIXER_READ_STEREODEVS:
return put_user(0, (long *) arg);
case SOUND_MIXER_READ_CAPS:
val = SOUND_CAP_EXCL_INPUT;
return put_user(val, (long *) arg);
case SOUND_MIXER_WRITE_VOLUME:
ret = get_user(val, (long *) arg);
if (ret)
return ret;
uda1341_volume = 63 - (((val & 0xff) + 1) * 63) / 100;
uda1341_l3_address(UDA1341_REG_DATA0);
uda1341_l3_data(uda1341_volume);
break;
case SOUND_MIXER_READ_VOLUME:
val = ((63 - uda1341_volume) * 100) / 63;
val |= val << 8;
return put_user(val, (long *) arg);
case SOUND_MIXER_READ_IGAIN:
val = ((31- mixer_igain) * 100) / 31;
return put_user(val, (int *) arg);
case SOUND_MIXER_WRITE_IGAIN:
ret = get_user(val, (int *) arg);
if (ret)
return ret;
mixer_igain = 31 - (val * 31 / 100);
/* use mixer gain channel 1*/
uda1341_l3_address(UDA1341_REG_DATA0);
uda1341_l3_data(EXTADDR(EXT0));
uda1341_l3_data(EXTDATA(EXT0_CH1_GAIN(mixer_igain)));
break;
default:
DPRINTK("mixer ioctl %u unknown\n", cmd);
return -ENOSYS;
}
return 0;
}
static int smdk2410_audio_ioctl(struct inode *inode, struct file *file,
uint cmd, ulong arg)
{
long val;
switch (cmd) {
case OSS_GETVERSION:
return put_user(SOUND_VERSION, (int *)arg);
case SNDCTL_DSP_SETFMT:
get_user(val, (long *) arg);
if (val & AUDIO_FMT_MASK) {
audio_fmt = val;
break;
} else
return -EINVAL;
case SNDCTL_DSP_CHANNELS:
case SNDCTL_DSP_STEREO:
get_user(val, (long *) arg);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -