diff options
Diffstat (limited to 'sound/oss/cs4232.c')
-rw-r--r-- | sound/oss/cs4232.c | 520 |
1 files changed, 520 insertions, 0 deletions
diff --git a/sound/oss/cs4232.c b/sound/oss/cs4232.c new file mode 100644 index 00000000000..6ec308f5d93 --- /dev/null +++ b/sound/oss/cs4232.c @@ -0,0 +1,520 @@ +/* + * Copyright (C) by Hannu Savolainen 1993-1997 + * + * cs4232.c + * + * The low level driver for Crystal CS4232 based cards. The CS4232 is + * a PnP compatible chip which contains a CS4231A codec, SB emulation, + * a MPU401 compatible MIDI port, joystick and synthesizer and IDE CD-ROM + * interfaces. This is just a temporary driver until full PnP support + * gets implemented. Just the WSS codec, FM synth and the MIDI ports are + * supported. Other interfaces are left uninitialized. + * + * ifdef ...WAVEFRONT... + * + * Support is provided for initializing the WaveFront synth + * interface as well, which is logical device #4. Note that if + * you have a Tropez+ card, you probably don't need to setup + * the CS4232-supported MIDI interface, since it corresponds to + * the internal 26-pin header that's hard to access. Using this + * requires an additional IRQ, a resource none too plentiful in + * this environment. Just don't set module parameters mpuio and + * mpuirq, and the MIDI port will be left uninitialized. You can + * still use the ICS2115 hosted MIDI interface which corresponds + * to the 9-pin D connector on the back of the card. + * + * endif ...WAVEFRONT... + * + * Supported chips are: + * CS4232 + * CS4236 + * CS4236B + * + * Note: You will need a PnP config setup to initialise some CS4232 boards + * anyway. + * + * Changes + * John Rood Added Bose Sound System Support. + * Toshio Spoor + * Alan Cox Modularisation, Basic cleanups. + * Paul Barton-Davis Separated MPU configuration, added + * Tropez+ (WaveFront) support + * Christoph Hellwig Adapted to module_init/module_exit, + * simple cleanups + * Arnaldo C. de Melo got rid of attach_uart401 + * Bartlomiej Zolnierkiewicz + * Added some __init/__initdata/__exit + * Marcus Meissner Added ISA PnP support. + */ + +#include <linux/config.h> +#include <linux/pnp.h> +#include <linux/module.h> +#include <linux/init.h> + +#include "sound_config.h" + +#include "ad1848.h" +#include "mpu401.h" + +#define KEY_PORT 0x279 /* Same as LPT1 status port */ +#define CSN_NUM 0x99 /* Just a random number */ +#define INDEX_ADDRESS 0x00 /* (R0) Index Address Register */ +#define INDEX_DATA 0x01 /* (R1) Indexed Data Register */ +#define PIN_CONTROL 0x0a /* (I10) Pin Control */ +#define ENABLE_PINS 0xc0 /* XCTRL0/XCTRL1 enable */ + +static void CS_OUT(unsigned char a) +{ + outb(a, KEY_PORT); +} + +#define CS_OUT2(a, b) {CS_OUT(a);CS_OUT(b);} +#define CS_OUT3(a, b, c) {CS_OUT(a);CS_OUT(b);CS_OUT(c);} + +static int __initdata bss = 0; +static int mpu_base, mpu_irq; +static int synth_base, synth_irq; +static int mpu_detected; + +static int probe_cs4232_mpu(struct address_info *hw_config) +{ + /* + * Just write down the config values. + */ + + mpu_base = hw_config->io_base; + mpu_irq = hw_config->irq; + + return 1; +} + +static unsigned char crystal_key[] = /* A 32 byte magic key sequence */ +{ + 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 void sleep(unsigned howlong) +{ + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(howlong); +} + +static void enable_xctrl(int baseio) +{ + unsigned char regd; + + /* + * Some IBM Aptiva's have the Bose Sound System. By default + * the Bose Amplifier is disabled. The amplifier will be + * activated, by setting the XCTRL0 and XCTRL1 bits. + * Volume of the monitor bose speakers/woofer, can then + * be set by changing the PCM volume. + * + */ + + printk("cs4232: enabling Bose Sound System Amplifier.\n"); + + /* Switch to Pin Control Address */ + regd = inb(baseio + INDEX_ADDRESS) & 0xe0; + outb(((unsigned char) (PIN_CONTROL | regd)), baseio + INDEX_ADDRESS ); + + /* Activate the XCTRL0 and XCTRL1 Pins */ + regd = inb(baseio + INDEX_DATA); + outb(((unsigned char) (ENABLE_PINS | regd)), baseio + INDEX_DATA ); +} + +static int __init probe_cs4232(struct address_info *hw_config, int isapnp_configured) +{ + int i, n; + int base = hw_config->io_base, irq = hw_config->irq; + int dma1 = hw_config->dma, dma2 = hw_config->dma2; + struct resource *ports; + + if (base == -1 || irq == -1 || dma1 == -1) { + printk(KERN_ERR "cs4232: dma, irq and io must be set.\n"); + return 0; + } + + /* + * Verify that the I/O port range is free. + */ + + ports = request_region(base, 4, "ad1848"); + if (!ports) { + printk(KERN_ERR "cs4232.c: I/O port 0x%03x not free\n", base); + return 0; + } + if (ad1848_detect(ports, NULL, hw_config->osp)) { + goto got_it; /* The card is already active */ + } + if (isapnp_configured) { + printk(KERN_ERR "cs4232.c: ISA PnP configured, but not detected?\n"); + goto fail; + } + + /* + * This version of the driver doesn't use the PnP method when configuring + * the card but a simplified method defined by Crystal. This means that + * just one CS4232 compatible device can exist on the system. Also this + * method conflicts with possible PnP support in the OS. For this reason + * driver is just a temporary kludge. + * + * Also the Cirrus/Crystal method doesn't always work. Try ISA PnP first ;) + */ + + /* + * Repeat initialization few times since it doesn't always succeed in + * first time. + */ + + for (n = 0; n < 4; n++) + { + /* + * Wake up the card by sending a 32 byte Crystal key to the key port. + */ + + for (i = 0; i < 32; i++) + CS_OUT(crystal_key[i]); + + sleep(HZ / 10); + + /* + * Now set the CSN (Card Select Number). + */ + + CS_OUT2(0x06, CSN_NUM); + + /* + * Then set some config bytes. First logical device 0 + */ + + CS_OUT2(0x15, 0x00); /* Select logical device 0 (WSS/SB/FM) */ + CS_OUT3(0x47, (base >> 8) & 0xff, base & 0xff); /* WSS base */ + + if (check_region(0x388, 4)) /* Not free */ + CS_OUT3(0x48, 0x00, 0x00) /* FM base off */ + else + CS_OUT3(0x48, 0x03, 0x88); /* FM base 0x388 */ + + CS_OUT3(0x42, 0x00, 0x00); /* SB base off */ + CS_OUT2(0x22, irq); /* SB+WSS IRQ */ + CS_OUT2(0x2a, dma1); /* SB+WSS DMA */ + + if (dma2 != -1) + CS_OUT2(0x25, dma2) /* WSS DMA2 */ + else + CS_OUT2(0x25, 4); /* No WSS DMA2 */ + + CS_OUT2(0x33, 0x01); /* Activate logical dev 0 */ + + sleep(HZ / 10); + + /* + * Initialize logical device 3 (MPU) + */ + + if (mpu_base != 0 && mpu_irq != 0) + { + CS_OUT2(0x15, 0x03); /* Select logical device 3 (MPU) */ + CS_OUT3(0x47, (mpu_base >> 8) & 0xff, mpu_base & 0xff); /* MPU base */ + CS_OUT2(0x22, mpu_irq); /* MPU IRQ */ + CS_OUT2(0x33, 0x01); /* Activate logical dev 3 */ + } + + if(synth_base != 0) + { + CS_OUT2 (0x15, 0x04); /* logical device 4 (WaveFront) */ + CS_OUT3 (0x47, (synth_base >> 8) & 0xff, + synth_base & 0xff); /* base */ + CS_OUT2 (0x22, synth_irq); /* IRQ */ + CS_OUT2 (0x33, 0x01); /* Activate logical dev 4 */ + } + + /* + * Finally activate the chip + */ + + CS_OUT(0x79); + + sleep(HZ / 5); + + /* + * Then try to detect the codec part of the chip + */ + + if (ad1848_detect(ports, NULL, hw_config->osp)) + goto got_it; + + sleep(HZ); + } +fail: + release_region(base, 4); + return 0; + +got_it: + if (dma2 == -1) + dma2 = dma1; + + hw_config->slots[0] = ad1848_init("Crystal audio controller", ports, + irq, + dma1, /* Playback DMA */ + dma2, /* Capture DMA */ + 0, + hw_config->osp, + THIS_MODULE); + + if (hw_config->slots[0] != -1 && + audio_devs[hw_config->slots[0]]->mixer_dev!=-1) + { + /* Assume the mixer map is as suggested in the CS4232 databook */ + AD1848_REROUTE(SOUND_MIXER_LINE1, SOUND_MIXER_LINE); + AD1848_REROUTE(SOUND_MIXER_LINE2, SOUND_MIXER_CD); + AD1848_REROUTE(SOUND_MIXER_LINE3, SOUND_MIXER_SYNTH); /* FM synth */ + } + if (mpu_base != 0 && mpu_irq != 0) + { + static struct address_info hw_config2 = { + 0 + }; /* Ensure it's initialized */ + + hw_config2.io_base = mpu_base; + hw_config2.irq = mpu_irq; + hw_config2.dma = -1; + hw_config2.dma2 = -1; + hw_config2.always_detect = 0; + hw_config2.name = NULL; + hw_config2.driver_use_1 = 0; + hw_config2.driver_use_2 = 0; + hw_config2.card_subtype = 0; + + if (probe_uart401(&hw_config2, THIS_MODULE)) + { + mpu_detected = 1; + } + else + { + mpu_base = mpu_irq = 0; + } + hw_config->slots[1] = hw_config2.slots[1]; + } + + if (bss) + enable_xctrl(base); + + return 1; +} + +static void __devexit unload_cs4232(struct address_info *hw_config) +{ + int base = hw_config->io_base, irq = hw_config->irq; + int dma1 = hw_config->dma, dma2 = hw_config->dma2; + + if (dma2 == -1) + dma2 = dma1; + + ad1848_unload(base, + irq, + dma1, /* Playback DMA */ + dma2, /* Capture DMA */ + 0); + + sound_unload_audiodev(hw_config->slots[0]); + if (mpu_base != 0 && mpu_irq != 0 && mpu_detected) + { + static struct address_info hw_config2 = + { + 0 + }; /* Ensure it's initialized */ + + hw_config2.io_base = mpu_base; + hw_config2.irq = mpu_irq; + hw_config2.dma = -1; + hw_config2.dma2 = -1; + hw_config2.always_detect = 0; + hw_config2.name = NULL; + hw_config2.driver_use_1 = 0; + hw_config2.driver_use_2 = 0; + hw_config2.card_subtype = 0; + hw_config2.slots[1] = hw_config->slots[1]; + + unload_uart401(&hw_config2); + } +} + +static struct address_info cfg; +static struct address_info cfg_mpu; + +static int __initdata io = -1; +static int __initdata irq = -1; +static int __initdata dma = -1; +static int __initdata dma2 = -1; +static int __initdata mpuio = -1; +static int __initdata mpuirq = -1; +static int __initdata synthio = -1; +static int __initdata synthirq = -1; +static int __initdata isapnp = 1; + +MODULE_DESCRIPTION("CS4232 based soundcard driver"); +MODULE_AUTHOR("Hannu Savolainen, Paul Barton-Davis"); +MODULE_LICENSE("GPL"); + +module_param(io, int, 0); +MODULE_PARM_DESC(io,"base I/O port for AD1848"); +module_param(irq, int, 0); +MODULE_PARM_DESC(irq,"IRQ for AD1848 chip"); +module_param(dma, int, 0); +MODULE_PARM_DESC(dma,"8 bit DMA for AD1848 chip"); +module_param(dma2, int, 0); +MODULE_PARM_DESC(dma2,"16 bit DMA for AD1848 chip"); +module_param(mpuio, int, 0); +MODULE_PARM_DESC(mpuio,"MPU 401 base address"); +module_param(mpuirq, int, 0); +MODULE_PARM_DESC(mpuirq,"MPU 401 IRQ"); +module_param(synthio, int, 0); +MODULE_PARM_DESC(synthio,"Maui WaveTable base I/O port"); +module_param(synthirq, int, 0); +MODULE_PARM_DESC(synthirq,"Maui WaveTable IRQ"); +module_param(isapnp, bool, 0); +MODULE_PARM_DESC(isapnp,"Enable ISAPnP probing (default 1)"); +module_param(bss, bool, 0); +MODULE_PARM_DESC(bss,"Enable Bose Sound System Support (default 0)"); + +/* + * Install a CS4232 based card. Need to have ad1848 and mpu401 + * loaded ready. + */ + +/* All cs4232 based cards have the main ad1848 card either as CSC0000 or + * CSC0100. */ +static const struct pnp_device_id cs4232_pnp_table[] = { + { .id = "CSC0100", .driver_data = 0 }, + { .id = "CSC0000", .driver_data = 0 }, + /* Guillemot Turtlebeach something appears to be cs4232 compatible + * (untested) */ + { .id = "GIM0100", .driver_data = 0 }, + { .id = ""} +}; + +MODULE_DEVICE_TABLE(pnp, cs4232_pnp_table); + +static int cs4232_pnp_probe(struct pnp_dev *dev, const struct pnp_device_id *dev_id) +{ + struct address_info *isapnpcfg; + + isapnpcfg=(struct address_info*)kmalloc(sizeof(*isapnpcfg),GFP_KERNEL); + if (!isapnpcfg) + return -ENOMEM; + + isapnpcfg->irq = pnp_irq(dev, 0); + isapnpcfg->dma = pnp_dma(dev, 0); + isapnpcfg->dma2 = pnp_dma(dev, 1); + isapnpcfg->io_base = pnp_port_start(dev, 0); + if (probe_cs4232(isapnpcfg,TRUE) == 0) { + printk(KERN_ERR "cs4232: ISA PnP card found, but not detected?\n"); + kfree(isapnpcfg); + return -ENODEV; + } + pnp_set_drvdata(dev,isapnpcfg); + return 0; +} + +static void __devexit cs4232_pnp_remove(struct pnp_dev *dev) +{ + struct address_info *cfg = pnp_get_drvdata(dev); + if (cfg) { + unload_cs4232(cfg); + kfree(cfg); + } +} + +static struct pnp_driver cs4232_driver = { + .name = "cs4232", + .id_table = cs4232_pnp_table, + .probe = cs4232_pnp_probe, + .remove = __devexit_p(cs4232_pnp_remove), +}; + +static int __init init_cs4232(void) +{ +#ifdef CONFIG_SOUND_WAVEFRONT_MODULE + if(synthio == -1) + printk(KERN_INFO "cs4232: set synthio and synthirq to use the wavefront facilities.\n"); + else { + synth_base = synthio; + synth_irq = synthirq; + } +#else + if(synthio != -1) + printk(KERN_WARNING "cs4232: wavefront support not enabled in this driver.\n"); +#endif + cfg.irq = -1; + + if (isapnp && + (pnp_register_driver(&cs4232_driver) > 0) + ) + return 0; + + if(io==-1||irq==-1||dma==-1) + { + printk(KERN_ERR "cs4232: Must set io, irq and dma.\n"); + return -ENODEV; + } + + cfg.io_base = io; + cfg.irq = irq; + cfg.dma = dma; + cfg.dma2 = dma2; + + cfg_mpu.io_base = -1; + cfg_mpu.irq = -1; + + if (mpuio != -1 && mpuirq != -1) { + cfg_mpu.io_base = mpuio; + cfg_mpu.irq = mpuirq; + probe_cs4232_mpu(&cfg_mpu); /* Bug always returns 0 not OK -- AC */ + } + + if (probe_cs4232(&cfg,FALSE) == 0) + return -ENODEV; + + return 0; +} + +static void __exit cleanup_cs4232(void) +{ + pnp_unregister_driver(&cs4232_driver); + if (cfg.irq != -1) + unload_cs4232(&cfg); /* Unloads global MPU as well, if needed */ +} + +module_init(init_cs4232); +module_exit(cleanup_cs4232); + +#ifndef MODULE +static int __init setup_cs4232(char *str) +{ + /* io, irq, dma, dma2 mpuio, mpuirq*/ + int ints[7]; + + /* If we have isapnp cards, no need for options */ + if (pnp_register_driver(&cs4232_driver) > 0) + return 1; + + str = get_options(str, ARRAY_SIZE(ints), ints); + + io = ints[1]; + irq = ints[2]; + dma = ints[3]; + dma2 = ints[4]; + mpuio = ints[5]; + mpuirq = ints[6]; + + return 1; +} + +__setup("cs4232=", setup_cs4232); +#endif |