From 8cf651db5719f82495dfe28657268cee419525c5 Mon Sep 17 00:00:00 2001 From: RichardG867 Date: Tue, 27 Jul 2021 22:53:24 -0300 Subject: [PATCH] AC97 improvements, including variable sample rate support on VIA --- src/include/86box/snd_ac97.h | 1 + src/sound/snd_ac97_codec.c | 75 +++++++++++++++++++++++++++++++++--- src/sound/snd_ac97_via.c | 64 ++++++++++++++++++++++-------- 3 files changed, 119 insertions(+), 21 deletions(-) diff --git a/src/include/86box/snd_ac97.h b/src/include/86box/snd_ac97.h index 8b5e89920..99601e2e7 100644 --- a/src/include/86box/snd_ac97.h +++ b/src/include/86box/snd_ac97.h @@ -28,6 +28,7 @@ extern uint8_t ac97_codec_read(ac97_codec_t *dev, uint8_t reg); extern void ac97_codec_write(ac97_codec_t *dev, uint8_t reg, uint8_t val); extern void ac97_codec_reset(void *priv); extern void ac97_codec_getattn(void *priv, uint8_t reg, int *l, int *r); +extern uint32_t ac97_codec_getrate(void *priv, uint8_t reg); 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); diff --git a/src/sound/snd_ac97_codec.c b/src/sound/snd_ac97_codec.c index 133b9f0c5..02ae9cbe9 100644 --- a/src/sound/snd_ac97_codec.c +++ b/src/sound/snd_ac97_codec.c @@ -79,6 +79,8 @@ ac97_codec_read(ac97_codec_t *dev, uint8_t reg) void ac97_codec_write(ac97_codec_t *dev, uint8_t reg, uint8_t val) { + uint8_t i; + ac97_codec_log("AC97 Codec %d: write(%02X, %02X)\n", dev->codec_id, reg, val); reg &= 0x7f; @@ -86,7 +88,7 @@ ac97_codec_write(ac97_codec_t *dev, uint8_t reg, uint8_t val) switch (reg) { case 0x00: case 0x01: /* Reset / ID code */ ac97_codec_reset(dev); - /* fall-through */ + return; case 0x08: case 0x09: /* Master Tone Control (optional) */ case 0x0d: /* Phone Volume MSB */ @@ -96,7 +98,8 @@ ac97_codec_write(ac97_codec_t *dev, uint8_t reg, uint8_t val) case 0x24: case 0x25: /* Audio Interrupt and Paging Mechanism (optional) */ case 0x26: /* Powerdown Ctrl/Stat LSB */ case 0x28: case 0x29: /* Extended Audio ID */ - //case 0x2a ... 0x59: /* Linux tests for audio capability by writing to 38-39 */ + case 0x2b: /* Extended Audio Status/Control MSB */ + //case 0x36 ... 0x59: /* Linux tests for audio capability by writing to 38-39 */ case 0x5a ... 0x5f: /* Vendor Reserved */ //case 0x60 ... 0x6f: case 0x70 ... 0x7f: /* Vendor Reserved */ @@ -107,7 +110,7 @@ ac97_codec_write(ac97_codec_t *dev, uint8_t reg, uint8_t val) case 0x05: /* Aux Out Volume MSB */ val &= 0xbf; - /* Convert 6-bit level 1xxxxx to 011111 per specification. */ + /* Convert 6-bit level 1xxxxx to 011111. */ if (val & 0x20) { val &= ~0x20; val |= 0x1f; @@ -125,7 +128,7 @@ ac97_codec_write(ac97_codec_t *dev, uint8_t reg, uint8_t val) case 0x06: /* Mono Volume LSB */ val &= 0x3f; - /* Convert 6-bit level 1xxxxx to 011111 per specification. */ + /* Convert 6-bit level 1xxxxx to 011111. */ if (val & 0x20) { val &= ~0x20; val |= 0x1f; @@ -172,6 +175,34 @@ ac97_codec_write(ac97_codec_t *dev, uint8_t reg, uint8_t val) case 0x21: /* General Purpose MSB */ val &= 0x83; break; + + case 0x2a: /* Extended Audio Status/Control LSB */ + val &= 0x0b; + + /* Reset DAC sample rates to 48 KHz if VRA is being cleared. */ + if (!(val & 0x01)) { + for (i = 0x2c; i <= 0x30; i += 2) + *((uint16_t *) &dev->regs[i]) = 48000; + } + + /* Reset ADC sample rates to 48 KHz if VRM is being cleared. */ + if (!(val & 0x08)) { + for (i = 0x32; i <= 0x34; i += 2) + *((uint16_t *) &dev->regs[i]) = 48000; + } + break; + + case 0x2c ... 0x31: /* DAC Rates */ + /* Writable only if VRA is set. */ + if (!(dev->regs[0x2a] & 0x01)) + return; + break; + + case 0x32 ... 0x35: /* ADC Rates */ + /* Writable only if VRM is set. */ + if (!(dev->regs[0x2a] & 0x08)) + return; + break; } dev->regs[reg] = val; @@ -182,17 +213,28 @@ void ac97_codec_reset(void *priv) { ac97_codec_t *dev = (ac97_codec_t *) priv; + uint8_t i; ac97_codec_log("AC97 Codec %d: reset()\n", dev->codec_id); memset(dev->regs, 0, sizeof(dev->regs)); - /* Mute outputs by default. */ - dev->regs[0x02] = dev->regs[0x04] = dev->regs[0x06] = 0x80; + /* Set default level and gain values. */ + for (i = 0x02; i <= 0x18; i += 2) { + if (i == 0x08) + continue; + if (i >= 0x0c) + dev->regs[i] = 0x08; + dev->regs[i | 1] = (i >= 0x10) ? 0x88 : 0x80; + } /* Flag codec as ready. */ dev->regs[0x26] = 0x0f; + /* Set up variable sample rate support. */ + dev->regs[0x28] = 0x0b; + ac97_codec_write(dev, 0x2a, 0x00); /* reset DAC/ADC sample rates */ + /* Set Codec and Vendor IDs. */ dev->regs[0x29] = (dev->codec_id << 6) | 0x02; dev->regs[0x7c] = dev->vendor_id >> 16; @@ -227,6 +269,24 @@ ac97_codec_getattn(void *priv, uint8_t reg, int *l, int *r) } +uint32_t +ac97_codec_getrate(void *priv, uint8_t reg) +{ + ac97_codec_t *dev = (ac97_codec_t *) priv; + + /* Get configured sample rate, which is always 48000 if VRA/VRM is not set. */ + uint32_t ret = *((uint16_t *) &dev->regs[reg]); + + /* If this is a DAC, double sample rate if DRA is set. */ + if ((reg < 0x32) && (dev->regs[0x2a] & 0x02)) + ret <<= 1; + + ac97_codec_log("AC97 Codec %d: getrate(%02X) = %d\n", dev->codec_id, reg, ret); + + return ret; +} + + static void * ac97_codec_init(const device_t *info) { @@ -248,6 +308,9 @@ ac97_codec_init(const device_t *info) ac97_codec += sizeof(ac97_codec_t *); dev->codec_id = ac97_codec_id++; + /* Initialize codec registers. */ + ac97_codec_reset(dev); + return dev; } diff --git a/src/sound/snd_ac97_via.c b/src/sound/snd_ac97_via.c index a83ddc17a..476d05474 100644 --- a/src/sound/snd_ac97_via.c +++ b/src/sound/snd_ac97_via.c @@ -44,7 +44,7 @@ typedef struct { typedef struct _ac97_via_ { uint16_t audio_sgd_base, audio_codec_base, modem_sgd_base, modem_codec_base; - uint8_t sgd_regs[256], pcm_enabled: 1, fm_enabled: 1; + uint8_t sgd_regs[256], pcm_enabled: 1, fm_enabled: 1, vsr_enabled: 1; struct { union { uint8_t regs_codec[2][128]; @@ -85,7 +85,8 @@ ac97_via_log(const char *fmt, ...) static void ac97_via_sgd_process(void *priv); -static void ac97_via_update_volumes(ac97_via_t *dev, ac97_codec_t *codec); +static void ac97_via_update_codec(ac97_via_t *dev, ac97_codec_t *codec); +static void ac97_via_speed_changed(void *priv); void @@ -126,20 +127,28 @@ ac97_via_write_control(void *priv, uint8_t modem, uint8_t val) ac97_via_log("AC97 VIA %d: write_control(%02X)\n", modem, val); + if (!modem) { + /* Set the variable sample rate flag now, so that the upcoming + update_codec can properly update the poller timer interval. */ + dev->vsr_enabled = !!(val & 0x08); + } + /* Reset and/or update volumes on all codecs. */ for (i = 0; i <= 1; i++) { - if (dev->codec[modem][i]) { - /* Reset codec if requested. */ - if (val & 0x40) - ac97_codec_reset(dev->codec[modem][i]); + if (!dev->codec[modem][i]) + continue; - /* Update volumes. */ - ac97_via_update_volumes(dev, dev->codec[modem][i]); - } + /* Reset codec if requested. */ + if (!(val & 0x40)) + ac97_codec_reset(dev->codec[modem][i]); + + /* Update primary codec state. */ + if (!modem && !i) + ac97_via_update_codec(dev, dev->codec[modem][i]); } if (!modem) { - /* Start or stop PCM playback. */ + /* Start or stop PCM playback. */ i = (val & 0xf4) == 0xc4; if (i && !dev->pcm_enabled) timer_advance_u64(&dev->timer_count, dev->timer_latch); @@ -174,12 +183,14 @@ ac97_via_update_irqs(ac97_via_t *dev) static void -ac97_via_update_volumes(ac97_via_t *dev, ac97_codec_t *codec) { +ac97_via_update_codec(ac97_via_t *dev, ac97_codec_t *codec) { + /* 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->pcm_vol_l, &dev->pcm_vol_r); ac97_codec_getattn(codec, 0x12, &dev->cd_vol_l, &dev->cd_vol_r); - pclog("master %d %d\npcm %d %d\ncd %d %d\n", dev->master_vol_l, dev->master_vol_r, dev->pcm_vol_l, dev->pcm_vol_r, dev->cd_vol_l, dev->cd_vol_r); + /* Update sample rate according to codec registers and the variable sample rate bit. */ + ac97_via_speed_changed(dev); } @@ -377,8 +388,9 @@ ac97_via_sgd_write(uint16_t addr, uint8_t val, void *priv) val |= 1; ac97_codec_write(codec, val, dev->codec_shadow[modem].regs_codec[i][val] = dev->sgd_regs[0x81]); - /* Update volumes. */ - ac97_via_update_volumes(dev, codec); + /* Update primary codec state. */ + if (!modem && !i) + ac97_via_update_codec(dev, codec); } /* Flag data/status/index for this codec as valid. */ @@ -729,11 +741,30 @@ ac97_via_get_buffer(int32_t *buffer, int len, void *priv) } +static void +via_ac97_filter_cd_audio(int channel, double *buffer, void *priv) +{ + ac97_via_t *dev = (ac97_via_t *) priv; + double c, volume = channel ? dev->cd_vol_r : dev->cd_vol_l; + + c = ((*buffer) * volume) / 65536.0; + *buffer = c; +} + + static void 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)); + 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); + else + freq = 48000.0; + + dev->timer_latch = (uint64_t) ((double) TIMER_USEC * (1000000.0 / freq)); dev->timer_fifo_latch = (uint64_t) ((double) TIMER_USEC * 10.0); } @@ -772,6 +803,9 @@ ac97_via_init(const device_t *info) /* Set up playback handler. */ sound_add_handler(ac97_via_get_buffer, dev); + /* Set up CD audio filter. */ + sound_set_cd_audio_filter(via_ac97_filter_cd_audio, dev); + return dev; }