📄 cs4218_tdm.c
字号:
SLEEP(read_sq.open_queue); if (SIGNAL_RECEIVED) goto err_out; } rc = 0; } read_sq.busy = 1; if (sq_allocate_read_buffers()) goto err_out_nobusy; read_sq_setup(numReadBufs,readbufSize<<10, sound_read_buffers); read_sq.open_mode = file->f_mode; } /* Start up the 4218 by: * Reset. * Enable, unreset. */ *((volatile uint *)HIOX_CSR4_ADDR) &= ~HIOX_CSR4_RSTAUDIO; eieio(); *((volatile uint *)HIOX_CSR4_ADDR) |= HIOX_CSR4_ENAUDIO; mdelay(50); *((volatile uint *)HIOX_CSR4_ADDR) |= HIOX_CSR4_RSTAUDIO; /* We need to send the current control word in case someone * opened /dev/mixer and changed things while we were shut * down. Chances are good the initialization that follows * would have done this, but it is still possible it wouldn't. */ cs4218_ctl_write(cs4218_control); sound.minDev = iminor(inode) & 0x0f; sound.soft = sound.dsp; sound.hard = sound.dsp; sound_init(); if ((iminor(inode) & 0x0f) == SND_DEV_AUDIO) { sound_set_speed(8000); sound_set_stereo(0); sound_set_format(AFMT_MU_LAW); } return nonseekable_open(inode, file);err_out_nobusy: if (file->f_mode & FMODE_WRITE) { sq.busy = 0; WAKE_UP(sq.open_queue); } if (file->f_mode & FMODE_READ) { read_sq.busy = 0; WAKE_UP(read_sq.open_queue); }err_out: return rc;}static void sq_reset(void){ sound_silence(); sq.active = 0; sq.count = 0; sq.front = (sq.rear+1) % sq.max_count;#if 0 init_tdm_buffers();#endif}static int sq_fsync(struct file *filp, struct dentry *dentry){ int rc = 0; sq.syncing = 1; sq_play(); /* there may be an incomplete frame waiting */ while (sq.active) { SLEEP(sq.sync_queue); if (SIGNAL_RECEIVED) { /* While waiting for audio output to drain, an * interrupt occurred. Stop audio output immediately * and clear the queue. */ sq_reset(); rc = -EINTR; break; } } sq.syncing = 0; return rc;}static int sq_release(struct inode *inode, struct file *file){ int rc = 0; if (sq.busy) rc = sq_fsync(file, file->f_dentry); sound.soft = sound.dsp; sound.hard = sound.dsp; sound_silence(); sq_release_read_buffers(); sq_release_buffers(); if (file->f_mode & FMODE_READ) { read_sq.busy = 0; WAKE_UP(read_sq.open_queue); } if (file->f_mode & FMODE_WRITE) { sq.busy = 0; WAKE_UP(sq.open_queue); } /* Shut down the SMC. */ cpmp->cp_smc[1].smc_smcmr &= ~(SMCMR_TEN | SMCMR_REN); /* Shut down the codec. */ *((volatile uint *)HIOX_CSR4_ADDR) |= HIOX_CSR4_RSTAUDIO; eieio(); *((volatile uint *)HIOX_CSR4_ADDR) &= ~HIOX_CSR4_ENAUDIO; /* Wake up a process waiting for the queue being released. * Note: There may be several processes waiting for a call * to open() returning. */ return rc;}static int sq_ioctl(struct inode *inode, struct file *file, u_int cmd, u_long arg){ u_long fmt; int data;#if 0 int size, nbufs;#else int size;#endif switch (cmd) { case SNDCTL_DSP_RESET: sq_reset(); return 0; case SNDCTL_DSP_POST: case SNDCTL_DSP_SYNC: return sq_fsync(file, file->f_dentry); /* ++TeSche: before changing any of these it's * probably wise to wait until sound playing has * settled down. */ case SNDCTL_DSP_SPEED: sq_fsync(file, file->f_dentry); IOCTL_IN(arg, data); return IOCTL_OUT(arg, sound_set_speed(data)); case SNDCTL_DSP_STEREO: sq_fsync(file, file->f_dentry); IOCTL_IN(arg, data); return IOCTL_OUT(arg, sound_set_stereo(data)); case SOUND_PCM_WRITE_CHANNELS: sq_fsync(file, file->f_dentry); IOCTL_IN(arg, data); return IOCTL_OUT(arg, sound_set_stereo(data-1)+1); case SNDCTL_DSP_SETFMT: sq_fsync(file, file->f_dentry); IOCTL_IN(arg, data); return IOCTL_OUT(arg, sound_set_format(data)); case SNDCTL_DSP_GETFMTS: fmt = 0; if (sound.trans_write) { if (sound.trans_write->ct_ulaw) fmt |= AFMT_MU_LAW; if (sound.trans_write->ct_alaw) fmt |= AFMT_A_LAW; if (sound.trans_write->ct_s8) fmt |= AFMT_S8; if (sound.trans_write->ct_u8) fmt |= AFMT_U8; if (sound.trans_write->ct_s16be) fmt |= AFMT_S16_BE; if (sound.trans_write->ct_u16be) fmt |= AFMT_U16_BE; if (sound.trans_write->ct_s16le) fmt |= AFMT_S16_LE; if (sound.trans_write->ct_u16le) fmt |= AFMT_U16_LE; } return IOCTL_OUT(arg, fmt); case SNDCTL_DSP_GETBLKSIZE: size = sq.block_size * sound.soft.size * (sound.soft.stereo + 1) / (sound.hard.size * (sound.hard.stereo + 1)); return IOCTL_OUT(arg, size); case SNDCTL_DSP_SUBDIVIDE: break;#if 0 /* Sorry can't do this at the moment. The CPM allocated buffers * long ago that can't be changed. */ case SNDCTL_DSP_SETFRAGMENT: if (sq.count || sq.active || sq.syncing) return -EINVAL; IOCTL_IN(arg, size); nbufs = size >> 16; if (nbufs < 2 || nbufs > numBufs) nbufs = numBufs; size &= 0xffff; if (size >= 8 && size <= 30) { size = 1 << size; size *= sound.hard.size * (sound.hard.stereo + 1); size /= sound.soft.size * (sound.soft.stereo + 1); if (size > (bufSize << 10)) size = bufSize << 10; } else size = bufSize << 10; sq_setup(numBufs, size, sound_buffers); sq.max_active = nbufs; return 0;#endif default: return mixer_ioctl(inode, file, cmd, arg); } return -EINVAL;}static struct file_operations sq_fops ={ .owner = THIS_MODULE, .llseek = sound_lseek, .read = sq_read, /* sq_read */ .write = sq_write, .ioctl = sq_ioctl, .open = sq_open, .release = sq_release,};static void __init sq_init(void){ sq_unit = register_sound_dsp(&sq_fops, -1); if (sq_unit < 0) return; init_waitqueue_head(&sq.action_queue); init_waitqueue_head(&sq.open_queue); init_waitqueue_head(&sq.sync_queue); init_waitqueue_head(&read_sq.action_queue); init_waitqueue_head(&read_sq.open_queue); init_waitqueue_head(&read_sq.sync_queue); sq.busy = 0; read_sq.busy = 0; /* whatever you like as startup mode for /dev/dsp, * (/dev/audio hasn't got a startup mode). note that * once changed a new open() will *not* restore these! */ sound.dsp.format = AFMT_S16_BE; sound.dsp.stereo = 1; sound.dsp.size = 16; /* set minimum rate possible without expanding */ sound.dsp.speed = 8000; /* before the first open to /dev/dsp this wouldn't be set */ sound.soft = sound.dsp; sound.hard = sound.dsp; sound_silence();}/* * /dev/sndstat *//* state.buf should not overflow! */static int state_open(struct inode *inode, struct file *file){ char *buffer = state.buf, *mach = "", cs4218_buf[50]; int len = 0; if (state.busy) return -EBUSY; state.ptr = 0; state.busy = 1; sprintf(cs4218_buf, "Crystal CS4218 on TDM, "); mach = cs4218_buf; len += sprintf(buffer+len, "%sDMA sound driver:\n", mach); len += sprintf(buffer+len, "\tsound.format = 0x%x", sound.soft.format); switch (sound.soft.format) { case AFMT_MU_LAW: len += sprintf(buffer+len, " (mu-law)"); break; case AFMT_A_LAW: len += sprintf(buffer+len, " (A-law)"); break; case AFMT_U8: len += sprintf(buffer+len, " (unsigned 8 bit)"); break; case AFMT_S8: len += sprintf(buffer+len, " (signed 8 bit)"); break; case AFMT_S16_BE: len += sprintf(buffer+len, " (signed 16 bit big)"); break; case AFMT_U16_BE: len += sprintf(buffer+len, " (unsigned 16 bit big)"); break; case AFMT_S16_LE: len += sprintf(buffer+len, " (signed 16 bit little)"); break; case AFMT_U16_LE: len += sprintf(buffer+len, " (unsigned 16 bit little)"); break; } len += sprintf(buffer+len, "\n"); len += sprintf(buffer+len, "\tsound.speed = %dHz (phys. %dHz)\n", sound.soft.speed, sound.hard.speed); len += sprintf(buffer+len, "\tsound.stereo = 0x%x (%s)\n", sound.soft.stereo, sound.soft.stereo ? "stereo" : "mono"); len += sprintf(buffer+len, "\tsq.block_size = %d sq.max_count = %d" " sq.max_active = %d\n", sq.block_size, sq.max_count, sq.max_active); len += sprintf(buffer+len, "\tsq.count = %d sq.rear_size = %d\n", sq.count, sq.rear_size); len += sprintf(buffer+len, "\tsq.active = %d sq.syncing = %d\n", sq.active, sq.syncing); state.len = len; return nonseekable_open(inode, file);}static int state_release(struct inode *inode, struct file *file){ state.busy = 0; return 0;}static ssize_t state_read(struct file *file, char *buf, size_t count, loff_t *ppos){ int n = state.len - state.ptr; if (n > count) n = count; if (n <= 0) return 0; if (copy_to_user(buf, &state.buf[state.ptr], n)) return -EFAULT; state.ptr += n; return n;}static struct file_operations state_fops ={ .owner = THIS_MODULE, .llseek = sound_lseek, .read = state_read, .open = state_open, .release = state_release,};static void __init state_init(void){ state_unit = register_sound_special(&state_fops, SND_DEV_STATUS); if (state_unit < 0) return; state.busy = 0;}/*** Common stuff ********************************************************/static long long sound_lseek(struct file *file, long long offset, int orig){ return -ESPIPE;}/*** Config & Setup **********************************************************/int __init tdm8xx_sound_init(void){ int i, has_sound; uint dp_offset; volatile uint *sirp; volatile cbd_t *bdp; volatile cpm8xx_t *cp; volatile smc_t *sp; volatile smc_uart_t *up; volatile immap_t *immap; has_sound = 0; /* Program the SI/TSA to use TDMa, connected to SMC2, for 4 bytes. */ cp = cpmp; /* Get pointer to Communication Processor */ immap = (immap_t *)IMAP_ADDR; /* and to internal registers */ /* Set all TDMa control bits to zero. This enables most features * we want. */ cp->cp_simode &= ~0x00000fff; /* Enable common receive/transmit clock pins, use IDL format. * Sync on falling edge, transmit rising clock, receive falling * clock, delay 1 bit on both Tx and Rx. Common Tx/Rx clocks and * sync. * Connect SMC2 to TSA. */ cp->cp_simode |= 0x80000141; /* Configure port A pins for TDMa operation. * The RPX-Lite (MPC850/823) loses SMC2 when TDM is used. */ immap->im_ioport.iop_papar |= 0x01c0; /* Enable TDMa functions */ immap->im_ioport.iop_padir |= 0x00c0; /* Enable TDMa Tx/Rx */ immap->im_ioport.iop_padir &= ~0x0100; /* Enable L1RCLKa */ immap->im_ioport.iop_pcpar |= 0x0800; /* Enable L1RSYNCa */ immap->im_ioport.iop_pcdir &= ~0x0800; /* Initialize the SI TDM routing table. We use TDMa only. * The receive table and transmit table each have only one * entry, to capture/send four bytes after each frame pulse. * The 16-bit ram entry is 0000 0001 1000 1111. (SMC2) */ cp->cp_sigmr = 0; sirp = (uint *)cp->cp_siram; *sirp = 0x018f0000; /* Receive entry */ sirp += 64; *sirp = 0x018f0000; /* Tramsmit entry */ /* Enable single TDMa routing. */ cp->cp_sigmr = 0x04; /* Initialize the SMC for transparent operation. */ sp = &cpmp->cp_smc[1]; up = (smc_uart_t *)&cp->cp_dparam[PROFF_SMC2]; /* We need to allocate a transmit and receive buffer * descriptors from dual port ram. */ dp_addr = cpm_dpalloc(sizeof(cbd_t) * numReadBufs, 8); /* Set the physical address of the host memory * buffers in the buffer descriptors, and the * virtual address for us to work with. */ bdp = (cbd_t *)&cp->cp_dpmem[dp_addr]; up->smc_rbase = dp_offset; rx_cur = rx_base = (cbd_t *)bdp; for (i=0; i<(numReadBufs-1); i++) { bdp->cbd_bufaddr = 0; bdp->cbd_datlen = 0; bdp->cbd_sc = BD_SC_EMPTY | BD_SC_INTRPT; bdp++; } bdp->cbd_bufaddr = 0; bdp->cbd_datlen = 0; bdp->cbd_sc = BD_SC_WRAP | BD_SC_EMPTY | BD_SC_INTRPT; /* Now, do the same for the transmit buffers. */ dp_offset = cpm_dpalloc(sizeof(cbd_t) * numBufs, 8); bdp = (cbd_t *)&cp->cp_dpmem[dp_addr]; up->smc_tbase = dp_offset; tx_cur = tx_base = (cbd_t *)bdp; for (i=0; i<(numBufs-1); i++) { bdp->cbd_bufaddr = 0; bdp->cbd_datlen = 0; bdp->cbd_sc = BD_SC_INTRPT; bdp++; } bdp->cbd_bufaddr = 0; bdp->cbd_datlen = 0; bdp->cbd_sc = (BD_SC_WRAP | BD_SC_INTRPT); /* Set transparent SMC mode. * A few things are specific to our application. The codec interface * is MSB first, hence the REVD selection. The CD/CTS pulse are * used by the TSA to indicate the frame start to the SMC. */ up->smc_rfcr = SCC_EB; up->smc_tfcr = SCC_EB; up->smc_mrblr = readbufSize * 1024; /* Set 16-bit reversed data, transparent mode. */ sp->smc_smcmr = smcr_mk_clen(15) | SMCMR_SM_TRANS | SMCMR_REVD | SMCMR_BS; /* Enable and clear events. * Because of FIFO delays, all we need is the receive interrupt * and we can process both the current receive and current * transmit interrupt within a few microseconds of
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -