This commit is contained in:
OBattler
2023-07-22 21:56:13 +02:00
3 changed files with 180 additions and 173 deletions

View File

@@ -17,7 +17,7 @@
#ifndef SOUND_AC97_H #ifndef SOUND_AC97_H
#define SOUND_AC97_H #define SOUND_AC97_H
#define AC97_VENDOR_ID(f, s, t, dev) ((((f) &0xff) << 24) | (((s) &0xff) << 16) | (((t) &0xff) << 8) | ((dev) &0xff)) #define AC97_VENDOR_ID(a, b, c, i) ((((a) &0xff) << 24) | (((b) &0xff) << 16) | (((c) &0xff) << 8) | ((i) &0xff))
/* Misc support bits (misc_flags). Most of these are not part of any /* Misc support bits (misc_flags). Most of these are not part of any
registers, but control enabling/disabling of registers and bits. */ registers, but control enabling/disabling of registers and bits. */
@@ -91,32 +91,26 @@
#define AC97_PRK (1 << 13) #define AC97_PRK (1 << 13)
#define AC97_PRL (1 << 14) #define AC97_PRL (1 << 14)
/* New codecs should be added to the end of this enum to avoid breaking configs. */ /* Codec IDs. */
enum { #define AC97_CODEC_AD1881 AC97_VENDOR_ID('A', 'D', 'S', 0x40)
AC97_CODEC_AD1881 = 0, #define AC97_CODEC_AK4540 AC97_VENDOR_ID('A', 'D', 'S', 0x40)
AC97_CODEC_ALC100 = 1, #define AC97_CODEC_ALC100 AC97_VENDOR_ID('A', 'L', 'C', 0x20)
AC97_CODEC_CS4297 = 2, #define AC97_CODEC_CS4297 AC97_VENDOR_ID('C', 'R', 'Y', 0x03)
AC97_CODEC_CS4297A = 3, #define AC97_CODEC_CS4297A AC97_VENDOR_ID('C', 'R', 'Y', 0x11)
AC97_CODEC_WM9701A = 4, #define AC97_CODEC_STAC9708 AC97_VENDOR_ID(0x83, 0x84, 0x76, 0x08)
AC97_CODEC_STAC9708 = 5, #define AC97_CODEC_STAC9721 AC97_VENDOR_ID(0x83, 0x84, 0x76, 0x09)
AC97_CODEC_STAC9721 = 6, #define AC97_CODEC_TR28023 AC97_VENDOR_ID('T', 'R', 'A', 0x03)
AC97_CODEC_AK4540 = 7 #define AC97_CODEC_WM9701A AC97_VENDOR_ID('W', 'M', 'L', 0x00)
};
typedef struct ac97_vendor_reg_t { typedef struct ac97_vendor_reg_t {
const uint16_t index; uint8_t page; /* for paged registers [60:6F], 0 otherwise */
const uint16_t value; uint8_t index;
const uint16_t write_mask; uint16_t value;
uint16_t write_mask;
} ac97_vendor_reg_t; } ac97_vendor_reg_t;
typedef struct ac97_codec_t { typedef struct ac97_codec_t {
uint32_t vendor_id; int model;
uint32_t min_rate;
uint32_t max_rate;
uint32_t misc_flags;
uint16_t reset_flags;
uint16_t extid_flags;
uint16_t powerdown_mask;
uint16_t regs[64]; uint16_t regs[64];
uint8_t codec_id; uint8_t codec_id;
uint8_t vendor_reg_page_max; uint8_t vendor_reg_page_max;
@@ -129,7 +123,7 @@ extern void ac97_codec_writew(ac97_codec_t *dev, uint8_t reg, uint16_
extern void ac97_codec_reset(void *priv); extern void ac97_codec_reset(void *priv);
extern void ac97_codec_getattn(void *priv, uint8_t reg, int *l, int *r); 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 uint32_t ac97_codec_getrate(void *priv, uint8_t reg);
extern const device_t *ac97_codec_get(int model); extern const device_t *ac97_codec_get(uint32_t id);
extern void ac97_via_set_slot(void *priv, int slot, int irq_pin); 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 uint8_t ac97_via_read_status(void *priv, uint8_t modem);
@@ -152,8 +146,10 @@ extern const device_t ak4540_device;
extern const device_t alc100_device; extern const device_t alc100_device;
extern const device_t cs4297_device; extern const device_t cs4297_device;
extern const device_t cs4297a_device; extern const device_t cs4297a_device;
# define ct1297_device tr28023_device
extern const device_t stac9708_device; extern const device_t stac9708_device;
extern const device_t stac9721_device; extern const device_t stac9721_device;
extern const device_t tr28023_device;
extern const device_t wm9701a_device; extern const device_t wm9701a_device;
extern const device_t ac97_via_device; extern const device_t ac97_via_device;

View File

@@ -27,81 +27,88 @@
#include <86box/snd_ac97.h> #include <86box/snd_ac97.h>
static const struct { static const struct {
const uint32_t vendor_id, min_rate, max_rate, misc_flags; /* definitions for misc_flags in snd_ac97.h */ const device_t *device;
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] */ /* Definitions for *_flags and vendor_regs in snd_ac97.h */
const ac97_vendor_reg_t *vendor_regs; /* bits [11:8] of index are the page number if applicable (registers [60:6F]) */ uint32_t min_rate;
const device_t *device; uint32_t max_rate;
uint32_t misc_flags;
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] */
const ac97_vendor_reg_t *vendor_regs;
} ac97_codecs[] = { } ac97_codecs[] = {
// clang-format off // clang-format off
[AC97_CODEC_AD1881] = { {
.vendor_id = AC97_VENDOR_ID('A', 'D', 'S', 0x40), .device = &ad1881_device,
.min_rate = 7000, .min_rate = 7000,
.max_rate = 48000, .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_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 */ .reset_flags = (1 << AC97_3D_SHIFT), /* datasheet contradicts itself on AC97_HPOUT */
.extid_flags = AC97_VRA, .extid_flags = AC97_VRA,
.powerdown_mask = 0x0bf, .pcsr_mask = 0xbf,
.vendor_regs = (const ac97_vendor_reg_t[]) {{0x74, 0x0000, 0xff07}, {0x76, 0x0404, 0xdde5}, {0x78, 48000, 0x0000}, {0x7a, 48000, 0x0000}, {0}}, .vendor_regs = (const ac97_vendor_reg_t[]) {{0, 0x74, 0x0000, 0xff07}, {0, 0x76, 0x0404, 0xdde5}, {0, 0x78, 48000, 0x0000}, {0, 0x7a, 48000, 0x0000}, {0}}
.device = &ad1881_device
}, },
[AC97_CODEC_AK4540] = { {
.vendor_id = AC97_VENDOR_ID('A', 'K', 'M', 0x00), .device = &ak4540_device,
.misc_flags = AC97_MONOOUT | AC97_PCBEEP | AC97_PHONE | AC97_VIDEO | AC97_AUXIN | AC97_MS | AC97_LPBK, .misc_flags = AC97_MONOOUT | AC97_PCBEEP | AC97_PHONE | AC97_VIDEO | AC97_AUXIN | AC97_MS | AC97_LPBK,
.powerdown_mask = 0x01f, .pcsr_mask = 0x1f
.device = &ak4540_device
}, },
[AC97_CODEC_ALC100] = { {
.vendor_id = AC97_VENDOR_ID('A', 'L', 'C', 0x20), .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_AUXOUT | AC97_MONOOUT | AC97_PCBEEP | AC97_PHONE | AC97_VIDEO | AC97_AUXIN | AC97_POP | AC97_MS | AC97_LPBK,
.reset_flags = (22 << AC97_3D_SHIFT), .reset_flags = (22 << AC97_3D_SHIFT),
.extid_flags = AC97_AMAP, .extid_flags = AC97_AMAP,
.powerdown_mask = 0x0bf, .pcsr_mask = 0xbf
.device = &alc100_device
}, },
[AC97_CODEC_CS4297] = { {
.vendor_id = AC97_VENDOR_ID('C', 'R', 'Y', 0x03), .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_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, .reset_flags = AC97_HPOUT | AC97_DAC_18B | AC97_ADC_18B,
.extid_flags = 0, .extid_flags = 0,
.powerdown_mask = 0x07f, .pcsr_mask = 0x7f,
.vendor_regs = (const ac97_vendor_reg_t[]) {{0x5a, 0x0301, 0x0000}, {0}}, .vendor_regs = (const ac97_vendor_reg_t[]) {{0, 0x5a, 0x0301, 0x0000}, {0}}
.device = &cs4297_device
}, },
[AC97_CODEC_CS4297A] = { {
.vendor_id = AC97_VENDOR_ID('C', 'R', 'Y', 0x11), .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_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), .reset_flags = AC97_HPOUT | AC97_DAC_20B | AC97_ADC_18B | (6 << AC97_3D_SHIFT),
.extid_flags = AC97_AMAP, .extid_flags = AC97_AMAP,
.powerdown_mask = 0x0ff, .pcsr_mask = 0xff,
.vendor_regs = (const ac97_vendor_reg_t[]) {{0x5e, 0x0000, 0x01b0}, {0x60, 0x0023, 0x0001}, {0x68, 0x0000, 0xdfff}, {0}}, .vendor_regs = (const ac97_vendor_reg_t[]) {{0, 0x5e, 0x0000, 0x01b0}, {0, 0x60, 0x0023, 0x0001}, {0, 0x68, 0x0000, 0xdfff}, {0}}
.device = &cs4297a_device
}, },
[AC97_CODEC_STAC9708] = { {
.vendor_id = AC97_VENDOR_ID(0x83, 0x84, 0x76, 0x08), .device = &stac9708_device,
.misc_flags = AC97_AUXOUT | AC97_MONOOUT | AC97_PCBEEP | AC97_PHONE | AC97_VIDEO | AC97_AUXIN | AC97_MS | AC97_LPBK, .misc_flags = 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, .reset_flags = (26 << AC97_3D_SHIFT) | AC97_DAC_18B | AC97_ADC_18B,
.extid_flags = AC97_SDAC, .extid_flags = AC97_SDAC,
.powerdown_mask = 0x2ff, .pcsr_mask = 0xff,
.vendor_regs = (const ac97_vendor_reg_t []) {{0x6c, 0x0000, 0x0003}, {0x74, 0x0000, 0x0003}, {0}}, .eascr_mask = 0x02,
.device = &stac9708_device .vendor_regs = (const ac97_vendor_reg_t[]) {{0, 0x6c, 0x0000, 0x0003}, {0, 0x74, 0x0000, 0x0003}, {0}}
}, },
[AC97_CODEC_STAC9721] = { {
.vendor_id = AC97_VENDOR_ID(0x83, 0x84, 0x76, 0x09), .device = &stac9721_device,
.misc_flags = AC97_AUXOUT | AC97_MONOOUT | AC97_PCBEEP | AC97_PHONE | AC97_VIDEO | AC97_AUXIN | AC97_MS | AC97_LPBK, .misc_flags = 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, .reset_flags = (26 << AC97_3D_SHIFT) | AC97_DAC_18B | AC97_ADC_18B,
.extid_flags = AC97_AMAP, .extid_flags = AC97_AMAP,
.powerdown_mask = 0x0ff, .pcsr_mask = 0xff,
.vendor_regs = (const ac97_vendor_reg_t []) {{0x6c, 0x0000, 0x0000}, {0x6e, 0x0000, 0x0003}, {0x70, 0x0000, 0xffff}, {0x72, 0x0000, 0x0006}, {0x74, 0x0000, 0x0003}, {0x76, 0x0000, 0xffff}, {0x78, 0x0000, 0x3802}, {0}}, .vendor_regs = (const ac97_vendor_reg_t[]) {{0, 0x6c, 0x0000, 0x0000}, {0, 0x6e, 0x0000, 0x0003}, {0, 0x70, 0x0000, 0xffff}, {0, 0x72, 0x0000, 0x0006}, {0, 0x74, 0x0000, 0x0003}, {0, 0x76, 0x0000, 0xffff}, {0, 0x78, 0x0000, 0x3802}, {0}}
.device = &stac9721_device
}, },
[AC97_CODEC_WM9701A] = { {
.vendor_id = AC97_VENDOR_ID('W', 'M', 'L', 0x00), .device = &tr28023_device,
.misc_flags = AC97_MASTER_6B | AC97_MONOOUT | AC97_MONOOUT_6B | AC97_PCBEEP | AC97_PHONE | AC97_POP | AC97_MS | AC97_LPBK,
.reset_flags = 0,
.extid_flags = 0,
.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_AUXOUT | AC97_MONOOUT | AC97_PCBEEP | AC97_PHONE | AC97_VIDEO | AC97_AUXIN | AC97_MS | AC97_LPBK,
.reset_flags = AC97_DAC_18B | AC97_ADC_18B, .reset_flags = AC97_DAC_18B | AC97_ADC_18B,
.extid_flags = 0, .extid_flags = 0,
.powerdown_mask = 0x03f, .pcsr_mask = 0x3f
.device = &wm9701a_device
} }
// clang-format on // clang-format on
}; };
@@ -163,7 +170,7 @@ ac97_codec_writew(ac97_codec_t *dev, uint8_t reg, uint16_t val)
ac97_codec_log("AC97 Codec %d: writew(%02X, %04X)\n", dev->codec_id, reg, val); ac97_codec_log("AC97 Codec %d: writew(%02X, %04X)\n", dev->codec_id, reg, val);
reg &= 0x7e; reg &= 0x7e;
uint16_t i = 0; uint16_t i = 0;
uint16_t prev = dev->regs[reg >> 1]; uint16_t prev = dev->regs[reg >> 1];
int j; int j;
@@ -176,7 +183,7 @@ ac97_codec_writew(ac97_codec_t *dev, uint8_t reg, uint16_t val)
val &= 0xbf3f; val &= 0xbf3f;
/* Convert 1xxxxx to 011111 where unsupported, per specification. */ /* Convert 1xxxxx to 011111 where unsupported, per specification. */
if (!(dev->misc_flags & AC97_MASTER_6B)) { if (!(ac97_codecs[dev->model].misc_flags & AC97_MASTER_6B)) {
clamp_5b: clamp_5b:
if (val & 0x2000) if (val & 0x2000)
val = (val & ~0x2000) | 0x1f00; val = (val & ~0x2000) | 0x1f00;
@@ -187,41 +194,41 @@ clamp_5b_r:
break; break;
case 0x04: /* Aux Out Volume */ case 0x04: /* Aux Out Volume */
if (!(dev->misc_flags & AC97_AUXOUT)) if (!(ac97_codecs[dev->model].misc_flags & AC97_AUXOUT))
return; return;
val &= 0xbf3f; val &= 0xbf3f;
/* Convert 1xxxxx to 011111 where unsupported, per specification. */ /* Convert 1xxxxx to 011111 where unsupported, per specification. */
if (!(dev->misc_flags & AC97_AUXOUT_6B)) if (!(ac97_codecs[dev->model].misc_flags & AC97_AUXOUT_6B))
goto clamp_5b; goto clamp_5b;
break; break;
case 0x06: /* Mono Out Volume */ case 0x06: /* Mono Out Volume */
if (!(dev->misc_flags & AC97_MONOOUT)) if (!(ac97_codecs[dev->model].misc_flags & AC97_MONOOUT))
return; return;
val &= 0x803f; val &= 0x803f;
/* Convert 1xxxxx to 011111 where unsupported, per specification. */ /* Convert 1xxxxx to 011111 where unsupported, per specification. */
if (!(dev->misc_flags & AC97_MONOOUT_6B)) if (!(ac97_codecs[dev->model].misc_flags & AC97_MONOOUT_6B))
goto clamp_5b_r; goto clamp_5b_r;
break; break;
case 0x08: /* Master Tone Control */ case 0x08: /* Master Tone Control */
if (!(dev->reset_flags & AC97_TONECTL)) if (!(ac97_codecs[dev->model].reset_flags & AC97_TONECTL))
return; return;
val &= 0x0f0f; val &= 0x0f0f;
break; break;
case 0x0a: /* PC Beep Volume */ case 0x0a: /* PC Beep Volume */
if (dev->misc_flags & AC97_PCBEEP) if (ac97_codecs[dev->model].misc_flags & AC97_PCBEEP)
i |= 0x801e; i |= 0x801e;
if (dev->misc_flags & AC97_PCBEEP_GEN) if (ac97_codecs[dev->model].misc_flags & AC97_PCBEEP_GEN)
i |= 0x1fe0; i |= 0x1fe0;
val &= i; val &= i;
break; break;
case 0x0c: /* Phone Volume */ case 0x0c: /* Phone Volume */
if (!(dev->misc_flags & AC97_PHONE)) if (!(ac97_codecs[dev->model].misc_flags & AC97_PHONE))
return; return;
val &= 0x801f; val &= 0x801f;
break; break;
@@ -238,12 +245,12 @@ line_gain:
break; break;
case 0x14: /* Video Volume */ case 0x14: /* Video Volume */
if (!(dev->misc_flags & AC97_VIDEO)) if (!(ac97_codecs[dev->model].misc_flags & AC97_VIDEO))
return; return;
goto line_gain; goto line_gain;
case 0x16: /* Aux In Volume */ case 0x16: /* Aux In Volume */
if (!(dev->misc_flags & AC97_AUXIN)) if (!(ac97_codecs[dev->model].misc_flags & AC97_AUXIN))
return; return;
goto line_gain; goto line_gain;
@@ -256,26 +263,26 @@ line_gain:
break; break;
case 0x1e: /* Record Gain Mic */ case 0x1e: /* Record Gain Mic */
if (!(dev->reset_flags & AC97_MICPCM)) if (!(ac97_codecs[dev->model].reset_flags & AC97_MICPCM))
return; return;
val &= 0x800f; val &= 0x800f;
break; break;
case 0x20: /* General Purpose */ case 0x20: /* General Purpose */
i = AC97_MIX | (dev->misc_flags & (AC97_POP | AC97_MS | AC97_LPBK)); i = AC97_MIX | (ac97_codecs[dev->model].misc_flags & (AC97_POP | AC97_MS | AC97_LPBK));
if (dev->reset_flags >> AC97_3D_SHIFT) if (ac97_codecs[dev->model].reset_flags >> AC97_3D_SHIFT)
i |= AC97_3D; i |= AC97_3D;
if (dev->reset_flags & AC97_SIMSTEREO) if (ac97_codecs[dev->model].reset_flags & AC97_SIMSTEREO)
i |= AC97_ST; i |= AC97_ST;
if (dev->reset_flags & AC97_LOUDNESS) if (ac97_codecs[dev->model].reset_flags & AC97_LOUDNESS)
i |= AC97_LD; i |= AC97_LD;
if (dev->extid_flags & AC97_DRA) if (ac97_codecs[dev->model].extid_flags & AC97_DRA)
i |= AC97_DRSS_MASK; i |= AC97_DRSS_MASK;
val &= i; val &= i;
break; break;
case 0x22: /* 3D Control */ case 0x22: /* 3D Control */
switch (dev->reset_flags >> AC97_3D_SHIFT) { switch (ac97_codecs[dev->model].reset_flags >> AC97_3D_SHIFT) {
case 1: /* Analog Devices */ case 1: /* Analog Devices */
case 6: /* Crystal */ case 6: /* Crystal */
val &= 0x000f; val &= 0x000f;
@@ -287,7 +294,7 @@ line_gain:
case 26: /* SigmaTel */ case 26: /* SigmaTel */
i = 0x0003; i = 0x0003;
if (dev->extid_flags & AC97_SDAC) if (ac97_codecs[dev->model].extid_flags & AC97_SDAC)
i |= 0x000c; i |= 0x000c;
val &= i; val &= i;
break; break;
@@ -298,13 +305,13 @@ line_gain:
break; break;
case 0x24: /* Audio Interrupt and Paging Mechanism */ case 0x24: /* Audio Interrupt and Paging Mechanism */
if ((dev->extid_flags & AC97_REV_MASK) < AC97_REV_2_3) if ((ac97_codecs[dev->model].extid_flags & AC97_REV_MASK) < AC97_REV_2_3)
return; return;
val &= 0x000f; val &= 0x000f;
break; break;
case 0x26: /* Powerdown Control/Status */ case 0x26: /* Powerdown Control/Status */
i = dev->powerdown_mask << 8; i = ac97_codecs[dev->model].pcsr_mask << 8;
val = (val & i) | (prev & ~i); val = (val & i) | (prev & ~i);
/* Update status bits to reflect powerdowns. */ /* Update status bits to reflect powerdowns. */
@@ -314,16 +321,16 @@ line_gain:
break; break;
case 0x28: /* Extended Audio ID */ case 0x28: /* Extended Audio ID */
if (dev->misc_flags & AC97_DSA) if (ac97_codecs[dev->model].misc_flags & AC97_DSA)
i |= 0x0030; i |= 0x0030;
val = (val & i) | (prev & ~i); val = (val & i) | (prev & ~i);
break; break;
case 0x2a: /* Extended Audio Status/Control */ case 0x2a: /* Extended Audio Status/Control */
i = dev->extid_flags & (AC97_VRA | AC97_DRA | AC97_SPDIF | AC97_VRM); i = ac97_codecs[dev->model].extid_flags & (AC97_VRA | AC97_DRA | AC97_SPDIF | AC97_VRM);
if (dev->extid_flags & AC97_SPDIF) if (ac97_codecs[dev->model].extid_flags & AC97_SPDIF)
i |= AC97_SPSA_MASK << AC97_SPSA_SHIFT; i |= AC97_SPSA_MASK << AC97_SPSA_SHIFT;
i |= (dev->powerdown_mask << 3) & 0x7800; /* multichannel powerdowns */ i |= (ac97_codecs[dev->model].eascr_mask << 11) & 0x7800;
val = (val & i) | (prev & ~i); val = (val & i) | (prev & ~i);
/* Reset DAC sample rates to 48 KHz (96 KHz with DRA) if VRA is being cleared. */ /* Reset DAC sample rates to 48 KHz (96 KHz with DRA) if VRA is being cleared. */
@@ -343,57 +350,57 @@ line_gain:
case 0x32: /* PCM L/R ADC Rate */ case 0x32: /* PCM L/R ADC Rate */
rate: /* Writable only if VRA/VRM is set. */ rate: /* Writable only if VRA/VRM is set. */
i = (reg >= 0x32) ? AC97_VRM : AC97_VRA; i = (reg >= 0x32) ? AC97_VRM : AC97_VRA;
if (!(dev->extid_flags & i)) if (!(ac97_codecs[dev->model].extid_flags & i))
return; return;
/* Limit to supported sample rate range. */ /* Limit to supported sample rate range. */
if (val < dev->min_rate) if (val < ac97_codecs[dev->model].min_rate)
val = dev->min_rate; val = ac97_codecs[dev->model].min_rate;
else if (val > dev->max_rate) else if (val > ac97_codecs[dev->model].max_rate)
val = dev->max_rate; val = ac97_codecs[dev->model].max_rate;
break; break;
case 0x2e: /* PCM Surround DAC Rate */ case 0x2e: /* PCM Surround DAC Rate */
if (!(dev->extid_flags & AC97_SDAC)) if (!(ac97_codecs[dev->model].extid_flags & AC97_SDAC))
return; return;
goto rate; goto rate;
case 0x30: /* PCM LFE DAC Rate */ case 0x30: /* PCM LFE DAC Rate */
if (!(dev->extid_flags & AC97_LDAC)) if (!(ac97_codecs[dev->model].extid_flags & AC97_LDAC))
return; return;
goto rate; goto rate;
case 0x34: /* Mic ADC Rate */ case 0x34: /* Mic ADC Rate */
if (!(dev->reset_flags & AC97_MICPCM)) if (!(ac97_codecs[dev->model].reset_flags & AC97_MICPCM))
return; return;
goto rate; goto rate;
case 0x36: /* Center/LFE Volume */ case 0x36: /* Center/LFE Volume */
if (dev->extid_flags & AC97_LDAC) if (ac97_codecs[dev->model].extid_flags & AC97_LDAC)
i |= 0xbf00; i |= 0xbf00;
if (dev->extid_flags & AC97_CDAC) if (ac97_codecs[dev->model].extid_flags & AC97_CDAC)
i |= 0x00bf; i |= 0x00bf;
val &= i; val &= i;
/* Convert 1xxxxx to 011111 where unsupported, per specification. */ /* Convert 1xxxxx to 011111 where unsupported, per specification. */
if (!(dev->misc_flags & AC97_LFE_6B) && (val & 0x2000)) if (!(ac97_codecs[dev->model].misc_flags & AC97_LFE_6B) && (val & 0x2000))
val = (val & ~0x2000) | 0x1f00; val = (val & ~0x2000) | 0x1f00;
if (!(dev->misc_flags & AC97_CENTER_6B)) if (!(ac97_codecs[dev->model].misc_flags & AC97_CENTER_6B))
goto clamp_5b_r; goto clamp_5b_r;
break; break;
case 0x38: /* Surround Volume */ case 0x38: /* Surround Volume */
if (!(dev->extid_flags & AC97_SDAC)) if (!(ac97_codecs[dev->model].extid_flags & AC97_SDAC))
return; return;
val &= 0xbfbf; val &= 0xbfbf;
/* Convert 1xxxxx to 011111 where unsupported, per specification. */ /* Convert 1xxxxx to 011111 where unsupported, per specification. */
if (!(dev->misc_flags & AC97_SURR_6B)) if (!(ac97_codecs[dev->model].misc_flags & AC97_SURR_6B))
goto clamp_5b; goto clamp_5b;
break; break;
case 0x3a: /* S/PDIF Control */ case 0x3a: /* S/PDIF Control */
if (!(dev->extid_flags & AC97_SPDIF)) if (!(ac97_codecs[dev->model].extid_flags & AC97_SPDIF))
return; return;
break; break;
@@ -411,31 +418,28 @@ rate: /* Writable only if VRA/VRM is set. */
prev = dev->vendor_reg_pages[(i << 3) | ((reg & 0x0e) >> 1)]; prev = dev->vendor_reg_pages[(i << 3) | ((reg & 0x0e) >> 1)];
} }
i <<= 8;
/* fall-through */ /* fall-through */
case 0x5a ... 0x5e: /* Vendor Reserved */ case 0x5a ... 0x5e: /* Vendor Reserved */
case 0x70 ... 0x7a: case 0x70 ... 0x7a:
/* Stop if no vendor-specific registers are defined. */ /* Stop if no vendor-specific registers are defined. */
if (!dev->vendor_regs) if (!ac97_codecs[dev->model].vendor_regs)
return; return;
/* Look for a matching vendor-specific register. */ /* Look for a matching vendor-specific register. */
i |= reg; for (j = 0; ac97_codecs[dev->model].vendor_regs[j].index; j++) {
for (j = 0; dev->vendor_regs[j].index; j++) {
/* If a match was found, inject written bits. */ /* If a match was found, inject written bits. */
if (dev->vendor_regs[j].index == i) { if ((ac97_codecs[dev->model].vendor_regs[j].page == i) && (ac97_codecs[dev->model].vendor_regs[j].index == reg)) {
val = (val & dev->vendor_regs[j].write_mask) | (prev & ~dev->vendor_regs[j].write_mask); val = (val & ac97_codecs[dev->model].vendor_regs[j].write_mask) | (prev & ~ac97_codecs[dev->model].vendor_regs[j].write_mask);
break; break;
} }
} }
/* No match found. */ /* No match found. */
if (!dev->vendor_regs[j].index) if (!ac97_codecs[dev->model].vendor_regs[j].index)
return; return;
/* Redirect a write to page 1+ to the right array, part 2. */ /* Redirect a write to page 1+ to the right array, part 2. */
i >>= 8;
if (i > 0) { if (i > 0) {
dev->vendor_reg_pages[(i << 3) | ((reg & 0x0e) >> 1)] = val; dev->vendor_reg_pages[(i << 3) | ((reg & 0x0e) >> 1)] = val;
return; return;
@@ -465,52 +469,51 @@ ac97_codec_reset(void *priv)
/* Set default level and gain values. */ /* Set default level and gain values. */
dev->regs[0x02 >> 1] = AC97_MUTE; dev->regs[0x02 >> 1] = AC97_MUTE;
if (dev->misc_flags & AC97_AUXOUT) if (ac97_codecs[dev->model].misc_flags & AC97_AUXOUT)
dev->regs[0x04 >> 1] = AC97_MUTE; dev->regs[0x04 >> 1] = AC97_MUTE;
if (dev->misc_flags & AC97_MONOOUT) if (ac97_codecs[dev->model].misc_flags & AC97_MONOOUT)
dev->regs[0x06 >> 1] = AC97_MUTE; dev->regs[0x06 >> 1] = AC97_MUTE;
if (dev->misc_flags & AC97_PHONE) if (ac97_codecs[dev->model].misc_flags & AC97_PHONE)
dev->regs[0x0c >> 1] = AC97_MUTE | 0x0008; dev->regs[0x0c >> 1] = AC97_MUTE | 0x0008;
dev->regs[0x0e >> 1] = AC97_MUTE | 0x0008; /* mic */ 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 */ 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) if (ac97_codecs[dev->model].misc_flags & AC97_VIDEO)
dev->regs[0x14 >> 1] = AC97_MUTE | 0x0808; dev->regs[0x14 >> 1] = AC97_MUTE | 0x0808;
if (dev->misc_flags & AC97_AUXIN) if (ac97_codecs[dev->model].misc_flags & AC97_AUXIN)
dev->regs[0x14 >> 1] = AC97_MUTE | 0x0808; dev->regs[0x14 >> 1] = AC97_MUTE | 0x0808;
dev->regs[0x1c >> 1] = AC97_MUTE; /* record gain */ dev->regs[0x1c >> 1] = AC97_MUTE; /* record gain */
if (dev->reset_flags & AC97_MICPCM) if (ac97_codecs[dev->model].reset_flags & AC97_MICPCM)
dev->regs[0x1e >> 1] = AC97_MUTE; /* mic record gain */ dev->regs[0x1e >> 1] = AC97_MUTE; /* mic record gain */
if (dev->misc_flags & AC97_LDAC) if (ac97_codecs[dev->model].misc_flags & AC97_LDAC)
dev->regs[0x36 >> 1] = AC97_MUTE_L; dev->regs[0x36 >> 1] = AC97_MUTE_L;
if (dev->misc_flags & AC97_CDAC) if (ac97_codecs[dev->model].misc_flags & AC97_CDAC)
dev->regs[0x36 >> 1] |= AC97_MUTE_R; dev->regs[0x36 >> 1] |= AC97_MUTE_R;
if (dev->misc_flags & AC97_SDAC) if (ac97_codecs[dev->model].misc_flags & AC97_SDAC)
dev->regs[0x38 >> 1] = AC97_MUTE_L | AC97_MUTE_R; dev->regs[0x38 >> 1] = AC97_MUTE_L | AC97_MUTE_R;
/* Set flags. */ /* Set flags. */
dev->regs[0x00 >> 1] = dev->reset_flags; dev->regs[0x00 >> 1] = ac97_codecs[dev->model].reset_flags;
dev->regs[0x26 >> 1] = 0x000f; /* codec ready */ dev->regs[0x26 >> 1] = 0x000f; /* codec ready */
dev->regs[0x28 >> 1] = (dev->codec_id << 14) | dev->extid_flags; 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 */ ac97_codec_writew(dev, 0x2a, 0x0000); /* reset variable DAC/ADC sample rates */
i = dev->extid_flags & (AC97_CDAC | AC97_SDAC | AC97_LDAC); 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 */ dev->regs[0x2a >> 1] |= i | (i << 5); /* any additional DACs are ready but powered down */
if (dev->extid_flags & AC97_SPDIF) if (ac97_codecs[dev->model].extid_flags & AC97_SPDIF)
dev->regs[0x2a >> 1] |= AC97_SPCV; dev->regs[0x2a >> 1] |= AC97_SPCV;
if (dev->reset_flags & AC97_MICPCM) if (ac97_codecs[dev->model].reset_flags & AC97_MICPCM)
dev->regs[0x2a >> 1] |= AC97_MADC | AC97_PRL; dev->regs[0x2a >> 1] |= AC97_MADC | AC97_PRL;
/* Set vendor ID. */ /* Set vendor ID. */
dev->regs[0x7c >> 1] = dev->vendor_id >> 16; dev->regs[0x7c >> 1] = ac97_codecs[dev->model].device->local >> 16;
dev->regs[0x7e >> 1] = dev->vendor_id; dev->regs[0x7e >> 1] = ac97_codecs[dev->model].device->local;
/* Set vendor-specific registers. */ /* Set vendor-specific registers. */
if (dev->vendor_regs) { if (ac97_codecs[dev->model].vendor_regs) {
for (uint16_t j = 0; dev->vendor_regs[j].index; j++) { for (i = 0; ac97_codecs[dev->model].vendor_regs[i].index; i++) {
i = (dev->vendor_regs[j].index >> 8) & 0x000f; if (ac97_codecs[dev->model].vendor_regs[i].page > 0)
if (i > 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;
dev->vendor_reg_pages[(i << 3) | (dev->vendor_regs[j].index >> 1)] = dev->vendor_regs[j].value;
else else
dev->regs[dev->vendor_regs[j].index >> 1] = dev->vendor_regs[j].value; dev->regs[ac97_codecs[dev->model].vendor_regs[i].index >> 1] = ac97_codecs[dev->model].vendor_regs[i].value;
} }
} }
} }
@@ -577,15 +580,18 @@ ac97_codec_init(const device_t *info)
ac97_codec_t *dev = malloc(sizeof(ac97_codec_t)); ac97_codec_t *dev = malloc(sizeof(ac97_codec_t));
memset(dev, 0, sizeof(ac97_codec_t)); memset(dev, 0, sizeof(ac97_codec_t));
dev->vendor_id = ac97_codecs[info->local].vendor_id; for (; dev->model < (sizeof(ac97_codecs) / sizeof(ac97_codecs[0])); dev->model++) {
dev->min_rate = ac97_codecs[info->local].min_rate; if (ac97_codecs[dev->model].device->local == info->local)
dev->max_rate = ac97_codecs[info->local].max_rate; break;
dev->reset_flags = ac97_codecs[info->local].reset_flags; }
dev->extid_flags = ac97_codecs[info->local].extid_flags; if (dev->model >= (sizeof(ac97_codecs) / sizeof(ac97_codecs[0]))) {
dev->misc_flags = ac97_codecs[info->local].misc_flags; fatal("AC97 Codec %d: Unknown ID %c%c%c%02X\n", ac97_codec_id, (info->local >> 24) & 0xff, (info->local >> 16) & 0xff, (info->local >> 8) & 0xff, info->local & 0xff);
dev->powerdown_mask = ac97_codecs[info->local].powerdown_mask; free(dev);
dev->vendor_regs = ac97_codecs[info->local].vendor_regs; return NULL;
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); }
ac97_codec_log("AC97 Codec %d: init(%c%c%c%02X)\n", ac97_codec_id,
(ac97_codecs[dev->model].device->local >> 24) & 0xff, (ac97_codecs[dev->model].device->local >> 16) & 0xff,
(ac97_codecs[dev->model].device->local >> 8) & 0xff, ac97_codecs[dev->model].device->local & 0xff);
/* Associate this codec to the current controller. */ /* Associate this codec to the current controller. */
if (!ac97_codec || (ac97_codec_count <= 0)) { if (!ac97_codec || (ac97_codec_count <= 0)) {
@@ -601,20 +607,17 @@ ac97_codec_init(const device_t *info)
dev->codec_id = ac97_codec_id++; dev->codec_id = ac97_codec_id++;
/* Allocate vendor-specific register pages if required. */ /* Allocate vendor-specific register pages if required. */
if (dev->vendor_regs) { if (ac97_codecs[dev->model].vendor_regs) {
/* Get the highest vendor-specific register page number. */ /* Get the highest vendor-specific register page number. */
int i; for (uint16_t i = 0; ac97_codecs[dev->model].vendor_regs[i].index; i++) {
dev->vendor_reg_page_max = 0; if (ac97_codecs[dev->model].vendor_regs[i].page > dev->vendor_reg_page_max)
for (uint16_t j = 0; dev->vendor_regs[j].index; j++) { dev->vendor_reg_page_max = ac97_codecs[dev->model].vendor_regs[i].page;
i = (dev->vendor_regs[j].index >> 8) & 0x000f;
if (i > dev->vendor_reg_page_max)
dev->vendor_reg_page_max = i;
} }
/* Allocate pages 1+. */ /* Allocate pages 1+. */
if (dev->vendor_reg_page_max > 0) { 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); 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; int i = 16 * dev->vendor_reg_page_max;
dev->vendor_reg_pages = (uint16_t *) malloc(i); dev->vendor_reg_pages = (uint16_t *) malloc(i);
memset(dev->vendor_reg_pages, 0, i); memset(dev->vendor_reg_pages, 0, i);
} }
@@ -641,12 +644,13 @@ ac97_codec_close(void *priv)
} }
const device_t * const device_t *
ac97_codec_get(int model) ac97_codec_get(uint32_t id)
{ {
if ((model >= 0) && (model < (sizeof(ac97_codecs) / sizeof(ac97_codecs[0])))) for (int i = 0; i < (sizeof(ac97_codecs) / sizeof(ac97_codecs[0])); i++) {
return ac97_codecs[model].device; if (ac97_codecs[i].device->local == id)
else return ac97_codecs[i].device;
return &cs4297a_device; /* fallback */ }
return &tr28023_device; /* fallback */
} }
const device_t ad1881_device = { const device_t ad1881_device = {
@@ -747,6 +751,20 @@ const device_t stac9721_device = {
.config = NULL .config = NULL
}; };
const device_t tr28023_device = {
.name = "TriTech TR28023 / Creative CT1297",
.internal_name = "tr28023",
.flags = DEVICE_AC97,
.local = AC97_CODEC_TR28023,
.init = ac97_codec_init,
.close = ac97_codec_close,
.reset = ac97_codec_reset,
{ .available = NULL },
.speed_changed = NULL,
.force_redraw = NULL,
.config = NULL
};
const device_t wm9701a_device = { const device_t wm9701a_device = {
.name = "Wolfson WM9701A", .name = "Wolfson WM9701A",
.internal_name = "wm9701a", .internal_name = "wm9701a",

View File

@@ -2060,7 +2060,7 @@ static const device_config_t es1371_config[] = {
// clang-format off // clang-format off
{ {
.name = "codec", .name = "codec",
.description = "CODEC", .description = "Codec",
.type = CONFIG_SELECTION, .type = CONFIG_SELECTION,
.selection = { .selection = {
{ {
@@ -2068,19 +2068,12 @@ static const device_config_t es1371_config[] = {
.value = AC97_CODEC_AK4540 .value = AC97_CODEC_AK4540
}, },
{ {
.description = "Crystal CS4297", .description = "TriTech TR28023 / Creative CT1297",
.value = AC97_CODEC_CS4297 .value = AC97_CODEC_TR28023
}, },
{ { .description = "" }
.description = "Crystal CS4297A",
.value = AC97_CODEC_CS4297A
},
{
.description = "SigmaTel STAC9721",
.value = AC97_CODEC_STAC9721
}
}, },
.default_int = AC97_CODEC_CS4297A .default_int = AC97_CODEC_TR28023
}, },
{ {
.name = "receive_input", .name = "receive_input",