📄 dbri.c
字号:
/* Stream could be closed by the time we run. */ if (first_td >= 0) { cmd = dbri_cmdlock(dbri, 2); *(cmd++) = DBRI_CMD(D_SDP, 0, dbri->pipes[info->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, 2); /* Reset our admin of the pipe. */ dbri->pipes[info->pipe].desc = first_td; } } info = &dbri->stream_info[DBRI_PLAY]; if (info->pipe >= 0) { first_td = dbri->pipes[info->pipe].first_desc; dprintk(D_DESC, "xmit_descs play @ TD %d\n", first_td); /* Stream could be closed by the time we run. */ if (first_td >= 0) { cmd = dbri_cmdlock(dbri, 2); *(cmd++) = DBRI_CMD(D_SDP, 0, dbri->pipes[info->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, 2); /* Reset our admin of the pipe. */ dbri->pipes[info->pipe].desc = first_td; } } spin_unlock_irqrestore(&dbri->lock, flags);}/* transmission_complete_intr() * * Called by main interrupt handler when DBRI signals transmission complete * on a pipe (interrupt triggered by the B bit in a transmit descriptor). * * Walks through the pipe's list of transmit buffer descriptors and marks * them as available. Stops when the first descriptor is found without * TBC (Transmit Buffer Complete) set, or we've run through them all. * * The DMA buffers are not released. They form a ring buffer and * they are filled by ALSA while others are transmitted by DMA. * */static void transmission_complete_intr(struct snd_dbri *dbri, int pipe){ struct dbri_streaminfo *info = &dbri->stream_info[DBRI_PLAY]; int td = dbri->pipes[pipe].desc; int status; while (td >= 0) { if (td >= DBRI_NO_DESCS) { printk(KERN_ERR "DBRI: invalid td on pipe %d\n", pipe); return; } status = DBRI_TD_STATUS(dbri->dma->desc[td].word4); if (!(status & DBRI_TD_TBC)) break; dprintk(D_INT, "TD %d, status 0x%02x\n", td, status); dbri->dma->desc[td].word4 = 0; /* Reset it for next time. */ info->offset += DBRI_RD_CNT(dbri->dma->desc[td].word1); td = dbri->next_desc[td]; dbri->pipes[pipe].desc = td; } /* Notify ALSA */ spin_unlock(&dbri->lock); snd_pcm_period_elapsed(info->substream); spin_lock(&dbri->lock);}static void reception_complete_intr(struct snd_dbri *dbri, int pipe){ struct dbri_streaminfo *info; int rd = dbri->pipes[pipe].desc; s32 status; if (rd < 0 || rd >= DBRI_NO_DESCS) { printk(KERN_ERR "DBRI: invalid rd on pipe %d\n", pipe); return; } dbri->pipes[pipe].desc = dbri->next_desc[rd]; status = dbri->dma->desc[rd].word1; dbri->dma->desc[rd].word1 = 0; /* Reset it for next time. */ info = &dbri->stream_info[DBRI_REC]; info->offset += DBRI_RD_CNT(status); /* FIXME: Check status */ dprintk(D_INT, "Recv RD %d, status 0x%02x, len %d\n", rd, DBRI_RD_STATUS(status), DBRI_RD_CNT(status)); /* Notify ALSA */ spin_unlock(&dbri->lock); snd_pcm_period_elapsed(info->substream); spin_lock(&dbri->lock);}static void dbri_process_one_interrupt(struct snd_dbri *dbri, int x){ int val = D_INTR_GETVAL(x); int channel = D_INTR_GETCHAN(x); int command = D_INTR_GETCMD(x); int code = D_INTR_GETCODE(x);#ifdef DBRI_DEBUG int rval = D_INTR_GETRVAL(x);#endif if (channel == D_INTR_CMD) { dprintk(D_CMD, "INTR: Command: %-5s Value:%d\n", cmds[command], val); } else { dprintk(D_INT, "INTR: Chan:%d Code:%d Val:%#x\n", channel, code, rval); } switch (code) { case D_INTR_CMDI: if (command != D_WAIT) printk(KERN_ERR "DBRI: Command read interrupt\n"); break; case D_INTR_BRDY: reception_complete_intr(dbri, channel); break; case D_INTR_XCMP: case D_INTR_MINT: transmission_complete_intr(dbri, channel); break; case D_INTR_UNDR: /* UNDR - Transmission underrun * resend SDP command with clear pipe bit (C) set */ { /* FIXME: do something useful in case of underrun */ printk(KERN_ERR "DBRI: Underrun error\n");#if 0 s32 *cmd; int pipe = channel; int td = dbri->pipes[pipe].desc; dbri->dma->desc[td].word4 = 0; cmd = dbri_cmdlock(dbri, NoGetLock); *(cmd++) = DBRI_CMD(D_SDP, 0, dbri->pipes[pipe].sdp | D_SDP_P | D_SDP_C | D_SDP_2SAME); *(cmd++) = dbri->dma_dvma + dbri_dma_off(desc, td); dbri_cmdsend(dbri, cmd);#endif } break; case D_INTR_FXDT: /* FXDT - Fixed data change */ if (dbri->pipes[channel].sdp & D_SDP_MSB) val = reverse_bytes(val, dbri->pipes[channel].length); if (dbri->pipes[channel].recv_fixed_ptr) *(dbri->pipes[channel].recv_fixed_ptr) = val; break; default: if (channel != D_INTR_CMD) printk(KERN_WARNING "DBRI: Ignored Interrupt: %d (0x%x)\n", code, x); }}/* dbri_process_interrupt_buffer advances through the DBRI's interrupt * buffer until it finds a zero word (indicating nothing more to do * right now). Non-zero words require processing and are handed off * to dbri_process_one_interrupt AFTER advancing the pointer. */static void dbri_process_interrupt_buffer(struct snd_dbri *dbri){ s32 x; while ((x = dbri->dma->intr[dbri->dbri_irqp]) != 0) { dbri->dma->intr[dbri->dbri_irqp] = 0; dbri->dbri_irqp++; if (dbri->dbri_irqp == DBRI_INT_BLK) dbri->dbri_irqp = 1; dbri_process_one_interrupt(dbri, x); }}static irqreturn_t snd_dbri_interrupt(int irq, void *dev_id){ struct snd_dbri *dbri = dev_id; static int errcnt = 0; int x; if (dbri == NULL) return IRQ_NONE; spin_lock(&dbri->lock); /* * Read it, so the interrupt goes away. */ x = sbus_readl(dbri->regs + REG1); if (x & (D_MRR | D_MLE | D_LBG | D_MBE)) { u32 tmp; if (x & D_MRR) printk(KERN_ERR "DBRI: Multiple Error Ack on SBus reg1=0x%x\n", x); if (x & D_MLE) printk(KERN_ERR "DBRI: Multiple Late Error on SBus reg1=0x%x\n", x); if (x & D_LBG) printk(KERN_ERR "DBRI: Lost Bus Grant on SBus reg1=0x%x\n", x); if (x & D_MBE) printk(KERN_ERR "DBRI: Burst Error on SBus reg1=0x%x\n", x); /* Some of these SBus errors cause the chip's SBus circuitry * to be disabled, so just re-enable and try to keep going. * * The only one I've seen is MRR, which will be triggered * if you let a transmit pipe underrun, then try to CDP it. * * If these things persist, we reset the chip. */ if ((++errcnt) % 10 == 0) { dprintk(D_INT, "Interrupt errors exceeded.\n"); dbri_reset(dbri); } else { tmp = sbus_readl(dbri->regs + REG0); tmp &= ~(D_D); sbus_writel(tmp, dbri->regs + REG0); } } dbri_process_interrupt_buffer(dbri); spin_unlock(&dbri->lock); return IRQ_HANDLED;}/**************************************************************************** PCM Interface****************************************************************************/static struct snd_pcm_hardware snd_dbri_pcm_hw = { .info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP_VALID, .formats = SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW | SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_BE, .rates = SNDRV_PCM_RATE_8000_48000 | SNDRV_PCM_RATE_5512, .rate_min = 5512, .rate_max = 48000, .channels_min = 1, .channels_max = 2, .buffer_bytes_max = 64 * 1024, .period_bytes_min = 1, .period_bytes_max = DBRI_TD_MAXCNT, .periods_min = 1, .periods_max = 1024,};static int snd_hw_rule_format(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule){ struct snd_interval *c = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); struct snd_mask *f = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); struct snd_mask fmt; snd_mask_any(&fmt); if (c->min > 1) { fmt.bits[0] &= SNDRV_PCM_FMTBIT_S16_BE; return snd_mask_refine(f, &fmt); } return 0;}static int snd_hw_rule_channels(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule){ struct snd_interval *c = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); struct snd_mask *f = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); struct snd_interval ch; snd_interval_any(&ch); if (!(f->bits[0] & SNDRV_PCM_FMTBIT_S16_BE)) { ch.min = 1; ch.max = 1; ch.integer = 1; return snd_interval_refine(c, &ch); } return 0;}static int snd_dbri_open(struct snd_pcm_substream *substream){ struct snd_dbri *dbri = snd_pcm_substream_chip(substream); struct snd_pcm_runtime *runtime = substream->runtime; struct dbri_streaminfo *info = DBRI_STREAM(dbri, substream); unsigned long flags; dprintk(D_USR, "open audio output.\n"); runtime->hw = snd_dbri_pcm_hw; spin_lock_irqsave(&dbri->lock, flags); info->substream = substream; info->offset = 0; info->dvma_buffer = 0; info->pipe = -1; spin_unlock_irqrestore(&dbri->lock, flags); snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, snd_hw_rule_format, NULL, SNDRV_PCM_HW_PARAM_FORMAT, -1); snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_FORMAT, snd_hw_rule_channels, NULL, SNDRV_PCM_HW_PARAM_CHANNELS, -1); cs4215_open(dbri); return 0;}static int snd_dbri_close(struct snd_pcm_substream *substream){ struct snd_dbri *dbri = snd_pcm_substream_chip(substream); struct dbri_streaminfo *info = DBRI_STREAM(dbri, substream); dprintk(D_USR, "close audio output.\n"); info->substream = NULL; info->offset = 0; return 0;}static int snd_dbri_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params){ struct snd_pcm_runtime *runtime = substream->runtime; struct snd_dbri *dbri = snd_pcm_substream_chip(substream); struct dbri_streaminfo *info = DBRI_STREAM(dbri, substream); int direction; int ret; /* set sampling rate, audio format and number of channels */ ret = cs4215_prepare(dbri, params_rate(hw_params), params_format(hw_params), params_channels(hw_params)); if (ret != 0) return ret; if ((ret = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0) { printk(KERN_ERR "malloc_pages failed with %d\n", ret); return ret; } /* hw_params can get called multiple times. Only map the DMA once. */ if (info->dvma_buffer == 0) { if (DBRI_STREAMNO(substream) == DBRI_PLAY) direction = SBUS_DMA_TODEVICE; else direction = SBUS_DMA_FROMDEVICE; info->dvma_buffer = sbus_map_single(dbri->sdev, runtime->dma_area, params_buffer_bytes(hw_params), direction); } direction = params_buffer_bytes(hw_params); dprintk(D_USR, "hw_params: %d bytes, dvma=%x\n", direction, info->dvma_buffer); return 0;}static int snd_dbri_hw_free(struct snd_pcm_substream *substream){ struct snd_dbri *dbri = snd_pcm_substream_chip(substream); struct dbri_streaminfo *info = DBRI_STREAM(dbri, substream); int direction; dprintk(D_USR, "hw_free.\n"); /* hw_free can get called multiple times. Only unmap the DMA once. */ if (info->dvma_buffer) { if (DBRI_STREAMNO(substream) == DBRI_PLAY) direction = SBUS_DMA_TODEVICE; else direction = SBUS_DMA_FROMDEVICE; sbus_unmap_single(dbri->sdev, info->dvma_buffer, substream->runtime->buffer_size, direction); info->dvma_buffer = 0; } if (info->pipe != -1) { reset_pipe(dbri, info->pipe); info->pipe = -1; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -