Crystal CS4237, part 2
This commit is contained in:
@@ -36,13 +36,35 @@
|
||||
|
||||
|
||||
enum {
|
||||
CRYSTAL_CS4237B = 37
|
||||
CRYSTAL_CS4237B = 0xc8
|
||||
};
|
||||
enum {
|
||||
CRYSTAL_SLAM_NONE = 0,
|
||||
CRYSTAL_SLAM_INDEX,
|
||||
CRYSTAL_SLAM_BYTE1,
|
||||
CRYSTAL_SLAM_BYTE2
|
||||
};
|
||||
|
||||
|
||||
static const uint8_t cs4237b_eeprom[384] = {
|
||||
0x55, 0xbb, 0x00, 0x00, 0x00, 0x03, 0x80, 0x80, 0x0b, 0x20, 0x04, 0x08, 0x10, 0x80, 0x00, 0x00, 0x00, 0x48, 0x75, 0xb9, 0xfc, 0x10, 0x03, /* CS4237B stuff */
|
||||
static const uint8_t slam_init_key[32] = { 0x96, 0x35, 0x9A, 0xCD, 0xE6, 0xF3, 0x79, 0xBC,
|
||||
0x5E, 0xAF, 0x57, 0x2B, 0x15, 0x8A, 0xC5, 0xE2,
|
||||
0xF1, 0xF8, 0x7C, 0x3E, 0x9F, 0x4F, 0x27, 0x13,
|
||||
0x09, 0x84, 0x42, 0xA1, 0xD0, 0x68, 0x34, 0x1A };
|
||||
static const uint8_t cs4237b_eeprom[] = {
|
||||
/* CS4237B configuration */
|
||||
0x55, 0xbb, /* magic */
|
||||
0x00, 0x00, /* length */
|
||||
0x00, 0x03, /* CD-ROM and modem decode */
|
||||
0x80, /* misc. config */
|
||||
0x80, /* global config */
|
||||
0x0b, /* chip ID */
|
||||
0x20, 0x04, 0x08, 0x10, 0x80, 0x00, 0x00, /* reserved */
|
||||
0x00, /* external decode length */
|
||||
0x48, /* reserved */
|
||||
0x75, 0xb9, 0xfc, /* IRQ routing */
|
||||
0x10, 0x03, /* DMA routing */
|
||||
|
||||
/* PnP resources */
|
||||
0x0e, 0x63, 0x42, 0x37, 0x00, 0x00, 0x00, 0x00, 0x00, /* CSC4237, dummy checksum (filled in by isapnp_add_card) */
|
||||
0x0a, 0x10, 0x01, /* PnP version 1.0, vendor version 0.1 */
|
||||
0x82, 0x0e, 0x00, 'C', 'r', 'y', 's', 't', 'a', 'l', ' ', 'C', 'o', 'd', 'e' ,'c', 0x00, /* ANSI identifier */
|
||||
@@ -106,11 +128,19 @@ typedef struct cs423x_t
|
||||
sb_t *sb;
|
||||
void *i2c, *eeprom;
|
||||
|
||||
uint16_t wss_base, opl_base, sb_base, ctrl_base, ram_addr, eeprom_size;
|
||||
uint8_t regs[8], indirect_regs[16], eeprom_data[2048], ram_dl;
|
||||
uint16_t wss_base, opl_base, sb_base, ctrl_base, ram_addr, eeprom_size: 11;
|
||||
uint8_t type, regs[8], indirect_regs[16], eeprom_data[2048], ram_dl;
|
||||
|
||||
uint8_t key_pos: 5, enable_slam: 1, slam_state: 2, slam_ld, slam_reg;
|
||||
isapnp_device_config_t *slam_config;
|
||||
} cs423x_t;
|
||||
|
||||
|
||||
static void cs423x_slam_remap(cs423x_t *dev, uint8_t enable);
|
||||
static void cs423x_ctxswitch_write(uint16_t addr, uint8_t val, void *priv);
|
||||
static void cs423x_pnp_config_changed(uint8_t ld, isapnp_device_config_t *config, void *priv);
|
||||
|
||||
|
||||
static uint8_t
|
||||
cs423x_read(uint16_t port, void *priv)
|
||||
{
|
||||
@@ -130,12 +160,15 @@ cs423x_read(uint16_t port, void *priv)
|
||||
break;
|
||||
|
||||
case 7: /* Global Status */
|
||||
ret = 0x00;
|
||||
if (dev->sb->mpu->state.irq_pending)
|
||||
/* Context switching: take active context and interrupt flag, then clear interrupt flag. */
|
||||
ret &= 0xc0;
|
||||
dev->regs[7] &= 0x80;
|
||||
|
||||
if (dev->sb->mpu->state.irq_pending) /* MPU interrupt */
|
||||
ret |= 0x08;
|
||||
if (dev->ad1848.regs[10] & 2)
|
||||
if (dev->ad1848.regs[10] & 2) /* WSS interrupt */
|
||||
ret |= 0x10;
|
||||
if (dev->sb->dsp.sb_irq8 || dev->sb->dsp.sb_irq16 || dev->sb->dsp.sb_irq401)
|
||||
if (dev->sb->dsp.sb_irq8 || dev->sb->dsp.sb_irq16 || dev->sb->dsp.sb_irq401) /* SBPro interrupt */
|
||||
ret |= 0x20;
|
||||
}
|
||||
|
||||
@@ -148,6 +181,7 @@ cs423x_write(uint16_t port, uint8_t val, void *priv)
|
||||
{
|
||||
cs423x_t *dev = (cs423x_t *) priv;
|
||||
uint8_t reg = port & 7;
|
||||
uint16_t eeprom_addr;
|
||||
|
||||
switch (reg) {
|
||||
case 1: /* EEPROM Interface */
|
||||
@@ -199,7 +233,16 @@ cs423x_write(uint16_t port, uint8_t val, void *priv)
|
||||
isapnp_enable_card(dev->pnp_card, 0);
|
||||
break;
|
||||
|
||||
/* TODO: Crystal's PnP bypass method? */
|
||||
case 0x56: /* Disable Crystal Key */
|
||||
cs423x_slam_remap(dev, 0);
|
||||
break;
|
||||
|
||||
case 0x57: /* Jump to ROM */
|
||||
break;
|
||||
|
||||
case 0x5a: /* Update Hardware Configuration Data */
|
||||
isapnp_update_card_rom(dev->pnp_card, dev->eeprom_data + 23, dev->eeprom_size - 23);
|
||||
break;
|
||||
|
||||
case 0xaa: /* Download RAM */
|
||||
dev->ram_dl = 1;
|
||||
@@ -220,8 +263,12 @@ cs423x_write(uint16_t port, uint8_t val, void *priv)
|
||||
case 3: /* data */
|
||||
/* The only documented RAM region is 0x4000 (384 bytes in size), for
|
||||
loading the chip's configuration and PnP ROM without an EEPROM. */
|
||||
if ((dev->ram_addr >= 0x4000) && (dev->ram_addr < 0x4180))
|
||||
dev->eeprom_data[dev->ram_addr - 0x3ffc] = val; /* skip first 4 bytes (EEPROM header) */
|
||||
if ((dev->ram_addr >= 0x4000) && (dev->ram_addr < 0x4180)) {
|
||||
eeprom_addr = dev->ram_addr - 0x3ffc; /* skip first 4 bytes (header on real EEPROM) */
|
||||
dev->eeprom_data[eeprom_addr] = val;
|
||||
if (dev->eeprom_size < ++eeprom_addr) /* update EEPROM size if required */
|
||||
dev->eeprom_size = eeprom_addr;
|
||||
}
|
||||
dev->ram_addr++;
|
||||
break;
|
||||
}
|
||||
@@ -240,25 +287,149 @@ cs423x_write(uint16_t port, uint8_t val, void *priv)
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
cs423x_slam_write(uint16_t addr, uint8_t val, void *priv)
|
||||
{
|
||||
cs423x_t *dev = (cs423x_t *) priv;
|
||||
uint8_t idx;
|
||||
|
||||
switch (dev->slam_state) {
|
||||
case CRYSTAL_SLAM_NONE:
|
||||
/* Not in SLAM: read and compare Crystal key. */
|
||||
if (val == slam_init_key[dev->key_pos]) {
|
||||
dev->key_pos++;
|
||||
/* Was the key successfully written? */
|
||||
if (!dev->key_pos) {
|
||||
/* Discard any pending logical device configuration, just to be safe. */
|
||||
if (dev->slam_config) {
|
||||
free(dev->slam_config);
|
||||
dev->slam_config = NULL;
|
||||
}
|
||||
|
||||
/* Enter SLAM. */
|
||||
dev->slam_state = CRYSTAL_SLAM_INDEX;
|
||||
}
|
||||
} else {
|
||||
dev->key_pos = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case CRYSTAL_SLAM_INDEX:
|
||||
/* Write register index. */
|
||||
dev->slam_reg = val;
|
||||
dev->slam_state = CRYSTAL_SLAM_BYTE1;
|
||||
break;
|
||||
|
||||
case CRYSTAL_SLAM_BYTE1:
|
||||
case CRYSTAL_SLAM_BYTE2:
|
||||
/* Write register value: two bytes for I/O ports, single byte otherwise. */
|
||||
switch (dev->slam_reg) {
|
||||
case 0x06: /* Card Select Number */
|
||||
isapnp_set_csn(dev->pnp_card, val);
|
||||
break;
|
||||
|
||||
case 0x15: /* Logical Device ID */
|
||||
/* Apply the previous logical device's configuration, and reuse its config structure. */
|
||||
if (dev->slam_config)
|
||||
cs423x_pnp_config_changed(dev->slam_ld, dev->slam_config, dev);
|
||||
else
|
||||
dev->slam_config = (isapnp_device_config_t *) malloc(sizeof(isapnp_device_config_t));
|
||||
|
||||
/* Start new logical device. */
|
||||
memset(dev->slam_config, 0, sizeof(isapnp_device_config_t));
|
||||
dev->slam_ld = val;
|
||||
break;
|
||||
|
||||
case 0x47: /* I/O Port Base Address 0 */
|
||||
case 0x48: /* I/O Port Base Address 1 */
|
||||
case 0x42: /* I/O Port Base Address 2 */
|
||||
idx = (dev->slam_reg == 0x42) ? 2 : (dev->slam_reg - 0x47);
|
||||
if (dev->slam_state == CRYSTAL_SLAM_BYTE1) {
|
||||
/* Set high byte, or ignore it if no logical device is selected. */
|
||||
if (dev->slam_config)
|
||||
dev->slam_config->io[idx].base = val << 8;
|
||||
|
||||
/* Prepare for the second (low byte) write. */
|
||||
dev->slam_state = CRYSTAL_SLAM_BYTE2;
|
||||
return;
|
||||
} else if (dev->slam_config) {
|
||||
/* Set low byte, or ignore it if no logical device is selected. */
|
||||
dev->slam_config->io[idx].base |= val;
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x22: /* Interrupt Select 0 */
|
||||
case 0x27: /* Interrupt Select 1 */
|
||||
/* Stop if no logical device is selected. */
|
||||
if (!dev->slam_config)
|
||||
break;
|
||||
|
||||
/* Set IRQ value. */
|
||||
idx = (dev->slam_reg == 0x22) ? 0 : 1;
|
||||
dev->slam_config->irq[idx].irq = val & 15;
|
||||
break;
|
||||
|
||||
case 0x2a: /* DMA Select 0 */
|
||||
case 0x25: /* DMA Select 1 */
|
||||
/* Stop if no logical device is selected. */
|
||||
if (!dev->slam_config)
|
||||
break;
|
||||
|
||||
/* Set DMA value. */
|
||||
idx = (dev->slam_reg == 0x2a) ? 0 : 1;
|
||||
dev->slam_config->dma[idx].dma = val & 7;
|
||||
break;
|
||||
|
||||
case 0x33: /* Activate Device */
|
||||
/* Stop if no logical device is selected. */
|
||||
if (!dev->slam_config)
|
||||
break;
|
||||
|
||||
/* Activate or deactivate the device. */
|
||||
dev->slam_config->activate = val & 0x01;
|
||||
break;
|
||||
|
||||
case 0x79: /* activate chip */
|
||||
/* Apply the last logical device's configuration. */
|
||||
if (dev->slam_config) {
|
||||
cs423x_pnp_config_changed(dev->slam_ld, dev->slam_config, dev);
|
||||
free(dev->slam_config);
|
||||
dev->slam_config = NULL;
|
||||
}
|
||||
|
||||
/* Exit out of SLAM. */
|
||||
dev->slam_state = CRYSTAL_SLAM_NONE;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Prepare for the next register, unless a two-byte read returns above. */
|
||||
dev->slam_state = CRYSTAL_SLAM_INDEX;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
cs423x_slam_remap(cs423x_t *dev, uint8_t enable)
|
||||
{
|
||||
/* Disable SLAM. */
|
||||
if (dev->enable_slam) {
|
||||
dev->enable_slam = 0;
|
||||
io_removehandler(0x279, 1, NULL, NULL, NULL, cs423x_slam_write, NULL, NULL, dev);
|
||||
}
|
||||
|
||||
/* Enable SLAM if not blocked by EEPROM configuration. */
|
||||
if (enable && !(dev->eeprom_data[7] & 0x10)) {
|
||||
dev->enable_slam = 1;
|
||||
io_sethandler(0x279, 1, NULL, NULL, NULL, cs423x_slam_write, NULL, NULL, dev);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static uint8_t
|
||||
cs423x_ctxswitch_read(uint16_t addr, void *priv)
|
||||
{
|
||||
cs423x_t *dev = (cs423x_t *) priv;
|
||||
uint8_t prev_context = dev->regs[7] & 0x80, switched = 0;
|
||||
|
||||
/* Determine the active context (WSS or SBPro) through the address being read/written. */
|
||||
if ((prev_context == 0x80) && ((addr & 0xfff0) == dev->sb_base)) {
|
||||
dev->regs[7] &= ~0x80;
|
||||
switched = 1;
|
||||
} else if ((prev_context == 0x00) && ((addr & 0xfffc) == dev->wss_base)) {
|
||||
dev->regs[7] |= 0x80;
|
||||
switched = 1;
|
||||
}
|
||||
|
||||
/* Fire the context switch interrupt if enabled. */
|
||||
if (switched && (dev->regs[0] & 0x20) && dev->ad1848.irq)
|
||||
picint(1 << dev->ad1848.irq);
|
||||
|
||||
cs423x_ctxswitch_write(addr, 0, priv);
|
||||
return 0xff; /* don't interfere with the actual handlers */
|
||||
}
|
||||
|
||||
@@ -266,7 +437,23 @@ cs423x_ctxswitch_read(uint16_t addr, void *priv)
|
||||
static void
|
||||
cs423x_ctxswitch_write(uint16_t addr, uint8_t val, void *priv)
|
||||
{
|
||||
cs423x_ctxswitch_read(addr, priv);
|
||||
cs423x_t *dev = (cs423x_t *) priv;
|
||||
uint8_t prev_context = dev->regs[7] & 0x80, switched = 0;
|
||||
|
||||
/* Determine the active context (WSS or SBPro) through the address being read/written. */
|
||||
if ((prev_context == 0x80) && ((addr & 0xfff0) == dev->sb_base)) {
|
||||
dev->regs[7] &= ~0x80;
|
||||
switched = 1;
|
||||
} else if ((prev_context == 0x00) && ((addr & 0xfffc) == dev->wss_base)) {
|
||||
dev->regs[7] |= 0x80;
|
||||
switched = 1;
|
||||
}
|
||||
|
||||
/* Fire the context switch interrupt if enabled. */
|
||||
if (switched && (dev->regs[0] & 0x20) && (dev->ad1848.irq > 0)) {
|
||||
dev->regs[7] |= 0x40; /* set interrupt flag */
|
||||
picint(1 << dev->ad1848.irq); /* control device shares its IRQ with WSS and SBPro */
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -395,37 +582,62 @@ cs423x_pnp_config_changed(uint8_t ld, isapnp_device_config_t *config, void *priv
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
cs423x_reset(void *priv)
|
||||
{
|
||||
cs423x_t *dev = (cs423x_t *) priv;
|
||||
|
||||
/* Reset registers. */
|
||||
memset(dev->indirect_regs, 0, sizeof(dev->indirect_regs));
|
||||
dev->indirect_regs[1] = dev->type;
|
||||
|
||||
/* Reset logical devices. */
|
||||
for (uint8_t i = 0; i < 6; i++)
|
||||
isapnp_reset_device(dev->pnp_card, i);
|
||||
|
||||
/* Enable SLAM. */
|
||||
cs423x_slam_remap(dev, 1);
|
||||
}
|
||||
|
||||
|
||||
static void *
|
||||
cs423x_init(const device_t *info)
|
||||
{
|
||||
cs423x_t *dev = malloc(sizeof(cs423x_t));
|
||||
memset(dev, 0, sizeof(cs423x_t));
|
||||
|
||||
dev->indirect_regs[1] = 0x88;
|
||||
|
||||
switch (info->local) {
|
||||
dev->type = info->local;
|
||||
switch (dev->type) {
|
||||
case CRYSTAL_CS4237B:
|
||||
dev->eeprom_size = sizeof(cs4237b_eeprom);
|
||||
memcpy(dev->eeprom_data, cs4237b_eeprom, dev->eeprom_size);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Initialize codecs. */
|
||||
dev->sb = (sb_t *) device_add(&sb_pro_cs423x_device);
|
||||
|
||||
ad1848_init(&dev->ad1848, AD1848_TYPE_DEFAULT);
|
||||
|
||||
ad1848_init(&dev->ad1848, AD1848_TYPE_CS4236);
|
||||
sound_add_handler(cs423x_get_buffer, dev);
|
||||
|
||||
/* Initialize I2C bus for the EEPROM. */
|
||||
dev->i2c = i2c_gpio_init("nvr_cs423x");
|
||||
|
||||
if (dev->eeprom_size) {
|
||||
/* Set EEPROM length. */
|
||||
dev->eeprom_data[2] = dev->eeprom_size >> 8;
|
||||
dev->eeprom_data[3] = dev->eeprom_size & 0xff;
|
||||
|
||||
/* Initialize I2C EEPROM. */
|
||||
dev->eeprom = i2c_eeprom_init(i2c_gpio_get_bus(dev->i2c), 0x50, dev->eeprom_data, sizeof(dev->eeprom_data), 1);
|
||||
}
|
||||
|
||||
/* Initialize ISAPnP. */
|
||||
dev->pnp_card = isapnp_add_card(&dev->eeprom_data[23], dev->eeprom_size - 23, cs423x_pnp_config_changed, NULL, NULL, NULL, dev);
|
||||
if (dev->eeprom_data[7] & 0x20) /* hide PnP card if PKD is set */
|
||||
isapnp_enable_card(dev->pnp_card, 0);
|
||||
|
||||
/* Initialize registers. */
|
||||
cs423x_reset(dev);
|
||||
|
||||
return dev;
|
||||
}
|
||||
@@ -459,7 +671,7 @@ const device_t cs4237b_device =
|
||||
"Crystal CS4237B",
|
||||
DEVICE_ISA | DEVICE_AT,
|
||||
CRYSTAL_CS4237B,
|
||||
cs423x_init, cs423x_close, NULL,
|
||||
cs423x_init, cs423x_close, cs423x_reset,
|
||||
{ NULL },
|
||||
cs423x_speed_changed,
|
||||
NULL,
|
||||
|
||||
Reference in New Issue
Block a user