1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480 |
- /*
- * sound/oss/sscape.c
- *
- * Low level driver for Ensoniq SoundScape
- *
- *
- * Copyright (C) by Hannu Savolainen 1993-1997
- *
- * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
- * Version 2 (June 1991). See the "COPYING" file distributed with this software
- * for more info.
- *
- *
- * Thomas Sailer : ioctl code reworked (vmalloc/vfree removed)
- * Sergey Smitienko : ensoniq p'n'p support
- * Christoph Hellwig : adapted to module_init/module_exit
- * Bartlomiej Zolnierkiewicz : added __init to attach_sscape()
- * Chris Rankin : Specify that this module owns the coprocessor
- * Arnaldo C. de Melo : added missing restore_flags in sscape_pnp_upload_file
- */
- #include <linux/init.h>
- #include <linux/module.h>
- #include "sound_config.h"
- #include "sound_firmware.h"
- #include <linux/types.h>
- #include <linux/errno.h>
- #include <linux/signal.h>
- #include <linux/fcntl.h>
- #include <linux/ctype.h>
- #include <linux/stddef.h>
- #include <linux/kmod.h>
- #include <asm/dma.h>
- #include <asm/io.h>
- #include <linux/wait.h>
- #include <linux/slab.h>
- #include <linux/ioport.h>
- #include <linux/delay.h>
- #include <linux/proc_fs.h>
- #include <linux/mm.h>
- #include <linux/spinlock.h>
- #include "coproc.h"
- #include "ad1848.h"
- #include "mpu401.h"
- /*
- * I/O ports
- */
- #define MIDI_DATA 0
- #define MIDI_CTRL 1
- #define HOST_CTRL 2
- #define TX_READY 0x02
- #define RX_READY 0x01
- #define HOST_DATA 3
- #define ODIE_ADDR 4
- #define ODIE_DATA 5
- /*
- * Indirect registers
- */
- #define GA_INTSTAT_REG 0
- #define GA_INTENA_REG 1
- #define GA_DMAA_REG 2
- #define GA_DMAB_REG 3
- #define GA_INTCFG_REG 4
- #define GA_DMACFG_REG 5
- #define GA_CDCFG_REG 6
- #define GA_SMCFGA_REG 7
- #define GA_SMCFGB_REG 8
- #define GA_HMCTL_REG 9
- /*
- * DMA channel identifiers (A and B)
- */
- #define SSCAPE_DMA_A 0
- #define SSCAPE_DMA_B 1
- #define PORT(name) (devc->base+name)
- /*
- * Host commands recognized by the OBP microcode
- */
-
- #define CMD_GEN_HOST_ACK 0x80
- #define CMD_GEN_MPU_ACK 0x81
- #define CMD_GET_BOARD_TYPE 0x82
- #define CMD_SET_CONTROL 0x88 /* Old firmware only */
- #define CMD_GET_CONTROL 0x89 /* Old firmware only */
- #define CTL_MASTER_VOL 0
- #define CTL_MIC_MODE 2
- #define CTL_SYNTH_VOL 4
- #define CTL_WAVE_VOL 7
- #define CMD_SET_EXTMIDI 0x8a
- #define CMD_GET_EXTMIDI 0x8b
- #define CMD_SET_MT32 0x8c
- #define CMD_GET_MT32 0x8d
- #define CMD_ACK 0x80
- #define IC_ODIE 1
- #define IC_OPUS 2
- typedef struct sscape_info
- {
- int base, irq, dma;
-
- int codec, codec_irq; /* required to setup pnp cards*/
- int codec_type;
- int ic_type;
- char* raw_buf;
- unsigned long raw_buf_phys;
- int buffsize; /* -------------------------- */
- spinlock_t lock;
- int ok; /* Properly detected */
- int failed;
- int dma_allocated;
- int codec_audiodev;
- int opened;
- int *osp;
- int my_audiodev;
- } sscape_info;
- static struct sscape_info adev_info = {
- 0
- };
- static struct sscape_info *devc = &adev_info;
- static int sscape_mididev = -1;
- /* Some older cards have assigned interrupt bits differently than new ones */
- static char valid_interrupts_old[] = {
- 9, 7, 5, 15
- };
- static char valid_interrupts_new[] = {
- 9, 5, 7, 10
- };
- static char *valid_interrupts = valid_interrupts_new;
- /*
- * See the bottom of the driver. This can be set by spea =0/1.
- */
-
- #ifdef REVEAL_SPEA
- static char old_hardware = 1;
- #else
- static char old_hardware;
- #endif
- static void sleep(unsigned howlong)
- {
- current->state = TASK_INTERRUPTIBLE;
- schedule_timeout(howlong);
- }
- static unsigned char sscape_read(struct sscape_info *devc, int reg)
- {
- unsigned long flags;
- unsigned char val;
- spin_lock_irqsave(&devc->lock,flags);
- outb(reg, PORT(ODIE_ADDR));
- val = inb(PORT(ODIE_DATA));
- spin_unlock_irqrestore(&devc->lock,flags);
- return val;
- }
- static void __sscape_write(int reg, int data)
- {
- outb(reg, PORT(ODIE_ADDR));
- outb(data, PORT(ODIE_DATA));
- }
- static void sscape_write(struct sscape_info *devc, int reg, int data)
- {
- unsigned long flags;
- spin_lock_irqsave(&devc->lock,flags);
- __sscape_write(reg, data);
- spin_unlock_irqrestore(&devc->lock,flags);
- }
- static unsigned char sscape_pnp_read_codec(sscape_info* devc, unsigned char reg)
- {
- unsigned char res;
- unsigned long flags;
- spin_lock_irqsave(&devc->lock,flags);
- outb( reg, devc -> codec);
- res = inb (devc -> codec + 1);
- spin_unlock_irqrestore(&devc->lock,flags);
- return res;
- }
- static void sscape_pnp_write_codec(sscape_info* devc, unsigned char reg, unsigned char data)
- {
- unsigned long flags;
-
- spin_lock_irqsave(&devc->lock,flags);
- outb( reg, devc -> codec);
- outb( data, devc -> codec + 1);
- spin_unlock_irqrestore(&devc->lock,flags);
- }
- static void host_open(struct sscape_info *devc)
- {
- outb((0x00), PORT(HOST_CTRL)); /* Put the board to the host mode */
- }
- static void host_close(struct sscape_info *devc)
- {
- outb((0x03), PORT(HOST_CTRL)); /* Put the board to the MIDI mode */
- }
- static int host_write(struct sscape_info *devc, unsigned char *data, int count)
- {
- unsigned long flags;
- int i, timeout_val;
- spin_lock_irqsave(&devc->lock,flags);
- /*
- * Send the command and data bytes
- */
- for (i = 0; i < count; i++)
- {
- for (timeout_val = 10000; timeout_val > 0; timeout_val--)
- if (inb(PORT(HOST_CTRL)) & TX_READY)
- break;
- if (timeout_val <= 0)
- {
- spin_unlock_irqrestore(&devc->lock,flags);
- return 0;
- }
- outb(data[i], PORT(HOST_DATA));
- }
- spin_unlock_irqrestore(&devc->lock,flags);
- return 1;
- }
- static int host_read(struct sscape_info *devc)
- {
- unsigned long flags;
- int timeout_val;
- unsigned char data;
- spin_lock_irqsave(&devc->lock,flags);
- /*
- * Read a byte
- */
- for (timeout_val = 10000; timeout_val > 0; timeout_val--)
- if (inb(PORT(HOST_CTRL)) & RX_READY)
- break;
- if (timeout_val <= 0)
- {
- spin_unlock_irqrestore(&devc->lock,flags);
- return -1;
- }
- data = inb(PORT(HOST_DATA));
- spin_unlock_irqrestore(&devc->lock,flags);
- return data;
- }
- #if 0 /* unused */
- static int host_command1(struct sscape_info *devc, int cmd)
- {
- unsigned char buf[10];
- buf[0] = (unsigned char) (cmd & 0xff);
- return host_write(devc, buf, 1);
- }
- #endif /* unused */
- static int host_command2(struct sscape_info *devc, int cmd, int parm1)
- {
- unsigned char buf[10];
- buf[0] = (unsigned char) (cmd & 0xff);
- buf[1] = (unsigned char) (parm1 & 0xff);
- return host_write(devc, buf, 2);
- }
- static int host_command3(struct sscape_info *devc, int cmd, int parm1, int parm2)
- {
- unsigned char buf[10];
- buf[0] = (unsigned char) (cmd & 0xff);
- buf[1] = (unsigned char) (parm1 & 0xff);
- buf[2] = (unsigned char) (parm2 & 0xff);
- return host_write(devc, buf, 3);
- }
- static void set_mt32(struct sscape_info *devc, int value)
- {
- host_open(devc);
- host_command2(devc, CMD_SET_MT32, value ? 1 : 0);
- if (host_read(devc) != CMD_ACK)
- {
- /* printk( "SNDSCAPE: Setting MT32 mode failed\n"); */
- }
- host_close(devc);
- }
- static void set_control(struct sscape_info *devc, int ctrl, int value)
- {
- host_open(devc);
- host_command3(devc, CMD_SET_CONTROL, ctrl, value);
- if (host_read(devc) != CMD_ACK)
- {
- /* printk( "SNDSCAPE: Setting control (%d) failed\n", ctrl); */
- }
- host_close(devc);
- }
- static void do_dma(struct sscape_info *devc, int dma_chan, unsigned long buf, int blk_size, int mode)
- {
- unsigned char temp;
- if (dma_chan != SSCAPE_DMA_A)
- {
- printk(KERN_WARNING "soundscape: Tried to use DMA channel != A. Why?\n");
- return;
- }
- audio_devs[devc->codec_audiodev]->flags &= ~DMA_AUTOMODE;
- DMAbuf_start_dma(devc->codec_audiodev, buf, blk_size, mode);
- audio_devs[devc->codec_audiodev]->flags |= DMA_AUTOMODE;
- temp = devc->dma << 4; /* Setup DMA channel select bits */
- if (devc->dma <= 3)
- temp |= 0x80; /* 8 bit DMA channel */
- temp |= 1; /* Trigger DMA */
- sscape_write(devc, GA_DMAA_REG, temp);
- temp &= 0xfe; /* Clear DMA trigger */
- sscape_write(devc, GA_DMAA_REG, temp);
- }
- static int verify_mpu(struct sscape_info *devc)
- {
- /*
- * The SoundScape board could be in three modes (MPU, 8250 and host).
- * If the card is not in the MPU mode, enabling the MPU driver will
- * cause infinite loop (the driver believes that there is always some
- * received data in the buffer.
- *
- * Detect this by looking if there are more than 10 received MIDI bytes
- * (0x00) in the buffer.
- */
- int i;
- for (i = 0; i < 10; i++)
- {
- if (inb(devc->base + HOST_CTRL) & 0x80)
- return 1;
- if (inb(devc->base) != 0x00)
- return 1;
- }
- printk(KERN_WARNING "SoundScape: The device is not in the MPU-401 mode\n");
- return 0;
- }
- static int sscape_coproc_open(void *dev_info, int sub_device)
- {
- if (sub_device == COPR_MIDI)
- {
- set_mt32(devc, 0);
- if (!verify_mpu(devc))
- return -EIO;
- }
- return 0;
- }
- static void sscape_coproc_close(void *dev_info, int sub_device)
- {
- struct sscape_info *devc = dev_info;
- unsigned long flags;
- spin_lock_irqsave(&devc->lock,flags);
- if (devc->dma_allocated)
- {
- __sscape_write(GA_DMAA_REG, 0x20); /* DMA channel disabled */
- devc->dma_allocated = 0;
- }
- spin_unlock_irqrestore(&devc->lock,flags);
- return;
- }
- static void sscape_coproc_reset(void *dev_info)
- {
- }
- static int sscape_download_boot(struct sscape_info *devc, unsigned char *block, int size, int flag)
- {
- unsigned long flags;
- unsigned char temp;
- volatile int done, timeout_val;
- static unsigned char codec_dma_bits;
- if (flag & CPF_FIRST)
- {
- /*
- * First block. Have to allocate DMA and to reset the board
- * before continuing.
- */
- spin_lock_irqsave(&devc->lock,flags);
- codec_dma_bits = sscape_read(devc, GA_CDCFG_REG);
- if (devc->dma_allocated == 0)
- devc->dma_allocated = 1;
- spin_unlock_irqrestore(&devc->lock,flags);
- sscape_write(devc, GA_HMCTL_REG,
- (temp = sscape_read(devc, GA_HMCTL_REG)) & 0x3f); /*Reset */
- for (timeout_val = 10000; timeout_val > 0; timeout_val--)
- sscape_read(devc, GA_HMCTL_REG); /* Delay */
- /* Take board out of reset */
- sscape_write(devc, GA_HMCTL_REG,
- (temp = sscape_read(devc, GA_HMCTL_REG)) | 0x80);
- }
- /*
- * Transfer one code block using DMA
- */
- if (audio_devs[devc->codec_audiodev]->dmap_out->raw_buf == NULL)
- {
- printk(KERN_WARNING "soundscape: DMA buffer not available\n");
- return 0;
- }
- memcpy(audio_devs[devc->codec_audiodev]->dmap_out->raw_buf, block, size);
- spin_lock_irqsave(&devc->lock,flags);
-
- /******** INTERRUPTS DISABLED NOW ********/
-
- do_dma(devc, SSCAPE_DMA_A,
- audio_devs[devc->codec_audiodev]->dmap_out->raw_buf_phys,
- size, DMA_MODE_WRITE);
- /*
- * Wait until transfer completes.
- */
-
- done = 0;
- timeout_val = 30;
- while (!done && timeout_val-- > 0)
- {
- int resid;
- if (HZ / 50)
- sleep(HZ / 50);
- clear_dma_ff(devc->dma);
- if ((resid = get_dma_residue(devc->dma)) == 0)
- done = 1;
- }
- spin_unlock_irqrestore(&devc->lock,flags);
- if (!done)
- return 0;
- if (flag & CPF_LAST)
- {
- /*
- * Take the board out of reset
- */
- outb((0x00), PORT(HOST_CTRL));
- outb((0x00), PORT(MIDI_CTRL));
- temp = sscape_read(devc, GA_HMCTL_REG);
- temp |= 0x40;
- sscape_write(devc, GA_HMCTL_REG, temp); /* Kickstart the board */
- /*
- * Wait until the ODB wakes up
- */
- spin_lock_irqsave(&devc->lock,flags);
- done = 0;
- timeout_val = 5 * HZ;
- while (!done && timeout_val-- > 0)
- {
- unsigned char x;
-
- sleep(1);
- x = inb(PORT(HOST_DATA));
- if (x == 0xff || x == 0xfe) /* OBP startup acknowledge */
- {
- DDB(printk("Soundscape: Acknowledge = %x\n", x));
- done = 1;
- }
- }
- sscape_write(devc, GA_CDCFG_REG, codec_dma_bits);
- spin_unlock_irqrestore(&devc->lock,flags);
- if (!done)
- {
- printk(KERN_ERR "soundscape: The OBP didn't respond after code download\n");
- return 0;
- }
- spin_lock_irqsave(&devc->lock,flags);
- done = 0;
- timeout_val = 5 * HZ;
- while (!done && timeout_val-- > 0)
- {
- sleep(1);
- if (inb(PORT(HOST_DATA)) == 0xfe) /* Host startup acknowledge */
- done = 1;
- }
- spin_unlock_irqrestore(&devc->lock,flags);
- if (!done)
- {
- printk(KERN_ERR "soundscape: OBP Initialization failed.\n");
- return 0;
- }
- printk(KERN_INFO "SoundScape board initialized OK\n");
- set_control(devc, CTL_MASTER_VOL, 100);
- set_control(devc, CTL_SYNTH_VOL, 100);
- #ifdef SSCAPE_DEBUG3
- /*
- * Temporary debugging aid. Print contents of the registers after
- * downloading the code.
- */
- {
- int i;
- for (i = 0; i < 13; i++)
- printk("I%d = %02x (new value)\n", i, sscape_read(devc, i));
- }
- #endif
- }
- return 1;
- }
- static int download_boot_block(void *dev_info, copr_buffer * buf)
- {
- if (buf->len <= 0 || buf->len > sizeof(buf->data))
- return -EINVAL;
- if (!sscape_download_boot(devc, buf->data, buf->len, buf->flags))
- {
- printk(KERN_ERR "soundscape: Unable to load microcode block to the OBP.\n");
- return -EIO;
- }
- return 0;
- }
- static int sscape_coproc_ioctl(void *dev_info, unsigned int cmd, void __user *arg, int local)
- {
- copr_buffer *buf;
- int err;
- switch (cmd)
- {
- case SNDCTL_COPR_RESET:
- sscape_coproc_reset(dev_info);
- return 0;
- case SNDCTL_COPR_LOAD:
- buf = (copr_buffer *) vmalloc(sizeof(copr_buffer));
- if (buf == NULL)
- return -ENOSPC;
- if (copy_from_user(buf, arg, sizeof(copr_buffer)))
- {
- vfree(buf);
- return -EFAULT;
- }
- err = download_boot_block(dev_info, buf);
- vfree(buf);
- return err;
-
- default:
- return -EINVAL;
- }
- }
- static coproc_operations sscape_coproc_operations =
- {
- "SoundScape M68K",
- THIS_MODULE,
- sscape_coproc_open,
- sscape_coproc_close,
- sscape_coproc_ioctl,
- sscape_coproc_reset,
- &adev_info
- };
- static struct resource *sscape_ports;
- static int sscape_is_pnp;
- static void __init attach_sscape(struct address_info *hw_config)
- {
- #ifndef SSCAPE_REGS
- /*
- * Config register values for Spea/V7 Media FX and Ensoniq S-2000.
- * These values are card
- * dependent. If you have another SoundScape based card, you have to
- * find the correct values. Do the following:
- * - Compile this driver with SSCAPE_DEBUG1 defined.
- * - Shut down and power off your machine.
- * - Boot with DOS so that the SSINIT.EXE program is run.
- * - Warm boot to {Linux|SYSV|BSD} and write down the lines displayed
- * when detecting the SoundScape.
- * - Modify the following list to use the values printed during boot.
- * Undefine the SSCAPE_DEBUG1
- */
- #define SSCAPE_REGS { \
- /* I0 */ 0x00, \
- /* I1 */ 0xf0, /* Note! Ignored. Set always to 0xf0 */ \
- /* I2 */ 0x20, /* Note! Ignored. Set always to 0x20 */ \
- /* I3 */ 0x20, /* Note! Ignored. Set always to 0x20 */ \
- /* I4 */ 0xf5, /* Ignored */ \
- /* I5 */ 0x10, \
- /* I6 */ 0x00, \
- /* I7 */ 0x2e, /* I7 MEM config A. Likely to vary between models */ \
- /* I8 */ 0x00, /* I8 MEM config B. Likely to vary between models */ \
- /* I9 */ 0x40 /* Ignored */ \
- }
- #endif
- unsigned long flags;
- static unsigned char regs[10] = SSCAPE_REGS;
- int i, irq_bits = 0xff;
- if (old_hardware)
- {
- valid_interrupts = valid_interrupts_old;
- conf_printf("Ensoniq SoundScape (old)", hw_config);
- }
- else
- conf_printf("Ensoniq SoundScape", hw_config);
- for (i = 0; i < 4; i++)
- {
- if (hw_config->irq == valid_interrupts[i])
- {
- irq_bits = i;
- break;
- }
- }
- if (hw_config->irq > 15 || (regs[4] = irq_bits == 0xff))
- {
- printk(KERN_ERR "Invalid IRQ%d\n", hw_config->irq);
- release_region(devc->base, 2);
- release_region(devc->base + 2, 6);
- if (sscape_is_pnp)
- release_region(devc->codec, 2);
- return;
- }
-
- if (!sscape_is_pnp) {
-
- spin_lock_irqsave(&devc->lock,flags);
- /* Host interrupt enable */
- sscape_write(devc, 1, 0xf0); /* All interrupts enabled */
- /* DMA A status/trigger register */
- sscape_write(devc, 2, 0x20); /* DMA channel disabled */
- /* DMA B status/trigger register */
- sscape_write(devc, 3, 0x20); /* DMA channel disabled */
- /* Host interrupt config reg */
- sscape_write(devc, 4, 0xf0 | (irq_bits << 2) | irq_bits);
- /* Don't destroy CD-ROM DMA config bits (0xc0) */
- sscape_write(devc, 5, (regs[5] & 0x3f) | (sscape_read(devc, 5) & 0xc0));
- /* CD-ROM config (WSS codec actually) */
- sscape_write(devc, 6, regs[6]);
- sscape_write(devc, 7, regs[7]);
- sscape_write(devc, 8, regs[8]);
- /* Master control reg. Don't modify CR-ROM bits. Disable SB emul */
- sscape_write(devc, 9, (sscape_read(devc, 9) & 0xf0) | 0x08);
- spin_unlock_irqrestore(&devc->lock,flags);
- }
- #ifdef SSCAPE_DEBUG2
- /*
- * Temporary debugging aid. Print contents of the registers after
- * changing them.
- */
- {
- int i;
- for (i = 0; i < 13; i++)
- printk("I%d = %02x (new value)\n", i, sscape_read(devc, i));
- }
- #endif
- if (probe_mpu401(hw_config, sscape_ports))
- hw_config->always_detect = 1;
- hw_config->name = "SoundScape";
- hw_config->irq *= -1; /* Negative value signals IRQ sharing */
- attach_mpu401(hw_config, THIS_MODULE);
- hw_config->irq *= -1; /* Restore it */
- if (hw_config->slots[1] != -1) /* The MPU driver installed itself */
- {
- sscape_mididev = hw_config->slots[1];
- midi_devs[hw_config->slots[1]]->coproc = &sscape_coproc_operations;
- }
- sscape_write(devc, GA_INTENA_REG, 0x80); /* Master IRQ enable */
- devc->ok = 1;
- devc->failed = 0;
- }
- static int detect_ga(sscape_info * devc)
- {
- unsigned char save;
- DDB(printk("Entered Soundscape detect_ga(%x)\n", devc->base));
- /*
- * First check that the address register of "ODIE" is
- * there and that it has exactly 4 writable bits.
- * First 4 bits
- */
-
- if ((save = inb(PORT(ODIE_ADDR))) & 0xf0)
- {
- DDB(printk("soundscape: Detect error A\n"));
- return 0;
- }
- outb((0x00), PORT(ODIE_ADDR));
- if (inb(PORT(ODIE_ADDR)) != 0x00)
- {
- DDB(printk("soundscape: Detect error B\n"));
- return 0;
- }
- outb((0xff), PORT(ODIE_ADDR));
- if (inb(PORT(ODIE_ADDR)) != 0x0f)
- {
- DDB(printk("soundscape: Detect error C\n"));
- return 0;
- }
- outb((save), PORT(ODIE_ADDR));
- /*
- * Now verify that some indirect registers return zero on some bits.
- * This may break the driver with some future revisions of "ODIE" but...
- */
- if (sscape_read(devc, 0) & 0x0c)
- {
- DDB(printk("soundscape: Detect error D (%x)\n", sscape_read(devc, 0)));
- return 0;
- }
- if (sscape_read(devc, 1) & 0x0f)
- {
- DDB(printk("soundscape: Detect error E\n"));
- return 0;
- }
- if (sscape_read(devc, 5) & 0x0f)
- {
- DDB(printk("soundscape: Detect error F\n"));
- return 0;
- }
- return 1;
- }
- static int sscape_read_host_ctrl(sscape_info* devc)
- {
- return host_read(devc);
- }
- static void sscape_write_host_ctrl2(sscape_info *devc, int a, int b)
- {
- host_command2(devc, a, b);
- }
- static int sscape_alloc_dma(sscape_info *devc)
- {
- char *start_addr, *end_addr;
- int dma_pagesize;
- int sz, size;
- struct page *page;
- if (devc->raw_buf != NULL) return 0; /* Already done */
- dma_pagesize = (devc->dma < 4) ? (64 * 1024) : (128 * 1024);
- devc->raw_buf = NULL;
- devc->buffsize = 8192*4;
- if (devc->buffsize > dma_pagesize) devc->buffsize = dma_pagesize;
- start_addr = NULL;
- /*
- * Now loop until we get a free buffer. Try to get smaller buffer if
- * it fails. Don't accept smaller than 8k buffer for performance
- * reasons.
- */
- while (start_addr == NULL && devc->buffsize > PAGE_SIZE) {
- for (sz = 0, size = PAGE_SIZE; size < devc->buffsize; sz++, size <<= 1);
- devc->buffsize = PAGE_SIZE * (1 << sz);
- start_addr = (char *) __get_free_pages(GFP_ATOMIC|GFP_DMA, sz);
- if (start_addr == NULL) devc->buffsize /= 2;
- }
- if (start_addr == NULL) {
- printk(KERN_ERR "sscape pnp init error: Couldn't allocate DMA buffer\n");
- return 0;
- } else {
- /* make some checks */
- end_addr = start_addr + devc->buffsize - 1;
- /* now check if it fits into the same dma-pagesize */
- if (((long) start_addr & ~(dma_pagesize - 1)) != ((long) end_addr & ~(dma_pagesize - 1))
- || end_addr >= (char *) (MAX_DMA_ADDRESS)) {
- printk(KERN_ERR "sscape pnp: Got invalid address 0x%lx for %db DMA-buffer\n", (long) start_addr, devc->buffsize);
- return 0;
- }
- }
- devc->raw_buf = start_addr;
- devc->raw_buf_phys = virt_to_bus(start_addr);
- for (page = virt_to_page(start_addr); page <= virt_to_page(end_addr); page++)
- SetPageReserved(page);
- return 1;
- }
- static void sscape_free_dma(sscape_info *devc)
- {
- int sz, size;
- unsigned long start_addr, end_addr;
- struct page *page;
- if (devc->raw_buf == NULL) return;
- for (sz = 0, size = PAGE_SIZE; size < devc->buffsize; sz++, size <<= 1);
- start_addr = (unsigned long) devc->raw_buf;
- end_addr = start_addr + devc->buffsize;
- for (page = virt_to_page(start_addr); page <= virt_to_page(end_addr); page++)
- ClearPageReserved(page);
- free_pages((unsigned long) devc->raw_buf, sz);
- devc->raw_buf = NULL;
- }
- /* Intel version !!!!!!!!! */
- static int sscape_start_dma(int chan, unsigned long physaddr, int count, int dma_mode)
- {
- unsigned long flags;
- flags = claim_dma_lock();
- disable_dma(chan);
- clear_dma_ff(chan);
- set_dma_mode(chan, dma_mode);
- set_dma_addr(chan, physaddr);
- set_dma_count(chan, count);
- enable_dma(chan);
- release_dma_lock(flags);
- return 0;
- }
- static void sscape_pnp_start_dma(sscape_info* devc, int arg )
- {
- int reg;
- if (arg == 0) reg = 2;
- else reg = 3;
- sscape_write(devc, reg, sscape_read( devc, reg) | 0x01);
- sscape_write(devc, reg, sscape_read( devc, reg) & 0xFE);
- }
- static int sscape_pnp_wait_dma (sscape_info* devc, int arg )
- {
- int reg;
- unsigned long i;
- unsigned char d;
- if (arg == 0) reg = 2;
- else reg = 3;
- sleep ( 1 );
- i = 0;
- do {
- d = sscape_read(devc, reg) & 1;
- if ( d == 1) break;
- i++;
- } while (i < 500000);
- d = sscape_read(devc, reg) & 1;
- return d;
- }
- static int sscape_pnp_alloc_dma(sscape_info* devc)
- {
- /* printk(KERN_INFO "sscape: requesting dma\n"); */
- if (request_dma(devc -> dma, "sscape")) return 0;
- /* printk(KERN_INFO "sscape: dma channel allocated\n"); */
- if (!sscape_alloc_dma(devc)) {
- free_dma(devc -> dma);
- return 0;
- };
- return 1;
- }
- static void sscape_pnp_free_dma(sscape_info* devc)
- {
- sscape_free_dma( devc);
- free_dma(devc -> dma );
- /* printk(KERN_INFO "sscape: dma released\n"); */
- }
- static int sscape_pnp_upload_file(sscape_info* devc, char* fn)
- {
- int done = 0;
- int timeout_val;
- char* data,*dt;
- int len,l;
- unsigned long flags;
- sscape_write( devc, 9, sscape_read(devc, 9 ) & 0x3F );
- sscape_write( devc, 2, (devc -> dma << 4) | 0x80 );
- sscape_write( devc, 3, 0x20 );
- sscape_write( devc, 9, sscape_read( devc, 9 ) | 0x80 );
-
- len = mod_firmware_load(fn, &data);
- if (len == 0) {
- printk(KERN_ERR "sscape: file not found: %s\n", fn);
- return 0;
- }
- dt = data;
- spin_lock_irqsave(&devc->lock,flags);
- while ( len > 0 ) {
- if (len > devc -> buffsize) l = devc->buffsize;
- else l = len;
- len -= l;
- memcpy(devc->raw_buf, dt, l); dt += l;
- sscape_start_dma(devc->dma, devc->raw_buf_phys, l, 0x48);
- sscape_pnp_start_dma ( devc, 0 );
- if (sscape_pnp_wait_dma ( devc, 0 ) == 0) {
- spin_unlock_irqrestore(&devc->lock,flags);
- return 0;
- }
- }
-
- spin_unlock_irqrestore(&devc->lock,flags);
- vfree(data);
-
- outb(0, devc -> base + 2);
- outb(0, devc -> base);
- sscape_write ( devc, 9, sscape_read( devc, 9 ) | 0x40);
- timeout_val = 5 * HZ;
- while (!done && timeout_val-- > 0)
- {
- unsigned char x;
- sleep(1);
- x = inb( devc -> base + 3);
- if (x == 0xff || x == 0xfe) /* OBP startup acknowledge */
- {
- //printk(KERN_ERR "Soundscape: Acknowledge = %x\n", x);
- done = 1;
- }
- }
- timeout_val = 5 * HZ;
- done = 0;
- while (!done && timeout_val-- > 0)
- {
- unsigned char x;
- sleep(1);
- x = inb( devc -> base + 3);
- if (x == 0xfe) /* OBP startup acknowledge */
- {
- //printk(KERN_ERR "Soundscape: Acknowledge = %x\n", x);
- done = 1;
- }
- }
- if ( !done ) printk(KERN_ERR "soundscape: OBP Initialization failed.\n");
- sscape_write( devc, 2, devc->ic_type == IC_ODIE ? 0x70 : 0x40);
- sscape_write( devc, 3, (devc -> dma << 4) + 0x80);
- return 1;
- }
- static void __init sscape_pnp_init_hw(sscape_info* devc)
- {
- unsigned char midi_irq = 0, sb_irq = 0;
- unsigned i;
- static char code_file_name[23] = "/sndscape/sndscape.cox";
-
- int sscape_joystic_enable = 0x7f;
- int sscape_mic_enable = 0;
- int sscape_ext_midi = 0;
- if ( !sscape_pnp_alloc_dma(devc) ) {
- printk(KERN_ERR "sscape: faild to allocate dma\n");
- return;
- }
- for (i = 0; i < 4; i++) {
- if ( devc -> irq == valid_interrupts[i] )
- midi_irq = i;
- if ( devc -> codec_irq == valid_interrupts[i] )
- sb_irq = i;
- }
- sscape_write( devc, 5, 0x50);
- sscape_write( devc, 7, 0x2e);
- sscape_write( devc, 8, 0x00);
- sscape_write( devc, 2, devc->ic_type == IC_ODIE ? 0x70 : 0x40);
- sscape_write( devc, 3, ( devc -> dma << 4) | 0x80);
- sscape_write (devc, 4, 0xF0 | (midi_irq<<2) | midi_irq);
- i = 0x10; //sscape_read(devc, 9) & (devc->ic_type == IC_ODIE ? 0xf0 : 0xc0);
- if (sscape_joystic_enable) i |= 8;
-
- sscape_write (devc, 9, i);
- sscape_write (devc, 6, 0x80);
- sscape_write (devc, 1, 0x80);
- if (devc -> codec_type == 2) {
- sscape_pnp_write_codec( devc, 0x0C, 0x50);
- sscape_pnp_write_codec( devc, 0x10, sscape_pnp_read_codec( devc, 0x10) & 0x3F);
- sscape_pnp_write_codec( devc, 0x11, sscape_pnp_read_codec( devc, 0x11) | 0xC0);
- sscape_pnp_write_codec( devc, 29, 0x20);
- }
- if (sscape_pnp_upload_file(devc, "/sndscape/scope.cod") == 0 ) {
- printk(KERN_ERR "sscape: faild to upload file /sndscape/scope.cod\n");
- sscape_pnp_free_dma(devc);
- return;
- }
- i = sscape_read_host_ctrl( devc );
-
- if ( (i & 0x0F) > 7 ) {
- printk(KERN_ERR "sscape: scope.cod faild\n");
- sscape_pnp_free_dma(devc);
- return;
- }
- if ( i & 0x10 ) sscape_write( devc, 7, 0x2F);
- code_file_name[21] = (char) ( i & 0x0F) + 0x30;
- if (sscape_pnp_upload_file( devc, code_file_name) == 0) {
- printk(KERN_ERR "sscape: faild to upload file %s\n", code_file_name);
- sscape_pnp_free_dma(devc);
- return;
- }
-
- if (devc->ic_type != IC_ODIE) {
- sscape_pnp_write_codec( devc, 10, (sscape_pnp_read_codec(devc, 10) & 0x7f) |
- ( sscape_mic_enable == 0 ? 0x00 : 0x80) );
- }
- sscape_write_host_ctrl2( devc, 0x84, 0x64 ); /* MIDI volume */
- sscape_write_host_ctrl2( devc, 0x86, 0x64 ); /* MIDI volume?? */
- sscape_write_host_ctrl2( devc, 0x8A, sscape_ext_midi);
- sscape_pnp_write_codec ( devc, 6, 0x3f ); //WAV_VOL
- sscape_pnp_write_codec ( devc, 7, 0x3f ); //WAV_VOL
- sscape_pnp_write_codec ( devc, 2, 0x1F ); //WD_CDXVOLL
- sscape_pnp_write_codec ( devc, 3, 0x1F ); //WD_CDXVOLR
- if (devc -> codec_type == 1) {
- sscape_pnp_write_codec ( devc, 4, 0x1F );
- sscape_pnp_write_codec ( devc, 5, 0x1F );
- sscape_write_host_ctrl2( devc, 0x88, sscape_mic_enable);
- } else {
- int t;
- sscape_pnp_write_codec ( devc, 0x10, 0x1F << 1);
- sscape_pnp_write_codec ( devc, 0x11, 0xC0 | (0x1F << 1));
- t = sscape_pnp_read_codec( devc, 0x00) & 0xDF;
- if ( (sscape_mic_enable == 0)) t |= 0;
- else t |= 0x20;
- sscape_pnp_write_codec ( devc, 0x00, t);
- t = sscape_pnp_read_codec( devc, 0x01) & 0xDF;
- if ( (sscape_mic_enable == 0) ) t |= 0;
- else t |= 0x20;
- sscape_pnp_write_codec ( devc, 0x01, t);
- sscape_pnp_write_codec ( devc, 0x40 | 29 , 0x20);
- outb(0, devc -> codec);
- }
- if (devc -> ic_type == IC_OPUS ) {
- int i = sscape_read( devc, 9 );
- sscape_write( devc, 9, i | 3 );
- sscape_write( devc, 3, 0x40);
- if (request_region(0x228, 1, "sscape setup junk")) {
- outb(0, 0x228);
- release_region(0x228,1);
- }
- sscape_write( devc, 3, (devc -> dma << 4) | 0x80);
- sscape_write( devc, 9, i );
- }
-
- host_close ( devc );
- sscape_pnp_free_dma(devc);
- }
- static int __init detect_sscape_pnp(sscape_info* devc)
- {
- long i, irq_bits = 0xff;
- unsigned int d;
- DDB(printk("Entered detect_sscape_pnp(%x)\n", devc->base));
- if (!request_region(devc->codec, 2, "sscape codec")) {
- printk(KERN_ERR "detect_sscape_pnp: port %x is not free\n", devc->codec);
- return 0;
- }
- if ((inb(devc->base + 2) & 0x78) != 0)
- goto fail;
- d = inb ( devc -> base + 4) & 0xF0;
- if (d & 0x80)
- goto fail;
-
- if (d == 0) {
- devc->codec_type = 1;
- devc->ic_type = IC_ODIE;
- } else if ( (d & 0x60) != 0) {
- devc->codec_type = 2;
- devc->ic_type = IC_OPUS;
- } else if ( (d & 0x40) != 0) { /* WTF? */
- devc->codec_type = 2;
- devc->ic_type = IC_ODIE;
- } else
- goto fail;
-
- sscape_is_pnp = 1;
-
- outb(0xFA, devc -> base+4);
- if ((inb( devc -> base+4) & 0x9F) != 0x0A)
- goto fail;
- outb(0xFE, devc -> base+4);
- if ( (inb(devc -> base+4) & 0x9F) != 0x0E)
- goto fail;
- if ( (inb(devc -> base+5) & 0x9F) != 0x0E)
- goto fail;
- if (devc->codec_type == 2) {
- if (devc->codec != devc->base + 8) {
- printk("soundscape warning: incorrect codec port specified\n");
- goto fail;
- }
- d = 0x10 | (sscape_read(devc, 9) & 0xCF);
- sscape_write(devc, 9, d);
- sscape_write(devc, 6, 0x80);
- } else {
- //todo: check codec is not base + 8
- }
- d = (sscape_read(devc, 9) & 0x3F) | 0xC0;
- sscape_write(devc, 9, d);
- for (i = 0; i < 550000; i++)
- if ( !(inb(devc -> codec) & 0x80) ) break;
- d = inb(devc -> codec);
- if (d & 0x80)
- goto fail;
- if ( inb(devc -> codec + 2) == 0xFF)
- goto fail;
- sscape_write(devc, 9, sscape_read(devc, 9) & 0x3F );
- d = inb(devc -> codec) & 0x80;
- if ( d == 0) {
- printk(KERN_INFO "soundscape: hardware detected\n");
- valid_interrupts = valid_interrupts_new;
- } else {
- printk(KERN_INFO "soundscape: board looks like media fx\n");
- valid_interrupts = valid_interrupts_old;
- old_hardware = 1;
- }
- sscape_write( devc, 9, 0xC0 | (sscape_read(devc, 9) & 0x3F) );
- for (i = 0; i < 550000; i++)
- if ( !(inb(devc -> codec) & 0x80))
- break;
-
- sscape_pnp_init_hw(devc);
- for (i = 0; i < 4; i++)
- {
- if (devc->codec_irq == valid_interrupts[i]) {
- irq_bits = i;
- break;
- }
- }
- sscape_write(devc, GA_INTENA_REG, 0x00);
- sscape_write(devc, GA_DMACFG_REG, 0x50);
- sscape_write(devc, GA_DMAA_REG, 0x70);
- sscape_write(devc, GA_DMAB_REG, 0x20);
- sscape_write(devc, GA_INTCFG_REG, 0xf0);
- sscape_write(devc, GA_CDCFG_REG, 0x89 | (devc->dma << 4) | (irq_bits << 1));
- sscape_pnp_write_codec( devc, 0, sscape_pnp_read_codec( devc, 0) | 0x20);
- sscape_pnp_write_codec( devc, 0, sscape_pnp_read_codec( devc, 1) | 0x20);
- return 1;
- fail:
- release_region(devc->codec, 2);
- return 0;
- }
- static int __init probe_sscape(struct address_info *hw_config)
- {
- devc->base = hw_config->io_base;
- devc->irq = hw_config->irq;
- devc->dma = hw_config->dma;
- devc->osp = hw_config->osp;
- #ifdef SSCAPE_DEBUG1
- /*
- * Temporary debugging aid. Print contents of the registers before
- * changing them.
- */
- {
- int i;
- for (i = 0; i < 13; i++)
- printk("I%d = %02x (old value)\n", i, sscape_read(devc, i));
- }
- #endif
- devc->failed = 1;
- sscape_ports = request_region(devc->base, 2, "mpu401");
- if (!sscape_ports)
- return 0;
- if (!request_region(devc->base + 2, 6, "SoundScape")) {
- release_region(devc->base, 2);
- return 0;
- }
- if (!detect_ga(devc)) {
- if (detect_sscape_pnp(devc))
- return 1;
- release_region(devc->base, 2);
- release_region(devc->base + 2, 6);
- return 0;
- }
- if (old_hardware) /* Check that it's really an old Spea/Reveal card. */
- {
- unsigned char tmp;
- int cc;
- if (!((tmp = sscape_read(devc, GA_HMCTL_REG)) & 0xc0))
- {
- sscape_write(devc, GA_HMCTL_REG, tmp | 0x80);
- for (cc = 0; cc < 200000; ++cc)
- inb(devc->base + ODIE_ADDR);
- }
- }
- return 1;
- }
- static int __init init_ss_ms_sound(struct address_info *hw_config)
- {
- int i, irq_bits = 0xff;
- int ad_flags = 0;
- struct resource *ports;
-
- if (devc->failed)
- {
- printk(KERN_ERR "soundscape: Card not detected\n");
- return 0;
- }
- if (devc->ok == 0)
- {
- printk(KERN_ERR "soundscape: Invalid initialization order.\n");
- return 0;
- }
- for (i = 0; i < 4; i++)
- {
- if (hw_config->irq == valid_interrupts[i])
- {
- irq_bits = i;
- break;
- }
- }
- if (irq_bits == 0xff) {
- printk(KERN_ERR "soundscape: Invalid MSS IRQ%d\n", hw_config->irq);
- return 0;
- }
-
- if (old_hardware)
- ad_flags = 0x12345677; /* Tell that we may have a CS4248 chip (Spea-V7 Media FX) */
- else if (sscape_is_pnp)
- ad_flags = 0x87654321; /* Tell that we have a soundscape pnp with 1845 chip */
- ports = request_region(hw_config->io_base, 4, "ad1848");
- if (!ports) {
- printk(KERN_ERR "soundscape: ports busy\n");
- return 0;
- }
- if (!ad1848_detect(ports, &ad_flags, hw_config->osp)) {
- release_region(hw_config->io_base, 4);
- return 0;
- }
- if (!sscape_is_pnp) /*pnp is already setup*/
- {
- /*
- * Setup the DMA polarity.
- */
- sscape_write(devc, GA_DMACFG_REG, 0x50);
-
- /*
- * Take the gate-array off of the DMA channel.
- */
- sscape_write(devc, GA_DMAB_REG, 0x20);
-
- /*
- * Init the AD1848 (CD-ROM) config reg.
- */
- sscape_write(devc, GA_CDCFG_REG, 0x89 | (hw_config->dma << 4) | (irq_bits << 1));
- }
-
- if (hw_config->irq == devc->irq)
- printk(KERN_WARNING "soundscape: Warning! The WSS mode can't share IRQ with MIDI\n");
-
- hw_config->slots[0] = ad1848_init(
- sscape_is_pnp ? "SoundScape" : "SoundScape PNP",
- ports,
- hw_config->irq,
- hw_config->dma,
- hw_config->dma,
- 0,
- devc->osp,
- THIS_MODULE);
-
- if (hw_config->slots[0] != -1) /* The AD1848 driver installed itself */
- {
- audio_devs[hw_config->slots[0]]->coproc = &sscape_coproc_operations;
- devc->codec_audiodev = hw_config->slots[0];
- devc->my_audiodev = hw_config->slots[0];
- /* Set proper routings here (what are they) */
- AD1848_REROUTE(SOUND_MIXER_LINE1, SOUND_MIXER_LINE);
- }
-
- #ifdef SSCAPE_DEBUG5
- /*
- * Temporary debugging aid. Print contents of the registers
- * after the AD1848 device has been initialized.
- */
- {
- int i;
- for (i = 0; i < 13; i++)
- printk("I%d = %02x\n", i, sscape_read(devc, i));
- }
- #endif
- return 1;
- }
- static void __exit unload_sscape(struct address_info *hw_config)
- {
- release_region(devc->base + 2, 6);
- unload_mpu401(hw_config);
- if (sscape_is_pnp)
- release_region(devc->codec, 2);
- }
- static void __exit unload_ss_ms_sound(struct address_info *hw_config)
- {
- ad1848_unload(hw_config->io_base,
- hw_config->irq,
- devc->dma,
- devc->dma,
- 0);
- sound_unload_audiodev(hw_config->slots[0]);
- }
- static struct address_info cfg;
- static struct address_info cfg_mpu;
- static int __initdata spea = -1;
- static int mss = 0;
- static int __initdata dma = -1;
- static int __initdata irq = -1;
- static int __initdata io = -1;
- static int __initdata mpu_irq = -1;
- static int __initdata mpu_io = -1;
- module_param(dma, int, 0);
- module_param(irq, int, 0);
- module_param(io, int, 0);
- module_param(spea, int, 0); /* spea=0/1 set the old_hardware */
- module_param(mpu_irq, int, 0);
- module_param(mpu_io, int, 0);
- module_param(mss, int, 0);
- static int __init init_sscape(void)
- {
- printk(KERN_INFO "Soundscape driver Copyright (C) by Hannu Savolainen 1993-1996\n");
-
- cfg.irq = irq;
- cfg.dma = dma;
- cfg.io_base = io;
- cfg_mpu.irq = mpu_irq;
- cfg_mpu.io_base = mpu_io;
- /* WEH - Try to get right dma channel */
- cfg_mpu.dma = dma;
-
- devc->codec = cfg.io_base;
- devc->codec_irq = cfg.irq;
- devc->codec_type = 0;
- devc->ic_type = 0;
- devc->raw_buf = NULL;
- spin_lock_init(&devc->lock);
- if (cfg.dma == -1 || cfg.irq == -1 || cfg.io_base == -1) {
- printk(KERN_ERR "DMA, IRQ, and IO port must be specified.\n");
- return -EINVAL;
- }
-
- if (cfg_mpu.irq == -1 && cfg_mpu.io_base != -1) {
- printk(KERN_ERR "MPU_IRQ must be specified if MPU_IO is set.\n");
- return -EINVAL;
- }
-
- if(spea != -1) {
- old_hardware = spea;
- printk(KERN_INFO "Forcing %s hardware support.\n",
- spea?"new":"old");
- }
- if (probe_sscape(&cfg_mpu) == 0)
- return -ENODEV;
- attach_sscape(&cfg_mpu);
-
- mss = init_ss_ms_sound(&cfg);
- return 0;
- }
- static void __exit cleanup_sscape(void)
- {
- if (mss)
- unload_ss_ms_sound(&cfg);
- unload_sscape(&cfg_mpu);
- }
- module_init(init_sscape);
- module_exit(cleanup_sscape);
- #ifndef MODULE
- static int __init setup_sscape(char *str)
- {
- /* io, irq, dma, mpu_io, mpu_irq */
- int ints[6];
-
- str = get_options(str, ARRAY_SIZE(ints), ints);
-
- io = ints[1];
- irq = ints[2];
- dma = ints[3];
- mpu_io = ints[4];
- mpu_irq = ints[5];
- return 1;
- }
- __setup("sscape=", setup_sscape);
- #endif
- MODULE_LICENSE("GPL");
|