浅析ac97声卡intel8x0的pci总线DMA物理地址填充和音频数据发送流程
DEVICE_INTEL_ICH4一共有6级缓冲[6个功能,一个pci卡最多有8个功能],每一级缓冲大小为ichdev->fragsize,即8192字节.
snd_intel8x0_probe
==> snd_intel8x0_create
static unsigned int bdbars[] = {
3, /* DEVICE_INTEL */
6, /* DEVICE_INTEL_ICH4 */
3, /* DEVICE_SIS */
6, /* DEVICE_ALI */
4, /* DEVICE_NFORCE */
};
chip->addr = pci_iomap(pci, 2, 0); // 映射pci总线bank2的物理地址到内核空间,该pci-bank2内部含有6级声音缓冲[对应6个功能,一个pci卡最多有8个功能]
chip->bdbars_count = bdbars[device_type]; // intel8x0每种不同ac97声卡型号,并不是对应的pci-bank数目,而是对应声卡中音频缓冲区bar数目,我的是DEVICE_INTEL_ICH4,一共有6个声音缓冲[luther.gliethttp]
for (i = 0; i < chip->bdbars_count; i++) {
ichdev = &chip->ichd[i];
ichdev->ichd = i;
ichdev->reg_offset = tbl[i].offset;
ichdev->int_sta_mask = tbl[i].int_sta_mask;
if (device_type == DEVICE_SIS) {
/* SiS 7012 swaps the registers */
ichdev->roff_sr = ICH_REG_OFF_PICB;
ichdev->roff_picb = ICH_REG_OFF_SR;
} else {
ichdev->roff_sr = ICH_REG_OFF_SR;
ichdev->roff_picb = ICH_REG_OFF_PICB;
}
if (device_type == DEVICE_ALI)
ichdev->ali_slot = (ichdev->reg_offset - 0x40) / 0x10;
/* SIS7012 handles the pcm data in bytes, others are in samples */
ichdev->pos_shift = (device_type == DEVICE_SIS) ? 0 : 1;
}
for (i = 0; i < chip->bdbars_count; i++) {
ichdev = &chip->ichd[i];
ichdev->bdbar = ((u32 *)chip->bdbars.area) + // 本bar声音缓冲对应的内核线性地址[luther.gliethttp]
(i * ICH_MAX_FRAGS * 2);
ichdev->bdbar_addr = chip->bdbars.addr + // 本bar声音缓冲对应的pci上的物理地址[luther.gliethttp]
(i * sizeof(u32) * ICH_MAX_FRAGS * 2);
int_sta_masks |= ichdev->int_sta_mask;
}
[22753.679940] +++++snd_dma_alloc_pages++size=1536,vaddr=c971e000,paddr=0971e000, type=2+++++
[22753.679947] bdbars[0].bdbar=c971e000 // 一共6级bar缓冲[6个功能,一个pci卡最多有8个功能],每个bar功能占用256字节内存空间
[22753.679949] bdbars[0].bdbar_addr=0971e000
[22753.679954] bdbars[1].bdbar=c971e100
[22753.679956] bdbars[1].bdbar_addr=0971e100
[22753.679961] bdbars[2].bdbar=c971e200
[22753.679962] bdbars[2].bdbar_addr=0971e200
[22753.679967] bdbars[3].bdbar=c971e300
[22753.679969] bdbars[3].bdbar_addr=0971e300
[22753.679973] bdbars[4].bdbar=c971e400
[22753.679975] bdbars[4].bdbar_addr=0971e400
[22753.679980] bdbars[5].bdbar=c971e500
[22753.679982] bdbars[5].bdbar_addr=0971e500
ok,上面已经将bar声卡上的音频缓冲区地址进行了获取和存储,接下来使用这些缓冲来传输音频数据[luther.gliethttp]
首先填充6个功能声音缓冲区DMA传输使用到的DDR音频数据内存地址[luther.gliethttp]
ioctl(SNDRV_PCM_IOCTL_PREPARE)
==> snd_pcm_playback_ioctl
==> snd_pcm_playback_ioctl1
==> snd_pcm_common_ioctl1
==> snd_pcm_prepare // prepare the PCM substream to be triggerable
==> snd_pcm_action_nonatomic(&snd_pcm_action_prepare,
substream, f_flags);
==> snd_pcm_action_single(ops, substream, state);
ops->pre_action(substream, state);
ops->do_action(substream, state);
ops->post_action(substream, state);
上面ops就是之前提到的snd_pcm_action_prepare
==> snd_pcm_do_prepare调用snd_pcm_do_reset(substream, 0);复位
substream->ops->prepare(substream);即snd_intel8x0_playback_ops.prepare
==> snd_intel8x0_pcm_prepare
static int snd_intel8x0_pcm_prepare(struct snd_pcm_substream *substream)
{
struct intel8x0 *chip = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
struct ichdev *ichdev = get_ichdev(substream);
ichdev->physbuf = runtime->dma_addr; // dma缓冲区地址
ichdev->size = snd_pcm_lib_buffer_bytes(substream); // 将帧缓冲大小转为字节空间大小[luther.gliethttp]
ichdev->fragsize = snd_pcm_lib_period_bytes(substream);
if (ichdev->ichd == ICHD_PCMOUT) {
snd_intel8x0_setup_pcm_out(chip, runtime); // 为play模式设置ac97寄存器[luther.gliethttp]
if (chip->device_type == DEVICE_INTEL_ICH4)
ichdev->pos_shift = (runtime->sample_bits > 16) ? 2 : 1;
}
snd_intel8x0_setup_periods(chip, ichdev); // 设置PCI总线ac97的bank地址空间[luther.gliethttp]
return 0;
}
==> snd_intel8x0_setup_periods
/*
* DMA I/O
*/
static void snd_intel8x0_setup_periods(struct intel8x0 *chip, struct ichdev *ichdev)
{
int idx;
u32 *bdbar = ichdev->bdbar; // 使用bar功能对应的内核线性地址,来操作声卡pci地址[luther.gliethttp]
unsigned long port = ichdev->reg_offset;
iputdword(chip, port + ICH_REG_OFF_BDBAR, ichdev->bdbar_addr);
if (ichdev->size == ichdev->fragsize) {
ichdev->ack_reload = ichdev->ack = 2;
ichdev->fragsize1 = ichdev->fragsize >> 1;
for (idx = 0; idx < (ICH_REG_LVI_MASK + 1) * 2; idx += 4) {
《浅析ac97声卡intel8x0的runtime->dma_area是怎么获取的》 bdbar[idx + 0] = cpu_to_le32(ichdev->physbuf);
bdbar[idx + 1] = cpu_to_le32(0x80000000 | /* interrupt on completion */
ichdev->fragsize1 >> ichdev->pos_shift);
bdbar[idx + 2] = cpu_to_le32(ichdev->physbuf + (ichdev->size >> 1));
bdbar[idx + 3] = cpu_to_le32(0x80000000 | /* interrupt on completion */
ichdev->fragsize1 >> ichdev->pos_shift);
}
ichdev->frags = 2;
} else {
// 对于DEVICE_INTEL_ICH4声卡
ichdev->ack_reload = ichdev->ack = 1;
ichdev->fragsize1 = ichdev->fragsize;
for (idx = 0; idx < (ICH_REG_LVI_MASK + 1) * 2; idx += 2) {
// 将音频申请到的DMA内存地址作为pci声卡进行DMA传输的数据源地址[luther.gliethttp]
bdbar[idx + 0] = cpu_to_le32(ichdev->physbuf +
(((idx >> 1) * ichdev->fragsize) %
ichdev->size));
bdbar[idx + 1] = cpu_to_le32(0x80000000 | /* interrupt on completion */
ichdev->fragsize >> ichdev->pos_shift);
// 使能中断,本级音频缓冲区bar功能音频数据发送完毕之后,声卡将通过pci触发snd_intel8x0_interrupt中断[luther.gliethttp]
#if 0
printk(KERN_DEBUG "bdbar[%i] = 0x%x [0x%x]\n",
idx + 0, bdbar[idx + 0], bdbar[idx + 1]);
#endif
}
ichdev->frags = ichdev->size / ichdev->fragsize;
}
iputbyte(chip, port + ICH_REG_OFF_LVI, ichdev->lvi = ICH_REG_LVI_MASK);
ichdev->civ = 0;
iputbyte(chip, port + ICH_REG_OFF_CIV, 0);
ichdev->lvi_frag = ICH_REG_LVI_MASK % ichdev->frags;
ichdev->position = 0;
#if 0
printk(KERN_DEBUG "lvi_frag = %i, frags = %i, period_size = 0x%x, "
"period_size1 = 0x%x\n",
ichdev->lvi_frag, ichdev->frags, ichdev->fragsize,
ichdev->fragsize1);
#endif
/* clear interrupts */
iputbyte(chip, port + ichdev->roff_sr, ICH_FIFOE | ICH_BCIS | ICH_LVBCI);
}
启动pci声卡的DMA传输
ioctl(SNDRV_PCM_IOCTL_START)
==> snd_pcm_playback_ioctl
==> snd_pcm_playback_ioctl1
==> snd_pcm_common_ioctl1
==> snd_pcm_action_lock_irq(&snd_pcm_action_start, substream, SNDRV_PCM_STATE_RUNNING);
==> snd_pcm_action_single // state等于SNDRV_PCM_STATE_RUNNING
static struct action_ops snd_pcm_action_start = {
.pre_action = snd_pcm_pre_start,
.do_action = snd_pcm_do_start,
.undo_action = snd_pcm_undo_start,
.post_action = snd_pcm_post_start
};
ops->pre_action(substream, state);
ops->do_action(substream, state);
ops->post_action(substream, state);
上面ops就是之前提到的snd_pcm_action_start
==> snd_pcm_do_start
==> substream->ops->trigger(substream, SNDRV_PCM_TRIGGER_START);即snd_intel8x0_playback_ops.trigger
==> snd_intel8x0_pcm_trigger启动ac97数据传输
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
val = ICH_IOCE | ICH_STARTBM; // 使能pci总线DMA音频数据传输[luther.gliethttp]
ichdev->last_pos = ichdev->position;
break;
当pci声卡ac97将本bar缓冲数据[一个有6级缓冲,对应pci的6个功能]传输完毕之后,pci声卡将出发中断,
执行snd_intel8x0_interrupt中断处理函数[luther.gliethttp]
static irqreturn_t snd_intel8x0_interrupt(int irq, void *dev_id)
{
struct intel8x0 *chip = dev_id;
struct ichdev *ichdev;
unsigned int status;
unsigned int i;
status = igetdword(chip, chip->int_sta_reg);
if (status == 0xffffffff) /* we are not yet resumed */
return IRQ_NONE;
if ((status & chip->int_sta_mask) == 0) {
if (status) {
/* ack */
iputdword(chip, chip->int_sta_reg, status);
if (! chip->buggy_irq)
status = 0;
}
return IRQ_RETVAL(status);
}
for (i = 0; i < chip->bdbars_count; i++) {
ichdev = &chip->ichd[i];
if (status & ichdev->int_sta_mask)
snd_intel8x0_update(chip, ichdev);
}
/* ack them */
iputdword(chip, chip->int_sta_reg, status & chip->int_sta_mask);
return IRQ_HANDLED;
}