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