diff --git a/src/chipset/via_pipc.c b/src/chipset/via_pipc.c index 6b855f5aa..248664983 100644 --- a/src/chipset/via_pipc.c +++ b/src/chipset/via_pipc.c @@ -493,14 +493,28 @@ pipc_reset_hard(void *priv) } dev->ac97_regs[i][0x1c] = 0x01; + if (i == 0) { + dev->ac97_regs[i][0x34] = 0xc0; + + dev->ac97_regs[i][0xc0] = 0x01; + dev->ac97_regs[i][0xc1] = 0x00; + dev->ac97_regs[i][0xc2] = 0x03; + dev->ac97_regs[i][0xc3] = 0x00; + + dev->ac97_regs[i][0xc4] = 0x00; + dev->ac97_regs[i][0xc5] = 0x00; + dev->ac97_regs[i][0xc6] = 0x00; + dev->ac97_regs[i][0xc7] = 0x00; + } + dev->ac97_regs[i][0x3d] = 0x03; - if (i == 0) + if (i == 0) { dev->ac97_regs[i][0x40] = 0x01; - - dev->ac97_regs[i][0x43] = 0x1c; - dev->ac97_regs[i][0x48] = 0x01; - dev->ac97_regs[i][0x4b] = 0x02; + dev->ac97_regs[i][0x43] = 0x1c; + dev->ac97_regs[i][0x48] = 0x01; + dev->ac97_regs[i][0x4b] = 0x00; + } pipc_sgd_handlers(dev, i); pipc_codec_handlers(dev, i); @@ -742,10 +756,12 @@ pipc_codec_handlers(pipc_t *dev, uint8_t modem) if (!dev->ac97) return; + uint32_t base = (dev->ac97_regs[modem][0x1d] << 8); + if (modem) - ac97_via_remap_modem_codec(dev->ac97, dev->ac97_regs[1][0x1d] << 8, dev->ac97_regs[1][0x04] & PCI_COMMAND_IO); + ac97_via_remap_modem_codec(dev->ac97, base, dev->ac97_regs[1][0x04] & PCI_COMMAND_IO); else - ac97_via_remap_audio_codec(dev->ac97, dev->ac97_regs[0][0x1d] << 8, dev->ac97_regs[0][0x04] & PCI_COMMAND_IO); + ac97_via_remap_audio_codec(dev->ac97, base, dev->ac97_regs[0][0x04] & PCI_COMMAND_IO); } static uint8_t @@ -1204,7 +1220,7 @@ pipc_write(int func, int addr, uint8_t val, void *priv) case 0x77: if ((dev->local >= VIA_PIPC_686A) && (val & 0x10)) - pclog("PIPC: Warning: Internal I/O APIC enabled.\n"); + warning("PIPC: Warning: Internal I/O APIC enabled.\n"); nvr_via_wp_set(!!(val & 0x04), 0x32, dev->nvr); nvr_via_wp_set(!!(val & 0x02), 0x0d, dev->nvr); break; @@ -1488,36 +1504,39 @@ pipc_write(int func, int addr, uint8_t val, void *priv) break; } } else if (func <= pm_func + 2) { /* AC97 / MC97 */ - /* Read-only addresses. */ - if ((addr < 0x4) || ((addr >= 0x6) && (addr < 0x9)) || ((addr >= 0xc) && (addr < 0x11)) || (addr == 0x16) || (addr == 0x17) || (addr == 0x1a) || (addr == 0x1b) || ((addr >= 0x1e) && (addr < 0x2c)) || ((addr >= 0x30) && (addr < 0x34)) || ((addr >= 0x35) && (addr < 0x3c)) || ((addr >= 0x3d) && (addr < 0x41)) || ((addr >= 0x45) && (addr < 0x4a)) || (addr >= 0x4c)) - return; - /* Small shortcut. */ func = func - pm_func - 1; - /* Check disable bits and specific read-only addresses for both controllers. */ - if ((func == 0) && (((addr >= 0x09) && (addr < 0xc)) || (addr == 0x44) || (dev->pci_isa_regs[0x85] & 0x04))) + /* Check disable bits. */ + if ((func == 0) && (dev->pci_isa_regs[0x85] & 0x04)) return; - if ((func == 1) && ((addr == 0x14) || (addr == 0x15) || (addr == 0x18) || (addr == 0x19) || (addr == 0x42) || (addr == 0x43) || (addr == 0x48) || (addr == 0x4a) || (addr == 0x4b) || (dev->pci_isa_regs[0x85] & 0x08))) + if ((func == 1) && (dev->pci_isa_regs[0x85] & 0x08)) return; switch (addr) { case 0x04: - dev->ac97_regs[func][addr] = val; + dev->ac97_regs[func][addr] = val & 0x01; pipc_sgd_handlers(dev, func); + if (func == 0) { + pipc_fmnmi_handlers(dev, func); + pipc_sb_handlers(dev, func); + } pipc_codec_handlers(dev, func); - pipc_fmnmi_handlers(dev, func); break; case 0x09: case 0x0a: case 0x0b: - if (dev->ac97_regs[func][0x44] & 0x20) + /* Not writable on audio, only on modem. */ + if ((func == 1) && (dev->ac97_regs[func][0x44] & 0x20)) dev->ac97_regs[func][addr] = val; break; - case 0x10: + /* + The lowest 10 bytes are always 0x01, indicating + a 256-byte I/O space. + */ case 0x11: dev->ac97_regs[func][addr] = val; pipc_sgd_handlers(dev, func); @@ -1525,21 +1544,26 @@ pipc_write(int func, int addr, uint8_t val, void *priv) case 0x14: case 0x15: - if (addr == 0x14) - val = (val & 0xfc) | 1; - dev->ac97_regs[func][addr] = val; - pipc_fmnmi_handlers(dev, func); + /* Not present on modem. */ + if (func == 0) { + if (addr == 0x14) + val = (val & 0xfc) | 1; + dev->ac97_regs[func][addr] = val; + pipc_fmnmi_handlers(dev, func); + } break; case 0x18: case 0x19: - if (addr == 0x18) - val = (val & 0xfc) | 1; - dev->ac97_regs[func][addr] = val; - pipc_sb_handlers(dev, func); + /* Not present on modem. */ + if (func == 0) { + if (addr == 0x18) + val = (val & 0xfc) | 1; + dev->ac97_regs[func][addr] = val; + pipc_sb_handlers(dev, func); + } break; - case 0x1c: case 0x1d: dev->ac97_regs[func][addr] = val; pipc_codec_handlers(dev, func); @@ -1549,39 +1573,84 @@ pipc_write(int func, int addr, uint8_t val, void *priv) case 0x2d: case 0x2e: case 0x2f: - if ((func == 0) && (dev->ac97_regs[func][0x42] & 0x20)) + if (((func == 0) && (dev->ac97_regs[func][0x42] & 0x20)) || + ((func == 1) && (dev->ac97_regs[func][0x44] & 0x10))) dev->ac97_regs[func][addr] = val; break; + case 0x3c: + dev->ac97_regs[func][addr] = val & 0x0f; + break; + case 0x41: dev->ac97_regs[func][addr] = val; ac97_via_write_control(dev->ac97, func, val); break; case 0x42: - case 0x4a: - case 0x4b: - dev->ac97_regs[0][addr] = dev->ac97_regs[1][addr] = val; - gameport_remap(dev->gameport, (dev->ac97_regs[0][0x42] & 0x08) ? ((dev->ac97_regs[0][0x4b] << 8) | (dev->ac97_regs[0][0x4a] & 0xf8)) : 0); - if (addr == 0x42) - pipc_sb_handlers(dev, func); + case 0x4a ... 0x4b: + if (func == 0) { + dev->ac97_regs[func][addr] = val; + gameport_remap(dev->gameport, (dev->ac97_regs[func][0x42] & 0x08) ? + ((dev->ac97_regs[func][0x4b] << 8) | + (dev->ac97_regs[func][0x4a] & 0xf8)) : 0); + + if (addr == 0x42) + pipc_sb_handlers(dev, func); + } break; case 0x43: - dev->ac97_regs[0][addr] = dev->ac97_regs[1][addr] = val; + if (func == 0) + dev->ac97_regs[func][addr] = val; break; case 0x44: - dev->ac97_regs[0][addr] = dev->ac97_regs[1][addr] = val & 0xf0; + if (func == 1) + dev->ac97_regs[func][addr] = val & 0xf0; break; - case 0x45: case 0x48: - dev->ac97_regs[0][addr] = dev->ac97_regs[1][addr] = val & 0x0f; + if (func == 0) + dev->ac97_regs[func][addr] = val & 0x0f; + break; + + case 0x80: + case 0x81: + case 0x82: + dev->ac97_regs[func][addr] = val; + break; + case 0x83: + dev->ac97_regs[func][addr] = ((dev->ac97_regs[func][addr] & 0x01) | + (val & 0xc0)) & ~(val & 0x0a); + break; + + case 0x88: + case 0x89: + dev->ac97_regs[func][addr] = val; + break; + case 0x8a: + case 0x8b: + dev->ac97_regs[func][addr] &= ~val; + break; + + case 0x8e: + case 0x8f: + dev->ac97_regs[func][addr] = val; + break; + + case 0xc4: + if (func == 0) + dev->ac97_regs[func][addr] = (dev->ac97_regs[func][addr] & 0x0c) | + (val & 0x03); + break; + case 0xc5: + if (func == 0) + dev->ac97_regs[func][addr] = (dev->ac97_regs[func][addr] & 0x60) | + (val & 0x9f); break; default: - dev->ac97_regs[func][addr] = val; break; } } diff --git a/src/sound/snd_ac97_codec.c b/src/sound/snd_ac97_codec.c index e92830f67..7a9951289 100644 --- a/src/sound/snd_ac97_codec.c +++ b/src/sound/snd_ac97_codec.c @@ -539,8 +539,9 @@ ac97_codec_getattn(void *priv, uint8_t reg, int *l, int *r) *l = codec_attn[0x3f - (l_val & 0x3f)]; *r = codec_attn[0x3f - (r_val & 0x3f)]; } else { /* 5-bit gain */ - *l = codec_attn[0x47 - (l_val & 0x1f)]; - *r = codec_attn[0x47 - (r_val & 0x1f)]; + /* Yes, 0x3f is correct, the 0x47 was most likely a decimal-hex mix-up. */ + *l = codec_attn[0x3f - (l_val & 0x1f)]; + *r = codec_attn[0x3f - (r_val & 0x1f)]; } /* Apply per-channel mute and center/LFE powerdowns where applicable. */ diff --git a/src/sound/snd_ac97_via.c b/src/sound/snd_ac97_via.c index 8c1e45c40..68ef5fe5d 100644 --- a/src/sound/snd_ac97_via.c +++ b/src/sound/snd_ac97_via.c @@ -24,6 +24,7 @@ #include <86box/86box.h> #include <86box/device.h> #include <86box/io.h> +#include <86box/dma.h> #include <86box/mem.h> #include <86box/pci.h> #include <86box/pic.h> @@ -31,10 +32,13 @@ #include <86box/sound.h> #include <86box/timer.h> #include <86box/plat_unused.h> +#include "cpu.h" 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; @@ -63,7 +67,7 @@ typedef struct _ac97_via_ { uint16_t audio_codec_base; uint16_t modem_sgd_base; uint16_t modem_codec_base; - uint8_t sgd_regs[256]; + uint8_t sgd_regs[2][256]; uint8_t pcm_enabled : 1; uint8_t fm_enabled : 1; uint8_t vsr_enabled : 1; @@ -78,7 +82,7 @@ typedef struct _ac97_via_ { int irq_pin; ac97_codec_t *codec[2][2]; - ac97_via_sgd_t sgd[6]; + ac97_via_sgd_t sgd[2][6]; int master_vol_l; int master_vol_r; @@ -105,7 +109,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); +static void ac97_via_update_codec(ac97_via_t *dev, int modem); static void ac97_via_speed_changed(void *priv); static void ac97_via_filter_cd_audio(int channel, double *buffer, void *priv); @@ -160,29 +164,29 @@ ac97_via_write_control(void *priv, uint8_t modem, uint8_t val) /* 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); + timer_advance_u64(&dev->sgd[0][0].poll_timer, dev->sgd[0][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[2].poll_timer, dev->sgd[2].timer_latch); + timer_advance_u64(&dev->sgd[0][2].poll_timer, dev->sgd[0][2].timer_latch); dev->fm_enabled = i; /* Update primary audio codec state. */ if (dev->codec[0][0]) - ac97_via_update_codec(dev); + ac97_via_update_codec(dev, 0); } } static void -ac97_via_update_irqs(ac97_via_t *dev) +ac97_via_update_irqs(ac97_via_t *dev, int modem) { /* 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[i] & (dev->sgd_regs[i | 0x2] & 0x03)) { + if (dev->sgd_regs[modem][i] & (dev->sgd_regs[modem][i | 0x2] & 0x03)) { pci_set_irq(dev->pci_slot, dev->irq_pin, &dev->irq_state); return; } @@ -192,15 +196,15 @@ ac97_via_update_irqs(ac97_via_t *dev) } static void -ac97_via_update_codec(ac97_via_t *dev) +ac97_via_update_codec(ac97_via_t *dev, int modem) { /* Get primary audio codec. */ - ac97_codec_t *codec = dev->codec[0][0]; + ac97_codec_t *codec = dev->codec[modem][0]; /* Update volumes according to codec registers. */ ac97_codec_getattn(codec, 0x02, &dev->master_vol_l, &dev->master_vol_r); - 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, 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, 0x12, &dev->cd_vol_l, &dev->cd_vol_r); /* Update sample rate according to codec registers and the variable sample rate flag. */ @@ -211,9 +215,9 @@ 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 +// #ifdef ENABLE_AC97_VIA_LOG uint8_t modem = (addr & 0xff00) == dev->modem_sgd_base; -#endif +// #endif addr &= 0xff; uint8_t ret; @@ -221,83 +225,83 @@ ac97_via_sgd_read(uint16_t addr, void *priv) /* Process SGD channel registers. */ switch (addr & 0xf) { case 0x4: - ret = dev->sgd[addr >> 4].entry_ptr; + ret = dev->sgd[modem][addr >> 4].entry_ptr; break; case 0x5: - ret = dev->sgd[addr >> 4].entry_ptr >> 8; + ret = dev->sgd[modem][addr >> 4].entry_ptr >> 8; break; case 0x6: - ret = dev->sgd[addr >> 4].entry_ptr >> 16; + ret = dev->sgd[modem][addr >> 4].entry_ptr >> 16; break; case 0x7: - ret = dev->sgd[addr >> 4].entry_ptr >> 24; + ret = dev->sgd[modem][addr >> 4].entry_ptr >> 24; break; case 0xc: - ret = dev->sgd[addr >> 4].sample_count; + ret = dev->sgd[modem][addr >> 4].sample_count; break; case 0xd: - ret = dev->sgd[addr >> 4].sample_count >> 8; + ret = dev->sgd[modem][addr >> 4].sample_count >> 8; break; case 0xe: - ret = dev->sgd[addr >> 4].sample_count >> 16; + ret = dev->sgd[modem][addr >> 4].sample_count >> 16; break; default: - ret = dev->sgd_regs[addr]; + ret = dev->sgd_regs[modem][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[modem][0x00] & 0x01); + ret |= (dev->sgd_regs[modem][0x10] & 0x01) << 1; + ret |= (dev->sgd_regs[modem][0x20] & 0x01) << 2; - ret |= (dev->sgd_regs[0x00] & 0x02) << 3; - ret |= (dev->sgd_regs[0x10] & 0x02) << 4; - ret |= (dev->sgd_regs[0x20] & 0x02) << 5; + ret |= (dev->sgd_regs[modem][0x00] & 0x02) << 3; + ret |= (dev->sgd_regs[modem][0x10] & 0x02) << 4; + ret |= (dev->sgd_regs[modem][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); + 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] & 0x80) >> 3; - ret |= (dev->sgd_regs[0x10] & 0x80) >> 2; - ret |= (dev->sgd_regs[0x20] & 0x80) >> 1; + ret |= (dev->sgd_regs[modem][0x00] & 0x80) >> 3; + ret |= (dev->sgd_regs[modem][0x10] & 0x80) >> 2; + ret |= (dev->sgd_regs[modem][0x20] & 0x80) >> 1; break; case 0x86: - ret = (dev->sgd_regs[0x40] & 0x01); - ret |= (dev->sgd_regs[0x50] & 0x01) << 1; + ret = (dev->sgd_regs[modem][0x40] & 0x01); + ret |= (dev->sgd_regs[modem][0x50] & 0x01) << 1; - ret |= (dev->sgd_regs[0x40] & 0x02) << 3; - ret |= (dev->sgd_regs[0x50] & 0x02) << 4; + ret |= (dev->sgd_regs[modem][0x40] & 0x02) << 3; + ret |= (dev->sgd_regs[modem][0x50] & 0x02) << 4; break; case 0x87: - ret = (dev->sgd_regs[0x40] & 0x04) >> 2; - ret |= (dev->sgd_regs[0x50] & 0x04) >> 1; + ret = (dev->sgd_regs[modem][0x40] & 0x04) >> 2; + ret |= (dev->sgd_regs[modem][0x50] & 0x04) >> 1; - ret |= (dev->sgd_regs[0x40] & 0x80) >> 3; - ret |= (dev->sgd_regs[0x50] & 0x80) >> 2; + ret |= (dev->sgd_regs[modem][0x40] & 0x80) >> 3; + ret |= (dev->sgd_regs[modem][0x50] & 0x80) >> 2; break; default: - ret = dev->sgd_regs[addr]; + ret = dev->sgd_regs[modem][addr]; break; } } - ac97_via_log("AC97 VIA %d: sgd_read(%02X) = %02X\n", modem, addr, ret); + ac97_via_log("[%04X:%08X] [%i] AC97 VIA %d: sgd_read(%02X) = %02X\n", CS, cpu_state.pc, msw & 1, modem, addr, ret); return ret; } @@ -311,7 +315,9 @@ ac97_via_sgd_write(uint16_t addr, uint8_t val, void *priv) ac97_codec_t *codec; addr &= 0xff; - ac97_via_log("AC97 VIA %d: sgd_write(%02X, %02X)\n", modem, addr, val); + ac97_via_log("[%04X:%08X] [%i] AC97 VIA %d: sgd_write(%02X, %02X)\n", CS, cpu_state.pc, msw & 1, modem, addr, val); + + // if ((CS == 0x10000) && (cpu_state.pc == 0x000073d1)) /* Check function-specific read only registers. */ if ((addr >= (modem ? 0x00 : 0x40)) && (addr < (modem ? 0x40 : 0x60))) @@ -324,42 +330,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[addr] &= ~(val & 0x07); + dev->sgd_regs[modem][addr] &= ~(val & 0x07); /* Update status interrupts. */ - ac97_via_update_irqs(dev); + ac97_via_update_irqs(dev, modem); return; case 0x1: /* Start SGD if requested. */ if (val & 0x80) { - if (dev->sgd_regs[addr & 0xf0] & 0x80) { + if (dev->sgd_regs[modem][addr & 0xf0] & 0x80) { /* Queue SGD trigger if already running. */ - dev->sgd_regs[addr & 0xf0] |= 0x08; + dev->sgd_regs[modem][addr & 0xf0] |= 0x08; } else { /* Start SGD immediately. */ - dev->sgd_regs[addr & 0xf0] = (dev->sgd_regs[addr & 0xf0] & ~0x47) | 0x80; + dev->sgd_regs[modem][addr & 0xf0] = (dev->sgd_regs[modem][addr & 0xf0] & ~0x47) | 0x80; /* Start at the specified entry pointer. */ - dev->sgd[addr >> 4].entry_ptr = *((uint32_t *) &dev->sgd_regs[(addr & 0xf0) | 0x4]) & 0xfffffffe; - dev->sgd[addr >> 4].restart = 2; + dev->sgd[modem][addr >> 4].entry_ptr = *((uint32_t *) &dev->sgd_regs[modem][(addr & 0xf0) | 0x4]) & 0xfffffffe; + dev->sgd[modem][addr >> 4].restart = 2; /* Start the actual SGD process. */ - ac97_via_sgd_process(&dev->sgd[addr >> 4]); + ac97_via_sgd_process(&dev->sgd[modem][addr >> 4]); } } /* Stop SGD if requested. */ if (val & 0x40) - dev->sgd_regs[addr & 0xf0] &= ~0x88; + dev->sgd_regs[modem][addr & 0xf0] &= ~0x88; val &= 0x08; /* (Un)pause SGD if requested. */ if (val & 0x08) - dev->sgd_regs[addr & 0xf0] |= 0x40; + dev->sgd_regs[modem][addr & 0xf0] |= 0x40; else - dev->sgd_regs[addr & 0xf0] &= ~0x40; + dev->sgd_regs[modem][addr & 0xf0] &= ~0x40; break; @@ -387,7 +393,7 @@ ac97_via_sgd_write(uint16_t addr, uint8_t val, void *priv) case 0x82: /* Determine the selected codec. */ - i = !!(dev->sgd_regs[0x83] & 0x40); + i = !!(dev->sgd_regs[modem][0x83] & 0x40); codec = dev->codec[modem][i]; /* Keep value in register if this codec is not present. */ @@ -395,20 +401,20 @@ ac97_via_sgd_write(uint16_t addr, uint8_t val, void *priv) /* Read from or write to codec. */ if (val & 0x80) { if (val & 1) { /* return 0x0000 on unaligned reads (real 686B behavior) */ - dev->sgd_regs[0x80] = dev->sgd_regs[0x81] = 0x00; + dev->sgd_regs[modem][0x80] = dev->sgd_regs[modem][0x81] = 0x00; } else { - *((uint16_t *) &dev->codec_shadow[modem].regs_codec[i][val & 0x7f]) = *((uint16_t *) &dev->sgd_regs[0x80]) = ac97_codec_readw(codec, val); + *((uint16_t *) &dev->codec_shadow[modem].regs_codec[i][val & 0x7f]) = *((uint16_t *) &dev->sgd_regs[modem][0x80]) = ac97_codec_readw(codec, val); } /* Flag data/status/index for this codec as valid. */ - dev->sgd_regs[0x83] |= 0x02 << (i << 1); + dev->sgd_regs[modem][0x83] |= 0x02 << (i << 1); } else if (!(val & 1)) { /* do nothing on unaligned writes */ ac97_codec_writew(codec, val, - *((uint16_t *) &dev->codec_shadow[modem].regs_codec[i][val & 0x7f]) = *((uint16_t *) &dev->sgd_regs[0x80])); + *((uint16_t *) &dev->codec_shadow[modem].regs_codec[i][val & 0x7f]) = *((uint16_t *) &dev->sgd_regs[modem][0x80])); /* Update primary audio codec state if that codec was written to. */ if (!modem && !i) { - ac97_via_update_codec(dev); + ac97_via_update_codec(dev, 0); /* 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 @@ -424,9 +430,9 @@ ac97_via_sgd_write(uint16_t addr, uint8_t val, void *priv) 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[addr] & ~(val & 0x0a)) | (val & 0xc0); + val = ((dev->sgd_regs[modem][addr] & 0x3f) & ~(val & 0x0a)) | (val & 0xc0); #else - val = dev->sgd_regs[addr] | (val & 0xc0); + val = (dev->sgd_regs[modem][addr] & 0x3f) | (val & 0xc0); #endif break; @@ -435,7 +441,7 @@ ac97_via_sgd_write(uint16_t addr, uint8_t val, void *priv) } } - dev->sgd_regs[addr] = val; + dev->sgd_regs[modem][addr] = val; } void @@ -479,6 +485,8 @@ ac97_via_codec_read(uint16_t addr, void *priv) ac97_via_log("AC97 VIA %d: codec_read(%02X) = %02X\n", modem, addr, ret); + ac97_via_log("[%04X:%08X] [%i] AC97 VIA %d: codec_read(%02X) = %02X\n", CS, cpu_state.pc, msw & 1, modem, addr, ret); + return ret; } @@ -489,6 +497,8 @@ ac97_via_codec_write(uint16_t addr, uint8_t val, void *priv) uint8_t modem = (addr & 0xff00) == dev->modem_codec_base; 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); + ac97_via_log("AC97 VIA %d: codec_write(%02X, %02X)\n", modem, addr, val); /* Unknown behavior, maybe it does write to the shadow registers? */ @@ -501,12 +511,12 @@ ac97_via_remap_audio_codec(void *priv, uint16_t new_io_base, uint8_t enable) ac97_via_t *dev = (ac97_via_t *) priv; if (dev->audio_codec_base) - io_removehandler(dev->audio_codec_base, 256, ac97_via_codec_read, NULL, NULL, ac97_via_codec_write, NULL, NULL, dev); + io_removehandler(dev->audio_codec_base, 4, ac97_via_codec_read, NULL, NULL, ac97_via_codec_write, NULL, NULL, dev); dev->audio_codec_base = new_io_base; if (dev->audio_codec_base && enable) - io_sethandler(dev->audio_codec_base, 256, ac97_via_codec_read, NULL, NULL, ac97_via_codec_write, NULL, NULL, dev); + io_sethandler(dev->audio_codec_base, 4, ac97_via_codec_read, NULL, NULL, ac97_via_codec_write, NULL, NULL, dev); } void @@ -515,12 +525,12 @@ ac97_via_remap_modem_codec(void *priv, uint16_t new_io_base, uint8_t enable) ac97_via_t *dev = (ac97_via_t *) priv; if (dev->modem_codec_base) - io_removehandler(dev->modem_codec_base, 256, ac97_via_codec_read, NULL, NULL, ac97_via_codec_write, NULL, NULL, dev); + io_removehandler(dev->modem_codec_base, 4, ac97_via_codec_read, NULL, NULL, ac97_via_codec_write, NULL, NULL, dev); dev->modem_codec_base = new_io_base; if (dev->modem_codec_base && enable) - io_sethandler(dev->modem_codec_base, 256, ac97_via_codec_read, NULL, NULL, ac97_via_codec_write, NULL, NULL, dev); + io_sethandler(dev->modem_codec_base, 4, ac97_via_codec_read, NULL, NULL, ac97_via_codec_write, NULL, NULL, dev); } static void @@ -551,12 +561,12 @@ 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->id] & 0xc4; + uint8_t sgd_status = dev->sgd_regs[sgd->modem][sgd->id] & 0xc4; if (!(sgd_status & 0x80)) return; /* Schedule next run. */ - timer_on_auto(&sgd->dma_timer, 10.0); + timer_on_auto(&sgd->dma_timer, 1.0); /* Process SGD if it's active, and the FIFO has room or is disabled. */ if (((sgd_status & 0xc7) == 0x80) && (sgd->always_run || ((sgd->fifo_end - sgd->fifo_pos) <= (sizeof(sgd->fifo) - 4)))) { @@ -564,13 +574,15 @@ ac97_via_sgd_process(void *priv) if (sgd->restart) { /* (Re)load entry pointer if required. */ if (sgd->restart & 2) - sgd->entry_ptr = *((uint32_t *) &dev->sgd_regs[sgd->id | 0x4]) & 0xfffffffe; /* TODO: probe real hardware - does "even addr" actually mean dword aligned? */ + sgd->entry_ptr = *((uint32_t *) &dev->sgd_regs[sgd->modem][sgd->id | 0x4]) & 0xfffffffe; /* TODO: probe real hardware - does "even addr" actually mean dword aligned? */ sgd->restart = 0; /* Read entry. */ - sgd->sample_ptr = mem_readl_phys(sgd->entry_ptr); + // sgd->sample_ptr = mem_readl_phys(sgd->entry_ptr); + dma_bm_read(sgd->entry_ptr, (uint8_t *) &sgd->sample_ptr, 4, 4); sgd->entry_ptr += 4; - sgd->sample_count = mem_readl_phys(sgd->entry_ptr); + // sgd->sample_count = mem_readl_phys(sgd->entry_ptr); + dma_bm_read(sgd->entry_ptr, (uint8_t *) &sgd->sample_count, 4, 4); sgd->entry_ptr += 4; #ifdef ENABLE_AC97_VIA_LOG if (((sgd->sample_ptr == 0xffffffff) && (sgd->sample_count == 0xffffffff)) || ((sgd->sample_ptr == 0x00000000) && (sgd->sample_count == 0x00000000))) @@ -588,10 +600,12 @@ ac97_via_sgd_process(void *priv) 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)])); + // mem_writel_phys(sgd->sample_ptr, *((uint32_t *) &sgd->fifo[sgd->fifo_end & (sizeof(sgd->fifo) - 1)])); + dma_bm_write(sgd->sample_ptr, &sgd->fifo[sgd->fifo_end & (sizeof(sgd->fifo) - 1)], 4, 4); } else { /* Read channel: write data to FIFO. */ - *((uint32_t *) &sgd->fifo[sgd->fifo_end & (sizeof(sgd->fifo) - 1)]) = mem_readl_phys(sgd->sample_ptr); + // *((uint32_t *) &sgd->fifo[sgd->fifo_end & (sizeof(sgd->fifo) - 1)]) = mem_readl_phys(sgd->sample_ptr); + dma_bm_read(sgd->sample_ptr, &sgd->fifo[sgd->fifo_end & (sizeof(sgd->fifo) - 1)], 4, 4); } sgd->fifo_end += 4; sgd->sample_ptr += 4; @@ -608,17 +622,17 @@ ac97_via_sgd_process(void *priv) ac97_via_log(" with STOP"); /* Raise STOP to pause SGD. */ - dev->sgd_regs[sgd->id] |= 0x04; + dev->sgd_regs[sgd->modem][sgd->id] |= 0x04; } if (sgd->entry_flags & 0x40) { ac97_via_log(" with FLAG"); /* Raise FLAG to pause SGD. */ - dev->sgd_regs[sgd->id] |= 0x01; + dev->sgd_regs[sgd->modem][sgd->id] |= 0x01; #ifdef ENABLE_AC97_VIA_LOG - if (dev->sgd_regs[sgd->id | 0x2] & 0x01) + if (dev->sgd_regs[sgd->modem][sgd->id | 0x2] & 0x01) ac97_via_log(" interrupt"); #endif } @@ -627,19 +641,19 @@ ac97_via_sgd_process(void *priv) ac97_via_log(" with EOL"); /* Raise EOL. */ - dev->sgd_regs[sgd->id] |= 0x02; + dev->sgd_regs[sgd->modem][sgd->id] |= 0x02; #ifdef ENABLE_AC97_VIA_LOG - if (dev->sgd_regs[sgd->id | 0x2] & 0x02) + if (dev->sgd_regs[sgd->modem][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)) { + if ((dev->sgd_regs[sgd->modem][sgd->id] & 0x08) || (dev->sgd_regs[sgd->modem][sgd->id | 0x2] & 0x80)) { ac97_via_log(" restart"); /* Un-queue trigger. */ - dev->sgd_regs[sgd->id] &= ~0x08; + dev->sgd_regs[sgd->modem][sgd->id] &= ~0x08; /* Go back to the starting block on the next run. */ sgd->restart = 2; @@ -647,13 +661,13 @@ ac97_via_sgd_process(void *priv) ac97_via_log(" finish"); /* Terminate SGD. */ - dev->sgd_regs[sgd->id] &= ~0x80; + dev->sgd_regs[sgd->modem][sgd->id] &= ~0x80; } } ac97_via_log("\n"); /* Fire any requested status interrupts. */ - ac97_via_update_irqs(dev); + ac97_via_update_irqs(dev, sgd->modem); } } } @@ -662,7 +676,7 @@ static void ac97_via_poll_stereo(void *priv) { ac97_via_t *dev = (ac97_via_t *) priv; - ac97_via_sgd_t *sgd = &dev->sgd[0]; /* Audio Read */ + ac97_via_sgd_t *sgd = &dev->sgd[0][0]; /* Audio Read */ /* Schedule next run if PCM playback is enabled. */ if (dev->pcm_enabled) @@ -672,7 +686,7 @@ ac97_via_poll_stereo(void *priv) ac97_via_update_stereo(dev, sgd); /* Feed next sample from the FIFO. */ - switch (dev->sgd_regs[sgd->id | 0x2] & 0x30) { + switch (dev->sgd_regs[0][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; @@ -718,7 +732,7 @@ static void ac97_via_poll_fm(void *priv) { ac97_via_t *dev = (ac97_via_t *) priv; - ac97_via_sgd_t *sgd = &dev->sgd[2]; /* FM Read */ + ac97_via_sgd_t *sgd = &dev->sgd[0][2]; /* FM Read */ /* Schedule next run if FM playback is enabled. */ if (dev->fm_enabled) @@ -746,15 +760,15 @@ 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]); - ac97_via_update_stereo(dev, &dev->sgd[2]); + ac97_via_update_stereo(dev, &dev->sgd[0][0]); + ac97_via_update_stereo(dev, &dev->sgd[0][2]); for (int c = 0; c < len * 2; c++) { - buffer[c] += dev->sgd[0].buffer[c] / 2; - buffer[c] += dev->sgd[2].buffer[c] / 2; + buffer[c] += dev->sgd[0][0].buffer[c] / 2; + buffer[c] += dev->sgd[0][2].buffer[c] / 2; } - dev->sgd[0].pos = dev->sgd[2].pos = 0; + dev->sgd[0][0].pos = dev->sgd[0][2].pos = 0; } static void @@ -780,8 +794,8 @@ ac97_via_speed_changed(void *priv) else freq = (double) SOUND_FREQ; - 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 */ + 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 */ } static void * @@ -799,19 +813,23 @@ ac97_via_init(UNUSED(const device_t *info)) /* 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; + for (uint8_t j = 0; j < 2; j++) { + dev->sgd[j][i].id = i << 4; + dev->sgd[j][i].dev = dev; - /* Disable the FIFO on SGDs we don't care about. */ - if ((i != 0) && (i != 2)) - dev->sgd[i].always_run = 1; + dev->sgd[j][i].modem = j; - timer_add(&dev->sgd[i].dma_timer, ac97_via_sgd_process, &dev->sgd[i], 0); + /* 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); + } } /* Set up playback pollers. */ - timer_add(&dev->sgd[0].poll_timer, ac97_via_poll_stereo, dev, 0); - timer_add(&dev->sgd[2].poll_timer, ac97_via_poll_fm, dev, 0); + 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); ac97_via_speed_changed(dev); /* Set up playback handler. */