162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * ALSA card-level driver for Turtle Beach Wavefront cards 462306a36Sopenharmony_ci * (Maui,Tropez,Tropez+) 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Copyright (c) 1997-1999 by Paul Barton-Davis <pbd@op.net> 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/init.h> 1062306a36Sopenharmony_ci#include <linux/interrupt.h> 1162306a36Sopenharmony_ci#include <linux/err.h> 1262306a36Sopenharmony_ci#include <linux/isa.h> 1362306a36Sopenharmony_ci#include <linux/pnp.h> 1462306a36Sopenharmony_ci#include <linux/module.h> 1562306a36Sopenharmony_ci#include <sound/core.h> 1662306a36Sopenharmony_ci#include <sound/initval.h> 1762306a36Sopenharmony_ci#include <sound/opl3.h> 1862306a36Sopenharmony_ci#include <sound/wss.h> 1962306a36Sopenharmony_ci#include <sound/snd_wavefront.h> 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ciMODULE_AUTHOR("Paul Barton-Davis <pbd@op.net>"); 2262306a36Sopenharmony_ciMODULE_DESCRIPTION("Turtle Beach Wavefront"); 2362306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_cistatic int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ 2662306a36Sopenharmony_cistatic char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ 2762306a36Sopenharmony_cistatic bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */ 2862306a36Sopenharmony_ci#ifdef CONFIG_PNP 2962306a36Sopenharmony_cistatic bool isapnp[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1}; 3062306a36Sopenharmony_ci#endif 3162306a36Sopenharmony_cistatic long cs4232_pcm_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */ 3262306a36Sopenharmony_cistatic int cs4232_pcm_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 5,7,9,11,12,15 */ 3362306a36Sopenharmony_cistatic long cs4232_mpu_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */ 3462306a36Sopenharmony_cistatic int cs4232_mpu_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 9,11,12,15 */ 3562306a36Sopenharmony_cistatic long ics2115_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */ 3662306a36Sopenharmony_cistatic int ics2115_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 2,9,11,12,15 */ 3762306a36Sopenharmony_cistatic long fm_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */ 3862306a36Sopenharmony_cistatic int dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 0,1,3,5,6,7 */ 3962306a36Sopenharmony_cistatic int dma2[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 0,1,3,5,6,7 */ 4062306a36Sopenharmony_cistatic bool use_cs4232_midi[SNDRV_CARDS]; 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_cimodule_param_array(index, int, NULL, 0444); 4362306a36Sopenharmony_ciMODULE_PARM_DESC(index, "Index value for WaveFront soundcard."); 4462306a36Sopenharmony_cimodule_param_array(id, charp, NULL, 0444); 4562306a36Sopenharmony_ciMODULE_PARM_DESC(id, "ID string for WaveFront soundcard."); 4662306a36Sopenharmony_cimodule_param_array(enable, bool, NULL, 0444); 4762306a36Sopenharmony_ciMODULE_PARM_DESC(enable, "Enable WaveFront soundcard."); 4862306a36Sopenharmony_ci#ifdef CONFIG_PNP 4962306a36Sopenharmony_cimodule_param_array(isapnp, bool, NULL, 0444); 5062306a36Sopenharmony_ciMODULE_PARM_DESC(isapnp, "ISA PnP detection for WaveFront soundcards."); 5162306a36Sopenharmony_ci#endif 5262306a36Sopenharmony_cimodule_param_hw_array(cs4232_pcm_port, long, ioport, NULL, 0444); 5362306a36Sopenharmony_ciMODULE_PARM_DESC(cs4232_pcm_port, "Port # for CS4232 PCM interface."); 5462306a36Sopenharmony_cimodule_param_hw_array(cs4232_pcm_irq, int, irq, NULL, 0444); 5562306a36Sopenharmony_ciMODULE_PARM_DESC(cs4232_pcm_irq, "IRQ # for CS4232 PCM interface."); 5662306a36Sopenharmony_cimodule_param_hw_array(dma1, int, dma, NULL, 0444); 5762306a36Sopenharmony_ciMODULE_PARM_DESC(dma1, "DMA1 # for CS4232 PCM interface."); 5862306a36Sopenharmony_cimodule_param_hw_array(dma2, int, dma, NULL, 0444); 5962306a36Sopenharmony_ciMODULE_PARM_DESC(dma2, "DMA2 # for CS4232 PCM interface."); 6062306a36Sopenharmony_cimodule_param_hw_array(cs4232_mpu_port, long, ioport, NULL, 0444); 6162306a36Sopenharmony_ciMODULE_PARM_DESC(cs4232_mpu_port, "port # for CS4232 MPU-401 interface."); 6262306a36Sopenharmony_cimodule_param_hw_array(cs4232_mpu_irq, int, irq, NULL, 0444); 6362306a36Sopenharmony_ciMODULE_PARM_DESC(cs4232_mpu_irq, "IRQ # for CS4232 MPU-401 interface."); 6462306a36Sopenharmony_cimodule_param_hw_array(ics2115_irq, int, irq, NULL, 0444); 6562306a36Sopenharmony_ciMODULE_PARM_DESC(ics2115_irq, "IRQ # for ICS2115."); 6662306a36Sopenharmony_cimodule_param_hw_array(ics2115_port, long, ioport, NULL, 0444); 6762306a36Sopenharmony_ciMODULE_PARM_DESC(ics2115_port, "Port # for ICS2115."); 6862306a36Sopenharmony_cimodule_param_hw_array(fm_port, long, ioport, NULL, 0444); 6962306a36Sopenharmony_ciMODULE_PARM_DESC(fm_port, "FM port #."); 7062306a36Sopenharmony_cimodule_param_array(use_cs4232_midi, bool, NULL, 0444); 7162306a36Sopenharmony_ciMODULE_PARM_DESC(use_cs4232_midi, "Use CS4232 MPU-401 interface (inaccessibly located inside your computer)"); 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci#ifdef CONFIG_PNP 7462306a36Sopenharmony_cistatic int isa_registered; 7562306a36Sopenharmony_cistatic int pnp_registered; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_cistatic const struct pnp_card_device_id snd_wavefront_pnpids[] = { 7862306a36Sopenharmony_ci /* Tropez */ 7962306a36Sopenharmony_ci { .id = "CSC7532", .devs = { { "CSC0000" }, { "CSC0010" }, { "PnPb006" }, { "CSC0004" } } }, 8062306a36Sopenharmony_ci /* Tropez+ */ 8162306a36Sopenharmony_ci { .id = "CSC7632", .devs = { { "CSC0000" }, { "CSC0010" }, { "PnPb006" }, { "CSC0004" } } }, 8262306a36Sopenharmony_ci { .id = "" } 8362306a36Sopenharmony_ci}; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pnp_card, snd_wavefront_pnpids); 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_cistatic int 8862306a36Sopenharmony_cisnd_wavefront_pnp (int dev, snd_wavefront_card_t *acard, struct pnp_card_link *card, 8962306a36Sopenharmony_ci const struct pnp_card_device_id *id) 9062306a36Sopenharmony_ci{ 9162306a36Sopenharmony_ci struct pnp_dev *pdev; 9262306a36Sopenharmony_ci int err; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci /* Check for each logical device. */ 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci /* CS4232 chip (aka "windows sound system") is logical device 0 */ 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci acard->wss = pnp_request_card_device(card, id->devs[0].id, NULL); 9962306a36Sopenharmony_ci if (acard->wss == NULL) 10062306a36Sopenharmony_ci return -EBUSY; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci /* there is a game port at logical device 1, but we ignore it completely */ 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci /* the control interface is logical device 2, but we ignore it 10562306a36Sopenharmony_ci completely. in fact, nobody even seems to know what it 10662306a36Sopenharmony_ci does. 10762306a36Sopenharmony_ci */ 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci /* Only configure the CS4232 MIDI interface if its been 11062306a36Sopenharmony_ci specifically requested. It is logical device 3. 11162306a36Sopenharmony_ci */ 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci if (use_cs4232_midi[dev]) { 11462306a36Sopenharmony_ci acard->mpu = pnp_request_card_device(card, id->devs[2].id, NULL); 11562306a36Sopenharmony_ci if (acard->mpu == NULL) 11662306a36Sopenharmony_ci return -EBUSY; 11762306a36Sopenharmony_ci } 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci /* The ICS2115 synth is logical device 4 */ 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci acard->synth = pnp_request_card_device(card, id->devs[3].id, NULL); 12262306a36Sopenharmony_ci if (acard->synth == NULL) 12362306a36Sopenharmony_ci return -EBUSY; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci /* PCM/FM initialization */ 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci pdev = acard->wss; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci /* An interesting note from the Tropez+ FAQ: 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci Q. [Ports] Why is the base address of the WSS I/O ports off by 4? 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci A. WSS I/O requires a block of 8 I/O addresses ("ports"). Of these, the first 13462306a36Sopenharmony_ci 4 are used to identify and configure the board. With the advent of PnP, 13562306a36Sopenharmony_ci these first 4 addresses have become obsolete, and software applications 13662306a36Sopenharmony_ci only use the last 4 addresses to control the codec chip. Therefore, the 13762306a36Sopenharmony_ci base address setting "skips past" the 4 unused addresses. 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci */ 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci err = pnp_activate_dev(pdev); 14262306a36Sopenharmony_ci if (err < 0) { 14362306a36Sopenharmony_ci snd_printk(KERN_ERR "PnP WSS pnp configure failure\n"); 14462306a36Sopenharmony_ci return err; 14562306a36Sopenharmony_ci } 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci cs4232_pcm_port[dev] = pnp_port_start(pdev, 0); 14862306a36Sopenharmony_ci fm_port[dev] = pnp_port_start(pdev, 1); 14962306a36Sopenharmony_ci dma1[dev] = pnp_dma(pdev, 0); 15062306a36Sopenharmony_ci dma2[dev] = pnp_dma(pdev, 1); 15162306a36Sopenharmony_ci cs4232_pcm_irq[dev] = pnp_irq(pdev, 0); 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci /* Synth initialization */ 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci pdev = acard->synth; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci err = pnp_activate_dev(pdev); 15862306a36Sopenharmony_ci if (err < 0) { 15962306a36Sopenharmony_ci snd_printk(KERN_ERR "PnP ICS2115 pnp configure failure\n"); 16062306a36Sopenharmony_ci return err; 16162306a36Sopenharmony_ci } 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci ics2115_port[dev] = pnp_port_start(pdev, 0); 16462306a36Sopenharmony_ci ics2115_irq[dev] = pnp_irq(pdev, 0); 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci /* CS4232 MPU initialization. Configure this only if 16762306a36Sopenharmony_ci explicitly requested, since its physically inaccessible and 16862306a36Sopenharmony_ci consumes another IRQ. 16962306a36Sopenharmony_ci */ 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci if (use_cs4232_midi[dev]) { 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci pdev = acard->mpu; 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci err = pnp_activate_dev(pdev); 17662306a36Sopenharmony_ci if (err < 0) { 17762306a36Sopenharmony_ci snd_printk(KERN_ERR "PnP MPU401 pnp configure failure\n"); 17862306a36Sopenharmony_ci cs4232_mpu_port[dev] = SNDRV_AUTO_PORT; 17962306a36Sopenharmony_ci } else { 18062306a36Sopenharmony_ci cs4232_mpu_port[dev] = pnp_port_start(pdev, 0); 18162306a36Sopenharmony_ci cs4232_mpu_irq[dev] = pnp_irq(pdev, 0); 18262306a36Sopenharmony_ci } 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci snd_printk (KERN_INFO "CS4232 MPU: port=0x%lx, irq=%i\n", 18562306a36Sopenharmony_ci cs4232_mpu_port[dev], 18662306a36Sopenharmony_ci cs4232_mpu_irq[dev]); 18762306a36Sopenharmony_ci } 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci snd_printdd ("CS4232: pcm port=0x%lx, fm port=0x%lx, dma1=%i, dma2=%i, irq=%i\nICS2115: port=0x%lx, irq=%i\n", 19062306a36Sopenharmony_ci cs4232_pcm_port[dev], 19162306a36Sopenharmony_ci fm_port[dev], 19262306a36Sopenharmony_ci dma1[dev], 19362306a36Sopenharmony_ci dma2[dev], 19462306a36Sopenharmony_ci cs4232_pcm_irq[dev], 19562306a36Sopenharmony_ci ics2115_port[dev], 19662306a36Sopenharmony_ci ics2115_irq[dev]); 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci return 0; 19962306a36Sopenharmony_ci} 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci#endif /* CONFIG_PNP */ 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_cistatic irqreturn_t snd_wavefront_ics2115_interrupt(int irq, void *dev_id) 20462306a36Sopenharmony_ci{ 20562306a36Sopenharmony_ci snd_wavefront_card_t *acard; 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci acard = (snd_wavefront_card_t *) dev_id; 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci if (acard == NULL) 21062306a36Sopenharmony_ci return IRQ_NONE; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci if (acard->wavefront.interrupts_are_midi) { 21362306a36Sopenharmony_ci snd_wavefront_midi_interrupt (acard); 21462306a36Sopenharmony_ci } else { 21562306a36Sopenharmony_ci snd_wavefront_internal_interrupt (acard); 21662306a36Sopenharmony_ci } 21762306a36Sopenharmony_ci return IRQ_HANDLED; 21862306a36Sopenharmony_ci} 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_cistatic struct snd_hwdep *snd_wavefront_new_synth(struct snd_card *card, 22162306a36Sopenharmony_ci int hw_dev, 22262306a36Sopenharmony_ci snd_wavefront_card_t *acard) 22362306a36Sopenharmony_ci{ 22462306a36Sopenharmony_ci struct snd_hwdep *wavefront_synth; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci if (snd_wavefront_detect (acard) < 0) { 22762306a36Sopenharmony_ci return NULL; 22862306a36Sopenharmony_ci } 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci if (snd_wavefront_start (&acard->wavefront) < 0) { 23162306a36Sopenharmony_ci return NULL; 23262306a36Sopenharmony_ci } 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci if (snd_hwdep_new(card, "WaveFront", hw_dev, &wavefront_synth) < 0) 23562306a36Sopenharmony_ci return NULL; 23662306a36Sopenharmony_ci strcpy (wavefront_synth->name, 23762306a36Sopenharmony_ci "WaveFront (ICS2115) wavetable synthesizer"); 23862306a36Sopenharmony_ci wavefront_synth->ops.open = snd_wavefront_synth_open; 23962306a36Sopenharmony_ci wavefront_synth->ops.release = snd_wavefront_synth_release; 24062306a36Sopenharmony_ci wavefront_synth->ops.ioctl = snd_wavefront_synth_ioctl; 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci return wavefront_synth; 24362306a36Sopenharmony_ci} 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_cistatic struct snd_hwdep *snd_wavefront_new_fx(struct snd_card *card, 24662306a36Sopenharmony_ci int hw_dev, 24762306a36Sopenharmony_ci snd_wavefront_card_t *acard, 24862306a36Sopenharmony_ci unsigned long port) 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci{ 25162306a36Sopenharmony_ci struct snd_hwdep *fx_processor; 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci if (snd_wavefront_fx_start (&acard->wavefront)) { 25462306a36Sopenharmony_ci snd_printk (KERN_ERR "cannot initialize YSS225 FX processor"); 25562306a36Sopenharmony_ci return NULL; 25662306a36Sopenharmony_ci } 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci if (snd_hwdep_new (card, "YSS225", hw_dev, &fx_processor) < 0) 25962306a36Sopenharmony_ci return NULL; 26062306a36Sopenharmony_ci sprintf (fx_processor->name, "YSS225 FX Processor at 0x%lx", port); 26162306a36Sopenharmony_ci fx_processor->ops.open = snd_wavefront_fx_open; 26262306a36Sopenharmony_ci fx_processor->ops.release = snd_wavefront_fx_release; 26362306a36Sopenharmony_ci fx_processor->ops.ioctl = snd_wavefront_fx_ioctl; 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci return fx_processor; 26662306a36Sopenharmony_ci} 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_cistatic snd_wavefront_mpu_id internal_id = internal_mpu; 26962306a36Sopenharmony_cistatic snd_wavefront_mpu_id external_id = external_mpu; 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_cistatic struct snd_rawmidi *snd_wavefront_new_midi(struct snd_card *card, 27262306a36Sopenharmony_ci int midi_dev, 27362306a36Sopenharmony_ci snd_wavefront_card_t *acard, 27462306a36Sopenharmony_ci unsigned long port, 27562306a36Sopenharmony_ci snd_wavefront_mpu_id mpu) 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci{ 27862306a36Sopenharmony_ci struct snd_rawmidi *rmidi; 27962306a36Sopenharmony_ci static int first = 1; 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci if (first) { 28262306a36Sopenharmony_ci first = 0; 28362306a36Sopenharmony_ci acard->wavefront.midi.base = port; 28462306a36Sopenharmony_ci if (snd_wavefront_midi_start (acard)) { 28562306a36Sopenharmony_ci snd_printk (KERN_ERR "cannot initialize MIDI interface\n"); 28662306a36Sopenharmony_ci return NULL; 28762306a36Sopenharmony_ci } 28862306a36Sopenharmony_ci } 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci if (snd_rawmidi_new (card, "WaveFront MIDI", midi_dev, 1, 1, &rmidi) < 0) 29162306a36Sopenharmony_ci return NULL; 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci if (mpu == internal_mpu) { 29462306a36Sopenharmony_ci strcpy(rmidi->name, "WaveFront MIDI (Internal)"); 29562306a36Sopenharmony_ci rmidi->private_data = &internal_id; 29662306a36Sopenharmony_ci } else { 29762306a36Sopenharmony_ci strcpy(rmidi->name, "WaveFront MIDI (External)"); 29862306a36Sopenharmony_ci rmidi->private_data = &external_id; 29962306a36Sopenharmony_ci } 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_wavefront_midi_output); 30262306a36Sopenharmony_ci snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_wavefront_midi_input); 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | 30562306a36Sopenharmony_ci SNDRV_RAWMIDI_INFO_INPUT | 30662306a36Sopenharmony_ci SNDRV_RAWMIDI_INFO_DUPLEX; 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci return rmidi; 30962306a36Sopenharmony_ci} 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_cistatic int snd_wavefront_card_new(struct device *pdev, int dev, 31262306a36Sopenharmony_ci struct snd_card **cardp) 31362306a36Sopenharmony_ci{ 31462306a36Sopenharmony_ci struct snd_card *card; 31562306a36Sopenharmony_ci snd_wavefront_card_t *acard; 31662306a36Sopenharmony_ci int err; 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci err = snd_devm_card_new(pdev, index[dev], id[dev], THIS_MODULE, 31962306a36Sopenharmony_ci sizeof(snd_wavefront_card_t), &card); 32062306a36Sopenharmony_ci if (err < 0) 32162306a36Sopenharmony_ci return err; 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci acard = card->private_data; 32462306a36Sopenharmony_ci acard->wavefront.irq = -1; 32562306a36Sopenharmony_ci spin_lock_init(&acard->wavefront.irq_lock); 32662306a36Sopenharmony_ci init_waitqueue_head(&acard->wavefront.interrupt_sleeper); 32762306a36Sopenharmony_ci spin_lock_init(&acard->wavefront.midi.open); 32862306a36Sopenharmony_ci spin_lock_init(&acard->wavefront.midi.virtual); 32962306a36Sopenharmony_ci acard->wavefront.card = card; 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci *cardp = card; 33262306a36Sopenharmony_ci return 0; 33362306a36Sopenharmony_ci} 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_cistatic int 33662306a36Sopenharmony_cisnd_wavefront_probe (struct snd_card *card, int dev) 33762306a36Sopenharmony_ci{ 33862306a36Sopenharmony_ci snd_wavefront_card_t *acard = card->private_data; 33962306a36Sopenharmony_ci struct snd_wss *chip; 34062306a36Sopenharmony_ci struct snd_hwdep *wavefront_synth; 34162306a36Sopenharmony_ci struct snd_rawmidi *ics2115_internal_rmidi = NULL; 34262306a36Sopenharmony_ci struct snd_rawmidi *ics2115_external_rmidi = NULL; 34362306a36Sopenharmony_ci struct snd_hwdep *fx_processor; 34462306a36Sopenharmony_ci int hw_dev = 0, midi_dev = 0, err; 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci /* --------- PCM --------------- */ 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci err = snd_wss_create(card, cs4232_pcm_port[dev], -1, 34962306a36Sopenharmony_ci cs4232_pcm_irq[dev], dma1[dev], dma2[dev], 35062306a36Sopenharmony_ci WSS_HW_DETECT, 0, &chip); 35162306a36Sopenharmony_ci if (err < 0) { 35262306a36Sopenharmony_ci snd_printk(KERN_ERR "can't allocate WSS device\n"); 35362306a36Sopenharmony_ci return err; 35462306a36Sopenharmony_ci } 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci err = snd_wss_pcm(chip, 0); 35762306a36Sopenharmony_ci if (err < 0) 35862306a36Sopenharmony_ci return err; 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci err = snd_wss_timer(chip, 0); 36162306a36Sopenharmony_ci if (err < 0) 36262306a36Sopenharmony_ci return err; 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci /* ---------- OPL3 synth --------- */ 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci if (fm_port[dev] > 0 && fm_port[dev] != SNDRV_AUTO_PORT) { 36762306a36Sopenharmony_ci struct snd_opl3 *opl3; 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci err = snd_opl3_create(card, fm_port[dev], fm_port[dev] + 2, 37062306a36Sopenharmony_ci OPL3_HW_OPL3_CS, 0, &opl3); 37162306a36Sopenharmony_ci if (err < 0) { 37262306a36Sopenharmony_ci snd_printk (KERN_ERR "can't allocate or detect OPL3 synth\n"); 37362306a36Sopenharmony_ci return err; 37462306a36Sopenharmony_ci } 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci err = snd_opl3_hwdep_new(opl3, hw_dev, 1, NULL); 37762306a36Sopenharmony_ci if (err < 0) 37862306a36Sopenharmony_ci return err; 37962306a36Sopenharmony_ci hw_dev++; 38062306a36Sopenharmony_ci } 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci /* ------- ICS2115 Wavetable synth ------- */ 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci acard->wavefront.res_base = 38562306a36Sopenharmony_ci devm_request_region(card->dev, ics2115_port[dev], 16, 38662306a36Sopenharmony_ci "ICS2115"); 38762306a36Sopenharmony_ci if (acard->wavefront.res_base == NULL) { 38862306a36Sopenharmony_ci snd_printk(KERN_ERR "unable to grab ICS2115 i/o region 0x%lx-0x%lx\n", 38962306a36Sopenharmony_ci ics2115_port[dev], ics2115_port[dev] + 16 - 1); 39062306a36Sopenharmony_ci return -EBUSY; 39162306a36Sopenharmony_ci } 39262306a36Sopenharmony_ci if (devm_request_irq(card->dev, ics2115_irq[dev], 39362306a36Sopenharmony_ci snd_wavefront_ics2115_interrupt, 39462306a36Sopenharmony_ci 0, "ICS2115", acard)) { 39562306a36Sopenharmony_ci snd_printk(KERN_ERR "unable to use ICS2115 IRQ %d\n", ics2115_irq[dev]); 39662306a36Sopenharmony_ci return -EBUSY; 39762306a36Sopenharmony_ci } 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci acard->wavefront.irq = ics2115_irq[dev]; 40062306a36Sopenharmony_ci card->sync_irq = acard->wavefront.irq; 40162306a36Sopenharmony_ci acard->wavefront.base = ics2115_port[dev]; 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci wavefront_synth = snd_wavefront_new_synth(card, hw_dev, acard); 40462306a36Sopenharmony_ci if (wavefront_synth == NULL) { 40562306a36Sopenharmony_ci snd_printk (KERN_ERR "can't create WaveFront synth device\n"); 40662306a36Sopenharmony_ci return -ENOMEM; 40762306a36Sopenharmony_ci } 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci strcpy (wavefront_synth->name, "ICS2115 Wavetable MIDI Synthesizer"); 41062306a36Sopenharmony_ci wavefront_synth->iface = SNDRV_HWDEP_IFACE_ICS2115; 41162306a36Sopenharmony_ci hw_dev++; 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci /* --------- Mixer ------------ */ 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci err = snd_wss_mixer(chip); 41662306a36Sopenharmony_ci if (err < 0) { 41762306a36Sopenharmony_ci snd_printk (KERN_ERR "can't allocate mixer device\n"); 41862306a36Sopenharmony_ci return err; 41962306a36Sopenharmony_ci } 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci /* -------- CS4232 MPU-401 interface -------- */ 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci if (cs4232_mpu_port[dev] > 0 && cs4232_mpu_port[dev] != SNDRV_AUTO_PORT) { 42462306a36Sopenharmony_ci err = snd_mpu401_uart_new(card, midi_dev, MPU401_HW_CS4232, 42562306a36Sopenharmony_ci cs4232_mpu_port[dev], 0, 42662306a36Sopenharmony_ci cs4232_mpu_irq[dev], NULL); 42762306a36Sopenharmony_ci if (err < 0) { 42862306a36Sopenharmony_ci snd_printk (KERN_ERR "can't allocate CS4232 MPU-401 device\n"); 42962306a36Sopenharmony_ci return err; 43062306a36Sopenharmony_ci } 43162306a36Sopenharmony_ci midi_dev++; 43262306a36Sopenharmony_ci } 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci /* ------ ICS2115 internal MIDI ------------ */ 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci if (ics2115_port[dev] > 0 && ics2115_port[dev] != SNDRV_AUTO_PORT) { 43762306a36Sopenharmony_ci ics2115_internal_rmidi = 43862306a36Sopenharmony_ci snd_wavefront_new_midi (card, 43962306a36Sopenharmony_ci midi_dev, 44062306a36Sopenharmony_ci acard, 44162306a36Sopenharmony_ci ics2115_port[dev], 44262306a36Sopenharmony_ci internal_mpu); 44362306a36Sopenharmony_ci if (ics2115_internal_rmidi == NULL) { 44462306a36Sopenharmony_ci snd_printk (KERN_ERR "can't setup ICS2115 internal MIDI device\n"); 44562306a36Sopenharmony_ci return -ENOMEM; 44662306a36Sopenharmony_ci } 44762306a36Sopenharmony_ci midi_dev++; 44862306a36Sopenharmony_ci } 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci /* ------ ICS2115 external MIDI ------------ */ 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci if (ics2115_port[dev] > 0 && ics2115_port[dev] != SNDRV_AUTO_PORT) { 45362306a36Sopenharmony_ci ics2115_external_rmidi = 45462306a36Sopenharmony_ci snd_wavefront_new_midi (card, 45562306a36Sopenharmony_ci midi_dev, 45662306a36Sopenharmony_ci acard, 45762306a36Sopenharmony_ci ics2115_port[dev], 45862306a36Sopenharmony_ci external_mpu); 45962306a36Sopenharmony_ci if (ics2115_external_rmidi == NULL) { 46062306a36Sopenharmony_ci snd_printk (KERN_ERR "can't setup ICS2115 external MIDI device\n"); 46162306a36Sopenharmony_ci return -ENOMEM; 46262306a36Sopenharmony_ci } 46362306a36Sopenharmony_ci midi_dev++; 46462306a36Sopenharmony_ci } 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci /* FX processor for Tropez+ */ 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci if (acard->wavefront.has_fx) { 46962306a36Sopenharmony_ci fx_processor = snd_wavefront_new_fx (card, 47062306a36Sopenharmony_ci hw_dev, 47162306a36Sopenharmony_ci acard, 47262306a36Sopenharmony_ci ics2115_port[dev]); 47362306a36Sopenharmony_ci if (fx_processor == NULL) { 47462306a36Sopenharmony_ci snd_printk (KERN_ERR "can't setup FX device\n"); 47562306a36Sopenharmony_ci return -ENOMEM; 47662306a36Sopenharmony_ci } 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci hw_dev++; 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci strcpy(card->driver, "Tropez+"); 48162306a36Sopenharmony_ci strcpy(card->shortname, "Turtle Beach Tropez+"); 48262306a36Sopenharmony_ci } else { 48362306a36Sopenharmony_ci /* Need a way to distinguish between Maui and Tropez */ 48462306a36Sopenharmony_ci strcpy(card->driver, "WaveFront"); 48562306a36Sopenharmony_ci strcpy(card->shortname, "Turtle Beach WaveFront"); 48662306a36Sopenharmony_ci } 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci /* ----- Register the card --------- */ 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci /* Not safe to include "Turtle Beach" in longname, due to 49162306a36Sopenharmony_ci length restrictions 49262306a36Sopenharmony_ci */ 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci sprintf(card->longname, "%s PCM 0x%lx irq %d dma %d", 49562306a36Sopenharmony_ci card->driver, 49662306a36Sopenharmony_ci chip->port, 49762306a36Sopenharmony_ci cs4232_pcm_irq[dev], 49862306a36Sopenharmony_ci dma1[dev]); 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci if (dma2[dev] >= 0 && dma2[dev] < 8) 50162306a36Sopenharmony_ci sprintf(card->longname + strlen(card->longname), "&%d", dma2[dev]); 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci if (cs4232_mpu_port[dev] > 0 && cs4232_mpu_port[dev] != SNDRV_AUTO_PORT) { 50462306a36Sopenharmony_ci sprintf (card->longname + strlen (card->longname), 50562306a36Sopenharmony_ci " MPU-401 0x%lx irq %d", 50662306a36Sopenharmony_ci cs4232_mpu_port[dev], 50762306a36Sopenharmony_ci cs4232_mpu_irq[dev]); 50862306a36Sopenharmony_ci } 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci sprintf (card->longname + strlen (card->longname), 51162306a36Sopenharmony_ci " SYNTH 0x%lx irq %d", 51262306a36Sopenharmony_ci ics2115_port[dev], 51362306a36Sopenharmony_ci ics2115_irq[dev]); 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci return snd_card_register(card); 51662306a36Sopenharmony_ci} 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_cistatic int snd_wavefront_isa_match(struct device *pdev, 51962306a36Sopenharmony_ci unsigned int dev) 52062306a36Sopenharmony_ci{ 52162306a36Sopenharmony_ci if (!enable[dev]) 52262306a36Sopenharmony_ci return 0; 52362306a36Sopenharmony_ci#ifdef CONFIG_PNP 52462306a36Sopenharmony_ci if (isapnp[dev]) 52562306a36Sopenharmony_ci return 0; 52662306a36Sopenharmony_ci#endif 52762306a36Sopenharmony_ci if (cs4232_pcm_port[dev] == SNDRV_AUTO_PORT) { 52862306a36Sopenharmony_ci snd_printk(KERN_ERR "specify CS4232 port\n"); 52962306a36Sopenharmony_ci return 0; 53062306a36Sopenharmony_ci } 53162306a36Sopenharmony_ci if (ics2115_port[dev] == SNDRV_AUTO_PORT) { 53262306a36Sopenharmony_ci snd_printk(KERN_ERR "specify ICS2115 port\n"); 53362306a36Sopenharmony_ci return 0; 53462306a36Sopenharmony_ci } 53562306a36Sopenharmony_ci return 1; 53662306a36Sopenharmony_ci} 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_cistatic int snd_wavefront_isa_probe(struct device *pdev, 53962306a36Sopenharmony_ci unsigned int dev) 54062306a36Sopenharmony_ci{ 54162306a36Sopenharmony_ci struct snd_card *card; 54262306a36Sopenharmony_ci int err; 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci err = snd_wavefront_card_new(pdev, dev, &card); 54562306a36Sopenharmony_ci if (err < 0) 54662306a36Sopenharmony_ci return err; 54762306a36Sopenharmony_ci err = snd_wavefront_probe(card, dev); 54862306a36Sopenharmony_ci if (err < 0) 54962306a36Sopenharmony_ci return err; 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci dev_set_drvdata(pdev, card); 55262306a36Sopenharmony_ci return 0; 55362306a36Sopenharmony_ci} 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci#define DEV_NAME "wavefront" 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_cistatic struct isa_driver snd_wavefront_driver = { 55862306a36Sopenharmony_ci .match = snd_wavefront_isa_match, 55962306a36Sopenharmony_ci .probe = snd_wavefront_isa_probe, 56062306a36Sopenharmony_ci /* FIXME: suspend, resume */ 56162306a36Sopenharmony_ci .driver = { 56262306a36Sopenharmony_ci .name = DEV_NAME 56362306a36Sopenharmony_ci }, 56462306a36Sopenharmony_ci}; 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci#ifdef CONFIG_PNP 56862306a36Sopenharmony_cistatic int snd_wavefront_pnp_detect(struct pnp_card_link *pcard, 56962306a36Sopenharmony_ci const struct pnp_card_device_id *pid) 57062306a36Sopenharmony_ci{ 57162306a36Sopenharmony_ci static int dev; 57262306a36Sopenharmony_ci struct snd_card *card; 57362306a36Sopenharmony_ci int res; 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci for ( ; dev < SNDRV_CARDS; dev++) { 57662306a36Sopenharmony_ci if (enable[dev] && isapnp[dev]) 57762306a36Sopenharmony_ci break; 57862306a36Sopenharmony_ci } 57962306a36Sopenharmony_ci if (dev >= SNDRV_CARDS) 58062306a36Sopenharmony_ci return -ENODEV; 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci res = snd_wavefront_card_new(&pcard->card->dev, dev, &card); 58362306a36Sopenharmony_ci if (res < 0) 58462306a36Sopenharmony_ci return res; 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci if (snd_wavefront_pnp (dev, card->private_data, pcard, pid) < 0) { 58762306a36Sopenharmony_ci if (cs4232_pcm_port[dev] == SNDRV_AUTO_PORT) { 58862306a36Sopenharmony_ci snd_printk (KERN_ERR "isapnp detection failed\n"); 58962306a36Sopenharmony_ci return -ENODEV; 59062306a36Sopenharmony_ci } 59162306a36Sopenharmony_ci } 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci res = snd_wavefront_probe(card, dev); 59462306a36Sopenharmony_ci if (res < 0) 59562306a36Sopenharmony_ci return res; 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci pnp_set_card_drvdata(pcard, card); 59862306a36Sopenharmony_ci dev++; 59962306a36Sopenharmony_ci return 0; 60062306a36Sopenharmony_ci} 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_cistatic struct pnp_card_driver wavefront_pnpc_driver = { 60362306a36Sopenharmony_ci .flags = PNP_DRIVER_RES_DISABLE, 60462306a36Sopenharmony_ci .name = "wavefront", 60562306a36Sopenharmony_ci .id_table = snd_wavefront_pnpids, 60662306a36Sopenharmony_ci .probe = snd_wavefront_pnp_detect, 60762306a36Sopenharmony_ci /* FIXME: suspend,resume */ 60862306a36Sopenharmony_ci}; 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci#endif /* CONFIG_PNP */ 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_cistatic int __init alsa_card_wavefront_init(void) 61362306a36Sopenharmony_ci{ 61462306a36Sopenharmony_ci int err; 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci err = isa_register_driver(&snd_wavefront_driver, SNDRV_CARDS); 61762306a36Sopenharmony_ci#ifdef CONFIG_PNP 61862306a36Sopenharmony_ci if (!err) 61962306a36Sopenharmony_ci isa_registered = 1; 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci err = pnp_register_card_driver(&wavefront_pnpc_driver); 62262306a36Sopenharmony_ci if (!err) 62362306a36Sopenharmony_ci pnp_registered = 1; 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_ci if (isa_registered) 62662306a36Sopenharmony_ci err = 0; 62762306a36Sopenharmony_ci#endif 62862306a36Sopenharmony_ci return err; 62962306a36Sopenharmony_ci} 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_cistatic void __exit alsa_card_wavefront_exit(void) 63262306a36Sopenharmony_ci{ 63362306a36Sopenharmony_ci#ifdef CONFIG_PNP 63462306a36Sopenharmony_ci if (pnp_registered) 63562306a36Sopenharmony_ci pnp_unregister_card_driver(&wavefront_pnpc_driver); 63662306a36Sopenharmony_ci if (isa_registered) 63762306a36Sopenharmony_ci#endif 63862306a36Sopenharmony_ci isa_unregister_driver(&snd_wavefront_driver); 63962306a36Sopenharmony_ci} 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_cimodule_init(alsa_card_wavefront_init) 64262306a36Sopenharmony_cimodule_exit(alsa_card_wavefront_exit) 643