From ff1a55d08d3a8be7160860971c4a6e48f51757eb Mon Sep 17 00:00:00 2001 From: RichardG867 Date: Tue, 13 Jul 2021 00:53:26 -0300 Subject: [PATCH] More AC97, now with VIA kinda sorta working and ES1371 --- src/chipset/via_pipc.c | 4 + src/include/86box/snd_ac97.h | 1 + src/pci.c | 2 +- src/sound/snd_ac97_codec.c | 15 +- src/sound/snd_ac97_via.c | 610 +++++++++++++++++++---------------- src/sound/snd_audiopci.c | 86 ++--- 6 files changed, 402 insertions(+), 316 deletions(-) diff --git a/src/chipset/via_pipc.c b/src/chipset/via_pipc.c index 9ad32deca..95e900b5e 100644 --- a/src/chipset/via_pipc.c +++ b/src/chipset/via_pipc.c @@ -1125,6 +1125,10 @@ pipc_write(int func, int addr, uint8_t val, void *priv) gameport_remap(dev->gameport, (dev->ac97_regs[0][0x42] & 0x08) ? ((dev->ac97_regs[0][0x4b] << 8) | dev->ac97_regs[0][0x4a]) : 0); break; + case 0x41: + dev->ac97_regs[func][addr] = val; + break; + case 0x43: dev->ac97_regs[0][addr] = dev->ac97_regs[1][addr] = val; break; diff --git a/src/include/86box/snd_ac97.h b/src/include/86box/snd_ac97.h index 8d66cdea4..e0345227a 100644 --- a/src/include/86box/snd_ac97.h +++ b/src/include/86box/snd_ac97.h @@ -41,6 +41,7 @@ extern ac97_codec_t **ac97_codec, **ac97_modem_codec; extern int ac97_codec_count, ac97_modem_codec_count; extern const device_t alc100_device; +extern const device_t cs4297a_device; extern const device_t ac97_via_device; #endif diff --git a/src/pci.c b/src/pci.c index a4272e6bd..5f1c48f50 100644 --- a/src/pci.c +++ b/src/pci.c @@ -73,7 +73,7 @@ static int trc_reg = 0; static void pci_reset_regs(void); -#define ENABLE_PCI_LOG 1 + #ifdef ENABLE_PCI_LOG int pci_do_log = ENABLE_PCI_LOG; diff --git a/src/sound/snd_ac97_codec.c b/src/sound/snd_ac97_codec.c index e96795011..f97b1c551 100644 --- a/src/sound/snd_ac97_codec.c +++ b/src/sound/snd_ac97_codec.c @@ -29,7 +29,8 @@ enum { - AC97_CODEC_ALC100 = AC97_CODEC_ID('A', 'L', 'C', 0x20) + AC97_CODEC_ALC100 = AC97_CODEC_ID('A', 'L', 'C', 0x20), + AC97_CODEC_CS4297A = AC97_CODEC_ID('C', 'R', 'Y', 0x11) }; #define ENABLE_AC97_CODEC_LOG 1 @@ -225,3 +226,15 @@ const device_t alc100_device = NULL, NULL }; + +const device_t cs4297a_device = +{ + "Crystal CS4297A", + DEVICE_AC97, + AC97_CODEC_CS4297A, + ac97_codec_init, ac97_codec_close, ac97_codec_reset, + { NULL }, + NULL, + NULL, + NULL +}; diff --git a/src/sound/snd_ac97_via.c b/src/sound/snd_ac97_via.c index 85e18b71b..0ee33b94c 100644 --- a/src/sound/snd_ac97_via.c +++ b/src/sound/snd_ac97_via.c @@ -30,21 +30,29 @@ #include <86box/sound.h> #include <86box/snd_ac97.h> -#define SGD_UNPAUSED (dev->sgd_regs[0x00] & 0xc4) == 0x80 - typedef struct { + uint8_t id; + struct _ac97_via_ *dev; + + uint64_t entry; + uint32_t entry_ptr, sample_ptr, fifo_pos, fifo_end; + int32_t sample_count; + uint8_t fifo[32]; + + pc_timer_t timer; +} ac97_via_sgd_t; + +typedef struct _ac97_via_ { uint16_t audio_sgd_base, audio_codec_base, modem_sgd_base, modem_codec_base; uint8_t sgd_regs[256], irq_stuck; - int slot, irq_pin, losticount; - uint64_t sgd_entry; - uint32_t sgd_entry_ptr, sgd_sample_ptr; - int32_t sgd_sample_count; + int slot, irq_pin; - ac97_codec_t *codec[4]; + ac97_codec_t *codec[2][2]; + ac97_via_sgd_t sgd[6]; pc_timer_t timer_count; - uint64_t timer_latch; + uint64_t timer_latch, timer_fifo_latch; int16_t out_l, out_r; double cd_vol_l, cd_vol_r; int16_t buffer[SOUNDBUFLEN * 2]; @@ -54,6 +62,7 @@ typedef struct { #define ENABLE_AC97_VIA_LOG 1 #ifdef ENABLE_AC97_VIA_LOG int ac97_via_do_log = ENABLE_AC97_VIA_LOG; +unsigned int ac97_via_lost_irqs = 0; static void ac97_via_log(const char *fmt, ...) @@ -71,7 +80,7 @@ ac97_via_log(const char *fmt, ...) #endif -static void ac97_via_poll(void *priv); +static void ac97_via_sgd_process(void *priv); void @@ -94,7 +103,7 @@ ac97_via_read_status(void *priv, uint8_t modem) /* Flag codecs as ready if present. */ for (uint8_t i = 0; i <= 1; i++) { - if (dev->codec[(modem << 1) | i]) + if (dev->codec[modem][i]) ret |= 0x01 << (i << 1); } @@ -105,40 +114,32 @@ ac97_via_read_status(void *priv, uint8_t modem) static void -ac97_via_sgd_startstop(ac97_via_t *dev) +ac97_via_update_irqs(ac97_via_t *dev, uint8_t iflag_clear) { - /* Start polling timer if SGD is unpaused. */ -#if 0 - if (SGD_UNPAUSED) { - ac97_via_log("AC97 VIA: Starting SGD at %08X\n", dev->sgd_entry_ptr); - timer_set_delay_u64(&dev->timer_count, dev->timer_latch); - } else { - ac97_via_log("AC97 VIA: Stopping SGD\n"); - timer_disable(&dev->timer_count); - dev->out_l = dev->out_r = 0; - } + /* Check interrupt flags on all SGDs. */ + for (uint8_t i = 0; i < (sizeof(dev->sgd) / sizeof(dev->sgd[0])); i++) { + if (dev->sgd_regs[i << 4] & (dev->sgd_regs[(i << 4) | 0x02] & 0x03)) { + ac97_via_log("AC97 VIA: Setting IRQ (sgd %d iflags %02X stuck %d)\n", + i, dev->sgd_regs[i << 4] & (dev->sgd_regs[(i << 4) | 0x02] & 0x03), dev->irq_stuck); + + if (dev->irq_stuck && !iflag_clear) { +#ifdef ENABLE_AC97_VIA_LOG + ac97_via_lost_irqs++; #endif -} + pci_clear_irq(dev->slot, dev->irq_pin); + } else { + pci_set_irq(dev->slot, dev->irq_pin); + } + dev->irq_stuck = !dev->irq_stuck; + return; + } + } -static void -ac97_via_sgd_block_start(ac97_via_t *dev) -{ - /* Start at first entry. */ - if (!dev->sgd_entry_ptr) - dev->sgd_entry_ptr = (dev->sgd_regs[0x07] << 24) | (dev->sgd_regs[0x06] << 16) | (dev->sgd_regs[0x05] << 8) | (dev->sgd_regs[0x04] & 0xfe); - - /* Read entry. */ - dev->sgd_entry = ((uint64_t) mem_readl_phys(dev->sgd_entry_ptr + 4) << 32ULL) | (uint64_t) mem_readl_phys(dev->sgd_entry_ptr); - if (dev->sgd_entry == 0xffffffffffffffffULL) - fatal("AC97 VIA: Invalid SGD entry at %08X\n", dev->sgd_entry_ptr); - - /* Set sample pointer and count. */ - dev->sgd_sample_ptr = dev->sgd_entry & 0xffffffff; - dev->sgd_sample_count = (dev->sgd_entry >> 32) & 0xffffff; - - ac97_via_log("AC97 VIA: Starting SGD block at %08X entry %08X%08X (start %08X len %06X) losticount %d\n", - dev->sgd_entry_ptr, mem_readl_phys(dev->sgd_entry_ptr + 4), mem_readl_phys(dev->sgd_entry_ptr), dev->sgd_sample_ptr, dev->sgd_sample_count, dev->losticount); + /* No interrupt pending. */ + //ac97_via_log("AC97 VIA: Clearing IRQ\n"); + pci_clear_irq(dev->slot, dev->irq_pin); + dev->irq_stuck = 0; } @@ -148,78 +149,86 @@ ac97_via_sgd_read(uint16_t addr, void *priv) ac97_via_t *dev = (ac97_via_t *) priv; uint8_t modem = (addr & 0xff00) == dev->modem_sgd_base; addr &= 0xff; - uint8_t ret = 0x00; + uint8_t ret; - switch (addr) { - case 0x04: - ret = dev->sgd_entry_ptr; - break; + if (!(addr & 0x80)) { + /* Process SGD channel registers. */ + switch (addr & 0xf) { + case 0x4: + ret = dev->sgd[addr >> 4].entry_ptr; + break; - case 0x05: - ret = dev->sgd_entry_ptr >> 8; - break; + case 0x5: + ret = dev->sgd[addr >> 4].entry_ptr >> 8; + break; - case 0x06: - ret = dev->sgd_entry_ptr >> 16; - break; + case 0x6: + ret = dev->sgd[addr >> 4].entry_ptr >> 16; + break; - case 0x07: - ret = dev->sgd_entry_ptr >> 24; - /*pclog("sgd state %02X unpaused %d rct %02X\n", dev->sgd_regs[0x00], SGD_UNPAUSED, dev->sgd_regs[0x02]); - pci_clear_irq(dev->slot, dev->irq_pin);*/ - break; + case 0x7: + ret = dev->sgd[addr >> 4].entry_ptr >> 24; + break; - case 0x0c: - ret = dev->sgd_sample_count; - break; + case 0xc: + ret = dev->sgd[addr >> 4].sample_count; + break; - case 0x0d: - ret = dev->sgd_sample_count >> 8; - break; + case 0xd: + ret = dev->sgd[addr >> 4].sample_count >> 8; + break; - case 0x0e: - ret = dev->sgd_sample_count >> 16; - break; + case 0xe: + ret = dev->sgd[addr >> 4].sample_count >> 16; + break; - case 0x84: - ret |= (dev->sgd_regs[0x00] & 0x01); - ret |= (dev->sgd_regs[0x10] & 0x01) << 1; - ret |= (dev->sgd_regs[0x20] & 0x01) << 2; + default: + ret = dev->sgd_regs[addr]; + break; + } + } else { + /* Process regular registers. */ + switch (addr) { + case 0x84: + ret = (dev->sgd_regs[0x00] & 0x01); + ret |= (dev->sgd_regs[0x10] & 0x01) << 1; + ret |= (dev->sgd_regs[0x20] & 0x01) << 2; - ret |= (dev->sgd_regs[0x00] & 0x02) << 3; - ret |= (dev->sgd_regs[0x10] & 0x02) << 4; - ret |= (dev->sgd_regs[0x20] & 0x02) << 5; - break; + ret |= (dev->sgd_regs[0x00] & 0x02) << 3; + ret |= (dev->sgd_regs[0x10] & 0x02) << 4; + ret |= (dev->sgd_regs[0x20] & 0x02) << 5; + break; - case 0x85: - ret |= (dev->sgd_regs[0x00] & 0x04) >> 2; - ret |= (dev->sgd_regs[0x10] & 0x04) >> 1; - ret |= (dev->sgd_regs[0x20] & 0x04); + case 0x85: + ret = (dev->sgd_regs[0x00] & 0x04) >> 2; + ret |= (dev->sgd_regs[0x10] & 0x04) >> 1; + ret |= (dev->sgd_regs[0x20] & 0x04); - ret |= (dev->sgd_regs[0x00] & 0x80) >> 3; - ret |= (dev->sgd_regs[0x10] & 0x80) >> 2; - ret |= (dev->sgd_regs[0x20] & 0x80) >> 1; - break; + ret |= (dev->sgd_regs[0x00] & 0x80) >> 3; + ret |= (dev->sgd_regs[0x10] & 0x80) >> 2; + ret |= (dev->sgd_regs[0x20] & 0x80) >> 1; + break; - case 0x86: - ret |= (dev->sgd_regs[0x40] & 0x01); - ret |= (dev->sgd_regs[0x50] & 0x01) << 1; + case 0x86: + ret = (dev->sgd_regs[0x40] & 0x01); + ret |= (dev->sgd_regs[0x50] & 0x01) << 1; - ret |= (dev->sgd_regs[0x40] & 0x02) << 3; - ret |= (dev->sgd_regs[0x50] & 0x02) << 4; - break; + ret |= (dev->sgd_regs[0x40] & 0x02) << 3; + ret |= (dev->sgd_regs[0x50] & 0x02) << 4; + break; - case 0x87: - ret |= (dev->sgd_regs[0x40] & 0x04) >> 2; - ret |= (dev->sgd_regs[0x50] & 0x04) >> 1; + case 0x87: + ret = (dev->sgd_regs[0x40] & 0x04) >> 2; + ret |= (dev->sgd_regs[0x50] & 0x04) >> 1; - ret |= (dev->sgd_regs[0x40] & 0x80) >> 3; - ret |= (dev->sgd_regs[0x50] & 0x80) >> 2; - break; + ret |= (dev->sgd_regs[0x40] & 0x80) >> 3; + ret |= (dev->sgd_regs[0x50] & 0x80) >> 2; + break; - default: - ret = dev->sgd_regs[addr]; - break; + default: + ret = dev->sgd_regs[addr]; + break; + } } ac97_via_log("AC97 VIA %d: sgd_read(%02X) = %02X\n", modem, addr, ret); @@ -244,8 +253,8 @@ ac97_via_sgd_write(uint16_t addr, uint8_t val, void *priv) if (addr >= (modem ? 0x90 : 0x88)) return; - /* Check read-only registers for each SGD channel. */ if (!(addr & 0x80)) { + /* Process SGD channel registers. */ switch (addr & 0xf) { case 0x0: /* Clear RWC status bits. */ @@ -254,102 +263,102 @@ ac97_via_sgd_write(uint16_t addr, uint8_t val, void *priv) dev->sgd_regs[addr] &= ~i; } - if (addr == 0x00) { - if (!(dev->sgd_regs[0x00] & (dev->sgd_regs[0x02] & 0x03))) { - ac97_via_log("AC97 VIA: Clearing IRQ (iflags %02X)\n", dev->sgd_regs[0x00] & (dev->sgd_regs[0x02] & 0x03)); - pci_clear_irq(dev->slot, dev->irq_pin); - dev->irq_stuck = 0; + ac97_via_update_irqs(dev, 1); + + return; + + case 0x1: + /* Start SGD if requested. */ + if (val & 0x80) { + if (dev->sgd_regs[addr & 0xf0] & 0x80) { + /* Queue SGD trigger. */ + dev->sgd_regs[addr & 0xf0] |= 0x08; + } else { + /* Start SGD immediately. */ + dev->sgd_regs[addr & 0xf0] |= 0x80; + dev->sgd_regs[addr & 0xf0] &= ~0x44; + + /* Start at the specified entry pointer. */ + dev->sgd[addr >> 4].entry = 0; + dev->sgd[addr >> 4].entry_ptr = (dev->sgd_regs[(addr & 0xf0) | 0x7] << 24) | (dev->sgd_regs[(addr & 0xf0) | 0x6] << 16) | (dev->sgd_regs[(addr & 0xf0) | 0x5] << 8) | (dev->sgd_regs[(addr & 0xf0) | 0x4] & 0xfe); + + /* Start the actual SGD process. */ + timer_advance_u64(&dev->sgd[addr >> 4].timer, 0); } - - /* Resume SGD if requested. */ - if (val & 0x04) - ac97_via_sgd_startstop(dev); } + /* Stop SGD if requested. */ + if (val & 0x40) + dev->sgd_regs[addr & 0xf0] &= ~0x88; - /* fall-through */ + val &= 0x04; + + /* (Un)pause SGD if requested. */ + if (val & 0x04) + dev->sgd_regs[addr & 0xf0] |= 0x40; + else + dev->sgd_regs[addr & 0xf0] &= ~0x40; + + break; + + case 0x2: + if (addr & 0x10) + val &= 0xf3; + break; case 0x3: case 0x8 ... 0xf: + /* Read-only registers. */ return; } - } + } else { + /* Process regular registers. */ + switch (addr) { + case 0x30 ... 0x3f: + case 0x60 ... 0x7f: + case 0x84 ... 0x87: + /* Read-only registers. */ + return; - switch (addr) { - case 0x30 ... 0x3f: - case 0x60 ... 0x7f: - /* Read-only registers. */ - return; + case 0x82: + /* Determine the selected codec. */ + i = !!(dev->sgd_regs[0x83] & 0x40); + codec = dev->codec[modem][i]; - case 0x01: - /* Start SGD if requested. */ - if (val & 0x80) { - if (dev->sgd_regs[0x00] & 0x80) { - /* Queue SGD trigger. */ - dev->sgd_regs[0x00] |= 0x08; - } else { - /* Start SGD immediately. */ - dev->sgd_regs[0x00] |= 0x80; - dev->sgd_regs[0x00] &= ~0x44; - - dev->sgd_entry = 0; - dev->sgd_entry_ptr = 0; + /* Read from or write to codec. */ + if (codec) { + if (val & 0x80) { + val <<= 1; + dev->sgd_regs[0x80] = ac97_codec_read(codec, val); + dev->sgd_regs[0x81] = ac97_codec_read(codec, val | 1); + } else { + val <<= 1; + ac97_codec_write(codec, val, dev->sgd_regs[0x80]); + ac97_codec_write(codec, val | 1, dev->sgd_regs[0x81]); + } + } else if (val & 0x80) { + /* Unknown behavior when reading from a non-existent codec. */ + dev->sgd_regs[0x80] = dev->sgd_regs[0x81] = 0xff; } - } - /* Stop SGD if requested. */ - if (val & 0x40) { - dev->sgd_regs[0x00] &= ~0x88; - } - val &= 0x04; + /* Flag data/status/index for this codec as valid. */ + dev->sgd_regs[0x83] |= 0x02 << (i * 2); + break; - /* (Un)pause SGD if requested. */ - if (val & 0x04) - dev->sgd_regs[0x00] |= 0x40; - else - dev->sgd_regs[0x00] &= ~0x40; + case 0x83: + val &= 0xca; - ac97_via_sgd_startstop(dev); - break; - - case 0x82: - /* Determine the selected codec. */ - i = !!(dev->sgd_regs[0x83] & 0x40); - codec = dev->codec[(modem << 1) | i]; - - /* Read from or write to codec. */ - if (codec) { - if (val & 0x80) { - val <<= 1; - dev->sgd_regs[0x80] = ac97_codec_read(codec, val); - dev->sgd_regs[0x81] = ac97_codec_read(codec, val | 1); - } else { - val <<= 1; - ac97_codec_write(codec, val, dev->sgd_regs[0x80]); - ac97_codec_write(codec, val | 1, dev->sgd_regs[0x81]); + /* Clear RWC bits. */ + for (i = 0x02; i <= 0x08; i <<= 2) { + #if 0 /* race condition with Linux clearing bits and starting SGD on the same dword write */ + if (val & i) + val &= ~i; + else + val |= dev->sgd_regs[addr] & i; + #else + val |= i; + #endif } - } else if (val & 0x80) { - /* Unknown behavior when reading from a non-existent codec. */ - dev->sgd_regs[0x80] = dev->sgd_regs[0x81] = 0xff; - } - - /* Flag data/status/index for this codec as valid. */ - dev->sgd_regs[0x83] |= 0x02 << (i * 2); - break; - - case 0x83: - val &= 0xca; - - /* Clear RWC bits. */ - for (i = 0x02; i <= 0x08; i <<= 2) { -#if 0 /* race condition with Linux clearing bits and starting SGD on the same dword write */ - if (val & i) - val &= ~i; - else - val |= dev->sgd_regs[addr] & i; -#else - val |= i; -#endif - } - break; + break; + } } dev->sgd_regs[addr] = val; @@ -395,7 +404,7 @@ ac97_via_codec_read(uint16_t addr, void *priv) uint8_t ret = 0xff; /* Bit 7 selects secondary codec. */ - ac97_codec_t *codec = dev->codec[(modem << 1) | (addr >> 7)]; + ac97_codec_t *codec = dev->codec[modem][addr >> 7]; if (codec) ret = ac97_codec_read(codec, addr & 0x7f); @@ -415,7 +424,7 @@ ac97_via_codec_write(uint16_t addr, uint8_t val, void *priv) ac97_via_log("AC97 VIA %d: codec_write(%02X, %02X)\n", modem, addr, val); /* Bit 7 selects secondary codec. */ - ac97_codec_t *codec = dev->codec[(modem << 1) | (addr >> 7)]; + ac97_codec_t *codec = dev->codec[modem][addr >> 7]; if (codec) ac97_codec_write(codec, addr & 0x7f, val); } @@ -461,109 +470,151 @@ ac97_via_update(ac97_via_t *dev) } +static void +ac97_via_sgd_process(void *priv) +{ + ac97_via_sgd_t *sgd = (ac97_via_sgd_t *) priv; + ac97_via_t *dev = sgd->dev; + + /* Process SGD if active, unless this is Audio Read and there's no room in the FIFO. */ + if (((dev->sgd_regs[sgd->id] & 0xc4) == 0x80) && (sgd->id || ((sgd->fifo_end - sgd->fifo_pos) <= (sizeof(sgd->fifo) - 4)))) { + /* Move on to the next block if no entry is present. */ + if (!sgd->entry) { + /* Start at first entry if no pointer is present. */ + if (!sgd->entry_ptr) + sgd->entry_ptr = (dev->sgd_regs[sgd->id | 0x7] << 24) | (dev->sgd_regs[sgd->id | 0x6] << 16) | (dev->sgd_regs[sgd->id | 0x5] << 8) | (dev->sgd_regs[sgd->id | 0x4] & 0xfe); + + /* Read entry. */ + sgd->entry = ((uint64_t) mem_readl_phys(sgd->entry_ptr + 4) << 32ULL) | (uint64_t) mem_readl_phys(sgd->entry_ptr); + if (sgd->entry == 0xffffffffffffffffULL) + fatal("AC97 VIA: Invalid SGD %d entry at %08X\n", sgd->id >> 4, sgd->entry_ptr); + + /* Set sample pointer and count. */ + sgd->sample_ptr = sgd->entry & 0xffffffff; + sgd->sample_count = (sgd->entry >> 32) & 0xffffff; + + ac97_via_log("AC97 VIA: Starting SGD %d block at %08X entry %08X%08X (start %08X len %06X) lostirqs %d\n", sgd->id >> 4, sgd->entry_ptr, + mem_readl_phys(sgd->entry_ptr + 4), mem_readl_phys(sgd->entry_ptr), sgd->sample_ptr, sgd->sample_count, ac97_via_lost_irqs); + } + + if (sgd->id & 0x10) { + /* Write channel: read data from FIFO. */ + mem_writel_phys(sgd->sample_ptr, *((uint32_t *) &sgd->fifo[sgd->fifo_end & (sizeof(sgd->fifo) - 1)])); + } else { + /* Read channel: write data to FIFO. */ + *((uint32_t *) &sgd->fifo[sgd->fifo_end & (sizeof(sgd->fifo) - 1)]) = mem_readl_phys(sgd->sample_ptr); + } + sgd->fifo_end += 4; + sgd->sample_ptr += 4; + sgd->sample_count -= 4; + + /* Check if we've hit the end of this block. */ + if (sgd->sample_count <= 0) { + ac97_via_log("AC97 VIA: Ending SGD %d block", sgd->id >> 4); + + /* Move on to the next block on the next run. */ + sgd->entry_ptr += 8; + + if (sgd->entry & 0x2000000000000000ULL) { + ac97_via_log(" with STOP"); + dev->sgd_regs[sgd->id] |= 0x04; + } + + if (sgd->entry & 0x4000000000000000ULL) { + ac97_via_log(" with FLAG"); + + /* Raise FLAG while also pausing SGD. */ + dev->sgd_regs[sgd->id] |= 0x05; + +#ifdef ENABLE_AC97_VIA_LOG + if (dev->sgd_regs[sgd->id | 0x2] & 0x01) + ac97_via_log(" interrupt"); +#endif + } + + if (sgd->entry & 0x8000000000000000ULL) { + ac97_via_log(" with EOL"); + + /* Raise EOL. */ + dev->sgd_regs[sgd->id] |= 0x02; + +#ifdef ENABLE_AC97_VIA_LOG + if (dev->sgd_regs[sgd->id | 0x2] & 0x02) + ac97_via_log(" interrupt"); +#endif + + /* Restart SGD if a trigger is queued or auto-start is enabled. */ + if ((dev->sgd_regs[sgd->id] & 0x08) || (dev->sgd_regs[sgd->id | 0x2] & 0x80)) { + ac97_via_log(" restart"); + + /* Un-queue trigger. */ + dev->sgd_regs[sgd->id] &= ~0x08; + + /* Go back to the starting block. */ + sgd->entry_ptr = 0; + } else { + ac97_via_log(" finish"); + + /* Terminate SGD. */ + dev->sgd_regs[sgd->id] &= ~0x80; + } + } + ac97_via_log("\n"); + + /* Start a new block. */ + sgd->entry = sgd->sample_count = 0; + } + } + + ac97_via_update_irqs(dev, 0); + + /* Continue SGD processing if active or an interrupt is pending. */ + if (dev->sgd_regs[sgd->id] & (0x80 | (dev->sgd_regs[sgd->id | 0x02] & 0x03))) + timer_advance_u64(&sgd->timer, dev->timer_fifo_latch); +} + + static void ac97_via_poll(void *priv) { ac97_via_t *dev = (ac97_via_t *) priv; + ac97_via_sgd_t *sgd = &dev->sgd[0]; /* Audio Read */ timer_advance_u64(&dev->timer_count, dev->timer_latch); ac97_via_update(dev); - /* Read only if SGD is active and not paused. */ - if (SGD_UNPAUSED) { - if (!dev->sgd_entry) { - /* Move on to the next block. */ - ac97_via_sgd_block_start(dev); - } + dev->out_l = dev->out_r = 0; - switch (dev->sgd_regs[0x02] & 0x30) { - case 0x00: /* Mono, 8-bit PCM */ - dev->out_l = dev->out_r = (mem_readb_phys(dev->sgd_sample_ptr++) ^ 0x80) * 256; - dev->sgd_sample_count--; - break; + switch (dev->sgd_regs[0x02] & 0x30) { + case 0x00: /* Mono, 8-bit PCM */ + if ((sgd->fifo_end - sgd->fifo_pos) >= 1) + dev->out_l = dev->out_r = (sgd->fifo[sgd->fifo_pos++ & (sizeof(sgd->fifo) - 1)] ^ 0x80) << 8; + break; - case 0x10: /* Stereo, 8-bit PCM */ - dev->out_l = (mem_readb_phys(dev->sgd_sample_ptr++) ^ 0x80) * 256; - dev->out_r = (mem_readb_phys(dev->sgd_sample_ptr++) ^ 0x80) * 256; - dev->sgd_sample_count -= 2; - break; - - case 0x20: /* Mono, 16-bit PCM */ - dev->out_l = dev->out_r = mem_readw_phys(dev->sgd_sample_ptr); - dev->sgd_sample_ptr += 2; - dev->sgd_sample_count -= 2; - break; - - case 0x30: /* Stereo, 16-bit PCM */ - dev->out_l = mem_readw_phys(dev->sgd_sample_ptr); - dev->sgd_sample_ptr += 2; - dev->out_r = mem_readw_phys(dev->sgd_sample_ptr); - dev->sgd_sample_ptr += 2; - dev->sgd_sample_count -= 4; - break; - } - - /* Check if we've hit the end of this block. */ - if (dev->sgd_sample_count <= 0) { - ac97_via_log("AC97 VIA: Ending SGD block"); - - /* Move on to the next block on the next poll. */ - dev->sgd_entry_ptr += 8; - - if (dev->sgd_entry & 0x2000000000000000ULL) { - ac97_via_log(" with STOP"); - dev->sgd_regs[0x00] |= 0x04; + case 0x10: /* Stereo, 8-bit PCM */ + if ((sgd->fifo_end - sgd->fifo_pos) >= 2) { + dev->out_l = (sgd->fifo[sgd->fifo_pos++ & (sizeof(sgd->fifo) - 1)] ^ 0x80) << 8; + dev->out_r = (sgd->fifo[sgd->fifo_pos++ & (sizeof(sgd->fifo) - 1)] ^ 0x80) << 8; } - if (dev->sgd_entry & 0x4000000000000000ULL) { - ac97_via_log(" with FLAG"); - dev->sgd_regs[0x00] |= 0x01; + break; - /* Pause SGD. */ - dev->sgd_regs[0x00] |= 0x04; - - /* Fire interrupt if requested. */ - if (dev->sgd_regs[0x02] & 0x01) - ac97_via_log(" interrupt"); + case 0x20: /* Mono, 16-bit PCM */ + if ((sgd->fifo_end - sgd->fifo_pos) >= 2) { + dev->out_l = dev->out_r = *((uint16_t *) &sgd->fifo[sgd->fifo_pos & (sizeof(sgd->fifo) - 1)]); + sgd->fifo_pos += 2; } - if (dev->sgd_entry & 0x8000000000000000ULL) { - ac97_via_log(" with EOL"); - dev->sgd_regs[0x00] |= 0x02; + break; - /* Fire interrupt if requested. */ - if (dev->sgd_regs[0x02] & 0x02) - ac97_via_log(" interrupt"); - - /* Restart SGD if a trigger is queued or auto-start is enabled. */ - if ((dev->sgd_regs[0x00] & 0x08) || (dev->sgd_regs[0x02] & 0x80)) { - ac97_via_log(" restart\n"); - dev->sgd_regs[0x00] &= ~0x08; - - dev->sgd_entry_ptr = 0; - } else { - ac97_via_log(" finish\n"); - dev->sgd_regs[0x00] &= ~0x80; - } - } else { - ac97_via_log("\n"); + case 0x30: /* Stereo, 16-bit PCM */ + pclog("fifo_end - fifo_pos = %d\n", sgd->fifo_end - sgd->fifo_pos); + if ((sgd->fifo_end - sgd->fifo_pos) >= 4) { + dev->out_l = *((uint16_t *) &sgd->fifo[sgd->fifo_pos & (sizeof(sgd->fifo) - 1)]); + sgd->fifo_pos += 2; + dev->out_r = *((uint16_t *) &sgd->fifo[sgd->fifo_pos & (sizeof(sgd->fifo) - 1)]); + sgd->fifo_pos += 2; } - - dev->sgd_entry = dev->sgd_sample_count = 0; - } - } else { - dev->out_l = dev->out_r = 0; - dev->cd_vol_l = dev->cd_vol_r = 0; - } - - if (dev->sgd_regs[0x00] & (dev->sgd_regs[0x02] & 0x03)) { - ac97_via_log("AC97 VIA: Setting IRQ (iflags %02X stuck %d)\n", dev->sgd_regs[0x00] & (dev->sgd_regs[0x02] & 0x03), dev->irq_stuck); - if (dev->irq_stuck) { - dev->losticount++; - pci_clear_irq(dev->slot, dev->irq_pin); - } else { - pci_set_irq(dev->slot, dev->irq_pin); - } - dev->irq_stuck = !dev->irq_stuck; + break; } } @@ -575,9 +626,8 @@ ac97_via_get_buffer(int32_t *buffer, int len, void *priv) ac97_via_update(dev); - for (int c = 0; c < len * 2; c++) { + for (int c = 0; c < len * 2; c++) buffer[c] += dev->buffer[c]; - } dev->pos = 0; } @@ -588,6 +638,7 @@ ac97_via_speed_changed(void *priv) { ac97_via_t *dev = (ac97_via_t *) priv; dev->timer_latch = (uint64_t) ((double) TIMER_USEC * (1000000.0 / 48000.0)); + dev->timer_fifo_latch = (uint64_t) ((double) TIMER_USEC * 10.0); } @@ -599,14 +650,25 @@ ac97_via_init(const device_t *info) ac97_via_log("AC97 VIA: init()\n"); - ac97_codec = &dev->codec[0]; - ac97_modem_codec = &dev->codec[2]; - ac97_codec_count = ac97_modem_codec_count = 2; + /* Set up codecs. */ + ac97_codec = &dev->codec[0][0]; + ac97_modem_codec = &dev->codec[1][0]; + ac97_codec_count = ac97_modem_codec_count = sizeof(dev->codec[0]) / sizeof(dev->codec[0][0]); + /* Set up SGD channels. */ + for (uint8_t i = 0; i < (sizeof(dev->sgd) / sizeof(dev->sgd[0])); i++) { + dev->sgd[i].id = i << 4; + dev->sgd[i].dev = dev; + + timer_add(&dev->sgd[i].timer, ac97_via_sgd_process, &dev->sgd[i], 0); + } + + /* Set up playback poller. */ timer_add(&dev->timer_count, ac97_via_poll, dev, 0); ac97_via_speed_changed(dev); timer_advance_u64(&dev->timer_count, dev->timer_latch); + /* Set up playback handler. */ sound_add_handler(ac97_via_get_buffer, dev); return dev; @@ -626,7 +688,7 @@ ac97_via_close(void *priv) const device_t ac97_via_device = { - "VIA VT82C686 AC97 Controller", + "VIA VT82C686 Integrated AC97 Controller", DEVICE_PCI, 0, ac97_via_init, ac97_via_close, NULL, diff --git a/src/sound/snd_audiopci.c b/src/sound/snd_audiopci.c index 1c697a3a8..9c65052f0 100644 --- a/src/sound/snd_audiopci.c +++ b/src/sound/snd_audiopci.c @@ -17,6 +17,7 @@ #include <86box/sound.h> #include <86box/midi.h> #include <86box/snd_mpu401.h> +#include <86box/snd_ac97.h> #define N 16 @@ -51,7 +52,7 @@ typedef struct { uint8_t uart_ctrl; uint8_t uart_status; - uint16_t codec_regs[128]; + ac97_codec_t *codec; uint32_t codec_ctrl; struct { @@ -358,11 +359,7 @@ static uint32_t es1371_inl(uint16_t port, void *p) break; case 0x14: - ret = es1371->codec_ctrl & 0x00ff0000; - ret |= es1371->codec_regs[(es1371->codec_ctrl >> 16) & 0x7f]; - if (((es1371->codec_ctrl >> 16) & 0x7f) == 0x26) - ret |= 0x0f; - ret |= CODEC_READY; + ret = es1371->codec_ctrl | CODEC_READY; break; case 0x30: @@ -593,39 +590,48 @@ static void es1371_outl(uint16_t port, uint32_t val, void *p) break; case 0x14: - es1371->codec_ctrl = val; - if (!(val & CODEC_READ)) - { -// audiopci_log("Write codec %02x %04x\n", (val >> 16) & 0x7f, val & 0xffff); - if ((((val >> 16) & 0x7f) != 0x7c) && (((val >> 16) & 0x7f) != 0x7e)) - es1371->codec_regs[(val >> 16) & 0x7f] = val & 0xffff; - switch ((val >> 16) & 0x7f) - { - case 0x02: /*Master volume*/ - if (val & 0x8000) - es1371->master_vol_l = es1371->master_vol_r = 0; - else - { - if (val & 0x2000) + if (val & CODEC_READ) { + es1371->codec_ctrl &= 0x00ff0000; + es1371->codec_ctrl |= ac97_codec_read(es1371->codec, (val >> 16) & 0x7f); + es1371->codec_ctrl |= ac97_codec_read(es1371->codec, ((val >> 16) & 0x7f) + 1) << 8; + } else { + es1371->codec_ctrl &= 0x00ffffff; + ac97_codec_write(es1371->codec, (val >> 16) & 0x7f, val & 0xff); + ac97_codec_write(es1371->codec, ((val >> 16) & 0x7f) + 1, val >> 8); + + switch ((val >> 16) & 0x7f) { + case 0x02: /* Master Volume LSB */ + if (es1371->codec->regs[0x03] & 0x80) + es1371->master_vol_l = es1371->master_vol_r = 0; + else if (val & 0x20) + es1371->master_vol_r = codec_attn[0]; + else + es1371->master_vol_r = codec_attn[0x1f - (val & 0x1f)]; + break; + + case 0x03: /* Master Volume MSB */ + if (val & 0x80) + es1371->master_vol_l = es1371->master_vol_r = 0; + else if (val & 0x20) es1371->master_vol_l = codec_attn[0]; else - es1371->master_vol_l = codec_attn[0x1f - ((val >> 8) & 0x1f)]; - if (val & 0x20) - es1371->master_vol_r = codec_attn[0]; - else - es1371->master_vol_r = codec_attn[0x1f - (val & 0x1f)]; - } - break; - case 0x12: /*CD volume*/ - if (val & 0x8000) - es1371->cd_vol_l = es1371->cd_vol_r = 0; - else - { - es1371->cd_vol_l = codec_attn[0x1f - ((val >> 8) & 0x1f)]; - es1371->cd_vol_r = codec_attn[0x1f - (val & 0x1f)]; - } - break; - } + es1371->master_vol_l = codec_attn[0x1f - (val & 0x1f)]; + break; + + case 0x12: /* CD Volume LSB */ + if (es1371->codec->regs[0x13] & 0x80) + es1371->cd_vol_l = es1371->cd_vol_r = 0; + else + es1371->cd_vol_r = codec_attn[0x1f - (val & 0x1f)]; + break; + + case 0x13: /* CD Volume MSB */ + if (val & 0x80) + es1371->cd_vol_l = es1371->cd_vol_r = 0; + else + es1371->cd_vol_l = codec_attn[0x1f - (val & 0x1f)]; + break; + } } break; @@ -1362,9 +1368,9 @@ static void *es1371_init(const device_t *info) generate_es1371_filter(); - /* Return a CS4297A like VMware does. */ - es1371->codec_regs[0x7c] = 0x4352; - es1371->codec_regs[0x7e] = 0x5910; + ac97_codec = &es1371->codec; + ac97_codec_count = 1; + device_add(&cs4297a_device); return es1371; }