Add multi-channel DMA to CMI8x38
This commit is contained in:
@@ -38,17 +38,18 @@ enum {
|
|||||||
};
|
};
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint8_t id, reg, always_run, playback_enabled;
|
uint8_t id, reg, always_run, playback_enabled, channels;
|
||||||
struct _cmi8x38_ *dev;
|
struct _cmi8x38_ *dev;
|
||||||
|
|
||||||
uint32_t sample_ptr, fifo_pos, fifo_end;
|
uint32_t sample_ptr, fifo_pos, fifo_end;
|
||||||
int32_t frame_count_dma, frame_count_fragment, sample_count_out;
|
int32_t frame_count_dma, frame_count_fragment, sample_count_out;
|
||||||
uint8_t fifo[32], restart;
|
uint8_t fifo[256], restart;
|
||||||
|
|
||||||
int16_t out_l, out_r;
|
int16_t out_fl, out_fr, out_rl, out_rr, out_c, out_lfe;
|
||||||
int vol_l, vol_r, pos;
|
int vol_l, vol_r, pos;
|
||||||
int32_t buffer[SOUNDBUFLEN * 2];
|
int32_t buffer[SOUNDBUFLEN * 2];
|
||||||
uint64_t timer_latch;
|
uint64_t timer_latch;
|
||||||
|
double dma_latch;
|
||||||
|
|
||||||
pc_timer_t dma_timer, poll_timer;
|
pc_timer_t dma_timer, poll_timer;
|
||||||
} cmi8x38_dma_t;
|
} cmi8x38_dma_t;
|
||||||
@@ -356,6 +357,8 @@ cmi8x38_write(uint16_t addr, uint8_t val, void *priv)
|
|||||||
if (dev->type == CMEDIA_CMI8338)
|
if (dev->type == CMEDIA_CMI8338)
|
||||||
return;
|
return;
|
||||||
#endif
|
#endif
|
||||||
|
/* Update sample rate. */
|
||||||
|
dev->io_regs[addr] = val;
|
||||||
cmi8x38_speed_changed(dev);
|
cmi8x38_speed_changed(dev);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -365,8 +368,14 @@ cmi8x38_write(uint16_t addr, uint8_t val, void *priv)
|
|||||||
else
|
else
|
||||||
val &= 0xe0;
|
val &= 0xe0;
|
||||||
|
|
||||||
if (addr == 0x0a)
|
if (addr == 0x0a) {
|
||||||
|
/* Set PCI latency timer if requested. */
|
||||||
dev->pci_regs[0x0d] = (val & 0x80) ? 0x48 : 0x20; /* clearing SETLAT48 is undefined */
|
dev->pci_regs[0x0d] = (val & 0x80) ? 0x48 : 0x20; /* clearing SETLAT48 is undefined */
|
||||||
|
} else {
|
||||||
|
/* Update channel count. */
|
||||||
|
dev->io_regs[addr] = val;
|
||||||
|
cmi8x38_speed_changed(dev);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x0e:
|
case 0x0e:
|
||||||
@@ -384,6 +393,10 @@ cmi8x38_write(uint16_t addr, uint8_t val, void *priv)
|
|||||||
return;
|
return;
|
||||||
else
|
else
|
||||||
val &= 0xf0;
|
val &= 0xf0;
|
||||||
|
|
||||||
|
/* Update channel count. */
|
||||||
|
dev->io_regs[addr] = val;
|
||||||
|
cmi8x38_speed_changed(dev);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x16:
|
case 0x16:
|
||||||
@@ -617,8 +630,8 @@ static void
|
|||||||
cmi8x38_update(cmi8x38_t *dev, cmi8x38_dma_t *dma)
|
cmi8x38_update(cmi8x38_t *dev, cmi8x38_dma_t *dma)
|
||||||
{
|
{
|
||||||
sb_ct1745_mixer_t *mixer = &dev->sb->mixer_sb16;
|
sb_ct1745_mixer_t *mixer = &dev->sb->mixer_sb16;
|
||||||
int32_t l = (dma->out_l * mixer->voice_l) * mixer->master_l,
|
int32_t l = (dma->out_fl * mixer->voice_l) * mixer->master_l,
|
||||||
r = (dma->out_r * mixer->voice_r) * mixer->master_r;
|
r = (dma->out_fr * mixer->voice_r) * mixer->master_r;
|
||||||
|
|
||||||
for (; dma->pos < sound_pos_global; dma->pos++) {
|
for (; dma->pos < sound_pos_global; dma->pos++) {
|
||||||
dma->buffer[dma->pos*2] = l;
|
dma->buffer[dma->pos*2] = l;
|
||||||
@@ -641,7 +654,7 @@ cmi8x38_dma_process(void *priv)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Schedule next run. */
|
/* Schedule next run. */
|
||||||
timer_on_auto(&dma->dma_timer, 10.0);
|
timer_on_auto(&dma->dma_timer, dma->dma_latch);
|
||||||
|
|
||||||
/* Process DMA if it's active, and the FIFO has room or is disabled. */
|
/* Process DMA if it's active, and the FIFO has room or is disabled. */
|
||||||
uint8_t dma_status = dev->io_regs[0x00] >> dma->id;
|
uint8_t dma_status = dev->io_regs[0x00] >> dma->id;
|
||||||
@@ -720,7 +733,7 @@ cmi8x38_poll(void *priv)
|
|||||||
switch ((dev->io_regs[0x08] >> (dma->id << 1)) & 0x03) {
|
switch ((dev->io_regs[0x08] >> (dma->id << 1)) & 0x03) {
|
||||||
case 0x00: /* Mono, 8-bit PCM */
|
case 0x00: /* Mono, 8-bit PCM */
|
||||||
if ((dma->fifo_end - dma->fifo_pos) >= 1) {
|
if ((dma->fifo_end - dma->fifo_pos) >= 1) {
|
||||||
dma->out_l = dma->out_r = (dma->fifo[dma->fifo_pos++ & (sizeof(dma->fifo) - 1)] ^ 0x80) << 8;
|
dma->out_fl = dma->out_fr = (dma->fifo[dma->fifo_pos++ & (sizeof(dma->fifo) - 1)] ^ 0x80) << 8;
|
||||||
dma->sample_count_out--;
|
dma->sample_count_out--;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -728,8 +741,8 @@ cmi8x38_poll(void *priv)
|
|||||||
|
|
||||||
case 0x01: /* Stereo, 8-bit PCM */
|
case 0x01: /* Stereo, 8-bit PCM */
|
||||||
if ((dma->fifo_end - dma->fifo_pos) >= 2) {
|
if ((dma->fifo_end - dma->fifo_pos) >= 2) {
|
||||||
dma->out_l = (dma->fifo[dma->fifo_pos++ & (sizeof(dma->fifo) - 1)] ^ 0x80) << 8;
|
dma->out_fl = (dma->fifo[dma->fifo_pos++ & (sizeof(dma->fifo) - 1)] ^ 0x80) << 8;
|
||||||
dma->out_r = (dma->fifo[dma->fifo_pos++ & (sizeof(dma->fifo) - 1)] ^ 0x80) << 8;
|
dma->out_fr = (dma->fifo[dma->fifo_pos++ & (sizeof(dma->fifo) - 1)] ^ 0x80) << 8;
|
||||||
dma->sample_count_out -= 2;
|
dma->sample_count_out -= 2;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -737,7 +750,7 @@ cmi8x38_poll(void *priv)
|
|||||||
|
|
||||||
case 0x02: /* Mono, 16-bit PCM */
|
case 0x02: /* Mono, 16-bit PCM */
|
||||||
if ((dma->fifo_end - dma->fifo_pos) >= 2) {
|
if ((dma->fifo_end - dma->fifo_pos) >= 2) {
|
||||||
dma->out_l = dma->out_r = *((uint16_t *) &dma->fifo[dma->fifo_pos & (sizeof(dma->fifo) - 1)]);
|
dma->out_fl = dma->out_fr = *((uint16_t *) &dma->fifo[dma->fifo_pos & (sizeof(dma->fifo) - 1)]);
|
||||||
dma->fifo_pos += 2;
|
dma->fifo_pos += 2;
|
||||||
dma->sample_count_out -= 2;
|
dma->sample_count_out -= 2;
|
||||||
return;
|
return;
|
||||||
@@ -745,19 +758,59 @@ cmi8x38_poll(void *priv)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x03: /* Stereo, 16-bit PCM */
|
case 0x03: /* Stereo, 16-bit PCM */
|
||||||
if ((dma->fifo_end - dma->fifo_pos) >= 4) {
|
switch (dma->channels) {
|
||||||
dma->out_l = *((uint16_t *) &dma->fifo[dma->fifo_pos & (sizeof(dma->fifo) - 1)]);
|
case 2:
|
||||||
dma->fifo_pos += 2;
|
if ((dma->fifo_end - dma->fifo_pos) >= 4) {
|
||||||
dma->out_r = *((uint16_t *) &dma->fifo[dma->fifo_pos & (sizeof(dma->fifo) - 1)]);
|
dma->out_fl = *((uint16_t *) &dma->fifo[dma->fifo_pos & (sizeof(dma->fifo) - 1)]);
|
||||||
dma->fifo_pos += 2;
|
dma->fifo_pos += 2;
|
||||||
dma->sample_count_out -= 4;
|
dma->out_fr = *((uint16_t *) &dma->fifo[dma->fifo_pos & (sizeof(dma->fifo) - 1)]);
|
||||||
return;
|
dma->fifo_pos += 2;
|
||||||
|
dma->out_c = dma->out_lfe = dma->out_rl = dma->out_rr = 0;
|
||||||
|
dma->sample_count_out -= 4;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 4:
|
||||||
|
if ((dma->fifo_end - dma->fifo_pos) >= 8) {
|
||||||
|
dma->out_fl = *((uint16_t *) &dma->fifo[dma->fifo_pos & (sizeof(dma->fifo) - 1)]);
|
||||||
|
dma->fifo_pos += 2;
|
||||||
|
dma->out_fr = *((uint16_t *) &dma->fifo[dma->fifo_pos & (sizeof(dma->fifo) - 1)]);
|
||||||
|
dma->fifo_pos += 2;
|
||||||
|
dma->out_rl = *((uint16_t *) &dma->fifo[dma->fifo_pos & (sizeof(dma->fifo) - 1)]);
|
||||||
|
dma->fifo_pos += 2;
|
||||||
|
dma->out_rr = *((uint16_t *) &dma->fifo[dma->fifo_pos & (sizeof(dma->fifo) - 1)]);
|
||||||
|
dma->fifo_pos += 2;
|
||||||
|
dma->out_c = dma->out_lfe = 0;
|
||||||
|
dma->sample_count_out -= 8;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 6:
|
||||||
|
if ((dma->fifo_end - dma->fifo_pos) >= 12) {
|
||||||
|
dma->out_fl = *((uint16_t *) &dma->fifo[dma->fifo_pos & (sizeof(dma->fifo) - 1)]);
|
||||||
|
dma->fifo_pos += 2;
|
||||||
|
dma->out_fr = *((uint16_t *) &dma->fifo[dma->fifo_pos & (sizeof(dma->fifo) - 1)]);
|
||||||
|
dma->fifo_pos += 2;
|
||||||
|
dma->out_c = *((uint16_t *) &dma->fifo[dma->fifo_pos & (sizeof(dma->fifo) - 1)]);
|
||||||
|
dma->fifo_pos += 2;
|
||||||
|
dma->out_lfe = *((uint16_t *) &dma->fifo[dma->fifo_pos & (sizeof(dma->fifo) - 1)]);
|
||||||
|
dma->fifo_pos += 2;
|
||||||
|
dma->out_rl = *((uint16_t *) &dma->fifo[dma->fifo_pos & (sizeof(dma->fifo) - 1)]);
|
||||||
|
dma->fifo_pos += 2;
|
||||||
|
dma->out_rr = *((uint16_t *) &dma->fifo[dma->fifo_pos & (sizeof(dma->fifo) - 1)]);
|
||||||
|
dma->fifo_pos += 2;
|
||||||
|
dma->sample_count_out -= 12;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Feed silence if the FIFO is empty. */
|
/* Feed silence if the FIFO is empty. */
|
||||||
dma->out_l = dma->out_r = 0;
|
dma->out_fl = dma->out_fr = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -788,9 +841,10 @@ cmi8x38_speed_changed(void *priv)
|
|||||||
{
|
{
|
||||||
cmi8x38_t *dev = (cmi8x38_t *) priv;
|
cmi8x38_t *dev = (cmi8x38_t *) priv;
|
||||||
double freq;
|
double freq;
|
||||||
uint8_t dsr = dev->io_regs[0x09], freqreg = dev->io_regs[0x05] >> 2;
|
uint8_t dsr = dev->io_regs[0x09], freqreg = dev->io_regs[0x05] >> 2,
|
||||||
|
chfmt45 = dev->io_regs[0x0b], chfmt6 = dev->io_regs[0x15];
|
||||||
char buf[256];
|
char buf[256];
|
||||||
sprintf(buf, "%02X-%02X", dsr, freqreg);
|
sprintf(buf, "%02X-%02X-%02X-%02X", dsr, freqreg, chfmt45, chfmt6);
|
||||||
|
|
||||||
/* CMI8338 claims the frequency controls are for DAC (playback) and ADC (recording)
|
/* CMI8338 claims the frequency controls are for DAC (playback) and ADC (recording)
|
||||||
respectively, while CMI8738 claims they're for channel 0 and channel 1. The Linux
|
respectively, while CMI8738 claims they're for channel 0 and channel 1. The Linux
|
||||||
@@ -805,12 +859,28 @@ cmi8x38_speed_changed(void *priv)
|
|||||||
case 0x03: freq = 128000.0; break;
|
case 0x03: freq = 128000.0; break;
|
||||||
default: freq = freqs[freqreg & 0x07]; break;
|
default: freq = freqs[freqreg & 0x07]; break;
|
||||||
}
|
}
|
||||||
sprintf(&buf[strlen(buf)], " %d:%X-%X-%.0f", i, dsr & 0x03, freqreg & 0x07, freq);
|
|
||||||
|
/* Set polling timer period. */
|
||||||
|
freq = 1000000.0 / freq;
|
||||||
|
dev->dma[i].timer_latch = (uint64_t) ((double) TIMER_USEC * freq);
|
||||||
|
|
||||||
|
/* Calculate channel count and set DMA timer period. */
|
||||||
|
if (dev->type == CMEDIA_CMI8338) {
|
||||||
|
stereo: dev->dma[i].channels = 2;
|
||||||
|
} else {
|
||||||
|
if (chfmt45 & 0x80)
|
||||||
|
dev->dma[i].channels = (chfmt6 & 0x80) ? 6 : 5;
|
||||||
|
else if (chfmt45 & 0x20)
|
||||||
|
dev->dma[i].channels = 4;
|
||||||
|
else
|
||||||
|
goto stereo;
|
||||||
|
}
|
||||||
|
dev->dma[i].dma_latch = freq / dev->dma[i].channels; /* frequency / approximately(dwords * 2) */
|
||||||
|
|
||||||
|
/* Shift sample rate configuration registers. */
|
||||||
|
sprintf(&buf[strlen(buf)], " %d:%X-%X-%.0f-%dC", i, dsr & 0x03, freqreg & 0x07, 1000000.0 / freq, dev->dma[i].channels);
|
||||||
dsr >>= 2;
|
dsr >>= 2;
|
||||||
freqreg >>= 3;
|
freqreg >>= 3;
|
||||||
|
|
||||||
/* Set period. */
|
|
||||||
dev->dma[i].timer_latch = (uint64_t) ((double) TIMER_USEC * (1000000.0 / freq));
|
|
||||||
}
|
}
|
||||||
ui_sb_bugui(buf);
|
ui_sb_bugui(buf);
|
||||||
}
|
}
|
||||||
@@ -867,7 +937,7 @@ cmi8x38_init(const device_t *info)
|
|||||||
|
|
||||||
/* Set the chip type. */
|
/* Set the chip type. */
|
||||||
cmi8x38_log("CMI8x38: init(%03X)\n", info->local);
|
cmi8x38_log("CMI8x38: init(%03X)\n", info->local);
|
||||||
dev->type = info->local;
|
dev->type = info->local & 0xff;
|
||||||
|
|
||||||
/* Initialize Sound Blaster 16. */
|
/* Initialize Sound Blaster 16. */
|
||||||
dev->sb = device_add_inst(&sb_16_compat_device, 1);
|
dev->sb = device_add_inst(&sb_16_compat_device, 1);
|
||||||
|
Reference in New Issue
Block a user