📄 dbri.c
字号:
first_td = td; } else { dbri->descs[last_td].next = td; dbri->dma->desc[last_td].nda = dbri->dma_dvma + dbri_dma_off(desc, td); } last_td = td; dvma_buffer += mylen; len -= mylen; } if (first_td == -1 || last_td == -1) { sbus_unmap_single(dbri->sdev, dvma_buffer_base, dvma_buffer - dvma_buffer_base + len, SBUS_DMA_TODEVICE); return; } dbri->dma->desc[last_td].word1 |= DBRI_TD_I | DBRI_TD_F | DBRI_TD_B; dbri->descs[last_td].buffer = buffer; dbri->descs[last_td].buffer_dvma = dvma_buffer_base; dbri->descs[last_td].len = dvma_buffer - dvma_buffer_base + len; dbri->descs[last_td].output_callback = callback; dbri->descs[last_td].output_callback_arg = callback_arg; for (td=first_td; td != -1; td = dbri->descs[td].next) { dprintk(D_DESC, ("DBRI TD %d: %08x %08x %08x %08x\n", td, dbri->dma->desc[td].word1, dbri->dma->desc[td].ba, dbri->dma->desc[td].nda, dbri->dma->desc[td].word4)); } save_and_cli(flags); if (pipe_active(dbri, pipe)) { /* Pipe is already active - find last TD in use * and link our first TD onto its end. Then issue * a CDP command to let the DBRI know there's more data. */ last_td = dbri->pipes[pipe].desc; while (dbri->descs[last_td].next != -1) last_td = dbri->descs[last_td].next; dbri->descs[last_td].next = first_td; dbri->dma->desc[last_td].nda = dbri->dma_dvma + dbri_dma_off(desc, first_td); cmd = dbri_cmdlock(dbri); *(cmd++) = DBRI_CMD(D_CDP, 0, pipe); dbri_cmdsend(dbri,cmd); } else { /* Pipe isn't active - issue an SDP command to start * our chain of TDs running. */ dbri->pipes[pipe].desc = first_td; cmd = dbri_cmdlock(dbri); *(cmd++) = DBRI_CMD(D_SDP, 0, dbri->pipes[pipe].sdp | D_SDP_P | D_SDP_EVERY | D_SDP_C); *(cmd++) = dbri->dma_dvma + dbri_dma_off(desc, first_td); dbri_cmdsend(dbri, cmd); } restore_flags(flags);}static void recv_on_pipe(struct dbri *dbri, int pipe, void * buffer, unsigned int len, void (*callback)(void *, int, unsigned int), void * callback_arg){ volatile s32 *cmd; int first_rd = -1; int last_rd = -1; int rd; __u32 bus_buffer, bus_buffer_base; if (pipe < 0 || pipe > 15) { printk("DBRI: recv_on_pipe: Illegal pipe number\n"); return; } if (dbri->pipes[pipe].sdp == 0) { printk("DBRI: recv_on_pipe: Uninitialized pipe %d\n", pipe); return; } if (dbri->pipes[pipe].sdp & D_SDP_TO_SER) { printk("DBRI: recv_on_pipe: Called on transmit pipe %d\n", pipe); return; } /* XXX Fix this XXX * Should be able to queue multiple buffers to receive on a pipe */ if (dbri->pipes[pipe].desc != -1) { printk("DBRI: recv_on_pipe: Called on active pipe %d\n", pipe); return; } /* Make sure buffer size is multiple of four */ len &= ~3; bus_buffer_base = bus_buffer = sbus_map_single(dbri->sdev, buffer, len, SBUS_DMA_FROMDEVICE); while (len > 0) { int rd, mylen; if (len > ((1 << 13) - 4)) { mylen = (1 << 13) - 4; } else { mylen = len; } for (rd = 0; rd < DBRI_NO_DESCS; rd ++) { if (! dbri->descs[rd].inuse) break; } if (rd == DBRI_NO_DESCS) { printk("DBRI recv_on_pipe: No descriptors\n"); break; } dbri->dma->desc[rd].word1 = 0; dbri->dma->desc[rd].ba = bus_buffer; dbri->dma->desc[rd].nda = 0; dbri->dma->desc[rd].word4 = DBRI_RD_B | DBRI_RD_BCNT(mylen); dbri->descs[rd].buffer = NULL; dbri->descs[rd].len = 0; dbri->descs[rd].input_callback = NULL; dbri->descs[rd].output_callback = NULL; dbri->descs[rd].next = -1; dbri->descs[rd].inuse = 1; if (first_rd == -1) first_rd = rd; if (last_rd != -1) { dbri->dma->desc[last_rd].nda = dbri->dma_dvma + dbri_dma_off(desc, rd); dbri->descs[last_rd].next = rd; } last_rd = rd; bus_buffer += mylen; len -= mylen; } if (last_rd == -1 || first_rd == -1) { sbus_unmap_single(dbri->sdev, bus_buffer_base, bus_buffer - bus_buffer_base + len, SBUS_DMA_FROMDEVICE); return; } for (rd=first_rd; rd != -1; rd = dbri->descs[rd].next) { dprintk(D_DESC, ("DBRI RD %d: %08x %08x %08x %08x\n", rd, dbri->dma->desc[rd].word1, dbri->dma->desc[rd].ba, dbri->dma->desc[rd].nda, dbri->dma->desc[rd].word4)); } dbri->descs[last_rd].buffer = buffer; dbri->descs[last_rd].buffer_dvma = bus_buffer_base; dbri->descs[last_rd].len = bus_buffer - bus_buffer_base + len; dbri->descs[last_rd].input_callback = callback; dbri->descs[last_rd].input_callback_arg = callback_arg; dbri->pipes[pipe].desc = first_rd; cmd = dbri_cmdlock(dbri); *(cmd++) = DBRI_CMD(D_SDP, 0, dbri->pipes[pipe].sdp | D_SDP_P | D_SDP_C); *(cmd++) = dbri->dma_dvma + dbri_dma_off(desc, first_rd); dbri_cmdsend(dbri, cmd);}/******************************************************************************************************* DBRI - CHI interface ********************************************************************************************************The CHI is a four-wire (clock, frame sync, data in, data out) time-divisionmultiplexed serial interface which the DBRI can operate in either master(give clock/frame sync) or slave (take clock/frame sync) mode.*/enum master_or_slave { CHImaster, CHIslave };static void reset_chi(struct dbri *dbri, enum master_or_slave master_or_slave, int bits_per_frame){ volatile s32 *cmd; int val; static int chi_initialized = 0; if (!chi_initialized) { cmd = dbri_cmdlock(dbri); /* Set CHI Anchor: Pipe 16 */ val = D_DTS_VI | D_DTS_INS | D_DTS_PRVIN(16) | D_PIPE(16); *(cmd++) = DBRI_CMD(D_DTS, 0, val); *(cmd++) = D_TS_ANCHOR | D_TS_NEXT(16); *(cmd++) = 0; val = D_DTS_VO | D_DTS_INS | D_DTS_PRVOUT(16) | D_PIPE(16); *(cmd++) = DBRI_CMD(D_DTS, 0, val); *(cmd++) = 0; *(cmd++) = D_TS_ANCHOR | D_TS_NEXT(16); dbri->pipes[16].sdp = 1; dbri->pipes[16].nextpipe = 16; dbri->chi_in_pipe = 16; dbri->chi_out_pipe = 16;#if 0 chi_initialized ++;#endif } else { int pipe; for (pipe = dbri->chi_in_pipe; pipe != 16; pipe = dbri->pipes[pipe].nextpipe) { unlink_time_slot(dbri, pipe, PIPEinput, 16, dbri->pipes[pipe].nextpipe); } for (pipe = dbri->chi_out_pipe; pipe != 16; pipe = dbri->pipes[pipe].nextpipe) { unlink_time_slot(dbri, pipe, PIPEoutput, 16, dbri->pipes[pipe].nextpipe); } dbri->chi_in_pipe = 16; dbri->chi_out_pipe = 16; cmd = dbri_cmdlock(dbri); } if (master_or_slave == CHIslave) { /* Setup DBRI for CHI Slave - receive clock, frame sync (FS) * * CHICM = 0 (slave mode, 8 kHz frame rate) * IR = give immediate CHI status interrupt * EN = give CHI status interrupt upon change */ *(cmd++) = DBRI_CMD(D_CHI, 0, D_CHI_CHICM(0)); } else { /* Setup DBRI for CHI Master - generate clock, FS * * BPF = bits per 8 kHz frame * 12.288 MHz / CHICM_divisor = clock rate * FD = 1 - drive CHIFS on rising edge of CHICK */ int clockrate = bits_per_frame * 8; int divisor = 12288 / clockrate; if (divisor > 255 || divisor * clockrate != 12288) printk("DBRI: illegal bits_per_frame in setup_chi\n"); *(cmd++) = DBRI_CMD(D_CHI, 0, D_CHI_CHICM(divisor) | D_CHI_FD | D_CHI_BPF(bits_per_frame)); } dbri->chi_bpf = bits_per_frame; /* CHI Data Mode * * RCE = 0 - receive on falling edge of CHICK * XCE = 1 - transmit on rising edge of CHICK * XEN = 1 - enable transmitter * 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); dbri_cmdsend(dbri, cmd);}/**************************************************************************************************** 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.*/static void mmcodec_default(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;}static void mmcodec_setup_pipes(struct dbri *dbri){ /* * 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 readonly) * 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); dbri->mm.status = 0; recv_fixed(dbri, 18, & dbri->mm.status); recv_fixed(dbri, 19, & dbri->mm.version);}static void mmcodec_setgain(struct dbri *dbri, int muted){ if (muted || dbri->perchip_info.output_muted) { dbri->mm.data[0] = 63; dbri->mm.data[1] = 63; } else { int left_gain = (dbri->perchip_info.play.gain / 4) % 64; int right_gain = (dbri->perchip_info.play.gain / 4) % 64; int outport = dbri->perchip_info.play.port; if (dbri->perchip_info.play.balance < AUDIO_MID_BALANCE) { right_gain *= dbri->perchip_info.play.balance; right_gain /= AUDIO_MID_BALANCE; } else { left_gain *= AUDIO_RIGHT_BALANCE - dbri->perchip_info.play.balance; left_gain /= AUDIO_MID_BALANCE; } dprintk(D_MM, ("DBRI: Setting codec gain left: %d right: %d\n", left_gain, right_gain)); dbri->mm.data[0] = (63 - left_gain); if (outport & AUDIO_HEADPHONE) dbri->mm.data[0] |= CS4215_HE; if (outport & AUDIO_LINE_OUT) dbri->mm.data[0] |= CS4215_LE; dbri->mm.data[1] = (63 - right_gain); if (outport & AUDIO_SPEAKER) dbri->mm.data[1] |= CS4215_SE; } xmit_fixed(dbri, 20, *(int *)dbri->mm.data);}static void mmcodec_init_data(struct dbri *dbri){ int data_width; u32 tmp; /* * 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... */ 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->perchip_info.play.channels * dbri->perchip_info.play.precision; link_time_slot(dbri, 20, PIPEoutput, 16, 32, dbri->mm.offset + 32); link_time_slot(dbri, 4, PIPEoutput, 16, data_width, dbri->mm.offset); link_time_slot(dbri, 6, PIPEinput, 16, data_width, dbri->mm.offset); link_time_slot(dbri, 21, PIPEinput, 16, 16, dbri->mm.offset + 40); mmcodec_setgain(dbri, 0); tmp = sbus_readl(dbri->regs + REG0); tmp |= D_C; /* Enable CHI */ sbus_writel(tmp, dbri->regs + REG0);}/* * Send the control information (i.e. audio format)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -