📄 dbri.c
字号:
* REN = 1 - enable receiver */ *(cmd++) = DBRI_CMD(D_PAUSE, 0, 0); *(cmd++) = DBRI_CMD(D_CDM, 0, D_CDM_XCE | D_CDM_XEN | D_CDM_REN); *(cmd++) = DBRI_CMD(D_PAUSE, 0, 0); dbri_cmdsend(dbri, cmd, 4);}/**************************************************************************************************** CS4215 audio codec management **************************************************************************************************In the standard SPARC audio configuration, the CS4215 codec is attachedto the DBRI via the CHI interface and few of the DBRI's PIO pins. * Lock must not be held before calling it.*/static __devinit void cs4215_setup_pipes(struct snd_dbri *dbri){ unsigned long flags; spin_lock_irqsave(&dbri->lock, flags); /* * Data mode: * Pipe 4: Send timeslots 1-4 (audio data) * Pipe 20: Send timeslots 5-8 (part of ctrl data) * Pipe 6: Receive timeslots 1-4 (audio data) * Pipe 21: Receive timeslots 6-7. We can only receive 20 bits via * interrupt, and the rest of the data (slot 5 and 8) is * not relevant for us (only for doublechecking). * * Control mode: * Pipe 17: Send timeslots 1-4 (slots 5-8 are read only) * Pipe 18: Receive timeslot 1 (clb). * Pipe 19: Receive timeslot 7 (version). */ setup_pipe(dbri, 4, D_SDP_MEM | D_SDP_TO_SER | D_SDP_MSB); setup_pipe(dbri, 20, D_SDP_FIXED | D_SDP_TO_SER | D_SDP_MSB); setup_pipe(dbri, 6, D_SDP_MEM | D_SDP_FROM_SER | D_SDP_MSB); setup_pipe(dbri, 21, D_SDP_FIXED | D_SDP_FROM_SER | D_SDP_MSB); setup_pipe(dbri, 17, D_SDP_FIXED | D_SDP_TO_SER | D_SDP_MSB); setup_pipe(dbri, 18, D_SDP_FIXED | D_SDP_FROM_SER | D_SDP_MSB); setup_pipe(dbri, 19, D_SDP_FIXED | D_SDP_FROM_SER | D_SDP_MSB); spin_unlock_irqrestore(&dbri->lock, flags); dbri_cmdwait(dbri);}static __devinit int cs4215_init_data(struct cs4215 *mm){ /* * No action, memory resetting only. * * Data Time Slot 5-8 * Speaker,Line and Headphone enable. Gain set to the half. * Input is mike. */ mm->data[0] = CS4215_LO(0x20) | CS4215_HE | CS4215_LE; mm->data[1] = CS4215_RO(0x20) | CS4215_SE; mm->data[2] = CS4215_LG(0x8) | CS4215_IS | CS4215_PIO0 | CS4215_PIO1; mm->data[3] = CS4215_RG(0x8) | CS4215_MA(0xf); /* * Control Time Slot 1-4 * 0: Default I/O voltage scale * 1: 8 bit ulaw, 8kHz, mono, high pass filter disabled * 2: Serial enable, CHI master, 128 bits per frame, clock 1 * 3: Tests disabled */ mm->ctrl[0] = CS4215_RSRVD_1 | CS4215_MLB; mm->ctrl[1] = CS4215_DFR_ULAW | CS4215_FREQ[0].csval; mm->ctrl[2] = CS4215_XCLK | CS4215_BSEL_128 | CS4215_FREQ[0].xtal; mm->ctrl[3] = 0; mm->status = 0; mm->version = 0xff; mm->precision = 8; /* For ULAW */ mm->channels = 1; return 0;}static void cs4215_setdata(struct snd_dbri *dbri, int muted){ if (muted) { dbri->mm.data[0] |= 63; dbri->mm.data[1] |= 63; dbri->mm.data[2] &= ~15; dbri->mm.data[3] &= ~15; } else { /* Start by setting the playback attenuation. */ struct dbri_streaminfo *info = &dbri->stream_info[DBRI_PLAY]; int left_gain = info->left_gain & 0x3f; int right_gain = info->right_gain & 0x3f; dbri->mm.data[0] &= ~0x3f; /* Reset the volume bits */ dbri->mm.data[1] &= ~0x3f; dbri->mm.data[0] |= (DBRI_MAX_VOLUME - left_gain); dbri->mm.data[1] |= (DBRI_MAX_VOLUME - right_gain); /* Now set the recording gain. */ info = &dbri->stream_info[DBRI_REC]; left_gain = info->left_gain & 0xf; right_gain = info->right_gain & 0xf; dbri->mm.data[2] |= CS4215_LG(left_gain); dbri->mm.data[3] |= CS4215_RG(right_gain); } xmit_fixed(dbri, 20, *(int *)dbri->mm.data);}/* * Set the CS4215 to data mode. */static void cs4215_open(struct snd_dbri *dbri){ int data_width; u32 tmp; unsigned long flags; dprintk(D_MM, "cs4215_open: %d channels, %d bits\n", dbri->mm.channels, dbri->mm.precision); /* Temporarily mute outputs, and wait 1/8000 sec (125 us) * to make sure this takes. This avoids clicking noises. */ cs4215_setdata(dbri, 1); udelay(125); /* * Data mode: * Pipe 4: Send timeslots 1-4 (audio data) * Pipe 20: Send timeslots 5-8 (part of ctrl data) * Pipe 6: Receive timeslots 1-4 (audio data) * Pipe 21: Receive timeslots 6-7. We can only receive 20 bits via * interrupt, and the rest of the data (slot 5 and 8) is * not relevant for us (only for doublechecking). * * Just like in control mode, the time slots are all offset by eight * bits. The CS4215, it seems, observes TSIN (the delayed signal) * even if it's the CHI master. Don't ask me... */ spin_lock_irqsave(&dbri->lock, flags); tmp = sbus_readl(dbri->regs + REG0); tmp &= ~(D_C); /* Disable CHI */ sbus_writel(tmp, dbri->regs + REG0); /* Switch CS4215 to data mode - set PIO3 to 1 */ sbus_writel(D_ENPIO | D_PIO1 | D_PIO3 | (dbri->mm.onboard ? D_PIO0 : D_PIO2), dbri->regs + REG2); reset_chi(dbri, CHIslave, 128); /* Note: this next doesn't work for 8-bit stereo, because the two * channels would be on timeslots 1 and 3, with 2 and 4 idle. * (See CS4215 datasheet Fig 15) * * DBRI non-contiguous mode would be required to make this work. */ data_width = dbri->mm.channels * dbri->mm.precision; link_time_slot(dbri, 4, 16, 16, data_width, dbri->mm.offset); link_time_slot(dbri, 20, 4, 16, 32, dbri->mm.offset + 32); link_time_slot(dbri, 6, 16, 16, data_width, dbri->mm.offset); link_time_slot(dbri, 21, 6, 16, 16, dbri->mm.offset + 40); /* FIXME: enable CHI after _setdata? */ tmp = sbus_readl(dbri->regs + REG0); tmp |= D_C; /* Enable CHI */ sbus_writel(tmp, dbri->regs + REG0); spin_unlock_irqrestore(&dbri->lock, flags); cs4215_setdata(dbri, 0);}/* * Send the control information (i.e. audio format) */static int cs4215_setctrl(struct snd_dbri *dbri){ int i, val; u32 tmp; unsigned long flags; /* FIXME - let the CPU do something useful during these delays */ /* Temporarily mute outputs, and wait 1/8000 sec (125 us) * to make sure this takes. This avoids clicking noises. */ cs4215_setdata(dbri, 1); udelay(125); /* * Enable Control mode: Set DBRI's PIO3 (4215's D/~C) to 0, then wait * 12 cycles <= 12/(5512.5*64) sec = 34.01 usec */ val = D_ENPIO | D_PIO1 | (dbri->mm.onboard ? D_PIO0 : D_PIO2); sbus_writel(val, dbri->regs + REG2); dprintk(D_MM, "cs4215_setctrl: reg2=0x%x\n", val); udelay(34); /* In Control mode, the CS4215 is a slave device, so the DBRI must * operate as CHI master, supplying clocking and frame synchronization. * * In Data mode, however, the CS4215 must be CHI master to insure * that its data stream is synchronous with its codec. * * The upshot of all this? We start by putting the DBRI into master * mode, program the CS4215 in Control mode, then switch the CS4215 * into Data mode and put the DBRI into slave mode. Various timing * requirements must be observed along the way. * * Oh, and one more thing, on a SPARCStation 20 (and maybe * others?), the addressing of the CS4215's time slots is * offset by eight bits, so we add eight to all the "cycle" * values in the Define Time Slot (DTS) commands. This is * done in hardware by a TI 248 that delays the DBRI->4215 * frame sync signal by eight clock cycles. Anybody know why? */ spin_lock_irqsave(&dbri->lock, flags); tmp = sbus_readl(dbri->regs + REG0); tmp &= ~D_C; /* Disable CHI */ sbus_writel(tmp, dbri->regs + REG0); reset_chi(dbri, CHImaster, 128); /* * Control mode: * Pipe 17: Send timeslots 1-4 (slots 5-8 are read only) * Pipe 18: Receive timeslot 1 (clb). * Pipe 19: Receive timeslot 7 (version). */ link_time_slot(dbri, 17, 16, 16, 32, dbri->mm.offset); link_time_slot(dbri, 18, 16, 16, 8, dbri->mm.offset); link_time_slot(dbri, 19, 18, 16, 8, dbri->mm.offset + 48); spin_unlock_irqrestore(&dbri->lock, flags); /* Wait for the chip to echo back CLB (Control Latch Bit) as zero */ dbri->mm.ctrl[0] &= ~CS4215_CLB; xmit_fixed(dbri, 17, *(int *)dbri->mm.ctrl); spin_lock_irqsave(&dbri->lock, flags); tmp = sbus_readl(dbri->regs + REG0); tmp |= D_C; /* Enable CHI */ sbus_writel(tmp, dbri->regs + REG0); spin_unlock_irqrestore(&dbri->lock, flags); for (i = 10; ((dbri->mm.status & 0xe4) != 0x20); --i) msleep_interruptible(1); if (i == 0) { dprintk(D_MM, "CS4215 didn't respond to CLB (0x%02x)\n", dbri->mm.status); return -1; } /* Disable changes to our copy of the version number, as we are about * to leave control mode. */ recv_fixed(dbri, 19, NULL); /* Terminate CS4215 control mode - data sheet says * "Set CLB=1 and send two more frames of valid control info" */ dbri->mm.ctrl[0] |= CS4215_CLB; xmit_fixed(dbri, 17, *(int *)dbri->mm.ctrl); /* Two frames of control info @ 8kHz frame rate = 250 us delay */ udelay(250); cs4215_setdata(dbri, 0); return 0;}/* * Setup the codec with the sampling rate, audio format and number of * channels. * As part of the process we resend the settings for the data * timeslots as well. */static int cs4215_prepare(struct snd_dbri *dbri, unsigned int rate, snd_pcm_format_t format, unsigned int channels){ int freq_idx; int ret = 0; /* Lookup index for this rate */ for (freq_idx = 0; CS4215_FREQ[freq_idx].freq != 0; freq_idx++) { if (CS4215_FREQ[freq_idx].freq == rate) break; } if (CS4215_FREQ[freq_idx].freq != rate) { printk(KERN_WARNING "DBRI: Unsupported rate %d Hz\n", rate); return -1; } switch (format) { case SNDRV_PCM_FORMAT_MU_LAW: dbri->mm.ctrl[1] = CS4215_DFR_ULAW; dbri->mm.precision = 8; break; case SNDRV_PCM_FORMAT_A_LAW: dbri->mm.ctrl[1] = CS4215_DFR_ALAW; dbri->mm.precision = 8; break; case SNDRV_PCM_FORMAT_U8: dbri->mm.ctrl[1] = CS4215_DFR_LINEAR8; dbri->mm.precision = 8; break; case SNDRV_PCM_FORMAT_S16_BE: dbri->mm.ctrl[1] = CS4215_DFR_LINEAR16; dbri->mm.precision = 16; break; default: printk(KERN_WARNING "DBRI: Unsupported format %d\n", format); return -1; } /* Add rate parameters */ dbri->mm.ctrl[1] |= CS4215_FREQ[freq_idx].csval; dbri->mm.ctrl[2] = CS4215_XCLK | CS4215_BSEL_128 | CS4215_FREQ[freq_idx].xtal; dbri->mm.channels = channels; if (channels == 2) dbri->mm.ctrl[1] |= CS4215_DFR_STEREO; ret = cs4215_setctrl(dbri); if (ret == 0) cs4215_open(dbri); /* set codec to data mode */ return ret;}/* * */static __devinit int cs4215_init(struct snd_dbri *dbri){ u32 reg2 = sbus_readl(dbri->regs + REG2); dprintk(D_MM, "cs4215_init: reg2=0x%x\n", reg2); /* Look for the cs4215 chips */ if (reg2 & D_PIO2) { dprintk(D_MM, "Onboard CS4215 detected\n"); dbri->mm.onboard = 1; } if (reg2 & D_PIO0) { dprintk(D_MM, "Speakerbox detected\n"); dbri->mm.onboard = 0; if (reg2 & D_PIO2) { printk(KERN_INFO "DBRI: Using speakerbox / " "ignoring onboard mmcodec.\n"); sbus_writel(D_ENPIO2, dbri->regs + REG2); } } if (!(reg2 & (D_PIO0 | D_PIO2))) { printk(KERN_ERR "DBRI: no mmcodec found.\n"); return -EIO; } cs4215_setup_pipes(dbri); cs4215_init_data(&dbri->mm); /* Enable capture of the status & version timeslots. */ recv_fixed(dbri, 18, &dbri->mm.status); recv_fixed(dbri, 19, &dbri->mm.version); dbri->mm.offset = dbri->mm.onboard ? 0 : 8; if (cs4215_setctrl(dbri) == -1 || dbri->mm.version == 0xff) { dprintk(D_MM, "CS4215 failed probe at offset %d\n", dbri->mm.offset); return -EIO; } dprintk(D_MM, "Found CS4215 at offset %d\n", dbri->mm.offset); return 0;}/******************************************************************************************************** DBRI interrupt handler *****************************************************************************************************The DBRI communicates with the CPU mainly via a circular interruptbuffer. When an interrupt is signaled, the CPU walks through thebuffer and calls dbri_process_one_interrupt() for each interrupt word.Complicated interrupts are handled by dedicated functions (whichappear first in this file). Any pending interrupts can be serviced bycalling dbri_process_interrupt_buffer(), which works even if the CPU'sinterrupts are disabled.*//* xmit_descs() * * Starts transmitting the current TD's for recording/playing. * For playback, ALSA has filled the DMA memory with new data (we hope). */static void xmit_descs(struct snd_dbri *dbri){ struct dbri_streaminfo *info; s32 *cmd; unsigned long flags; int first_td; if (dbri == NULL) return; /* Disabled */ info = &dbri->stream_info[DBRI_REC]; spin_lock_irqsave(&dbri->lock, flags); if (info->pipe >= 0) { first_td = dbri->pipes[info->pipe].first_desc; dprintk(D_DESC, "xmit_descs rec @ TD %d\n", first_td);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -