diff --git a/src/chipset/via_pipc.c b/src/chipset/via_pipc.c index a4d83220b..a476b9b99 100644 --- a/src/chipset/via_pipc.c +++ b/src/chipset/via_pipc.c @@ -987,7 +987,7 @@ pipc_read(int func, int addr, void *priv) } } else if ((func <= (pm_func + 2)) && !(dev->pci_isa_regs[0x85] & ((func == (pm_func + 1)) ? 0x04 : 0x08))) { /* AC97 / MC97 */ if (addr == 0x40) - ret = ac97_via_read_status(dev->ac97, func - pm_func - 1); + ret = ac97_via_read_status(dev->ac97); else ret = dev->ac97_regs[func - pm_func - 1][addr]; } @@ -1583,7 +1583,7 @@ pipc_write(int func, int addr, uint8_t val, void *priv) case 0x41: dev->ac97_regs[func][addr] = val; - ac97_via_write_control(dev->ac97, func, val); + ac97_via_write_control(dev->ac97, val); break; case 0x42: diff --git a/src/include/86box/snd_ac97.h b/src/include/86box/snd_ac97.h index 3af68d2c8..5f639d31e 100644 --- a/src/include/86box/snd_ac97.h +++ b/src/include/86box/snd_ac97.h @@ -125,8 +125,8 @@ extern uint32_t ac97_codec_getrate(void *priv, uint8_t reg); extern const device_t *ac97_codec_get(uint32_t id); extern void ac97_via_set_slot(void *priv, int slot, int irq_pin); -extern uint8_t ac97_via_read_status(void *priv, uint8_t modem); -extern void ac97_via_write_control(void *priv, uint8_t modem, uint8_t val); +extern uint8_t ac97_via_read_status(void *priv); +extern void ac97_via_write_control(void *priv, uint8_t val); extern void ac97_via_remap_audio_sgd(void *priv, uint16_t new_io_base, uint8_t enable); extern void ac97_via_remap_modem_sgd(void *priv, uint16_t new_io_base, uint8_t enable); extern void ac97_via_remap_audio_codec(void *priv, uint16_t new_io_base, uint8_t enable); diff --git a/src/sound/snd_ac97_via.c b/src/sound/snd_ac97_via.c index 28b802a6f..469e27717 100644 --- a/src/sound/snd_ac97_via.c +++ b/src/sound/snd_ac97_via.c @@ -10,7 +10,7 @@ * * Authors: RichardG, * - * Copyright 2021 RichardG. + * Copyright 2021-2025 RichardG. */ #include #include @@ -35,8 +35,6 @@ typedef struct ac97_via_sgd_t { uint8_t id; uint8_t always_run; - uint8_t modem; - uint8_t pad; struct _ac97_via_ *dev; uint32_t entry_ptr; @@ -65,22 +63,17 @@ typedef struct _ac97_via_ { uint16_t audio_codec_base; uint16_t modem_sgd_base; uint16_t modem_codec_base; - uint8_t sgd_regs[2][256]; + uint8_t sgd_regs[256]; uint8_t pcm_enabled : 1; uint8_t fm_enabled : 1; uint8_t vsr_enabled : 1; - struct { - union { - uint8_t regs_codec[2][128]; - uint8_t regs_linear[256]; - }; - } codec_shadow[2]; - uint8_t pci_slot; - uint8_t irq_state; - int irq_pin; + uint8_t codec_shadow[256]; + uint8_t pci_slot; + uint8_t irq_state; + int irq_pin; - ac97_codec_t *codec[2][2]; - ac97_via_sgd_t sgd[2][6]; + ac97_codec_t *codec[2]; + ac97_via_sgd_t sgd[6]; int master_vol_l; int master_vol_r; @@ -107,7 +100,7 @@ ac97_via_log(const char *fmt, ...) #endif static void ac97_via_sgd_process(void *priv); -static void ac97_via_update_codec(ac97_via_t *dev, int modem); +static void ac97_via_update_codec(ac97_via_t *dev); static void ac97_via_speed_changed(void *priv); static void ac97_via_filter_cd_audio(int channel, double *buffer, void *priv); @@ -118,73 +111,70 @@ ac97_via_set_slot(void *priv, int slot, int irq_pin) ac97_via_log("AC97 VIA: set_slot(%d, %d)\n", slot, irq_pin); - dev->pci_slot = slot; - dev->irq_pin = irq_pin; + dev->pci_slot = slot; + dev->irq_pin = irq_pin; } uint8_t -ac97_via_read_status(void *priv, uint8_t modem) +ac97_via_read_status(void *priv) { const ac97_via_t *dev = (ac97_via_t *) priv; uint8_t ret = 0x00; /* Flag each codec as ready if present. */ - for (uint8_t i = 0; i <= 1; i++) { - if (dev->codec[modem][i]) + for (uint8_t i = 0; i < (sizeof(dev->codec) / sizeof(dev->codec[0])); i++) { + if (dev->codec[i]) ret |= 0x01 << (i << 1); } - ac97_via_log("AC97 VIA %d: read_status() = %02X\n", modem, ret); + ac97_via_log("AC97 VIA: read_status() = %02X\n", ret); return ret; } void -ac97_via_write_control(void *priv, uint8_t modem, uint8_t val) +ac97_via_write_control(void *priv, uint8_t val) { ac97_via_t *dev = (ac97_via_t *) priv; uint8_t i; - ac97_via_log("AC97 VIA %d: write_control(%02X)\n", modem, val); + ac97_via_log("AC97 VIA: write_control(%02X)\n", val); /* Reset codecs if requested. */ if (!(val & 0x40)) { - for (i = 0; i <= 1; i++) { - if (dev->codec[modem][i]) - ac97_codec_reset(dev->codec[modem][i]); + for (i = 0; i < (sizeof(dev->codec) / sizeof(dev->codec[0])); i++) { + if (dev->codec[i]) + ac97_codec_reset(dev->codec[i]); } } - if (!modem) { - /* Set the variable sample rate flag. */ - dev->vsr_enabled = (val & 0xf8) == 0xc8; + /* Set the variable sample rate flag. */ + dev->vsr_enabled = (val & 0xf8) == 0xc8; - /* Start or stop PCM playback. */ - i = (val & 0xf4) == 0xc4; - if (i && !dev->pcm_enabled) - timer_advance_u64(&dev->sgd[0][0].poll_timer, dev->sgd[0][0].timer_latch); - dev->pcm_enabled = i; + /* Start or stop PCM playback. */ + i = (val & 0xf4) == 0xc4; + if (i && !dev->pcm_enabled) + timer_advance_u64(&dev->sgd[0].poll_timer, dev->sgd[0].timer_latch); + dev->pcm_enabled = i; - /* Start or stop FM playback. */ - i = (val & 0xf2) == 0xc2; - if (i && !dev->fm_enabled) - timer_advance_u64(&dev->sgd[0][2].poll_timer, dev->sgd[0][2].timer_latch); - dev->fm_enabled = i; + /* Start or stop FM playback. */ + i = (val & 0xf2) == 0xc2; + if (i && !dev->fm_enabled) + timer_advance_u64(&dev->sgd[2].poll_timer, dev->sgd[2].timer_latch); + dev->fm_enabled = i; - /* Update primary audio codec state. */ - if (dev->codec[0][0]) - ac97_via_update_codec(dev, 0); - } + /* Update audio codec state. */ + ac97_via_update_codec(dev); } static void -ac97_via_update_irqs(ac97_via_t *dev, int modem) +ac97_via_update_irqs(ac97_via_t *dev) { /* Check interrupt flags in all SGDs. */ for (uint8_t i = 0x00; i < ((sizeof(dev->sgd) / sizeof(dev->sgd[0])) << 4); i += 0x10) { /* Stop immediately if any flag is set. Doing it this way optimizes rising edges for the playback SGD (0 - first to be checked). */ - if (dev->sgd_regs[modem][i] & (dev->sgd_regs[modem][i | 0x2] & 0x03)) { + if (dev->sgd_regs[i] & (dev->sgd_regs[i | 0x2] & 0x03)) { pci_set_irq(dev->pci_slot, dev->irq_pin, &dev->irq_state); return; } @@ -194,28 +184,26 @@ ac97_via_update_irqs(ac97_via_t *dev, int modem) } static void -ac97_via_update_codec(ac97_via_t *dev, int modem) +ac97_via_update_codec(ac97_via_t *dev) { - /* Get primary audio codec. */ - ac97_codec_t *codec = dev->codec[modem][0]; - /* Update volumes according to codec registers. */ + ac97_codec_t *codec = dev->codec[0]; /* assume primary codec */ ac97_codec_getattn(codec, 0x02, &dev->master_vol_l, &dev->master_vol_r); - ac97_codec_getattn(codec, 0x18, &dev->sgd[modem][0].vol_l, &dev->sgd[modem][0].vol_r); - ac97_codec_getattn(codec, 0x18, &dev->sgd[modem][2].vol_l, &dev->sgd[modem][2].vol_r); /* VIAFMTSR sets Master, CD and PCM volumes to 0 dB */ + ac97_codec_getattn(codec, 0x18, &dev->sgd[0].vol_l, &dev->sgd[0].vol_r); + ac97_codec_getattn(codec, 0x18, &dev->sgd[2].vol_l, &dev->sgd[2].vol_r); /* VIAFMTSR sets Master, CD and PCM volumes to 0 dB */ ac97_codec_getattn(codec, 0x12, &dev->cd_vol_l, &dev->cd_vol_r); /* Update sample rate according to codec registers and the variable sample rate flag. */ ac97_via_speed_changed(dev); } -uint8_t +static uint8_t ac97_via_sgd_read(uint16_t addr, void *priv) { const ac97_via_t *dev = (ac97_via_t *) priv; -// #ifdef ENABLE_AC97_VIA_LOG - uint8_t modem = (addr & 0xff00) == dev->modem_sgd_base; -// #endif +#ifdef ENABLE_AC97_VIA_LOG + uint8_t modem = (addr & 0xff00) == dev->modem_sgd_base; +#endif addr &= 0xff; uint8_t ret; @@ -223,78 +211,78 @@ ac97_via_sgd_read(uint16_t addr, void *priv) /* Process SGD channel registers. */ switch (addr & 0xf) { case 0x4: - ret = dev->sgd[modem][addr >> 4].entry_ptr; + ret = dev->sgd[addr >> 4].entry_ptr; break; case 0x5: - ret = dev->sgd[modem][addr >> 4].entry_ptr >> 8; + ret = dev->sgd[addr >> 4].entry_ptr >> 8; break; case 0x6: - ret = dev->sgd[modem][addr >> 4].entry_ptr >> 16; + ret = dev->sgd[addr >> 4].entry_ptr >> 16; break; case 0x7: - ret = dev->sgd[modem][addr >> 4].entry_ptr >> 24; + ret = dev->sgd[addr >> 4].entry_ptr >> 24; break; case 0xc: - ret = dev->sgd[modem][addr >> 4].sample_count; + ret = dev->sgd[addr >> 4].sample_count; break; case 0xd: - ret = dev->sgd[modem][addr >> 4].sample_count >> 8; + ret = dev->sgd[addr >> 4].sample_count >> 8; break; case 0xe: - ret = dev->sgd[modem][addr >> 4].sample_count >> 16; + ret = dev->sgd[addr >> 4].sample_count >> 16; break; default: - ret = dev->sgd_regs[modem][addr]; + ret = dev->sgd_regs[addr]; break; } } else { /* Process regular registers. */ switch (addr) { case 0x84: - ret = (dev->sgd_regs[modem][0x00] & 0x01); - ret |= (dev->sgd_regs[modem][0x10] & 0x01) << 1; - ret |= (dev->sgd_regs[modem][0x20] & 0x01) << 2; + ret = (dev->sgd_regs[0x00] & 0x01); + ret |= (dev->sgd_regs[0x10] & 0x01) << 1; + ret |= (dev->sgd_regs[0x20] & 0x01) << 2; - ret |= (dev->sgd_regs[modem][0x00] & 0x02) << 3; - ret |= (dev->sgd_regs[modem][0x10] & 0x02) << 4; - ret |= (dev->sgd_regs[modem][0x20] & 0x02) << 5; + 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[modem][0x00] & 0x04) >> 2; - ret |= (dev->sgd_regs[modem][0x10] & 0x04) >> 1; - ret |= (dev->sgd_regs[modem][0x20] & 0x04); + ret = (dev->sgd_regs[0x00] & 0x04) >> 2; + ret |= (dev->sgd_regs[0x10] & 0x04) >> 1; + ret |= (dev->sgd_regs[0x20] & 0x04); - ret |= (dev->sgd_regs[modem][0x00] & 0x80) >> 3; - ret |= (dev->sgd_regs[modem][0x10] & 0x80) >> 2; - ret |= (dev->sgd_regs[modem][0x20] & 0x80) >> 1; + 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[modem][0x40] & 0x01); - ret |= (dev->sgd_regs[modem][0x50] & 0x01) << 1; + ret = (dev->sgd_regs[0x40] & 0x01); + ret |= (dev->sgd_regs[0x50] & 0x01) << 1; - ret |= (dev->sgd_regs[modem][0x40] & 0x02) << 3; - ret |= (dev->sgd_regs[modem][0x50] & 0x02) << 4; + ret |= (dev->sgd_regs[0x40] & 0x02) << 3; + ret |= (dev->sgd_regs[0x50] & 0x02) << 4; break; case 0x87: - ret = (dev->sgd_regs[modem][0x40] & 0x04) >> 2; - ret |= (dev->sgd_regs[modem][0x50] & 0x04) >> 1; + ret = (dev->sgd_regs[0x40] & 0x04) >> 2; + ret |= (dev->sgd_regs[0x50] & 0x04) >> 1; - ret |= (dev->sgd_regs[modem][0x40] & 0x80) >> 3; - ret |= (dev->sgd_regs[modem][0x50] & 0x80) >> 2; + ret |= (dev->sgd_regs[0x40] & 0x80) >> 3; + ret |= (dev->sgd_regs[0x50] & 0x80) >> 2; break; default: - ret = dev->sgd_regs[modem][addr]; + ret = dev->sgd_regs[addr]; break; } } @@ -304,7 +292,7 @@ ac97_via_sgd_read(uint16_t addr, void *priv) return ret; } -void +static void ac97_via_sgd_write(uint16_t addr, uint8_t val, void *priv) { ac97_via_t *dev = (ac97_via_t *) priv; @@ -328,42 +316,42 @@ ac97_via_sgd_write(uint16_t addr, uint8_t val, void *priv) switch (addr & 0xf) { case 0x0: /* Clear RWC status bits. */ - dev->sgd_regs[modem][addr] &= ~(val & 0x07); + dev->sgd_regs[addr] &= ~(val & 0x07); /* Update status interrupts. */ - ac97_via_update_irqs(dev, modem); + ac97_via_update_irqs(dev); return; case 0x1: /* Start SGD if requested. */ if (val & 0x80) { - if (dev->sgd_regs[modem][addr & 0xf0] & 0x80) { + if (dev->sgd_regs[addr & 0xf0] & 0x80) { /* Queue SGD trigger if already running. */ - dev->sgd_regs[modem][addr & 0xf0] |= 0x08; + dev->sgd_regs[addr & 0xf0] |= 0x08; } else { /* Start SGD immediately. */ - dev->sgd_regs[modem][addr & 0xf0] = (dev->sgd_regs[modem][addr & 0xf0] & ~0x47) | 0x80; + dev->sgd_regs[addr & 0xf0] = (dev->sgd_regs[addr & 0xf0] & ~0x47) | 0x80; /* Start at the specified entry pointer. */ - dev->sgd[modem][addr >> 4].entry_ptr = AS_U32(dev->sgd_regs[modem][(addr & 0xf0) | 0x4]) & 0xfffffffe; - dev->sgd[modem][addr >> 4].restart = 2; + dev->sgd[addr >> 4].entry_ptr = AS_U32(dev->sgd_regs[(addr & 0xf0) | 0x4]) & 0xfffffffe; + dev->sgd[addr >> 4].restart = 2; /* Start the actual SGD process. */ - ac97_via_sgd_process(&dev->sgd[modem][addr >> 4]); + ac97_via_sgd_process(&dev->sgd[addr >> 4]); } } /* Stop SGD if requested. */ if (val & 0x40) - dev->sgd_regs[modem][addr & 0xf0] &= ~0x88; + dev->sgd_regs[addr & 0xf0] &= ~0x88; val &= 0x08; /* (Un)pause SGD if requested. */ if (val & 0x08) - dev->sgd_regs[modem][addr & 0xf0] |= 0x40; + dev->sgd_regs[addr & 0xf0] |= 0x40; else - dev->sgd_regs[modem][addr & 0xf0] &= ~0x40; + dev->sgd_regs[addr & 0xf0] &= ~0x40; break; @@ -391,55 +379,95 @@ ac97_via_sgd_write(uint16_t addr, uint8_t val, void *priv) case 0x82: /* Determine the selected codec. */ - i = !!(dev->sgd_regs[modem][0x83] & 0x40); - codec = dev->codec[modem][i]; + i = !!(dev->sgd_regs[0x83] & 0x40); + codec = dev->codec[i]; /* Keep value in register if this codec is not present. */ if (codec) { /* Read from or write to codec. */ if (val & 0x80) { - if (val & 1) { /* return 0x0000 on unaligned reads (real 686B behavior) */ - dev->sgd_regs[modem][0x80] = dev->sgd_regs[modem][0x81] = 0x00; - } else { - AS_U16(dev->codec_shadow[modem].regs_codec[i][val & 0x7f]) = AS_U16(dev->sgd_regs[modem][0x80]) = ac97_codec_readw(codec, val); - } + if (val & 1) /* return 0x0000 on unaligned reads (real 686B behavior) */ + AS_U16(dev->sgd_regs[0x80]) = 0x0000; + else + AS_U16(dev->codec_shadow[(i << 7) | (val & 0x7f)]) = AS_U16(dev->sgd_regs[0x80]) = ac97_codec_readw(codec, val); /* Flag data/status/index for this codec as valid. */ - dev->sgd_regs[modem][0x83] |= 0x02 << (i << 1); + dev->sgd_regs[0x83] |= 0x02 << (i << 1); } else if (!(val & 1)) { /* do nothing on unaligned writes */ ac97_codec_writew(codec, val, - AS_U16(dev->codec_shadow[modem].regs_codec[i][val & 0x7f]) = AS_U16(dev->sgd_regs[modem][0x80])); + AS_U16(dev->codec_shadow[(i << 7) | val]) = AS_U16(dev->sgd_regs[0x80])); - /* Update primary audio codec state if that codec was written to. */ - if (!modem && !i) { - ac97_via_update_codec(dev, 0); + /* Update audio codec state. */ + ac97_via_update_codec(dev); - /* Set up CD audio filter if CD volume was written to. Setting it - up at init prevents CD audio from working on other cards, but - this works as the CD channel is muted by default per AC97 spec. */ - if (val == 0x12) - sound_set_cd_audio_filter(ac97_via_filter_cd_audio, dev); - } + /* Set up CD audio filter if CD volume was written to. Setting it + up at init prevents CD audio from working on other cards, but + this works as the CD channel is muted by default per AC97 spec. */ + if (!i && (val == 0x12)) + sound_set_cd_audio_filter(ac97_via_filter_cd_audio, dev); } } - break; case 0x83: /* Clear RWC status bits. */ -#if 0 /* race condition with Linux accessing a register and clearing status bits on the same dword write */ - val = ((dev->sgd_regs[modem][addr] & 0x3f) & ~(val & 0x0a)) | (val & 0xc0); -#else - val = (dev->sgd_regs[modem][addr] & 0x3f) | (val & 0xc0); -#endif + val = ((dev->sgd_regs[addr] & 0x3f) & ~(val & 0x0a)) | (val & 0xc0); break; + case 0x88 ... 0x89: + dev->sgd_regs[addr] = val; + + /* Send GPO to codec. */ + /*for (uint8_t i = 0; i < (sizeof(dev->codec) / sizeof(dev->codec[0])); i++) { + if (dev->codec[i]) + ac97_codec_setgpo(dev->codec[i], AS_U16(dev->sgd_regs[0x88])); + }*/ + return; + + case 0x8a ... 0x8b: + /* Clear RWC status bits. */ + val = dev->sgd_regs[addr] & ~val; + break; + + case 0x8c ... 0x8d: + return; + default: break; } } - dev->sgd_regs[modem][addr] = val; + dev->sgd_regs[addr] = val; +} + +static void +ac97_via_sgd_writew(uint16_t addr, uint16_t val, void *priv) +{ + if ((addr & 0xfe) == 0x82) { + /* Invert order on writes to 82-83 to ensure the correct codec ID is set and + any status bits are cleared before performing the codec register operation. */ + ac97_via_sgd_write(addr + 1, val >> 8, priv); + ac97_via_sgd_write(addr, val & 0xff, priv); + } else { + ac97_via_sgd_write(addr, val & 0xff, priv); + ac97_via_sgd_write(addr + 1, val >> 8, priv); + } +} + +static void +ac97_via_sgd_writel(uint16_t addr, uint32_t val, void *priv) +{ + ac97_via_sgd_write(addr, val & 0xff, priv); + ac97_via_sgd_write(addr + 1, val >> 8, priv); + if ((addr & 0xfc) == 0x80) { + /* Invert order on writes to 82-83 to ensure the correct codec ID is set and + any status bits are cleared before performing the codec register operation. */ + ac97_via_sgd_write(addr + 3, val >> 24, priv); + ac97_via_sgd_write(addr + 2, val >> 16, priv); + } else { + ac97_via_sgd_write(addr + 2, val >> 16, priv); + ac97_via_sgd_write(addr + 3, val >> 24, priv); + } } void @@ -448,12 +476,12 @@ ac97_via_remap_audio_sgd(void *priv, uint16_t new_io_base, uint8_t enable) ac97_via_t *dev = (ac97_via_t *) priv; if (dev->audio_sgd_base) - io_removehandler(dev->audio_sgd_base, 256, ac97_via_sgd_read, NULL, NULL, ac97_via_sgd_write, NULL, NULL, dev); + io_removehandler(dev->audio_sgd_base, 256, ac97_via_sgd_read, NULL, NULL, ac97_via_sgd_write, ac97_via_sgd_writew, ac97_via_sgd_writel, dev); dev->audio_sgd_base = new_io_base; if (dev->audio_sgd_base && enable) - io_sethandler(dev->audio_sgd_base, 256, ac97_via_sgd_read, NULL, NULL, ac97_via_sgd_write, NULL, NULL, dev); + io_sethandler(dev->audio_sgd_base, 256, ac97_via_sgd_read, NULL, NULL, ac97_via_sgd_write, ac97_via_sgd_writew, ac97_via_sgd_writel, dev); } void @@ -462,24 +490,26 @@ ac97_via_remap_modem_sgd(void *priv, uint16_t new_io_base, uint8_t enable) ac97_via_t *dev = (ac97_via_t *) priv; if (dev->modem_sgd_base) - io_removehandler(dev->modem_sgd_base, 256, ac97_via_sgd_read, NULL, NULL, ac97_via_sgd_write, NULL, NULL, dev); + io_removehandler(dev->modem_sgd_base, 256, ac97_via_sgd_read, NULL, NULL, ac97_via_sgd_write, ac97_via_sgd_writew, ac97_via_sgd_writel, dev); dev->modem_sgd_base = new_io_base; if (dev->modem_sgd_base && enable) - io_sethandler(dev->modem_sgd_base, 256, ac97_via_sgd_read, NULL, NULL, ac97_via_sgd_write, NULL, NULL, dev); + io_sethandler(dev->modem_sgd_base, 256, ac97_via_sgd_read, NULL, NULL, ac97_via_sgd_write, ac97_via_sgd_writew, ac97_via_sgd_writel, dev); } uint8_t ac97_via_codec_read(uint16_t addr, void *priv) { const ac97_via_t *dev = (ac97_via_t *) priv; +#ifdef ENABLE_AC97_VIA_LOG uint8_t modem = (addr & 0xff00) == dev->modem_codec_base; +#endif uint8_t ret = 0xff; addr &= 0xff; - ret = dev->codec_shadow[modem].regs_linear[addr]; + ret = dev->codec_shadow[addr]; ac97_via_log("AC97 VIA %d: codec_read(%02X) = %02X\n", modem, addr, ret); @@ -492,7 +522,9 @@ void ac97_via_codec_write(uint16_t addr, uint8_t val, void *priv) { ac97_via_t *dev = (ac97_via_t *) priv; +#ifdef ENABLE_AC97_VIA_LOG uint8_t modem = (addr & 0xff00) == dev->modem_codec_base; +#endif addr &= 0xff; ac97_via_log("[%04X:%08X] [%i] AC97 VIA %d: codec_write(%02X, %02X)\n", CS, cpu_state.pc, msw & 1, modem, addr, val); @@ -500,7 +532,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); /* Unknown behavior, maybe it does write to the shadow registers? */ - dev->codec_shadow[modem].regs_linear[addr] = val; + dev->codec_shadow[addr] = val; } void @@ -564,7 +596,7 @@ ac97_via_sgd_process(void *priv) ac97_via_t *dev = sgd->dev; /* Stop if this SGD is not active. */ - uint8_t sgd_status = dev->sgd_regs[sgd->modem][sgd->id] & 0xc4; + uint8_t sgd_status = dev->sgd_regs[sgd->id] & 0xc4; if (!(sgd_status & 0x80)) return; @@ -577,7 +609,7 @@ ac97_via_sgd_process(void *priv) if (sgd->restart) { /* (Re)load entry pointer if required. */ if (sgd->restart & 2) - sgd->entry_ptr = AS_U32(dev->sgd_regs[sgd->modem][sgd->id | 0x4]) & 0xfffffffe; /* TODO: probe real hardware - does "even addr" actually mean dword aligned? */ + sgd->entry_ptr = AS_U32(dev->sgd_regs[sgd->id | 0x4]) & 0xfffffffe; /* TODO: probe real hardware - does "even addr" actually mean dword aligned? */ sgd->restart = 0; /* Read entry. */ @@ -625,17 +657,17 @@ ac97_via_sgd_process(void *priv) ac97_via_log(" with STOP"); /* Raise STOP to pause SGD. */ - dev->sgd_regs[sgd->modem][sgd->id] |= 0x04; + dev->sgd_regs[sgd->id] |= 0x04; } if (sgd->entry_flags & 0x40) { ac97_via_log(" with FLAG"); /* Raise FLAG to pause SGD. */ - dev->sgd_regs[sgd->modem][sgd->id] |= 0x01; + dev->sgd_regs[sgd->id] |= 0x01; #ifdef ENABLE_AC97_VIA_LOG - if (dev->sgd_regs[sgd->modem][sgd->id | 0x2] & 0x01) + if (dev->sgd_regs[sgd->id | 0x2] & 0x01) ac97_via_log(" interrupt"); #endif } @@ -644,19 +676,19 @@ ac97_via_sgd_process(void *priv) ac97_via_log(" with EOL"); /* Raise EOL. */ - dev->sgd_regs[sgd->modem][sgd->id] |= 0x02; + dev->sgd_regs[sgd->id] |= 0x02; #ifdef ENABLE_AC97_VIA_LOG - if (dev->sgd_regs[sgd->modem][sgd->id | 0x2] & 0x02) + 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->modem][sgd->id] & 0x08) || (dev->sgd_regs[sgd->modem][sgd->id | 0x2] & 0x80)) { + 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->modem][sgd->id] &= ~0x08; + dev->sgd_regs[sgd->id] &= ~0x08; /* Go back to the starting block on the next run. */ sgd->restart = 2; @@ -664,13 +696,13 @@ ac97_via_sgd_process(void *priv) ac97_via_log(" finish"); /* Terminate SGD. */ - dev->sgd_regs[sgd->modem][sgd->id] &= ~0x80; + dev->sgd_regs[sgd->id] &= ~0x80; } } ac97_via_log("\n"); /* Fire any requested status interrupts. */ - ac97_via_update_irqs(dev, sgd->modem); + ac97_via_update_irqs(dev); } } } @@ -678,8 +710,8 @@ ac97_via_sgd_process(void *priv) static void ac97_via_poll_stereo(void *priv) { - ac97_via_t *dev = (ac97_via_t *) priv; - ac97_via_sgd_t *sgd = &dev->sgd[0][0]; /* Audio Read */ + ac97_via_sgd_t *sgd = (ac97_via_sgd_t *) priv; + ac97_via_t *dev = sgd->dev; /* Schedule next run if PCM playback is enabled. */ if (dev->pcm_enabled) @@ -689,7 +721,7 @@ ac97_via_poll_stereo(void *priv) ac97_via_update_stereo(dev, sgd); /* Feed next sample from the FIFO. */ - switch (dev->sgd_regs[0][sgd->id | 0x2] & 0x30) { + switch (dev->sgd_regs[sgd->id | 0x2] & 0x30) { case 0x00: /* Mono, 8-bit PCM */ if ((sgd->fifo_end - sgd->fifo_pos) >= 1) { sgd->out_l = sgd->out_r = (sgd->fifo[sgd->fifo_pos++ & (sizeof(sgd->fifo) - 1)] ^ 0x80) << 8; @@ -734,8 +766,8 @@ ac97_via_poll_stereo(void *priv) static void ac97_via_poll_fm(void *priv) { - ac97_via_t *dev = (ac97_via_t *) priv; - ac97_via_sgd_t *sgd = &dev->sgd[0][2]; /* FM Read */ + ac97_via_sgd_t *sgd = (ac97_via_sgd_t *) priv; + ac97_via_t *dev = sgd->dev; /* Schedule next run if FM playback is enabled. */ if (dev->fm_enabled) @@ -763,15 +795,17 @@ ac97_via_get_buffer(int32_t *buffer, int len, void *priv) { ac97_via_t *dev = (ac97_via_t *) priv; - ac97_via_update_stereo(dev, &dev->sgd[0][0]); - ac97_via_update_stereo(dev, &dev->sgd[0][2]); + ac97_via_update_stereo(dev, &dev->sgd[0]); + ac97_via_update_stereo(dev, &dev->sgd[2]); + ac97_via_update_stereo(dev, &dev->sgd[4]); for (int c = 0; c < len * 2; c++) { - buffer[c] += dev->sgd[0][0].buffer[c] / 2; - buffer[c] += dev->sgd[0][2].buffer[c] / 2; + buffer[c] += dev->sgd[0].buffer[c] / 2; + buffer[c] += dev->sgd[2].buffer[c] / 2; + buffer[c] += dev->sgd[4].buffer[c] / 2; } - dev->sgd[0][0].pos = dev->sgd[0][2].pos = 0; + dev->sgd[0].pos = dev->sgd[2].pos = dev->sgd[4].pos = 0; } static void @@ -792,13 +826,20 @@ ac97_via_speed_changed(void *priv) double freq; /* Get variable sample rate if enabled. */ - if (dev->vsr_enabled && dev->codec[0][0]) - freq = ac97_codec_getrate(dev->codec[0][0], 0x2c); + if (dev->vsr_enabled && dev->codec[0]) + freq = ac97_codec_getrate(dev->codec[0], 0x2c); else freq = (double) SOUND_FREQ; - dev->sgd[0][0].timer_latch = (uint64_t) ((double) TIMER_USEC * (1000000.0 / freq)); - dev->sgd[0][2].timer_latch = (uint64_t) ((double) TIMER_USEC * (1000000.0 / 24000.0)); /* FM operates at a fixed 24 KHz */ + dev->sgd[0].timer_latch = (uint64_t) ((double) TIMER_USEC * (1000000.0 / freq)); + dev->sgd[2].timer_latch = (uint64_t) ((double) TIMER_USEC * (1000000.0 / 24000.0)); /* FM operates at a fixed 24 KHz */ + + if (dev->codec[1]) + freq = ac97_codec_getrate(dev->codec[1], 0x40); + else + freq = (double) SOUND_FREQ; + + dev->sgd[4].timer_latch = dev->sgd[5].timer_latch = (uint64_t) ((double) TIMER_USEC * (1000000.0 / freq)); } static void * @@ -809,30 +850,27 @@ ac97_via_init(UNUSED(const device_t *info)) ac97_via_log("AC97 VIA: init()\n"); /* 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]); + ac97_codec = &dev->codec[0]; + ac97_modem_codec = &dev->codec[1]; + ac97_codec_count = sizeof(dev->codec) / sizeof(dev->codec[0]); + ac97_modem_codec_count = 1; ac97_codec_id = ac97_modem_codec_id = 0; /* Set up SGD channels. */ for (uint8_t i = 0; i < (sizeof(dev->sgd) / sizeof(dev->sgd[0])); i++) { - for (uint8_t j = 0; j < 2; j++) { - dev->sgd[j][i].id = i << 4; - dev->sgd[j][i].dev = dev; + dev->sgd[i].id = i << 4; + dev->sgd[i].dev = dev; - dev->sgd[j][i].modem = j; + /* Disable the FIFO on SGDs we don't care about. */ + if ((i != 0) && (i != 2)) + dev->sgd[i].always_run = 1; - /* Disable the FIFO on SGDs we don't care about. */ - if ((i != 0) && (i != 2)) - dev->sgd[j][i].always_run = 1; - - timer_add(&dev->sgd[j][i].dma_timer, ac97_via_sgd_process, &dev->sgd[j][i], 0); - } + timer_add(&dev->sgd[i].dma_timer, ac97_via_sgd_process, &dev->sgd[i], 0); } /* Set up playback pollers. */ - timer_add(&dev->sgd[0][0].poll_timer, ac97_via_poll_stereo, dev, 0); - timer_add(&dev->sgd[0][2].poll_timer, ac97_via_poll_fm, dev, 0); + timer_add(&dev->sgd[0].poll_timer, ac97_via_poll_stereo, &dev->sgd[0], 0); + timer_add(&dev->sgd[2].poll_timer, ac97_via_poll_fm, &dev->sgd[2], 0); ac97_via_speed_changed(dev); /* Set up playback handler. */