Fixed clock rate calculation (multiple bugs).

Fixed sound stutter after start.
Added generic macros to deal with SIS7012 and Intel differences.
Fixed SIS7012 init (PICB register was written, instead of SR).


git-svn-id: file:///srv/svn/repos/haiku/trunk/current@3290 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
beveloper
2003-05-23 20:44:12 +00:00
parent f4949fca43
commit acd429ac89
5 changed files with 67 additions and 58 deletions
@@ -241,6 +241,16 @@ ac97_attach(ac97_dev **_dev, codec_reg_read reg_read, codec_reg_write reg_write,
void void
ac97_detach(ac97_dev *dev) ac97_detach(ac97_dev *dev)
{ {
/* Mute everything */
ac97_reg_update_bits(dev, AC97_CENTER_LFE_VOLUME, 0x8000, 0x8000);
ac97_reg_update_bits(dev, AC97_SURR_VOLUME, 0x8000, 0x8000);
ac97_reg_update_bits(dev, AC97_MASTER_VOLUME, 0x8000, 0x8000);
ac97_reg_update_bits(dev, AC97_AUX_OUT_VOLUME, 0x8000, 0x8000);
ac97_reg_update_bits(dev, AC97_MONO_VOLUME, 0x8000, 0x8000);
ac97_reg_update_bits(dev, AC97_PCM_OUT_VOLUME, 0x8000, 0x8000);
ac97_reg_update_bits(dev, AC97_CD_VOLUME, 0x8000, 0x8000);
ac97_reg_update_bits(dev, AC97_LINE_IN_VOLUME, 0x8000, 0x8000);
free(dev); free(dev);
} }
@@ -65,9 +65,7 @@ status_t probe_device(void)
config->mmbar = 0; config->mmbar = 0;
config->mbbar = 0; config->mbbar = 0;
config->irq = 0; config->irq = 0;
config->sample_size = 2; config->type = TYPE_DEFAULT;
config->swap_reg = false;
config->type = 0;
config->log_mmbar = 0; config->log_mmbar = 0;
config->log_mbbar = 0; config->log_mbbar = 0;
config->area_mmbar = -1; config->area_mmbar = -1;
@@ -94,8 +92,7 @@ status_t probe_device(void)
config->type = TYPE_ICH4; config->type = TYPE_ICH4;
} else if (pciinfo->vendor_id == 0x1039 && pciinfo->device_id == 0x7012) { /* verified */ } else if (pciinfo->vendor_id == 0x1039 && pciinfo->device_id == 0x7012) { /* verified */
config->name = "SiS SI7012"; config->name = "SiS SI7012";
config->swap_reg = true; config->type = TYPE_SIS7012;
config->sample_size = 1;
} else if (pciinfo->vendor_id == 0x10DE && pciinfo->device_id == 0x01B1) { } else if (pciinfo->vendor_id == 0x10DE && pciinfo->device_id == 0x01B1) {
config->name = "NVIDIA nForce (MCP)"; config->name = "NVIDIA nForce (MCP)";
} else if (pciinfo->vendor_id == 0x10DE && pciinfo->device_id == 0x006A) { } else if (pciinfo->vendor_id == 0x10DE && pciinfo->device_id == 0x006A) {
@@ -37,8 +37,6 @@ typedef struct
uint32 nabmbar; uint32 nabmbar;
uint32 irq; uint32 irq;
uint32 type; uint32 type;
int sample_size;
bool swap_reg;
uint32 mmbar; // ich4 uint32 mmbar; // ich4
uint32 mbbar; // ich4 uint32 mbbar; // ich4
void * log_mmbar; // ich4 void * log_mmbar; // ich4
@@ -56,6 +54,14 @@ extern device_config *config;
status_t probe_device(void); status_t probe_device(void);
#define TYPE_DEFAULT 0x00
#define TYPE_ICH4 0x01 #define TYPE_ICH4 0x01
#define TYPE_SIS7012 0x02
/* The SIS7012 chipset has SR and PICB registers swapped when compared to Intel */
#define GET_REG_X_PICB(cfg) (((cfg)->type == TYPE_SIS7012) ? _ICH_REG_X_SR : _ICH_REG_X_PICB)
#define GET_REG_X_SR(cfg) (((cfg)->type == TYPE_SIS7012) ? _ICH_REG_X_PICB : _ICH_REG_X_SR)
/* Each 16 bit sample is counted as 1 in SIS7012 chipsets, 2 in all others */
#define GET_HW_SAMPLE_SIZE(cfg) (((cfg)->type == TYPE_SIS7012) ? 1 : 2)
#endif #endif
@@ -49,8 +49,8 @@ enum ICH_X_REGISTER_OFFSETS /* add base address to get the PI, PO or MC reg */
ICH_REG_X_BDBAR = 0x00, ICH_REG_X_BDBAR = 0x00,
ICH_REG_X_CIV = 0x04, ICH_REG_X_CIV = 0x04,
ICH_REG_X_LVI = 0x05, ICH_REG_X_LVI = 0x05,
ICH_REG_X_SR = 0x06, _ICH_REG_X_SR = 0x06, /* use GET_REG_X_SR() macro from config.h */
ICH_REG_X_PICB = 0x08, _ICH_REG_X_PICB = 0x08, /* use GET_REG_X_PICB() macro from config.h */
ICH_REG_X_PIV = 0x0A, ICH_REG_X_PIV = 0x0A,
ICH_REG_X_CR = 0x0B ICH_REG_X_CR = 0x0B
}; };
+45 -49
View File
@@ -58,6 +58,7 @@ void chan_free_resources(void);
status_t map_io_memory(void); status_t map_io_memory(void);
status_t unmap_io_memory(void); status_t unmap_io_memory(void);
int32 int_thread(void *data); int32 int_thread(void *data);
void stop_chan(ich_chan *chan);
#if DEBUG > 2 #if DEBUG > 2
void dump_chan(ich_chan *chan) void dump_chan(ich_chan *chan)
@@ -100,7 +101,7 @@ void init_chan(ich_chan *chan)
chan->buffer[i] = ((char *)chan->buffer_log_base) + (i % BUFFER_COUNT) * BUFFER_SIZE; chan->buffer[i] = ((char *)chan->buffer_log_base) + (i % BUFFER_COUNT) * BUFFER_SIZE;
chan->bd[i] = (ich_bd *) (((char *)chan->bd_log_base) + i * sizeof(ich_bd)); chan->bd[i] = (ich_bd *) (((char *)chan->bd_log_base) + i * sizeof(ich_bd));
chan->bd[i]->buffer = ((uint32)chan->buffer_phy_base) + (i % BUFFER_COUNT) * BUFFER_SIZE; chan->bd[i]->buffer = ((uint32)chan->buffer_phy_base) + (i % BUFFER_COUNT) * BUFFER_SIZE;
chan->bd[i]->length = BUFFER_SIZE / config->sample_size; chan->bd[i]->length = BUFFER_SIZE / GET_HW_SAMPLE_SIZE(config);
chan->bd[i]->flags = ICH_BDC_FLAG_IOC; chan->bd[i]->flags = ICH_BDC_FLAG_IOC;
} }
@@ -128,7 +129,7 @@ void start_chan(ich_chan *chan)
chan->played_real_time = 0; chan->played_real_time = 0;
// step 1: clear status bits // step 1: clear status bits
ich_reg_write_16(chan->regbase + ICH_REG_X_SR, SR_FIFOE | SR_BCIS | SR_LVBCI); ich_reg_write_16(chan->regbase + GET_REG_X_SR(config), SR_FIFOE | SR_BCIS | SR_LVBCI);
// step 2: prepare buffer transfer // step 2: prepare buffer transfer
ich_reg_write_8(chan->regbase + ICH_REG_X_LVI, (civ + 28) % ICH_BD_COUNT); ich_reg_write_8(chan->regbase + ICH_REG_X_LVI, (civ + 28) % ICH_BD_COUNT);
// step 3: enable interrupts & busmaster transfer // step 3: enable interrupts & busmaster transfer
@@ -137,6 +138,12 @@ void start_chan(ich_chan *chan)
chan->running = true; chan->running = true;
} }
void stop_chan(ich_chan *chan)
{
ich_reg_write_8(chan->regbase + ICH_REG_X_CR, ich_reg_read_8(chan->regbase + ICH_REG_X_CR) & ~CR_RPBM);
chan->running = false;
snooze(10000); // 10 ms
}
void reset_chan(ich_chan *chan) void reset_chan(ich_chan *chan)
{ {
@@ -175,7 +182,7 @@ bool interrupt_test(void)
install_io_interrupt_handler(config->irq, ich_test_int, testresult, 0); install_io_interrupt_handler(config->irq, ich_test_int, testresult, 0);
// clear status bits // clear status bits
ich_reg_write_16(chan_po->regbase + ICH_REG_X_SR, SR_FIFOE | SR_BCIS | SR_LVBCI); ich_reg_write_16(chan_po->regbase + GET_REG_X_SR(config), SR_FIFOE | SR_BCIS | SR_LVBCI);
// start transfer of 1 buffer // start transfer of 1 buffer
ich_reg_write_8(chan_po->regbase + ICH_REG_X_LVI, (ich_reg_read_8(chan_po->regbase + ICH_REG_X_LVI) + 1) % ICH_BD_COUNT); ich_reg_write_8(chan_po->regbase + ICH_REG_X_LVI, (ich_reg_read_8(chan_po->regbase + ICH_REG_X_LVI) + 1) % ICH_BD_COUNT);
// enable interrupts & busmaster transfer // enable interrupts & busmaster transfer
@@ -322,15 +329,15 @@ dump_hardware_regs()
LOG(("PI ICH_REG_X_BDBAR = %#x\n",ich_reg_read_32(ICH_REG_X_BDBAR + ICH_REG_PI_BASE))); LOG(("PI ICH_REG_X_BDBAR = %#x\n",ich_reg_read_32(ICH_REG_X_BDBAR + ICH_REG_PI_BASE)));
LOG(("PI ICH_REG_X_CIV = %#x\n",ich_reg_read_8(ICH_REG_X_CIV + ICH_REG_PI_BASE))); LOG(("PI ICH_REG_X_CIV = %#x\n",ich_reg_read_8(ICH_REG_X_CIV + ICH_REG_PI_BASE)));
LOG(("PI ICH_REG_X_LVI = %#x\n",ich_reg_read_8(ICH_REG_X_LVI + ICH_REG_PI_BASE))); LOG(("PI ICH_REG_X_LVI = %#x\n",ich_reg_read_8(ICH_REG_X_LVI + ICH_REG_PI_BASE)));
LOG(("PI ICH_REG_X_SR = %#x\n",ich_reg_read_16(ICH_REG_X_SR + ICH_REG_PI_BASE))); LOG(("PI REG_X_SR = %#x\n",ich_reg_read_16(GET_REG_X_SR(config) + ICH_REG_PI_BASE)));
LOG(("PI ICH_REG_X_PICB = %#x\n",ich_reg_read_16(ICH_REG_X_PICB + ICH_REG_PI_BASE))); LOG(("PI REG_X_PICB = %#x\n",ich_reg_read_16(GET_REG_X_PICB(config) + ICH_REG_PI_BASE)));
LOG(("PI ICH_REG_X_PIV = %#x\n",ich_reg_read_8(ICH_REG_X_PIV + ICH_REG_PI_BASE))); LOG(("PI ICH_REG_X_PIV = %#x\n",ich_reg_read_8(ICH_REG_X_PIV + ICH_REG_PI_BASE)));
LOG(("PI ICH_REG_X_CR = %#x\n",ich_reg_read_8(ICH_REG_X_CR + ICH_REG_PI_BASE))); LOG(("PI ICH_REG_X_CR = %#x\n",ich_reg_read_8(ICH_REG_X_CR + ICH_REG_PI_BASE)));
LOG(("PO ICH_REG_X_BDBAR = %#x\n",ich_reg_read_32(ICH_REG_X_BDBAR + ICH_REG_PO_BASE))); LOG(("PO ICH_REG_X_BDBAR = %#x\n",ich_reg_read_32(ICH_REG_X_BDBAR + ICH_REG_PO_BASE)));
LOG(("PO ICH_REG_X_CIV = %#x\n",ich_reg_read_8(ICH_REG_X_CIV + ICH_REG_PO_BASE))); LOG(("PO ICH_REG_X_CIV = %#x\n",ich_reg_read_8(ICH_REG_X_CIV + ICH_REG_PO_BASE)));
LOG(("PO ICH_REG_X_LVI = %#x\n",ich_reg_read_8(ICH_REG_X_LVI + ICH_REG_PO_BASE))); LOG(("PO ICH_REG_X_LVI = %#x\n",ich_reg_read_8(ICH_REG_X_LVI + ICH_REG_PO_BASE)));
LOG(("PO ICH_REG_X_SR = %#x\n",ich_reg_read_16(ICH_REG_X_SR + ICH_REG_PO_BASE))); LOG(("PO REG_X_SR = %#x\n",ich_reg_read_16(GET_REG_X_SR(config) + ICH_REG_PO_BASE)));
LOG(("PO ICH_REG_X_PICB = %#x\n",ich_reg_read_16(ICH_REG_X_PICB + ICH_REG_PO_BASE))); LOG(("PO REG_X_PICB = %#x\n",ich_reg_read_16(GET_REG_X_PICB(config) + ICH_REG_PO_BASE)));
LOG(("PO ICH_REG_X_PIV = %#x\n",ich_reg_read_8(ICH_REG_X_PIV + ICH_REG_PO_BASE))); LOG(("PO ICH_REG_X_PIV = %#x\n",ich_reg_read_8(ICH_REG_X_PIV + ICH_REG_PO_BASE)));
LOG(("PO ICH_REG_X_CR = %#x\n",ich_reg_read_8(ICH_REG_X_CR + ICH_REG_PO_BASE))); LOG(("PO ICH_REG_X_CR = %#x\n",ich_reg_read_8(ICH_REG_X_CR + ICH_REG_PO_BASE)));
} }
@@ -359,24 +366,31 @@ ich_clock_get()
do { do {
civ1 = ich_reg_read_8 (ICH_REG_PO_BASE + ICH_REG_X_CIV); civ1 = ich_reg_read_8 (ICH_REG_PO_BASE + ICH_REG_X_CIV);
picb = ich_reg_read_16(ICH_REG_PO_BASE + (config->swap_reg ? ICH_REG_X_SR : ICH_REG_X_PICB)); picb = ich_reg_read_16(ICH_REG_PO_BASE + GET_REG_X_PICB(config));
civ2 = ich_reg_read_8 (ICH_REG_PO_BASE + ICH_REG_X_CIV); civ2 = ich_reg_read_8 (ICH_REG_PO_BASE + ICH_REG_X_CIV);
} while (civ1 != civ2); } while (civ1 != civ2);
starttime = system_time(); starttime = system_time();
startframe = (civ1 * (BUFFER_SIZE / 4)); startframe = (civ1 * (BUFFER_SIZE / 4));
if (picb < (BUFFER_SIZE / 4)) picb = (picb * GET_HW_SAMPLE_SIZE(config)) / 4; /* convert picb into frames */
if (picb <= (BUFFER_SIZE / 4))
startframe += (BUFFER_SIZE / 4) - picb; startframe += (BUFFER_SIZE / 4) - picb;
else
LOG(("wrong picb = %d, max %d\n", picb, BUFFER_SIZE / 4));
snooze(100000); snooze(40000);
do { do {
civ1 = ich_reg_read_8 (ICH_REG_PO_BASE + ICH_REG_X_CIV); civ1 = ich_reg_read_8 (ICH_REG_PO_BASE + ICH_REG_X_CIV);
picb = ich_reg_read_16(ICH_REG_PO_BASE + (config->swap_reg ? ICH_REG_X_SR : ICH_REG_X_PICB)); picb = ich_reg_read_16(ICH_REG_PO_BASE + GET_REG_X_PICB(config));
civ2 = ich_reg_read_8 (ICH_REG_PO_BASE + ICH_REG_X_CIV); civ2 = ich_reg_read_8 (ICH_REG_PO_BASE + ICH_REG_X_CIV);
} while (civ1 != civ2); } while (civ1 != civ2);
stoptime = system_time(); stoptime = system_time();
stopframe = (civ1 * (BUFFER_SIZE / 4)); stopframe = (civ1 * (BUFFER_SIZE / 4));
if (picb < (BUFFER_SIZE / 4)) picb = (picb * GET_HW_SAMPLE_SIZE(config)) / 4; /* convert picb into frames */
if (picb <= (BUFFER_SIZE / 4))
stopframe += (BUFFER_SIZE / 4) - picb; stopframe += (BUFFER_SIZE / 4) - picb;
else
LOG(("wrong picb = %d, max %d\n", picb, BUFFER_SIZE / 4));
if (stopframe < startframe) if (stopframe < startframe)
stopframe += ICH_BD_COUNT * (BUFFER_SIZE / 4); stopframe += ICH_BD_COUNT * (BUFFER_SIZE / 4);
@@ -388,9 +402,6 @@ ich_clock_get()
void void
ich_clock_calibrate() ich_clock_calibrate()
{ {
int i, j;
#define RATES 20
uint32 rates[RATES];
uint32 rate; uint32 rate;
if (!ac97_set_rate(config->ac97, AC97_PCM_FRONT_DAC_RATE, 48000)) { if (!ac97_set_rate(config->ac97, AC97_PCM_FRONT_DAC_RATE, 48000)) {
@@ -398,35 +409,18 @@ ich_clock_calibrate()
return; return;
} }
if (!chan_po->running) start_chan(chan_po);
start_chan(chan_po);
for (i = 0; i < RATES; i++) #if DEBUG > 0
rates[i] = ich_clock_get(); {
int i;
// for (i = 0; i < RATES; i++) for (i = 0; i < 20; i++)
// LOG(("ich_clock_calibrate: rate %ld\n", rates[i])); LOG(("ich_clock_calibrate: rate %ld\n", ich_clock_get()));
// LOG(("ich_clock_calibrate: =========\n"));
for (i = 0; i < RATES; i++) {
for (j = 0; j < (RATES - 1); j++) {
if (rates[j] > rates[j + 1]) {
uint32 temp = rates[j];
rates[j] = rates[j + 1];
rates[j + 1] = temp;
}
} }
} #endif
for (i = 0; i < RATES; i++) rate = ich_clock_get();
LOG(("ich_clock_calibrate: rate %ld\n", rates[i])); LOG(("ich_clock_calibrate: rate %ld\n", rate));
rate = 0;
for (i = 3; i < (RATES - 3); i++)
rate += rates[i];
rate /= (RATES - 6);
LOG(("ich_clock_calibrate: finished, rate %ld\n", rate));
if (rate > (44100 - 1000) && rate < (44100 + 1000)) { if (rate > (44100 - 1000) && rate < (44100 + 1000)) {
LOG(("ich_clock_calibrate: setting clock 44100\n")); LOG(("ich_clock_calibrate: setting clock 44100\n"));
@@ -448,6 +442,8 @@ ich_clock_calibrate()
ac97_set_clock(config->ac97, 48000); ac97_set_clock(config->ac97, 48000);
ac97_set_rate(config->ac97, AC97_PCM_FRONT_DAC_RATE, 48000); ac97_set_rate(config->ac97, AC97_PCM_FRONT_DAC_RATE, 48000);
} }
stop_chan(chan_po);
} }
status_t status_t
@@ -720,7 +716,7 @@ int32 ich_test_int(void *check)
* pointed to by check to true. * pointed to by check to true.
*/ */
uint32 sta = ich_reg_read_32(ICH_REG_GLOB_STA); uint32 sta = ich_reg_read_32(ICH_REG_GLOB_STA);
uint16 sr = ich_reg_read_16(chan_po->regbase + (config->swap_reg ? ICH_REG_X_PICB : ICH_REG_X_SR)); uint16 sr = ich_reg_read_16(chan_po->regbase + GET_REG_X_SR(config));
if ((sta & STA_INTMASK) == 0) if ((sta & STA_INTMASK) == 0)
return B_UNHANDLED_INTERRUPT; return B_UNHANDLED_INTERRUPT;
@@ -729,7 +725,7 @@ int32 ich_test_int(void *check)
*(bool *)check = true; // notify *(bool *)check = true; // notify
sr &= SR_FIFOE | SR_BCIS | SR_LVBCI; sr &= SR_FIFOE | SR_BCIS | SR_LVBCI;
ich_reg_write_16(chan_po->regbase + (config->swap_reg ? ICH_REG_X_PICB : ICH_REG_X_SR),sr); ich_reg_write_16(chan_po->regbase + GET_REG_X_SR(config), sr);
return B_HANDLED_INTERRUPT; return B_HANDLED_INTERRUPT;
} }
@@ -748,7 +744,7 @@ int32 ich_int(void *unused)
} }
if (sta & STA_POINT) { // pcm-out if (sta & STA_POINT) { // pcm-out
uint16 sr = ich_reg_read_16(chan_po->regbase + (config->swap_reg ? ICH_REG_X_PICB : ICH_REG_X_SR)); uint16 sr = ich_reg_read_16(chan_po->regbase + GET_REG_X_SR(config));
sr &= SR_FIFOE | SR_BCIS | SR_LVBCI; sr &= SR_FIFOE | SR_BCIS | SR_LVBCI;
if (sr & SR_BCIS) { // a buffer completed if (sr & SR_BCIS) { // a buffer completed
@@ -780,11 +776,11 @@ int32 ich_int(void *unused)
release_sem_etc(chan_po->buffer_ready_sem,1,B_DO_NOT_RESCHEDULE); release_sem_etc(chan_po->buffer_ready_sem,1,B_DO_NOT_RESCHEDULE);
} }
ich_reg_write_16(chan_po->regbase + (config->swap_reg ? ICH_REG_X_PICB : ICH_REG_X_SR),sr); ich_reg_write_16(chan_po->regbase + GET_REG_X_SR(config), sr);
} }
if (sta & STA_PIINT) { // pcm-in if (sta & STA_PIINT) { // pcm-in
uint16 sr = ich_reg_read_16(chan_pi->regbase + (config->swap_reg ? ICH_REG_X_PICB : ICH_REG_X_SR)); uint16 sr = ich_reg_read_16(chan_pi->regbase + GET_REG_X_SR(config));
sr &= SR_FIFOE | SR_BCIS | SR_LVBCI; sr &= SR_FIFOE | SR_BCIS | SR_LVBCI;
if (sr & SR_BCIS) { // a buffer completed if (sr & SR_BCIS) { // a buffer completed
@@ -816,16 +812,16 @@ int32 ich_int(void *unused)
release_sem_etc(chan_pi->buffer_ready_sem,1,B_DO_NOT_RESCHEDULE); release_sem_etc(chan_pi->buffer_ready_sem,1,B_DO_NOT_RESCHEDULE);
} }
ich_reg_write_16(chan_pi->regbase + (config->swap_reg ? ICH_REG_X_PICB : ICH_REG_X_SR),sr); ich_reg_write_16(chan_pi->regbase + GET_REG_X_SR(config), sr);
} }
if (sta & STA_MINT) { // mic-in if (sta & STA_MINT) { // mic-in
uint16 sr = ich_reg_read_16(chan_mc->regbase + (config->swap_reg ? ICH_REG_X_PICB : ICH_REG_X_SR)); uint16 sr = ich_reg_read_16(chan_mc->regbase + GET_REG_X_SR(config));
sr &= SR_FIFOE | SR_BCIS | SR_LVBCI; sr &= SR_FIFOE | SR_BCIS | SR_LVBCI;
if (sr & SR_BCIS) { // a buffer completed if (sr & SR_BCIS) { // a buffer completed
release_sem_etc(chan_mc->buffer_ready_sem,1,B_DO_NOT_RESCHEDULE); release_sem_etc(chan_mc->buffer_ready_sem,1,B_DO_NOT_RESCHEDULE);
} }
ich_reg_write_16(chan_mc->regbase + (config->swap_reg ? ICH_REG_X_PICB : ICH_REG_X_SR),sr); ich_reg_write_16(chan_mc->regbase + GET_REG_X_SR(config), sr);
} }
return B_HANDLED_INTERRUPT; return B_HANDLED_INTERRUPT;
} }