Merge branch '86Box:master' into rubyusb-cpublock

This commit is contained in:
win2kgamer
2025-11-15 21:36:44 -06:00
committed by GitHub
4 changed files with 460 additions and 255 deletions

View File

@@ -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:

View File

@@ -8,9 +8,11 @@
*
* Definitions for AC'97 audio emulation.
*
*
*
* Authors: RichardG, <richardg867@gmail.com>
*
* Copyright 2021 RichardG.
* Copyright 2021-2025 RichardG.
*/
#ifndef SOUND_AC97_H
#define SOUND_AC97_H
@@ -19,23 +21,26 @@
/* Misc support bits (misc_flags). Most of these are not part of any
registers, but control enabling/disabling of registers and bits. */
#define AC97_MASTER_6B (1 << 0) /* register 02 bits [13,5] (ML5/MR5) */
#define AC97_AUXOUT (1 << 1) /* register 04 */
#define AC97_AUXOUT_6B (1 << 2) /* register 04 bits [13,5] (ML5/MR5) */
#define AC97_MONOOUT (1 << 3) /* register 06 */
#define AC97_MONOOUT_6B (1 << 4) /* register 06 bit 5 (MM5) */
#define AC97_PCBEEP (1 << 5) /* register 0A */
#define AC97_PCBEEP_GEN (1 << 6) /* register 0A bits [12:5] (F[7:0]) */
#define AC97_PHONE (1 << 9) /* register 0C */
#define AC97_VIDEO (1 << 10) /* register 14 */
#define AC97_AUXIN (1 << 11) /* register 16 */
#define AC97_AUDIO (1 << 0) /* audio codec */
#define AC97_MODEM (1 << 1) /* modem codec */
#define AC97_MASTER_6B (1 << 2) /* register 02 bits [13,5] (ML5/MR5) */
#define AC97_AUXOUT (1 << 3) /* register 04 */
#define AC97_AUXOUT_6B (1 << 4) /* register 04 bits [13,5] (ML5/MR5) */
#define AC97_MONOOUT (1 << 5) /* register 06 */
#define AC97_MONOOUT_6B (1 << 6) /* register 06 bit 5 (MM5) */
#define AC97_PCBEEP (1 << 9) /* register 0A */
#define AC97_PCBEEP_GEN (1 << 10) /* register 0A bits [12:5] (F[7:0]) */
#define AC97_PHONE (1 << 11) /* register 0C */
#define AC97_VIDEO (1 << 12) /* register 14 */
#define AC97_AUXIN (1 << 13) /* register 16 */
#define AC97_POP (1 << 15) /* register 20 bit 15 (POP) - definition shared with General Purpose bits */
#define AC97_MS (1 << 8) /* register 20 bit 8 (MS) - definition shared with General Purpose bits */
#define AC97_LPBK (1 << 7) /* register 20 bit 7 (LPBK) - definition shared with General Purpose bits */
#define AC97_DSA (1 << 12) /* register 28 bits [5:4] (DSA[1:0]) */
#define AC97_LFE_6B (1 << 13) /* register 36 bit 13 (LFE5) */
#define AC97_CENTER_6B (1 << 14) /* register 36 bit 5 (CNT5) */
#define AC97_SURR_6B (1 << 16) /* register 38 bits [13,5] (LSR5/RSR5) */
#define AC97_DSA (1 << 14) /* register 28 bits [5:4] (DSA[1:0]) */
#define AC97_LFE_6B (1 << 16) /* register 36 bit 13 (LFE5) */
#define AC97_CENTER_6B (1 << 17) /* register 36 bit 5 (CNT5) */
#define AC97_SURR_6B (1 << 18) /* register 38 bits [13,5] (LSR5/RSR5) */
#define AC97_GAIN_3B (1 << 19) /* registers [1C:1E] (audio) or [46:4A] (modem) bits [8,0] are always 0 (spec violation?) if this is set */
/* Reset bits (reset_flags), register 00. */
#define AC97_MICPCM (1 << 0)
@@ -89,6 +94,14 @@
#define AC97_PRK (1 << 13)
#define AC97_PRL (1 << 14)
/* Extended Modem ID bits, register 3C. */
#define AC97_LIN1 (1 << 0)
#define AC97_LIN2 (1 << 1)
#define AC97_HSET (1 << 2)
#define AC97_CID1 (1 << 3)
#define AC97_CID2 (1 << 4)
#define AC97_CIDR (1 << 7) /* special case: not part of register 3C, but rather 56 bit 13 */
/* Codec IDs. */
#define AC97_CODEC_AD1881 AC97_VENDOR_ID('A', 'D', 'S', 0x40)
#define AC97_CODEC_AK4540 AC97_VENDOR_ID('A', 'D', 'S', 0x40)
@@ -115,6 +128,8 @@ typedef struct ac97_codec_t {
uint8_t vendor_reg_page_max;
const ac97_vendor_reg_t *vendor_regs;
uint16_t *vendor_reg_pages;
uint16_t gpi;
uint16_t gpo;
} ac97_codec_t;
extern uint16_t ac97_codec_readw(ac97_codec_t *dev, uint8_t reg);
@@ -122,22 +137,21 @@ extern void ac97_codec_writew(ac97_codec_t *dev, uint8_t reg, uint16_
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_codec_setgpi(void *priv, uint16_t gpi);
extern void ac97_codec_setgpo(void *priv, uint16_t gpo);
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);
extern void ac97_via_remap_modem_codec(void *priv, uint16_t new_io_base, uint8_t enable);
extern ac97_codec_t **ac97_codec;
extern ac97_codec_t **ac97_modem_codec;
extern int ac97_codec_count;
extern int ac97_modem_codec_count;
extern int ac97_codec_id;
extern int ac97_modem_codec_id;
#ifdef EMU_DEVICE_H
extern const device_t ad1881_device;

View File

@@ -10,7 +10,7 @@
*
* Authors: RichardG, <richardg867@gmail.com>
*
* Copyright 2021 RichardG.
* Copyright 2021-2025 RichardG.
*/
#include <stdarg.h>
#include <stdint.h>
@@ -35,7 +35,10 @@ static const struct {
uint16_t reset_flags;
uint16_t extid_flags;
uint8_t pcsr_mask; /* register 26 bits [15:8] */
uint8_t eascr_mask; /* register 2A bits [14:11] */
uint8_t eascr_mask; /* register 2A bits [14:11] (audio) or 56 bits ... */
uint8_t modem_flags;
uint16_t gpi_mask; /* modem GPIO input-capable bits */
uint16_t gpo_mask; /* modem GPIO output-capable bits */
const ac97_vendor_reg_t *vendor_regs;
} ac97_codecs[] = {
@@ -44,7 +47,7 @@ static const struct {
.device = &ad1881_device,
.min_rate = 7000,
.max_rate = 48000,
.misc_flags = AC97_MASTER_6B | AC97_MONOOUT | AC97_PCBEEP | AC97_PHONE | AC97_VIDEO | AC97_AUXIN | AC97_POP | AC97_MS | AC97_LPBK,
.misc_flags = AC97_AUDIO | AC97_MASTER_6B | AC97_MONOOUT | AC97_PCBEEP | AC97_PHONE | AC97_VIDEO | AC97_AUXIN | AC97_POP | AC97_MS | AC97_LPBK,
.reset_flags = (1 << AC97_3D_SHIFT), /* datasheet contradicts itself on AC97_HPOUT */
.extid_flags = AC97_VRA,
.pcsr_mask = 0xbf,
@@ -52,26 +55,26 @@ static const struct {
},
{
.device = &ak4540_device,
.misc_flags = AC97_MONOOUT | AC97_PCBEEP | AC97_PHONE | AC97_VIDEO | AC97_AUXIN | AC97_MS | AC97_LPBK,
.misc_flags = AC97_AUDIO | AC97_MONOOUT | AC97_PCBEEP | AC97_PHONE | AC97_VIDEO | AC97_AUXIN | AC97_MS | AC97_LPBK,
.pcsr_mask = 0x1f
},
{
.device = &alc100_device,
.misc_flags = AC97_AUXOUT | AC97_MONOOUT | AC97_PCBEEP | AC97_PHONE | AC97_VIDEO | AC97_AUXIN | AC97_POP | AC97_MS | AC97_LPBK,
.misc_flags = AC97_AUDIO | AC97_AUXOUT | AC97_MONOOUT | AC97_PCBEEP | AC97_PHONE | AC97_VIDEO | AC97_AUXIN | AC97_POP | AC97_MS | AC97_LPBK,
.reset_flags = (22 << AC97_3D_SHIFT),
.extid_flags = AC97_AMAP,
.pcsr_mask = 0xbf
},
{
.device = &cs4297_device,
.misc_flags = AC97_MASTER_6B | AC97_AUXOUT | AC97_AUXOUT_6B | AC97_MONOOUT | AC97_MONOOUT_6B | AC97_PCBEEP | AC97_PHONE | AC97_VIDEO | AC97_AUXIN | AC97_MS | AC97_LPBK,
.misc_flags = AC97_AUDIO | AC97_MASTER_6B | AC97_AUXOUT | AC97_AUXOUT_6B | AC97_MONOOUT | AC97_MONOOUT_6B | AC97_PCBEEP | AC97_PHONE | AC97_VIDEO | AC97_AUXIN | AC97_MS | AC97_LPBK,
.reset_flags = AC97_HPOUT | AC97_DAC_18B | AC97_ADC_18B,
.pcsr_mask = 0x7f,
.vendor_regs = (const ac97_vendor_reg_t[]) {{0, 0x5a, 0x0301, 0x0000}, {0}}
},
{
.device = &cs4297a_device,
.misc_flags = AC97_MASTER_6B | AC97_AUXOUT | AC97_MONOOUT | AC97_PCBEEP | AC97_PHONE | AC97_VIDEO | AC97_AUXIN | AC97_MS | AC97_LPBK,
.misc_flags = AC97_AUDIO | AC97_MASTER_6B | AC97_AUXOUT | AC97_MONOOUT | AC97_PCBEEP | AC97_PHONE | AC97_VIDEO | AC97_AUXIN | AC97_MS | AC97_LPBK,
.reset_flags = AC97_HPOUT | AC97_DAC_20B | AC97_ADC_18B | (6 << AC97_3D_SHIFT),
.extid_flags = AC97_AMAP,
.pcsr_mask = 0xff,
@@ -79,7 +82,7 @@ static const struct {
},
{
.device = &stac9708_device,
.misc_flags = AC97_AUXOUT | AC97_MONOOUT | AC97_PCBEEP | AC97_PHONE | AC97_VIDEO | AC97_AUXIN | AC97_MS | AC97_LPBK,
.misc_flags = AC97_AUDIO | AC97_AUXOUT | AC97_MONOOUT | AC97_PCBEEP | AC97_PHONE | AC97_VIDEO | AC97_AUXIN | AC97_MS | AC97_LPBK,
.reset_flags = (26 << AC97_3D_SHIFT) | AC97_DAC_18B | AC97_ADC_18B,
.extid_flags = AC97_SDAC,
.pcsr_mask = 0xff,
@@ -88,7 +91,7 @@ static const struct {
},
{
.device = &stac9721_device,
.misc_flags = AC97_AUXOUT | AC97_MONOOUT | AC97_PCBEEP | AC97_PHONE | AC97_VIDEO | AC97_AUXIN | AC97_MS | AC97_LPBK,
.misc_flags = AC97_AUDIO | AC97_AUXOUT | AC97_MONOOUT | AC97_PCBEEP | AC97_PHONE | AC97_VIDEO | AC97_AUXIN | AC97_MS | AC97_LPBK,
.reset_flags = (26 << AC97_3D_SHIFT) | AC97_DAC_18B | AC97_ADC_18B,
.extid_flags = AC97_AMAP,
.pcsr_mask = 0xff,
@@ -96,18 +99,18 @@ static const struct {
},
{
.device = &tr28023_device,
.misc_flags = AC97_MASTER_6B | AC97_MONOOUT | AC97_MONOOUT_6B | AC97_PCBEEP | AC97_PHONE | AC97_POP | AC97_MS | AC97_LPBK,
.misc_flags = AC97_AUDIO | AC97_MASTER_6B | AC97_MONOOUT | AC97_MONOOUT_6B | AC97_PCBEEP | AC97_PHONE | AC97_POP | AC97_MS | AC97_LPBK,
.pcsr_mask = 0x3f
},
{
.device = &w83971d_device,
.misc_flags = AC97_MASTER_6B | AC97_MONOOUT | AC97_MONOOUT_6B | AC97_PCBEEP | AC97_PHONE | AC97_VIDEO | AC97_AUXIN | AC97_MS | AC97_LPBK,
.misc_flags = AC97_AUDIO | AC97_MASTER_6B | AC97_MONOOUT | AC97_MONOOUT_6B | AC97_PCBEEP | AC97_PHONE | AC97_VIDEO | AC97_AUXIN | AC97_MS | AC97_LPBK,
.reset_flags = (27 << AC97_3D_SHIFT),
.pcsr_mask = 0x3f
},
{
.device = &wm9701a_device,
.misc_flags = AC97_AUXOUT | AC97_MONOOUT | AC97_PCBEEP | AC97_PHONE | AC97_VIDEO | AC97_AUXIN | AC97_MS | AC97_LPBK,
.misc_flags = AC97_AUDIO | AC97_AUXOUT | AC97_MONOOUT | AC97_PCBEEP | AC97_PHONE | AC97_VIDEO | AC97_AUXIN | AC97_MS | AC97_LPBK,
.reset_flags = AC97_DAC_18B | AC97_ADC_18B,
.pcsr_mask = 0x3f
}
@@ -143,11 +146,10 @@ static const int32_t codec_attn[] = {
};
ac97_codec_t **ac97_codec = NULL;
ac97_codec_t **ac97_modem_codec = NULL;
int ac97_codec_count = 0;
int ac97_modem_codec_count = 0;
int ac97_codec_id = 0;
int ac97_modem_codec_id = 0;
static void ac97_codec_reset_ex(ac97_codec_t *dev, uint8_t flags);
uint16_t
ac97_codec_readw(ac97_codec_t *dev, uint8_t reg)
@@ -155,10 +157,15 @@ ac97_codec_readw(ac97_codec_t *dev, uint8_t reg)
/* Redirect a read from extended pages 1+ to the right array. */
reg &= 0x7e;
uint16_t ret = dev->regs[0x24 >> 1] & 0x000f;
if ((ret > 0) && (reg >= 0x60) && (reg < 0x6f))
if ((ret > 0) && (reg >= 0x60) && (reg < 0x6f)) { /* Extended */
ret = (ret <= dev->vendor_reg_page_max) ? dev->vendor_reg_pages[(ret << 3) | ((reg & 0x0e) >> 1)] : 0;
else
} else if (reg == 0x54) { /* GPIO Status */
ret = dev->gpo & ~dev->regs[0x4c >> 1]; /* outputs */
ret |= dev->gpi & dev->regs[0x4c >> 1] & ~dev->regs[0x50 >> 1]; /* non-sticky inputs */
ret |= dev->regs[reg >> 1] & dev->regs[0x4c >> 1] & dev->regs[0x50 >> 1]; /* sticky inputs */
} else {
ret = dev->regs[reg >> 1];
}
ac97_codec_log("AC97 Codec %d: readw(%02X) = %04X\n", dev->codec_id, reg, ret);
@@ -175,9 +182,15 @@ ac97_codec_writew(ac97_codec_t *dev, uint8_t reg, uint16_t val)
uint16_t prev = dev->regs[reg >> 1];
int j;
/* Initial filtering by codec type. */
if (!(ac97_codecs[dev->model].misc_flags & AC97_AUDIO) && (reg <= 0x3a))
return;
if (!(ac97_codecs[dev->model].misc_flags & AC97_MODEM) && (reg >= 0x3c) && (reg <= 0x58))
return;
switch (reg) {
case 0x00: /* Reset / ID code */
ac97_codec_reset(dev);
ac97_codec_reset_ex(dev, AC97_AUDIO);
return;
case 0x02: /* Master Volume */
@@ -260,13 +273,13 @@ line_gain:
break;
case 0x1c: /* Record Gain */
val &= 0x8f0f;
val &= (ac97_codecs[dev->model].misc_flags & AC97_GAIN_3B) ? 0x8e0e : 0x8f0f;
break;
case 0x1e: /* Record Gain Mic */
if (!(ac97_codecs[dev->model].reset_flags & AC97_MICPCM))
return;
val &= 0x800f;
val &= (ac97_codecs[dev->model].misc_flags & AC97_GAIN_3B) ? 0x800e : 0x800f;
break;
case 0x20: /* General Purpose */
@@ -350,11 +363,11 @@ line_gain:
case 0x2c: /* PCM Front DAC Rate */
case 0x32: /* PCM L/R ADC Rate */
rate: /* Writable only if VRA/VRM is set. */
rate_vrx: /* Writable only if VRA/VRM is set. */
i = (reg >= 0x32) ? AC97_VRM : AC97_VRA;
if (!(ac97_codecs[dev->model].extid_flags & i))
return;
rate:
/* Limit to supported sample rate range. */
if (val < ac97_codecs[dev->model].min_rate)
val = ac97_codecs[dev->model].min_rate;
@@ -365,17 +378,17 @@ rate: /* Writable only if VRA/VRM is set. */
case 0x2e: /* PCM Surround DAC Rate */
if (!(ac97_codecs[dev->model].extid_flags & AC97_SDAC))
return;
goto rate;
goto rate_vrx;
case 0x30: /* PCM LFE DAC Rate */
if (!(ac97_codecs[dev->model].extid_flags & AC97_LDAC))
return;
goto rate;
goto rate_vrx;
case 0x34: /* Mic ADC Rate */
if (!(ac97_codecs[dev->model].reset_flags & AC97_MICPCM))
return;
goto rate;
goto rate_vrx;
case 0x36: /* Center/LFE Volume */
if (ac97_codecs[dev->model].extid_flags & AC97_LDAC)
@@ -406,6 +419,86 @@ rate: /* Writable only if VRA/VRM is set. */
return;
break;
case 0x3c: /* Reset / Extended Modem ID */
ac97_codec_reset_ex(dev, AC97_MODEM);
return;
case 0x3e: /* Extended Modem Control/Status */
i = 0x0300;
if (ac97_codecs[dev->model].modem_flags & AC97_LIN1)
i |= 0x0c00;
if (ac97_codecs[dev->model].modem_flags & AC97_LIN2)
i |= 0x3000;
if (ac97_codecs[dev->model].modem_flags & AC97_HSET)
i |= 0xc000;
val &= i;
/* Update status bits to reflect powerdowns. */
val |= (~val & i) >> 8;
break;
case 0x40: /* Line1 DAC/ADC Rate */
if (!(ac97_codecs[dev->model].modem_flags & AC97_LIN1))
return;
goto rate;
case 0x42: /* Line2 DAC/ADC Rate */
if (!(ac97_codecs[dev->model].modem_flags & AC97_LIN2))
return;
goto rate;
case 0x44: /* Handset DAC/ADC Rate */
if (!(ac97_codecs[dev->model].modem_flags & AC97_HSET))
return;
goto rate;
case 0x46: /* Line 1 DAC/ADC Level */
if (!(ac97_codecs[dev->model].modem_flags & AC97_LIN1))
return;
modem_gain:
val &= (ac97_codecs[dev->model].misc_flags & AC97_GAIN_3B) ? 0x8e8e : 0x8f8f;
break;
case 0x48: /* Line 2 DAC/ADC Level */
if (!(ac97_codecs[dev->model].modem_flags & AC97_LIN2))
return;
goto modem_gain;
case 0x4a: /* Handset DAC/ADC Level */
if (!(ac97_codecs[dev->model].modem_flags & AC97_HSET))
return;
goto modem_gain;
case 0x56: /* Miscellaneous Modem AFE Status/Control */
if (ac97_codecs[dev->model].modem_flags & AC97_LIN1)
i |= 0x0007;
if (ac97_codecs[dev->model].modem_flags & AC97_LIN2)
i |= 0x0070;
if (ac97_codecs[dev->model].modem_flags & AC97_HSET)
i |= 0x0700;
val &= i;
break;
case 0x4c: /* GPIO Pin Configuration */
val &= ac97_codecs[dev->model].gpi_mask | ac97_codecs[dev->model].gpo_mask;
break;
case 0x4e: /* GPIO Pin Polarity/Type */
val |= ~(ac97_codecs[dev->model].gpi_mask | ac97_codecs[dev->model].gpo_mask);
break;
case 0x50: /* GPIO Pin Sticky */
dev->regs[0x54 >> 1] &= val; /* clear sticky inputs that are no longer sticky (assumed undefined behavior) */
fallthrough;
case 0x52: /* GPIO Pin Wake-up Mask */
val &= ac97_codecs[dev->model].gpi_mask;
break;
case 0x54: /* GPIO Pin Status */
val = dev->regs[reg >> 1] & ~val; /* clear sticky inputs */
break;
case 0x60 ... 0x6e: /* Extended */
/* Get extended register page. */
i = dev->regs[0x24 >> 1] & 0x000f;
@@ -462,47 +555,83 @@ void
ac97_codec_reset(void *priv)
{
ac97_codec_t *dev = (ac97_codec_t *) priv;
uint16_t i;
ac97_codec_reset_ex(dev, AC97_AUDIO | AC97_MODEM);
}
ac97_codec_log("AC97 Codec %d: reset()\n", dev->codec_id);
static void
ac97_codec_reset_ex(ac97_codec_t *dev, uint8_t flags)
{
ac97_codec_log("AC97 Codec %d: reset(%02X)\n", dev->codec_id, flags);
memset(dev->regs, 0, sizeof(dev->regs));
/* Set default level and gain values. */
dev->regs[0x02 >> 1] = AC97_MUTE;
if (ac97_codecs[dev->model].misc_flags & AC97_AUXOUT)
dev->regs[0x04 >> 1] = AC97_MUTE;
if (ac97_codecs[dev->model].misc_flags & AC97_MONOOUT)
dev->regs[0x06 >> 1] = AC97_MUTE;
if (ac97_codecs[dev->model].misc_flags & AC97_PHONE)
dev->regs[0x0c >> 1] = AC97_MUTE | 0x0008;
dev->regs[0x0e >> 1] = AC97_MUTE | 0x0008; /* mic */
dev->regs[0x10 >> 1] = dev->regs[0x12 >> 1] = dev->regs[0x18 >> 1] = AC97_MUTE | 0x0808; /* line in, CD, PCM out */
if (ac97_codecs[dev->model].misc_flags & AC97_VIDEO)
dev->regs[0x14 >> 1] = AC97_MUTE | 0x0808;
if (ac97_codecs[dev->model].misc_flags & AC97_AUXIN)
dev->regs[0x14 >> 1] = AC97_MUTE | 0x0808;
dev->regs[0x1c >> 1] = AC97_MUTE; /* record gain */
if (ac97_codecs[dev->model].reset_flags & AC97_MICPCM)
dev->regs[0x1e >> 1] = AC97_MUTE; /* mic record gain */
if (ac97_codecs[dev->model].misc_flags & AC97_LDAC)
dev->regs[0x36 >> 1] = AC97_MUTE_L;
if (ac97_codecs[dev->model].misc_flags & AC97_CDAC)
dev->regs[0x36 >> 1] |= AC97_MUTE_R;
if (ac97_codecs[dev->model].misc_flags & AC97_SDAC)
dev->regs[0x38 >> 1] = AC97_MUTE_L | AC97_MUTE_R;
if ((flags & AC97_AUDIO) && (ac97_codecs[dev->model].misc_flags & AC97_AUDIO)) {
/* Set default level and gain values. */
dev->regs[0x02 >> 1] = AC97_MUTE;
if (ac97_codecs[dev->model].misc_flags & AC97_AUXOUT)
dev->regs[0x04 >> 1] = AC97_MUTE;
if (ac97_codecs[dev->model].misc_flags & AC97_MONOOUT)
dev->regs[0x06 >> 1] = AC97_MUTE;
if (ac97_codecs[dev->model].misc_flags & AC97_PHONE)
dev->regs[0x0c >> 1] = AC97_MUTE | 0x0008;
dev->regs[0x0e >> 1] = AC97_MUTE | 0x0008; /* mic */
dev->regs[0x10 >> 1] = dev->regs[0x12 >> 1] = dev->regs[0x18 >> 1] = AC97_MUTE | 0x0808; /* line in, CD, PCM out */
if (ac97_codecs[dev->model].misc_flags & AC97_VIDEO)
dev->regs[0x14 >> 1] = AC97_MUTE | 0x0808;
if (ac97_codecs[dev->model].misc_flags & AC97_AUXIN)
dev->regs[0x16 >> 1] = AC97_MUTE | 0x0808;
dev->regs[0x18 >> 1] = AC97_MUTE | 0x0808; /* PCM */
dev->regs[0x1c >> 1] = AC97_MUTE; /* record gain */
if (ac97_codecs[dev->model].reset_flags & AC97_MICPCM)
dev->regs[0x1e >> 1] = AC97_MUTE; /* mic record gain */
if (ac97_codecs[dev->model].misc_flags & AC97_LDAC)
dev->regs[0x36 >> 1] = AC97_MUTE_L;
if (ac97_codecs[dev->model].misc_flags & AC97_CDAC)
dev->regs[0x36 >> 1] |= AC97_MUTE_R;
if (ac97_codecs[dev->model].misc_flags & AC97_SDAC)
dev->regs[0x38 >> 1] = AC97_MUTE_L | AC97_MUTE_R;
/* Set flags. */
dev->regs[0x00 >> 1] = ac97_codecs[dev->model].reset_flags;
dev->regs[0x26 >> 1] = 0x000f; /* codec ready */
dev->regs[0x28 >> 1] = (dev->codec_id << 14) | ac97_codecs[dev->model].extid_flags;
ac97_codec_writew(dev, 0x2a, 0x0000); /* reset variable DAC/ADC sample rates */
i = ac97_codecs[dev->model].extid_flags & (AC97_CDAC | AC97_SDAC | AC97_LDAC);
dev->regs[0x2a >> 1] |= i | (i << 5); /* any additional DACs are ready but powered down */
if (ac97_codecs[dev->model].extid_flags & AC97_SPDIF)
dev->regs[0x2a >> 1] |= AC97_SPCV;
if (ac97_codecs[dev->model].reset_flags & AC97_MICPCM)
dev->regs[0x2a >> 1] |= AC97_MADC | AC97_PRL;
/* Set flags. */
dev->regs[0x00 >> 1] = ac97_codecs[dev->model].reset_flags;
dev->regs[0x26 >> 1] = 0x000f; /* codec ready */
dev->regs[0x28 >> 1] = (dev->codec_id << 14) | ac97_codecs[dev->model].extid_flags;
ac97_codec_writew(dev, 0x2a, 0x0000); /* reset variable DAC/ADC sample rates */
uint16_t i = ac97_codecs[dev->model].extid_flags & (AC97_CDAC | AC97_SDAC | AC97_LDAC);
dev->regs[0x2a >> 1] |= i | (i << 5); /* any additional DACs are ready but powered down */
if (ac97_codecs[dev->model].extid_flags & AC97_SPDIF)
dev->regs[0x2a >> 1] |= AC97_SPCV;
if (ac97_codecs[dev->model].reset_flags & AC97_MICPCM)
dev->regs[0x2a >> 1] |= AC97_MADC | AC97_PRL;
}
if ((flags & AC97_MODEM) && (ac97_codecs[dev->model].misc_flags & AC97_MODEM)) {
if (ac97_codecs[dev->model].modem_flags & AC97_LIN1) {
dev->regs[0x3e >> 1] |= 0x0c00;
dev->regs[0x40 >> 1] = /*4*/8000;
dev->regs[0x46 >> 1] = 0x8080;
}
if (ac97_codecs[dev->model].modem_flags & AC97_LIN2) {
dev->regs[0x3e >> 1] |= 0x3000;
dev->regs[0x42 >> 1] = /*4*/8000;
dev->regs[0x48 >> 1] = 0x8080;
}
if (ac97_codecs[dev->model].modem_flags & AC97_HSET) {
dev->regs[0x3e >> 1] |= 0xc000;
dev->regs[0x44 >> 1] = /*4*/8000;
dev->regs[0x4a >> 1] = 0x8080;
}
dev->regs[0x4c >> 1] = ac97_codecs[dev->model].gpi_mask | ac97_codecs[dev->model].gpo_mask;
dev->regs[0x4e >> 1] = 0xffff;
/* Set flags. */
dev->regs[0x3c >> 1] = (dev->codec_id << 14) | (ac97_codecs[dev->model].modem_flags & ~AC97_CIDR);
if (ac97_codecs[dev->model].modem_flags & AC97_CIDR)
dev->regs[0x56 >> 1] |= 0x2000;
if (ac97_codecs[dev->model].modem_flags & AC97_CID1)
dev->regs[0x56 >> 1] |= 0x4000;
if (ac97_codecs[dev->model].modem_flags & AC97_CID2)
dev->regs[0x56 >> 1] |= 0x8000;
}
/* Set vendor ID. */
dev->regs[0x7c >> 1] = ac97_codecs[dev->model].device->local >> 16;
@@ -510,13 +639,16 @@ ac97_codec_reset(void *priv)
/* Set vendor-specific registers. */
if (ac97_codecs[dev->model].vendor_regs) {
for (i = 0; ac97_codecs[dev->model].vendor_regs[i].index; i++) {
for (int i = 0; ac97_codecs[dev->model].vendor_regs[i].index; i++) {
if (ac97_codecs[dev->model].vendor_regs[i].page > 0)
dev->vendor_reg_pages[(ac97_codecs[dev->model].vendor_regs[i].page << 3) | (ac97_codecs[dev->model].vendor_regs[i].index >> 1)] = ac97_codecs[dev->model].vendor_regs[i].value;
else
dev->regs[ac97_codecs[dev->model].vendor_regs[i].index >> 1] = ac97_codecs[dev->model].vendor_regs[i].value;
}
}
if (flags & AC97_MODEM)
dev->gpi = dev->gpo = 0;
}
void
@@ -561,7 +693,7 @@ ac97_codec_getattn(void *priv, uint8_t reg, int *l, int *r)
uint32_t
ac97_codec_getrate(void *priv, uint8_t reg)
{
const ac97_codec_t *dev = (ac97_codec_t *) priv;
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 = dev->regs[reg >> 1];
@@ -575,6 +707,29 @@ ac97_codec_getrate(void *priv, uint8_t reg)
return ret;
}
void
ac97_codec_setgpi(void *priv, uint16_t gpi)
{
ac97_codec_t *dev = (ac97_codec_t *) priv;
ac97_codec_log("AC97 Codec %d: setgpi(%04X)\n", dev->codec_id, gpi);
/* Set status bits for sticky inputs. */
gpi &= ac97_codecs[dev->model].gpi_mask;
dev->regs[0x54 >> 1] |= (dev->gpi ^ gpi) & dev->regs[0x4c >> 1] & dev->regs[0x50 >> 1]; /* set on (transition & input & sticky) */
dev->gpi = gpi;
}
void
ac97_codec_setgpo(void *priv, uint16_t gpo)
{
ac97_codec_t *dev = (ac97_codec_t *) priv;
ac97_codec_log("AC97 Codec %d: setgpo(%04X)\n", dev->codec_id, gpo);
dev->gpo = gpo & ac97_codecs[dev->model].gpo_mask;
}
static void *
ac97_codec_init(const device_t *info)
{
@@ -603,7 +758,7 @@ ac97_codec_init(const device_t *info)
if (--ac97_codec_count == 0)
ac97_codec = NULL;
else
ac97_codec += sizeof(ac97_codec_t *);
ac97_codec++;
dev->codec_id = ac97_codec_id++;
/* Allocate vendor-specific register pages if required. */

View File

@@ -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,25 @@ 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_id = ac97_modem_codec_id = 0;
ac97_codec = &dev->codec[0];
ac97_codec_count = sizeof(dev->codec) / sizeof(dev->codec[0]);
ac97_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. */