AC97 codec overhaul, now with proper enabling through settings and proper codec features

This commit is contained in:
RichardG867 2021-11-12 00:43:03 -03:00
parent d95f7cb8d2
commit 6527fe9993
8 changed files with 502 additions and 159 deletions

View file

@ -18,14 +18,97 @@
# define EMU_SND_AC97_H
#define AC97_VENDOR_ID(f, s, t, dev) ((((f) & 0xff) << 24) | (((s) & 0xff) << 16) | (((t) & 0xff) << 8) | ((dev) & 0xff))
/* 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_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) */
/* Reset bits (reset_flags), register 00. */
#define AC97_MICPCM (1 << 0)
#define AC97_MODEMLINE (1 << 1)
#define AC97_TONECTL (1 << 2)
#define AC97_SIMSTEREO (1 << 3)
#define AC97_HPOUT (1 << 4)
#define AC97_LOUDNESS (1 << 5)
#define AC97_DAC_18B (1 << 6)
#define AC97_DAC_20B (1 << 7)
#define AC97_ADC_18B (1 << 8)
#define AC97_ADC_20B (1 << 9)
#define AC97_3D_SHIFT 10
/* Extended Audio ID bits (extid_flags), register 28. */
#define AC97_VRA (1 << 0)
#define AC97_DRA (1 << 1)
#define AC97_SPDIF (1 << 2)
#define AC97_VRM (1 << 3)
#define AC97_CDAC (1 << 6)
#define AC97_SDAC (1 << 7)
#define AC97_LDAC (1 << 8)
#define AC97_AMAP (1 << 9)
#define AC97_REV_2_1 (0 << 10)
#define AC97_REV_2_2 (1 << 10)
#define AC97_REV_2_3 (2 << 10)
#define AC97_REV_MASK (3 << 10)
/* Volume bits. */
#define AC97_MUTE (1 << 15)
#define AC97_MUTE_L (1 << 15)
#define AC97_MUTE_R (1 << 7)
/* General Purpose bits, register 20. */
/* POP already defined */
#define AC97_ST (1 << 14)
#define AC97_3D (1 << 13)
#define AC97_LD (1 << 12)
#define AC97_DRSS_MASK (3 << 10)
#define AC97_MIX (1 << 9)
/* MS already defined */
/* LPBK already defined */
/* Extended Audio Status/Control bits, register 2A. */
#define AC97_SPSA_SHIFT 4
#define AC97_SPSA_MASK 3
#define AC97_MADC (1 << 9)
#define AC97_SPCV (1 << 10)
#define AC97_PRI (1 << 11)
#define AC97_PRJ (1 << 12)
#define AC97_PRK (1 << 13)
#define AC97_PRL (1 << 14)
typedef struct {
uint32_t vendor_id;
uint8_t codec_id, regs[128];
const uint16_t index, value, write_mask;
} ac97_vendor_reg_t;
typedef struct {
uint32_t vendor_id, max_rate, misc_flags;
uint16_t reset_flags, extid_flags,
powerdown_mask, regs[64];
uint8_t codec_id, vendor_reg_page_max;
const ac97_vendor_reg_t *vendor_regs;
uint16_t *vendor_reg_pages;
} ac97_codec_t;
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 uint16_t ac97_codec_readw(ac97_codec_t *dev, uint8_t reg);
extern void ac97_codec_writew(ac97_codec_t *dev, uint8_t reg, uint16_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);

View file

@ -37,6 +37,7 @@
#include "cpu.h"
#include <86box/machine.h>
#include <86box/clock.h>
#include <86box/sound.h>
#include <86box/snd_ac97.h>
@ -452,7 +453,8 @@ machine_at_6via90ap_init(const machine_t *model)
hwm_values.temperatures[1] += 2; /* System offset */
hwm_values.temperatures[2] = 0; /* unused */
device_add(&alc100_device); /* ALC100P identified on similar Acorp boards (694TA, 6VIA90A1) */
if (sound_card_current == SOUND_INTERNAL)
device_add(&alc100_device); /* ALC100P identified on similar Acorp boards (694TA, 6VIA90A1) */
return ret;
}

View file

@ -40,6 +40,7 @@
#include <86box/video.h>
#include "cpu.h"
#include <86box/machine.h>
#include <86box/sound.h>
#include <86box/snd_ac97.h>
@ -141,7 +142,8 @@ machine_at_ficva503a_init(const machine_t *model)
hwm_values.temperatures[1] += 2; /* System offset */
hwm_values.temperatures[2] = 0; /* unused */
device_add(&wm9701a_device); /* on daughtercard */
if (sound_card_current == SOUND_INTERNAL)
device_add(&wm9701a_device); /* on daughtercard */
return ret;
}

View file

@ -374,7 +374,7 @@ const machine_t machines[] = {
/* Apollo MVP3 */
{ "[VIA MVP3] AOpen AX59 Pro", "ax59pro", MACHINE_TYPE_SOCKETS7, CPU_PKG_SOCKET5_7, 0, 66666667, 124242424, 1300, 3520, 1.5, 5.5, MACHINE_AGP | MACHINE_BUS_PS2 | MACHINE_IDE_DUAL, 8192,1048576, 8192, 255, machine_at_ax59pro_init, NULL },
{ "[VIA MVP3] FIC VA-503+", "ficva503p", MACHINE_TYPE_SOCKETS7, CPU_PKG_SOCKET5_7, 0, 66666667, 124242424, 2000, 3200, 1.5, 5.5, MACHINE_AGP | MACHINE_BUS_PS2 | MACHINE_IDE_DUAL, 8192,1048576, 8192, 255, machine_at_mvp3_init, NULL },
{ "[VIA MVP3] FIC VA-503A", "ficva503a", MACHINE_TYPE_SOCKETS7, CPU_PKG_SOCKET5_7, 0, 66666667, 124242424, 1800, 3100, 1.5, 5.5, MACHINE_AGP | MACHINE_BUS_PS2 | MACHINE_IDE_DUAL, 8192, 786432, 8192, 255, machine_at_ficva503a_init, NULL },
{ "[VIA MVP3] FIC VA-503A", "ficva503a", MACHINE_TYPE_SOCKETS7, CPU_PKG_SOCKET5_7, 0, 66666667, 124242424, 1800, 3100, 1.5, 5.5, MACHINE_AGP | MACHINE_BUS_PS2 | MACHINE_BUS_AC97 | MACHINE_IDE_DUAL, 8192, 786432, 8192, 255, machine_at_ficva503a_init, NULL },
/* Socket 8 machines */
/* 450KX */
@ -459,7 +459,7 @@ const machine_t machines[] = {
{ "[VIA Apollo Pro 133] ECS P6BAP", "p6bap", MACHINE_TYPE_SOCKET370, CPU_PKG_SOCKET370, 0, 66666667, 150000000, 1300, 3500, 1.5, 8.0, MACHINE_AGP | MACHINE_BUS_PS2 | MACHINE_IDE_DUAL, 8192,1572864, 8192, 255, machine_at_p6bap_init, NULL },
{ "[VIA Apollo Pro 133A] BCM GT694VA", "gt694va", MACHINE_TYPE_SOCKET370, CPU_PKG_SOCKET370, 0, 66666667, 133333333, 1300, 3500, 1.5, 8.0, MACHINE_AGP | MACHINE_BUS_PS2 | MACHINE_IDE_DUAL, 16384,3145728, 8192, 255, machine_at_gt694va_init, NULL },
{ "[VIA Apollo Pro 133A] ASUS CUV4X-LS", "cuv4xls", MACHINE_TYPE_SOCKET370, CPU_PKG_SOCKET370, 0, 66666667, 150000000, 1300, 3500, 1.5, 8.0, (MACHINE_AGP & ~MACHINE_AT) | MACHINE_BUS_PS2 | MACHINE_BUS_AC97 | MACHINE_IDE_DUAL,16384,4194304, 8192, 255, machine_at_cuv4xls_init, NULL },
{ "[VIA Apollo Pro 133A] Acorp 6VIA90AP", "6via90ap", MACHINE_TYPE_SOCKET370, CPU_PKG_SOCKET370, 0, 66666667, 150000000, 1300, 3500, MACHINE_MULTIPLIER_FIXED, MACHINE_AGP | MACHINE_BUS_PS2 | MACHINE_IDE_DUAL | MACHINE_GAMEPORT, 16384,3145728, 8192, 255, machine_at_6via90ap_init, NULL },
{ "[VIA Apollo Pro 133A] Acorp 6VIA90AP", "6via90ap", MACHINE_TYPE_SOCKET370, CPU_PKG_SOCKET370, 0, 66666667, 150000000, 1300, 3500, MACHINE_MULTIPLIER_FIXED, MACHINE_AGP | MACHINE_BUS_PS2 | MACHINE_BUS_AC97 | MACHINE_IDE_DUAL | MACHINE_SOUND | MACHINE_GAMEPORT,16384,3145728, 8192, 255, machine_at_6via90ap_init, NULL },
/* Miscellaneous/Fake/Hypervisor machines */
{ "[i440BX] Microsoft Virtual PC 2007", "vpc2007", MACHINE_TYPE_MISC, CPU_PKG_SLOT1, CPU_BLOCK(CPU_PENTIUM2, CPU_CYRIX3S), 0, 0, 0, 0, 0, 0, MACHINE_PCI | MACHINE_BUS_PS2 | MACHINE_IDE_DUAL, 8192,1048576, 8192, 255, machine_at_vpc2007_init, NULL },

View file

@ -25,15 +25,63 @@
#include <86box/io.h>
#include <86box/snd_ac97.h>
#define AC97_VENDOR_ID(f, s, t, dev) ((((f) & 0xff) << 24) | (((s) & 0xff) << 16) | (((t) & 0xff) << 8) | ((dev) & 0xff))
enum {
AC97_CODEC_AD1881 = AC97_VENDOR_ID('A', 'D', 'S', 0x40),
AC97_CODEC_ALC100 = AC97_VENDOR_ID('A', 'L', 'C', 0x20),
AC97_CODEC_CS4297 = AC97_VENDOR_ID('C', 'R', 'Y', 0x03),
AC97_CODEC_CS4297A = AC97_VENDOR_ID('C', 'R', 'Y', 0x11),
AC97_CODEC_WM9701A = AC97_VENDOR_ID('W', 'M', 'L', 0x00)
AC97_CODEC_AD1881,
AC97_CODEC_ALC100,
AC97_CODEC_CS4297,
AC97_CODEC_CS4297A,
AC97_CODEC_WM9701A
};
static const struct {
const uint32_t vendor_id, max_rate, misc_flags; /* definitions for misc_flags in snd_ac97.h */
const uint16_t reset_flags, extid_flags, /* definitions in snd_ac97.h */
powerdown_mask; /* bits [7:0] => register 26 bits [15:8]; bits [11:8] => register 2A bits [14:11] */
const ac97_vendor_reg_t *vendor_regs; /* bits [11:8] of index are the page number if applicable (registers [60:6F]) */
} ac97_codecs[] = {
[AC97_CODEC_AD1881] = {
.vendor_id = AC97_VENDOR_ID('A', 'D', 'S', 0x40),
.max_rate = 48000,
.misc_flags = 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,
.powerdown_mask = 0x0bf
},
[AC97_CODEC_ALC100] = {
.vendor_id = AC97_VENDOR_ID('A', 'L', 'C', 0x20),
.max_rate = 48000,
.misc_flags = 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,
.powerdown_mask = 0x0bf
},
[AC97_CODEC_CS4297] = {
.vendor_id = AC97_VENDOR_ID('C', 'R', 'Y', 0x03),
.max_rate = 48000,
.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,
.reset_flags = AC97_HPOUT | AC97_DAC_18B | AC97_ADC_18B,
.extid_flags = 0,
.powerdown_mask = 0x07f,
.vendor_regs = (const ac97_vendor_reg_t[]) {{0x05a, 0x0301, 0x0000}, {0}}
},
[AC97_CODEC_CS4297A] = {
.vendor_id = AC97_VENDOR_ID('C', 'R', 'Y', 0x11),
.max_rate = 48000,
.misc_flags = 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,
.powerdown_mask = 0x0ff,
.vendor_regs = (const ac97_vendor_reg_t[]) {{0x05e, 0x0000, 0x01b0}, {0x060, 0x0023, 0x0001}, {0x068, 0x0000, 0xdfff}, {0}}
},
[AC97_CODEC_WM9701A] = {
.vendor_id = AC97_VENDOR_ID('W', 'M', 'L', 0x00),
.max_rate = 48000,
.misc_flags = AC97_AUXOUT | AC97_MONOOUT | AC97_PCBEEP | AC97_PHONE | AC97_VIDEO | AC97_AUXIN | AC97_MS | AC97_LPBK,
.reset_flags = AC97_DAC_18B | AC97_ADC_18B,
.extid_flags = 0,
.powerdown_mask = 0x03f
}
};
@ -56,6 +104,8 @@ ac97_codec_log(const char *fmt, ...)
#endif
static const int32_t codec_attn[] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1, 1, 1, 1, 2, 2, 2, 3, 4, 5, 6, 8, 10, 12, 16, 20,
25, 32, 41, 51, 65, 82, 103, 130, 164, 206, 260, 327, 412, 519, 653, 822,
1036, 1304, 1641, 2067, 2602, 3276, 4125, 5192, 6537, 8230, 10362, 13044, 16422, 20674, 26027, 32767,
41305, 52068, 65636, 82739, 104299, 131477, 165737, 208925
@ -66,147 +116,296 @@ int ac97_codec_count = 0, ac97_modem_codec_count = 0,
ac97_codec_id = 0, ac97_modem_codec_id = 0;
uint8_t
ac97_codec_read(ac97_codec_t *dev, uint8_t reg)
uint16_t
ac97_codec_readw(ac97_codec_t *dev, uint8_t reg)
{
uint8_t ret = dev->regs[reg & 0x7f];
/* 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))
ret = (ret <= dev->vendor_reg_page_max) ? dev->vendor_reg_pages[(ret << 3) | ((reg & 0x0e) >> 1)] : 0;
else
ret = dev->regs[reg >> 1];
ac97_codec_log("AC97 Codec %d: read(%02X) = %02X\n", dev->codec_id, reg, ret);
ac97_codec_log("AC97 Codec %d: readw(%02X) = %04X\n", dev->codec_id, reg, ret);
return ret;
}
void
ac97_codec_write(ac97_codec_t *dev, uint8_t reg, uint8_t val)
ac97_codec_writew(ac97_codec_t *dev, uint8_t reg, uint16_t val)
{
uint8_t i;
ac97_codec_log("AC97 Codec %d: writew(%02X, %04X)\n", dev->codec_id, reg, val);
ac97_codec_log("AC97 Codec %d: write(%02X, %02X)\n", dev->codec_id, reg, val);
reg &= 0x7f;
reg &= 0x7e;
uint16_t i = 0, prev = dev->regs[reg >> 1];
int j;
switch (reg) {
case 0x00: case 0x01: /* Reset / ID code */
case 0x00: /* Reset / ID code */
ac97_codec_reset(dev);
return;
case 0x08: case 0x09: /* Master Tone Control (optional) */
case 0x0d: /* Phone Volume MSB */
case 0x0f: /* Mic Volume MSB */
case 0x1e: case 0x1f: /* Record Gain Mic (optional) */
case 0x22: case 0x23: /* 3D Control (optional) */
case 0x24: case 0x25: /* Audio Interrupt and Paging Mechanism (optional) */
case 0x26: /* Powerdown Ctrl/Stat LSB */
case 0x28: case 0x29: /* Extended Audio ID */
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 */
/* Read-only registers. */
return;
case 0x02: /* Master Volume */
val &= 0xbf3f;
case 0x02: /* Master Volume LSB */
case 0x04: /* Aux Out Volume LSB */
case 0x06: /* Mono Volume LSB */
val &= 0x3f;
/* fall-through */
case 0x03: /* Master Volume MSB */
case 0x05: /* Aux Out Volume MSB */
val &= 0xbf;
/* Limit level to a maximum of 011111. */
if (val & 0x20) {
val &= ~0x20;
val |= 0x1f;
/* Convert 1xxxxx to 011111 where unsupported, per specification. */
if (!(dev->misc_flags & AC97_MASTER_6B)) {
clamp_5b: if (val & 0x2000)
val = (val & ~0x2000) | 0x1f00;
clamp_5b_r: if (val & 0x0020)
val = (val & ~0x0020) | 0x001f;
}
break;
case 0x07: /* Mono Volume MSB */
case 0x0b: /* PC Beep Volume MSB */
case 0x20: /* General Purpose LSB */
val &= 0x80;
case 0x04: /* Aux Out Volume */
if (!(dev->misc_flags & AC97_AUXOUT))
return;
val &= 0xbf3f;
/* Convert 1xxxxx to 011111 where unsupported, per specification. */
if (!(dev->misc_flags & AC97_AUXOUT_6B))
goto clamp_5b;
break;
case 0x0a: /* PC Beep Volume LSB */
val &= 0x1e;
case 0x06: /* Mono Out Volume */
if (!(dev->misc_flags & AC97_MONOOUT))
return;
val &= 0x803f;
/* Convert 1xxxxx to 011111 where unsupported, per specification. */
if (!(dev->misc_flags & AC97_MONOOUT_6B))
goto clamp_5b_r;
break;
case 0x0c: /* Phone Volume LSB */
case 0x10: /* Line In Volume LSB */
case 0x12: /* CD Volume LSB */
case 0x14: /* Video Volume LSB */
case 0x16: /* Aux In Volume LSB */
case 0x18: /* PCM Out Volume LSB */
val &= 0x1f;
case 0x08: /* Master Tone Control */
if (!(dev->reset_flags & AC97_TONECTL))
return;
val &= 0x0f0f;
break;
case 0x0e: /* Mic Volume LSB */
val &= 0x5f;
case 0x0a: /* PC Beep Volume */
if (dev->misc_flags & AC97_PCBEEP)
i |= 0x801e;
if (dev->misc_flags & AC97_PCBEEP_GEN)
i |= 0x1fe0;
val &= i;
break;
case 0x11: /* Line In Volume MSB */
case 0x13: /* CD Volume MSB */
case 0x15: /* Video Volume MSB */
case 0x17: /* Aux In Volume MSB */
case 0x19: /* PCM Out Volume MSB */
val &= 0x9f;
case 0x0c: /* Phone Volume */
if (!(dev->misc_flags & AC97_PHONE))
return;
val &= 0x801f;
break;
case 0x1a: case 0x1b: /* Record Select */
val &= 0x07;
case 0x0e: /* Mic Volume */
val &= 0x805f;
break;
case 0x1c: /* Record Gain LSB */
val &= 0x0f;
case 0x10: /* Line In Volume */
case 0x12: /* CD Volume */
case 0x18: /* PCM Out Volume */
line_gain: val &= 0x9f1f;
break;
case 0x1d: /* Record Gain MSB */
val &= 0x8f;
case 0x14: /* Video Volume */
if (!(dev->misc_flags & AC97_VIDEO))
return;
goto line_gain;
case 0x16: /* Aux In Volume */
if (!(dev->misc_flags & AC97_AUXIN))
return;
goto line_gain;
case 0x1a: /* Record Select Control */
val &= 0x0707;
break;
case 0x21: /* General Purpose MSB */
val &= 0x83;
case 0x1c: /* Record Gain */
val &= 0x8f0f;
break;
case 0x2a: /* Extended Audio Status/Control LSB */
#ifdef AC97_CODEC_FULL_RATE_RANGE /* enable DRA (double rate) support */
val &= 0x0b;
#else
val &= 0x09;
#endif
case 0x1e: /* Record Gain Mic */
if (!(dev->reset_flags & AC97_MICPCM))
return;
val &= 0x800f;
break;
case 0x20: /* General Purpose */
i = AC97_MIX | (dev->misc_flags & (AC97_POP | AC97_MS | AC97_LPBK));
if (dev->reset_flags >> AC97_3D_SHIFT)
i |= AC97_3D;
if (dev->reset_flags & AC97_SIMSTEREO)
i |= AC97_ST;
if (dev->reset_flags & AC97_LOUDNESS)
i |= AC97_LD;
if (dev->extid_flags & AC97_DRA)
i |= AC97_DRSS_MASK;
val &= i;
break;
case 0x22: /* 3D Control */
switch (dev->reset_flags >> AC97_3D_SHIFT) {
case 1: /* Analog Devices */
case 6: /* Crystal */
val &= 0x000f;
break;
case 22: /* Avance Logic / Realtek */
val &= 0x0003;
break;
default:
return;
}
break;
case 0x24: /* Audio Interrupt and Paging Mechanism */
if ((dev->extid_flags & AC97_REV_MASK) < AC97_REV_2_3)
return;
val &= 0x000f;
break;
case 0x26: /* Powerdown Control/Status */
i = dev->powerdown_mask << 8;
val = (val & i) | (prev & ~i);
break;
case 0x28: /* Extended Audio ID */
if (dev->misc_flags & AC97_DSA)
i |= 0x0030;
val = (val & i) | (prev & ~i);
break;
case 0x2a: /* Extended Audio Status/Control */
i = dev->extid_flags & (AC97_VRA | AC97_DRA | AC97_SPDIF | AC97_VRM);
if (dev->extid_flags & AC97_SPDIF)
i |= AC97_SPSA_MASK << AC97_SPSA_SHIFT;
i |= (dev->powerdown_mask << 3) & 0x7800; /* multichannel powerdowns */
val = (val & i) | (prev & ~i);
/* Reset DAC sample rates to 48 KHz (96 KHz with DRA) if VRA is being cleared. */
if (!(val & 0x01)) {
if (!(val & AC97_VRA)) {
for (i = 0x2c; i <= 0x30; i += 2)
*((uint16_t *) &dev->regs[i]) = 48000;
dev->regs[i >> 1] = 48000;
}
/* Reset ADC sample rates to 48 KHz if VRM is being cleared. */
if (!(val & 0x08)) {
if (!(val & AC97_VRM)) {
for (i = 0x32; i <= 0x34; i += 2)
*((uint16_t *) &dev->regs[i]) = 48000;
dev->regs[i >> 1] = 48000;
}
break;
case 0x2c ... 0x35: /* DAC/ADC Rates */
/* Writable only if VRA/VRM is set. */
i = (reg >= 0x32) ? 0x08 : 0x01;
if (!(dev->regs[0x2a] & i))
case 0x2c: /* PCM Front DAC Rate */
case 0x32: /* PCM L/R ADC Rate */
rate: /* Writable only if VRA/VRM is set. */
i = (reg >= 0x32) ? AC97_VRM : AC97_VRA;
if (!(dev->extid_flags & i))
return;
#ifndef AC97_CODEC_FULL_RATE_RANGE
/* Limit to 48 KHz on MSB write. */
if ((reg & 1) && (((val << 8) | dev->regs[reg & 0x7e]) > 48000)) {
*((uint16_t *) &dev->regs[reg & 0x7e]) = 48000;
/* Limit to maximum rate. */
if (val > dev->max_rate)
val = dev->max_rate;
break;
case 0x2e: /* PCM Surround DAC Rate */
if (!(dev->extid_flags & AC97_SDAC))
return;
goto rate;
case 0x30: /* PCM LFE DAC Rate */
if (!(dev->extid_flags & AC97_LDAC))
return;
goto rate;
case 0x34: /* Mic ADC Rate */
if (!(dev->reset_flags & AC97_MICPCM))
return;
goto rate;
case 0x36: /* Center/LFE Volume */
if (dev->extid_flags & AC97_LDAC)
i |= 0xbf00;
if (dev->extid_flags & AC97_CDAC)
i |= 0x00bf;
val &= i;
/* Convert 1xxxxx to 011111 where unsupported, per specification. */
if (!(dev->misc_flags & AC97_LFE_6B) && (val & 0x2000))
val = (val & ~0x2000) | 0x1f00;
if (!(dev->misc_flags & AC97_CENTER_6B))
goto clamp_5b_r;
break;
case 0x38: /* Surround Volume */
if (!(dev->extid_flags & AC97_SDAC))
return;
val &= 0xbfbf;
/* Convert 1xxxxx to 011111 where unsupported, per specification. */
if (!(dev->misc_flags & AC97_SURR_6B))
goto clamp_5b;
break;
case 0x3a: /* S/PDIF Control */
if (!(dev->extid_flags & AC97_SPDIF))
return;
break;
case 0x60 ... 0x6e: /* Extended */
/* Get extended register page. */
i = dev->regs[0x24 >> 1] & 0x000f;
/* Redirect a write to page 1+ to the right array, part 1. */
if (i > 0) {
/* Don't overflow the pages. */
if (i > dev->vendor_reg_page_max)
return;
/* Get actual previous value. */
prev = dev->vendor_reg_pages[(i << 3) | ((reg & 0x0e) >> 1)];
}
i <<= 8;
/* fall-through */
case 0x5a ... 0x5e: /* Vendor Reserved */
case 0x70 ... 0x7a:
/* Stop if no vendor-specific registers are defined. */
if (!dev->vendor_regs)
return;
/* Look for a matching vendor-specific register. */
i |= reg;
for (j = 0; dev->vendor_regs[j].index; j++) {
/* If a match was found, inject written bits. */
if (dev->vendor_regs[j].index == i) {
val = (val & dev->vendor_regs[j].write_mask) | (prev & ~dev->vendor_regs[j].write_mask);
break;
}
}
/* No match found. */
if (!dev->vendor_regs[j].index)
return;
/* Redirect a write to page 1+ to the right array, part 2. */
i >>= 8;
if (i > 0) {
dev->vendor_reg_pages[(i << 3) | ((reg & 0x0e) >> 1)] = val;
return;
}
#endif
break;
case 0x7c: /* Vendor ID1 */
case 0x7e: /* Vendor ID2 */
return;
}
dev->regs[reg] = val;
dev->regs[reg >> 1] = val;
}
@ -214,38 +413,59 @@ void
ac97_codec_reset(void *priv)
{
ac97_codec_t *dev = (ac97_codec_t *) priv;
uint8_t i;
uint16_t i, j;
ac97_codec_log("AC97 Codec %d: reset()\n", dev->codec_id);
memset(dev->regs, 0, sizeof(dev->regs));
/* 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;
dev->regs[0x02 >> 1] = AC97_MUTE;
if (dev->misc_flags & AC97_AUXOUT)
dev->regs[0x04 >> 1] = AC97_MUTE;
if (dev->misc_flags & AC97_MONOOUT)
dev->regs[0x06 >> 1] = AC97_MUTE;
if (dev->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 (dev->misc_flags & AC97_VIDEO)
dev->regs[0x14 >> 1] = AC97_MUTE | 0x0808;
if (dev->misc_flags & AC97_AUXIN)
dev->regs[0x14 >> 1] = AC97_MUTE | 0x0808;
if (dev->misc_flags & AC97_LDAC)
dev->regs[0x36 >> 1] = AC97_MUTE_L;
if (dev->misc_flags & AC97_CDAC)
dev->regs[0x36 >> 1] |= AC97_MUTE_R;
if (dev->misc_flags & AC97_SDAC)
dev->regs[0x38 >> 1] = AC97_MUTE_L | AC97_MUTE_R;
/* Set flags. */
dev->regs[0x00 >> 1] = dev->reset_flags;
dev->regs[0x26 >> 1] = 0x000f; /* codec ready */
dev->regs[0x28 >> 1] = (dev->codec_id << 14) | dev->extid_flags;
ac97_codec_writew(dev, 0x2a, 0x0000); /* reset variable DAC/ADC sample rates */
i = dev->extid_flags & (AC97_CDAC | AC97_SDAC | AC97_LDAC);
dev->regs[0x2a >> 1] |= i | (i << 5); /* any additional DACs are ready but powered down */
if (dev->extid_flags & AC97_SPDIF)
dev->regs[0x2a >> 1] |= AC97_SPCV;
if (dev->reset_flags & AC97_MICPCM)
dev->regs[0x2a >> 1] |= AC97_MADC | AC97_PRL;
/* Set vendor ID. */
dev->regs[0x7c >> 1] = dev->vendor_id >> 16;
dev->regs[0x7e >> 1] = dev->vendor_id;
/* Set vendor-specific registers. */
if (dev->vendor_regs) {
for (j = 0; dev->vendor_regs[j].index; j++) {
i = (dev->vendor_regs[j].index >> 8) & 0x000f;
if (i > 0)
dev->vendor_reg_pages[(i << 3) | (dev->vendor_regs[j].index >> 1)] = dev->vendor_regs[j].value;
else
dev->regs[dev->vendor_regs[j].index >> 1] = dev->vendor_regs[j].value;
}
}
/* Flag codec as ready. */
dev->regs[0x26] = 0x0f;
/* Set up variable sample rate support. */
#ifdef AC97_CODEC_FULL_RATE_RANGE /* enable DRA (double rate) support */
dev->regs[0x28] = 0x0b;
#else
dev->regs[0x28] = 0x09;
#endif
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;
dev->regs[0x7d] = dev->vendor_id >> 24;
dev->regs[0x7e] = dev->vendor_id;
dev->regs[0x7f] = dev->vendor_id >> 8;
}
@ -253,24 +473,38 @@ void
ac97_codec_getattn(void *priv, uint8_t reg, int *l, int *r)
{
ac97_codec_t *dev = (ac97_codec_t *) priv;
uint8_t r_val = dev->regs[reg],
l_val = dev->regs[reg | 1];
uint16_t val = dev->regs[reg >> 1];
if (l_val & 0x80) { /* mute */
/* Apply full mute and powerdowns. */
int full_mute = (reg < 0x36);
if ((full_mute && (val & AC97_MUTE)) || /* full mute */
(dev->regs[0x26 >> 1] & 0x3e00) || /* DAC powerdown */
((reg == 0x38) && (dev->regs[0x2a >> 1] & AC97_PRJ))) { /* surround DAC powerdown */
*l = 0;
*r = 0;
return;
} else { /* per-channel mute */
/* Determine attenuation value. */
uint8_t l_val = val >> 8, r_val = val;
if (reg <= 0x06) { /* 6-bit level */
*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)];
}
/* Apply per-channel mute and center/LFE powerdowns where applicable. */
if (!full_mute) {
if ((val & AC97_MUTE_L) || /* left mute */
((reg == 0x36) && (dev->regs[0x2a >> 1] & AC97_PRK))) /* LFE DAC powerdown */
*l = 0;
if ((val & AC97_MUTE_R) || /* right mute */
((reg == 0x36) && (dev->regs[0x2a >> 1] & AC97_PRI))) /* center DAC powerdown */
*r = 0;
}
}
l_val &= 0x1f;
r_val &= 0x1f;
if (reg < 0x10) { /* 5-bit level (converted from 6-bit on register write) */
*l = codec_attn[0x1f - l_val];
*r = codec_attn[0x1f - r_val];
} else { /* 5-bit gain */
*l = codec_attn[0x27 - l_val];
*r = codec_attn[0x27 - r_val];
}
ac97_codec_log("AC97 Codec %d: getattn(%02X) = %d %d\n", dev->codec_id, reg, *l, *r);
}
@ -280,13 +514,11 @@ 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]);
uint32_t ret = dev->regs[reg >> 1];
#ifdef AC97_CODEC_FULL_RATE_RANGE
/* If this is a DAC, double sample rate if DRA is set. */
if ((reg < 0x32) && (dev->regs[0x2a] & 0x02))
/* If this is the PCM DAC, double sample rate if DRA is set. */
if ((reg == 0x2c) && (dev->regs[0x2a >> 1] & AC97_DRA))
ret <<= 1;
#endif
ac97_codec_log("AC97 Codec %d: getrate(%02X) = %d\n", dev->codec_id, reg, ret);
@ -300,7 +532,12 @@ ac97_codec_init(const device_t *info)
ac97_codec_t *dev = malloc(sizeof(ac97_codec_t));
memset(dev, 0, sizeof(ac97_codec_t));
dev->vendor_id = info->local;
dev->vendor_id = ac97_codecs[info->local].vendor_id;
dev->max_rate = ac97_codecs[info->local].max_rate;
dev->extid_flags = ac97_codecs[info->local].extid_flags;
dev->misc_flags = ac97_codecs[info->local].misc_flags;
dev->powerdown_mask = ac97_codecs[info->local].powerdown_mask;
dev->vendor_regs = ac97_codecs[info->local].vendor_regs;
ac97_codec_log("AC97 Codec %d: init(%c%c%c%02X)\n", ac97_codec_id, (dev->vendor_id >> 24) & 0xff, (dev->vendor_id >> 16) & 0xff, (dev->vendor_id >> 8) & 0xff, dev->vendor_id & 0xff);
/* Associate this codec to the current controller. */
@ -315,6 +552,26 @@ ac97_codec_init(const device_t *info)
ac97_codec += sizeof(ac97_codec_t *);
dev->codec_id = ac97_codec_id++;
/* Allocate vendor-specific register pages if required. */
if (dev->vendor_regs) {
/* Get the highest vendor-specific register page number. */
int i, j;
dev->vendor_reg_page_max = 0;
for (j = 0; dev->vendor_regs[j].index; j++) {
i = (dev->vendor_regs[j].index >> 8) & 0x000f;
if (i > dev->vendor_reg_page_max)
dev->vendor_reg_page_max = i;
}
/* Allocate pages 1+. */
if (dev->vendor_reg_page_max > 0) {
ac97_codec_log("AC97 Codec %d: Allocating %d vendor-specific register pages\n", dev->codec_id, dev->vendor_reg_page_max);
i = 16 * dev->vendor_reg_page_max;
dev->vendor_reg_pages = (uint16_t *) malloc(i);
memset(dev->vendor_reg_pages, 0, i);
}
}
/* Initialize codec registers. */
ac97_codec_reset(dev);
@ -329,6 +586,8 @@ ac97_codec_close(void *priv)
ac97_codec_log("AC97 Codec %d: close()\n", dev->codec_id);
if (dev->vendor_reg_pages)
free(dev->vendor_reg_pages);
free(dev);
}

View file

@ -376,19 +376,18 @@ ac97_via_sgd_write(uint16_t addr, uint8_t val, void *priv)
if (codec) {
/* Read from or write to codec. */
if (val & 0x80) {
if (val & 1) { /* return 0x00 on unaligned reads */
if (val & 1) { /* return 0x0000 on unaligned reads (real 686B behavior) */
dev->sgd_regs[0x80] = dev->sgd_regs[0x81] = 0x00;
} else {
dev->sgd_regs[0x80] = dev->codec_shadow[modem].regs_codec[i][(val & 0x7f)] = ac97_codec_read(codec, val);
dev->sgd_regs[0x81] = dev->codec_shadow[modem].regs_codec[i][(val & 0x7f) | 1] = ac97_codec_read(codec, val | 1);
*((uint16_t *) &dev->codec_shadow[modem].regs_codec[i][val & 0x7f]) = *((uint16_t *) &dev->sgd_regs[0x80]) =
ac97_codec_readw(codec, val);
}
/* Flag data/status/index for this codec as valid. */
if (val & 0x80)
dev->sgd_regs[0x83] |= 0x02 << (i << 1);
dev->sgd_regs[0x83] |= 0x02 << (i << 1);
} else if (!(val & 1)) { /* do nothing on unaligned writes */
ac97_codec_write(codec, val, dev->codec_shadow[modem].regs_codec[i][(val & 0x7f)] = dev->sgd_regs[0x80]);
ac97_codec_write(codec, val | 1, dev->codec_shadow[modem].regs_codec[i][(val & 0x7f) | 1] = dev->sgd_regs[0x81]);
ac97_codec_writew(codec, val,
*((uint16_t *) &dev->codec_shadow[modem].regs_codec[i][val & 0x7f]) = *((uint16_t *) &dev->sgd_regs[0x80]));
/* Update primary audio codec state if that codec was written to. */
if (!modem && !i)

View file

@ -1013,13 +1013,10 @@ es1371_outl(uint16_t port, uint32_t val, void *p)
case 0x14:
if (val & CODEC_READ) {
dev->codec_ctrl &= 0x00ff0000;
val = (val >> 16) & 0x7e;
dev->codec_ctrl |= ac97_codec_read(dev->codec, val);
dev->codec_ctrl |= ac97_codec_read(dev->codec, val | 1) << 8;
dev->codec_ctrl |= ac97_codec_readw(dev->codec, val >> 16);
} else {
dev->codec_ctrl = val & 0x00ffffff;
ac97_codec_write(dev->codec, (val >> 16) & 0x7e, val & 0xff);
ac97_codec_write(dev->codec, ((val >> 16) & 0x7e) | 1, val >> 8);
ac97_codec_writew(dev->codec, val >> 16, val);
ac97_codec_getattn(dev->codec, 0x02, &dev->master_vol_l, &dev->master_vol_r);
ac97_codec_getattn(dev->codec, 0x12, &dev->cd_vol_l, &dev->cd_vol_r);

View file

@ -110,6 +110,7 @@ static const SOUND_CARD sound_cards[] =
{ "sbmcv", &sb_mcv_device },
{ "sbpromcv", &sb_pro_mcv_device },
{ "es1371", &es1371_device },
{ "ad1881", &ad1881_device },
{ "cs4297a", &cs4297a_device },
{ "", NULL }
};