📄 ac97.c
字号:
switch (index) { case PI_SR: case PO_SR: case MC_SR: r = &s->bm_regs[GET_BM (index)]; r->sr |= val & ~(SR_RO_MASK | SR_WCLEAR_MASK); update_sr (s, r, r->sr & ~(val & SR_WCLEAR_MASK)); dolog ("SR[%d] <- %#x (sr %#x)\n", GET_BM (index), val, r->sr); break; default: dolog ("U nabm writew %#x <- %#x\n", addr, val); break; }}static void nabm_writel (void *opaque, uint32_t addr, uint32_t val){ PCIAC97LinkState *d = opaque; AC97LinkState *s = &d->ac97; AC97BusMasterRegs *r = NULL; uint32_t index = addr - s->base[1]; switch (index) { case PI_BDBAR: case PO_BDBAR: case MC_BDBAR: r = &s->bm_regs[GET_BM (index)]; r->bdbar = val & ~3; dolog ("BDBAR[%d] <- %#x (bdbar %#x)\n", GET_BM (index), val, r->bdbar); break; case GLOB_CNT: if (val & GC_WR) warm_reset (s); if (val & GC_CR) cold_reset (s); if (!(val & (GC_WR | GC_CR))) s->glob_cnt = val & GC_VALID_MASK; dolog ("glob_cnt <- %#x (glob_cnt %#x)\n", val, s->glob_cnt); break; case GLOB_STA: s->glob_sta &= ~(val & GS_WCLEAR_MASK); s->glob_sta |= (val & ~(GS_WCLEAR_MASK | GS_RO_MASK)) & GS_VALID_MASK; dolog ("glob_sta <- %#x (glob_sta %#x)\n", val, s->glob_sta); break; default: dolog ("U nabm writel %#x <- %#x\n", addr, val); break; }}static int write_audio (AC97LinkState *s, AC97BusMasterRegs *r, int max, int *stop){ uint8_t tmpbuf[4096]; uint32_t addr = r->bd.addr; uint32_t temp = r->picb << 1; uint32_t written = 0; int to_copy = 0; temp = audio_MIN (temp, max); if (!temp) { *stop = 1; return 0; } while (temp) { int copied; to_copy = audio_MIN (temp, sizeof (tmpbuf)); cpu_physical_memory_read (addr, tmpbuf, to_copy); copied = AUD_write (s->voice_po, tmpbuf, to_copy); dolog ("write_audio max=%x to_copy=%x copied=%x\n", max, to_copy, copied); if (!copied) { *stop = 1; break; } temp -= copied; addr += copied; written += copied; } if (!temp) { if (to_copy < 4) { dolog ("whoops\n"); s->last_samp = 0; } else { s->last_samp = *(uint32_t *) &tmpbuf[to_copy - 4]; } } r->bd.addr = addr; return written;}static void write_bup (AC97LinkState *s, int elapsed){ int written = 0; dolog ("write_bup\n"); if (!(s->bup_flag & BUP_SET)) { if (s->bup_flag & BUP_LAST) { int i; uint8_t *p = s->silence; for (i = 0; i < sizeof (s->silence) / 4; i++, p += 4) { *(uint32_t *) p = s->last_samp; } } else { memset (s->silence, 0, sizeof (s->silence)); } s->bup_flag |= BUP_SET; } while (elapsed) { int temp = audio_MIN (elapsed, sizeof (s->silence)); while (temp) { int copied = AUD_write (s->voice_po, s->silence, temp); if (!copied) return; temp -= copied; elapsed -= copied; written += copied; } }}static int read_audio (AC97LinkState *s, AC97BusMasterRegs *r, int max, int *stop){ uint8_t tmpbuf[4096]; uint32_t addr = r->bd.addr; uint32_t temp = r->picb << 1; uint32_t nread = 0; int to_copy = 0; SWVoiceIn *voice = (r - s->bm_regs) == MC_INDEX ? s->voice_mc : s->voice_pi; temp = audio_MIN (temp, max); if (!temp) { *stop = 1; return 0; } while (temp) { int acquired; to_copy = audio_MIN (temp, sizeof (tmpbuf)); acquired = AUD_read (voice, tmpbuf, to_copy); if (!acquired) { *stop = 1; break; } cpu_physical_memory_write (addr, tmpbuf, acquired); temp -= acquired; addr += acquired; nread += acquired; } r->bd.addr = addr; return nread;}static void transfer_audio (AC97LinkState *s, int index, int elapsed){ AC97BusMasterRegs *r = &s->bm_regs[index]; int written = 0, stop = 0; if (r->sr & SR_DCH) { if (r->cr & CR_RPBM) { switch (index) { case PO_INDEX: write_bup (s, elapsed); break; } } return; } while ((elapsed >> 1) && !stop) { int temp; if (!r->bd_valid) { dolog ("invalid bd\n"); fetch_bd (s, r); } if (!r->picb) { dolog ("fresh bd %d is empty %#x %#x\n", r->civ, r->bd.addr, r->bd.ctl_len); if (r->civ == r->lvi) { r->sr |= SR_DCH; /* CELV? */ s->bup_flag = 0; break; } r->sr &= ~SR_CELV; r->civ = r->piv; r->piv = (r->piv + 1) % 32; fetch_bd (s, r); return; } switch (index) { case PO_INDEX: temp = write_audio (s, r, elapsed, &stop); written += temp; elapsed -= temp; r->picb -= (temp >> 1); break; case PI_INDEX: case MC_INDEX: temp = read_audio (s, r, elapsed, &stop); elapsed -= temp; r->picb -= (temp >> 1); break; } if (!r->picb) { uint32_t new_sr = r->sr & ~SR_CELV; if (r->bd.ctl_len & BD_IOC) { new_sr |= SR_BCIS; } if (r->civ == r->lvi) { dolog ("Underrun civ (%d) == lvi (%d)\n", r->civ, r->lvi); new_sr |= SR_LVBCI | SR_DCH | SR_CELV; stop = 1; s->bup_flag = (r->bd.ctl_len & BD_BUP) ? BUP_LAST : 0; } else { r->civ = r->piv; r->piv = (r->piv + 1) % 32; fetch_bd (s, r); } update_sr (s, r, new_sr); } }}static void pi_callback (void *opaque, int avail){ transfer_audio (opaque, PI_INDEX, avail);}static void mc_callback (void *opaque, int avail){ transfer_audio (opaque, MC_INDEX, avail);}static void po_callback (void *opaque, int free){ transfer_audio (opaque, PO_INDEX, free);}static void ac97_save (QEMUFile *f, void *opaque){ size_t i; uint8_t active[LAST_INDEX]; AC97LinkState *s = opaque; qemu_put_be32s (f, &s->glob_cnt); qemu_put_be32s (f, &s->glob_sta); qemu_put_be32s (f, &s->cas); for (i = 0; i < sizeof (s->bm_regs) / sizeof (s->bm_regs[0]); ++i) { AC97BusMasterRegs *r = &s->bm_regs[i]; qemu_put_be32s (f, &r->bdbar); qemu_put_8s (f, &r->civ); qemu_put_8s (f, &r->lvi); qemu_put_be16s (f, &r->sr); qemu_put_be16s (f, &r->picb); qemu_put_8s (f, &r->piv); qemu_put_8s (f, &r->cr); qemu_put_be32s (f, &r->bd_valid); qemu_put_be32s (f, &r->bd.addr); qemu_put_be32s (f, &r->bd.ctl_len); } qemu_put_buffer (f, s->mixer_data, sizeof (s->mixer_data)); active[PI_INDEX] = AUD_is_active_in (s->voice_pi) ? 1 : 0; active[PO_INDEX] = AUD_is_active_out (s->voice_po) ? 1 : 0; active[MC_INDEX] = AUD_is_active_in (s->voice_mc) ? 1 : 0; qemu_put_buffer (f, active, sizeof (active));}static int ac97_load (QEMUFile *f, void *opaque, int version_id){ size_t i; uint8_t active[LAST_INDEX]; AC97LinkState *s = opaque; if (version_id != 1) return -EINVAL; qemu_get_be32s (f, &s->glob_cnt); qemu_get_be32s (f, &s->glob_sta); qemu_get_be32s (f, &s->cas); for (i = 0; i < sizeof (s->bm_regs) / sizeof (s->bm_regs[0]); ++i) { AC97BusMasterRegs *r = &s->bm_regs[i]; qemu_get_be32s (f, &r->bdbar); qemu_get_8s (f, &r->civ); qemu_get_8s (f, &r->lvi); qemu_get_be16s (f, &r->sr); qemu_get_be16s (f, &r->picb); qemu_get_8s (f, &r->piv); qemu_get_8s (f, &r->cr); qemu_get_be32s (f, &r->bd_valid); qemu_get_be32s (f, &r->bd.addr); qemu_get_be32s (f, &r->bd.ctl_len); } qemu_get_buffer (f, s->mixer_data, sizeof (s->mixer_data)); qemu_get_buffer (f, active, sizeof (active));#ifdef USE_MIXER record_select (s, mixer_load (s, AC97_Record_Select));#define V_(a, b) set_volume (s, a, b, mixer_load (s, a)) V_ (AC97_Master_Volume_Mute, AUD_MIXER_VOLUME); V_ (AC97_PCM_Out_Volume_Mute, AUD_MIXER_PCM); V_ (AC97_Line_In_Volume_Mute, AUD_MIXER_LINE_IN);#undef V_#endif reset_voices (s, active); s->bup_flag = 0; s->last_samp = 0; return 0;}static void ac97_map (PCIDevice *pci_dev, int region_num, uint32_t addr, uint32_t size, int type){ PCIAC97LinkState *d = (PCIAC97LinkState *) pci_dev; AC97LinkState *s = &d->ac97; if (!region_num) { s->base[0] = addr; register_ioport_read (addr, 256 * 1, 1, nam_readb, d); register_ioport_read (addr, 256 * 2, 2, nam_readw, d); register_ioport_read (addr, 256 * 4, 4, nam_readl, d); register_ioport_write (addr, 256 * 1, 1, nam_writeb, d); register_ioport_write (addr, 256 * 2, 2, nam_writew, d); register_ioport_write (addr, 256 * 4, 4, nam_writel, d); } else { s->base[1] = addr; register_ioport_read (addr, 64 * 1, 1, nabm_readb, d); register_ioport_read (addr, 64 * 2, 2, nabm_readw, d); register_ioport_read (addr, 64 * 4, 4, nabm_readl, d); register_ioport_write (addr, 64 * 1, 1, nabm_writeb, d); register_ioport_write (addr, 64 * 2, 2, nabm_writew, d); register_ioport_write (addr, 64 * 4, 4, nabm_writel, d); }}static void ac97_on_reset (void *opaque){ AC97LinkState *s = opaque; reset_bm_regs (s, &s->bm_regs[0]); reset_bm_regs (s, &s->bm_regs[1]); reset_bm_regs (s, &s->bm_regs[2]); /* * Reset the mixer too. The Windows XP driver seems to rely on * this. At least it wants to read the vendor id before it resets * the codec manually. */ mixer_reset (s);}int ac97_init (PCIBus *bus, AudioState *audio){ PCIAC97LinkState *d; AC97LinkState *s; uint8_t *c; if (!bus) { AUD_log ("ac97", "No PCI bus\n"); return -1; } if (!audio) { AUD_log ("ac97", "No audio state\n"); return -1; } d = (PCIAC97LinkState *) pci_register_device (bus, "AC97", sizeof (PCIAC97LinkState), -1, NULL, NULL); if (!d) { AUD_log ("ac97", "Failed to register PCI device\n"); return -1; } s = &d->ac97; s->pci_dev = &d->dev; c = d->dev.config; c[0x00] = 0x86; /* vid vendor id intel ro */ c[0x01] = 0x80; /* intel */ c[0x02] = 0x15; /* did device id 82801 ro */ c[0x03] = 0x24; /* 82801aa */ c[0x04] = 0x00; /* pcicmd pci command rw, ro */ c[0x05] = 0x00; c[0x06] = 0x80; /* pcists pci status rwc, ro */ c[0x07] = 0x02; c[0x08] = 0x01; /* rid revision ro */ c[0x09] = 0x00; /* pi programming interface ro */ c[0x0a] = 0x01; /* scc sub class code ro */ c[0x0b] = 0x04; /* bcc base class code ro */ c[0x0e] = 0x00; /* headtyp header type ro */ c[0x10] = 0x01; /* nabmar native audio mixer base address rw */ c[0x11] = 0x00; c[0x12] = 0x00; c[0x13] = 0x00; c[0x14] = 0x01; /* nabmbar native audio bus mastering base address rw */ c[0x15] = 0x00; c[0x16] = 0x00; c[0x17] = 0x00; c[0x2c] = 0x86; /* svid subsystem vendor id rwo */ c[0x2d] = 0x80; c[0x2e] = 0x00; /* sid subsystem id rwo */ c[0x2f] = 0x00; c[0x3c] = 0x00; /* intr_ln interrupt line rw */ c[0x3d] = 0x01; /* intr_pn interrupt pin ro */ pci_register_io_region (&d->dev, 0, 256 * 4, PCI_ADDRESS_SPACE_IO, ac97_map); pci_register_io_region (&d->dev, 1, 64 * 4, PCI_ADDRESS_SPACE_IO, ac97_map); register_savevm ("ac97", 0, 1, ac97_save, ac97_load, s); qemu_register_reset (ac97_on_reset, s); AUD_register_card (audio, "ac97", &s->card); ac97_on_reset (s); return 0;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -