mirror of
https://github.com/86Box/86Box.git
synced 2026-02-22 09:35:32 -07:00
VIA AC97: Big rework, with proper sharing of audio/modem registers (again) and codec buses
This commit is contained in:
@@ -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:
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
*
|
||||
* Authors: RichardG, <richardg867@gmail.com>
|
||||
*
|
||||
* Copyright 2021 RichardG.
|
||||
* Copyright 2021-2025 RichardG.
|
||||
*/
|
||||
#include <stdarg.h>
|
||||
#include <stdint.h>
|
||||
@@ -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. */
|
||||
|
||||
Reference in New Issue
Block a user