162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Driver for AMD InterWave soundcard 462306a36Sopenharmony_ci * Copyright (c) by Jaroslav Kysela <perex@perex.cz> 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * 1999/07/22 Erik Inge Bolso <knan@mo.himolde.no> 762306a36Sopenharmony_ci * * mixer group handlers 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/init.h> 1162306a36Sopenharmony_ci#include <linux/err.h> 1262306a36Sopenharmony_ci#include <linux/isa.h> 1362306a36Sopenharmony_ci#include <linux/delay.h> 1462306a36Sopenharmony_ci#include <linux/pnp.h> 1562306a36Sopenharmony_ci#include <linux/module.h> 1662306a36Sopenharmony_ci#include <asm/dma.h> 1762306a36Sopenharmony_ci#include <sound/core.h> 1862306a36Sopenharmony_ci#include <sound/gus.h> 1962306a36Sopenharmony_ci#include <sound/wss.h> 2062306a36Sopenharmony_ci#ifdef SNDRV_STB 2162306a36Sopenharmony_ci#include <sound/tea6330t.h> 2262306a36Sopenharmony_ci#endif 2362306a36Sopenharmony_ci#define SNDRV_LEGACY_FIND_FREE_IRQ 2462306a36Sopenharmony_ci#define SNDRV_LEGACY_FIND_FREE_DMA 2562306a36Sopenharmony_ci#include <sound/initval.h> 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ciMODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>"); 2862306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 2962306a36Sopenharmony_ci#ifndef SNDRV_STB 3062306a36Sopenharmony_ciMODULE_DESCRIPTION("AMD InterWave"); 3162306a36Sopenharmony_ci#else 3262306a36Sopenharmony_ciMODULE_DESCRIPTION("AMD InterWave STB with TEA6330T"); 3362306a36Sopenharmony_ci#endif 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_cistatic int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ 3662306a36Sopenharmony_cistatic char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ 3762306a36Sopenharmony_cistatic bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP; /* Enable this card */ 3862306a36Sopenharmony_ci#ifdef CONFIG_PNP 3962306a36Sopenharmony_cistatic bool isapnp[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1}; 4062306a36Sopenharmony_ci#endif 4162306a36Sopenharmony_cistatic long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x210,0x220,0x230,0x240,0x250,0x260 */ 4262306a36Sopenharmony_ci#ifdef SNDRV_STB 4362306a36Sopenharmony_cistatic long port_tc[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x350,0x360,0x370,0x380 */ 4462306a36Sopenharmony_ci#endif 4562306a36Sopenharmony_cistatic int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 2,3,5,9,11,12,15 */ 4662306a36Sopenharmony_cistatic int dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 0,1,3,5,6,7 */ 4762306a36Sopenharmony_cistatic int dma2[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 0,1,3,5,6,7 */ 4862306a36Sopenharmony_cistatic int joystick_dac[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 29}; 4962306a36Sopenharmony_ci /* 0 to 31, (0.59V-4.52V or 0.389V-2.98V) */ 5062306a36Sopenharmony_cistatic int midi[SNDRV_CARDS]; 5162306a36Sopenharmony_cistatic int pcm_channels[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 2}; 5262306a36Sopenharmony_cistatic int effect[SNDRV_CARDS]; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci#ifdef SNDRV_STB 5562306a36Sopenharmony_ci#define PFX "interwave-stb: " 5662306a36Sopenharmony_ci#define INTERWAVE_DRIVER "snd_interwave_stb" 5762306a36Sopenharmony_ci#define INTERWAVE_PNP_DRIVER "interwave-stb" 5862306a36Sopenharmony_ci#else 5962306a36Sopenharmony_ci#define PFX "interwave: " 6062306a36Sopenharmony_ci#define INTERWAVE_DRIVER "snd_interwave" 6162306a36Sopenharmony_ci#define INTERWAVE_PNP_DRIVER "interwave" 6262306a36Sopenharmony_ci#endif 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_cimodule_param_array(index, int, NULL, 0444); 6562306a36Sopenharmony_ciMODULE_PARM_DESC(index, "Index value for InterWave soundcard."); 6662306a36Sopenharmony_cimodule_param_array(id, charp, NULL, 0444); 6762306a36Sopenharmony_ciMODULE_PARM_DESC(id, "ID string for InterWave soundcard."); 6862306a36Sopenharmony_cimodule_param_array(enable, bool, NULL, 0444); 6962306a36Sopenharmony_ciMODULE_PARM_DESC(enable, "Enable InterWave soundcard."); 7062306a36Sopenharmony_ci#ifdef CONFIG_PNP 7162306a36Sopenharmony_cimodule_param_array(isapnp, bool, NULL, 0444); 7262306a36Sopenharmony_ciMODULE_PARM_DESC(isapnp, "ISA PnP detection for specified soundcard."); 7362306a36Sopenharmony_ci#endif 7462306a36Sopenharmony_cimodule_param_hw_array(port, long, ioport, NULL, 0444); 7562306a36Sopenharmony_ciMODULE_PARM_DESC(port, "Port # for InterWave driver."); 7662306a36Sopenharmony_ci#ifdef SNDRV_STB 7762306a36Sopenharmony_cimodule_param_hw_array(port_tc, long, ioport, NULL, 0444); 7862306a36Sopenharmony_ciMODULE_PARM_DESC(port_tc, "Tone control (TEA6330T - i2c bus) port # for InterWave driver."); 7962306a36Sopenharmony_ci#endif 8062306a36Sopenharmony_cimodule_param_hw_array(irq, int, irq, NULL, 0444); 8162306a36Sopenharmony_ciMODULE_PARM_DESC(irq, "IRQ # for InterWave driver."); 8262306a36Sopenharmony_cimodule_param_hw_array(dma1, int, dma, NULL, 0444); 8362306a36Sopenharmony_ciMODULE_PARM_DESC(dma1, "DMA1 # for InterWave driver."); 8462306a36Sopenharmony_cimodule_param_hw_array(dma2, int, dma, NULL, 0444); 8562306a36Sopenharmony_ciMODULE_PARM_DESC(dma2, "DMA2 # for InterWave driver."); 8662306a36Sopenharmony_cimodule_param_array(joystick_dac, int, NULL, 0444); 8762306a36Sopenharmony_ciMODULE_PARM_DESC(joystick_dac, "Joystick DAC level 0.59V-4.52V or 0.389V-2.98V for InterWave driver."); 8862306a36Sopenharmony_cimodule_param_array(midi, int, NULL, 0444); 8962306a36Sopenharmony_ciMODULE_PARM_DESC(midi, "MIDI UART enable for InterWave driver."); 9062306a36Sopenharmony_cimodule_param_array(pcm_channels, int, NULL, 0444); 9162306a36Sopenharmony_ciMODULE_PARM_DESC(pcm_channels, "Reserved PCM channels for InterWave driver."); 9262306a36Sopenharmony_cimodule_param_array(effect, int, NULL, 0444); 9362306a36Sopenharmony_ciMODULE_PARM_DESC(effect, "Effects enable for InterWave driver."); 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_cistruct snd_interwave { 9662306a36Sopenharmony_ci int irq; 9762306a36Sopenharmony_ci struct snd_card *card; 9862306a36Sopenharmony_ci struct snd_gus_card *gus; 9962306a36Sopenharmony_ci struct snd_wss *wss; 10062306a36Sopenharmony_ci#ifdef SNDRV_STB 10162306a36Sopenharmony_ci struct resource *i2c_res; 10262306a36Sopenharmony_ci#endif 10362306a36Sopenharmony_ci unsigned short gus_status_reg; 10462306a36Sopenharmony_ci unsigned short pcm_status_reg; 10562306a36Sopenharmony_ci#ifdef CONFIG_PNP 10662306a36Sopenharmony_ci struct pnp_dev *dev; 10762306a36Sopenharmony_ci#ifdef SNDRV_STB 10862306a36Sopenharmony_ci struct pnp_dev *devtc; 10962306a36Sopenharmony_ci#endif 11062306a36Sopenharmony_ci#endif 11162306a36Sopenharmony_ci}; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci#ifdef CONFIG_PNP 11562306a36Sopenharmony_cistatic int isa_registered; 11662306a36Sopenharmony_cistatic int pnp_registered; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_cistatic const struct pnp_card_device_id snd_interwave_pnpids[] = { 11962306a36Sopenharmony_ci#ifndef SNDRV_STB 12062306a36Sopenharmony_ci /* Gravis UltraSound Plug & Play */ 12162306a36Sopenharmony_ci { .id = "GRV0001", .devs = { { .id = "GRV0000" } } }, 12262306a36Sopenharmony_ci /* STB SoundRage32 */ 12362306a36Sopenharmony_ci { .id = "STB011a", .devs = { { .id = "STB0010" } } }, 12462306a36Sopenharmony_ci /* MED3210 */ 12562306a36Sopenharmony_ci { .id = "DXP3201", .devs = { { .id = "DXP0010" } } }, 12662306a36Sopenharmony_ci /* Dynasonic Pro */ 12762306a36Sopenharmony_ci /* This device also have CDC1117:DynaSonix Pro Audio Effects Processor */ 12862306a36Sopenharmony_ci { .id = "CDC1111", .devs = { { .id = "CDC1112" } } }, 12962306a36Sopenharmony_ci /* Panasonic PCA761AW Audio Card */ 13062306a36Sopenharmony_ci { .id = "ADV55ff", .devs = { { .id = "ADV0010" } } }, 13162306a36Sopenharmony_ci /* InterWave STB without TEA6330T */ 13262306a36Sopenharmony_ci { .id = "ADV550a", .devs = { { .id = "ADV0010" } } }, 13362306a36Sopenharmony_ci#else 13462306a36Sopenharmony_ci /* InterWave STB with TEA6330T */ 13562306a36Sopenharmony_ci { .id = "ADV550a", .devs = { { .id = "ADV0010" }, { .id = "ADV0015" } } }, 13662306a36Sopenharmony_ci#endif 13762306a36Sopenharmony_ci { .id = "" } 13862306a36Sopenharmony_ci}; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pnp_card, snd_interwave_pnpids); 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci#endif /* CONFIG_PNP */ 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci#ifdef SNDRV_STB 14662306a36Sopenharmony_cistatic void snd_interwave_i2c_setlines(struct snd_i2c_bus *bus, int ctrl, int data) 14762306a36Sopenharmony_ci{ 14862306a36Sopenharmony_ci unsigned long port = bus->private_value; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci#if 0 15162306a36Sopenharmony_ci printk(KERN_DEBUG "i2c_setlines - 0x%lx <- %i,%i\n", port, ctrl, data); 15262306a36Sopenharmony_ci#endif 15362306a36Sopenharmony_ci outb((data << 1) | ctrl, port); 15462306a36Sopenharmony_ci udelay(10); 15562306a36Sopenharmony_ci} 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_cistatic int snd_interwave_i2c_getclockline(struct snd_i2c_bus *bus) 15862306a36Sopenharmony_ci{ 15962306a36Sopenharmony_ci unsigned long port = bus->private_value; 16062306a36Sopenharmony_ci unsigned char res; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci res = inb(port) & 1; 16362306a36Sopenharmony_ci#if 0 16462306a36Sopenharmony_ci printk(KERN_DEBUG "i2c_getclockline - 0x%lx -> %i\n", port, res); 16562306a36Sopenharmony_ci#endif 16662306a36Sopenharmony_ci return res; 16762306a36Sopenharmony_ci} 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_cistatic int snd_interwave_i2c_getdataline(struct snd_i2c_bus *bus, int ack) 17062306a36Sopenharmony_ci{ 17162306a36Sopenharmony_ci unsigned long port = bus->private_value; 17262306a36Sopenharmony_ci unsigned char res; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci if (ack) 17562306a36Sopenharmony_ci udelay(10); 17662306a36Sopenharmony_ci res = (inb(port) & 2) >> 1; 17762306a36Sopenharmony_ci#if 0 17862306a36Sopenharmony_ci printk(KERN_DEBUG "i2c_getdataline - 0x%lx -> %i\n", port, res); 17962306a36Sopenharmony_ci#endif 18062306a36Sopenharmony_ci return res; 18162306a36Sopenharmony_ci} 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_cistatic struct snd_i2c_bit_ops snd_interwave_i2c_bit_ops = { 18462306a36Sopenharmony_ci .setlines = snd_interwave_i2c_setlines, 18562306a36Sopenharmony_ci .getclock = snd_interwave_i2c_getclockline, 18662306a36Sopenharmony_ci .getdata = snd_interwave_i2c_getdataline, 18762306a36Sopenharmony_ci}; 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_cistatic int snd_interwave_detect_stb(struct snd_interwave *iwcard, 19062306a36Sopenharmony_ci struct snd_gus_card *gus, int dev, 19162306a36Sopenharmony_ci struct snd_i2c_bus **rbus) 19262306a36Sopenharmony_ci{ 19362306a36Sopenharmony_ci unsigned long port; 19462306a36Sopenharmony_ci struct snd_i2c_bus *bus; 19562306a36Sopenharmony_ci struct snd_card *card = iwcard->card; 19662306a36Sopenharmony_ci char name[32]; 19762306a36Sopenharmony_ci int err; 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci *rbus = NULL; 20062306a36Sopenharmony_ci port = port_tc[dev]; 20162306a36Sopenharmony_ci if (port == SNDRV_AUTO_PORT) { 20262306a36Sopenharmony_ci port = 0x350; 20362306a36Sopenharmony_ci if (gus->gf1.port == 0x250) { 20462306a36Sopenharmony_ci port = 0x360; 20562306a36Sopenharmony_ci } 20662306a36Sopenharmony_ci while (port <= 0x380) { 20762306a36Sopenharmony_ci iwcard->i2c_res = devm_request_region(card->dev, port, 1, 20862306a36Sopenharmony_ci "InterWave (I2C bus)"); 20962306a36Sopenharmony_ci if (iwcard->i2c_res) 21062306a36Sopenharmony_ci break; 21162306a36Sopenharmony_ci port += 0x10; 21262306a36Sopenharmony_ci } 21362306a36Sopenharmony_ci } else { 21462306a36Sopenharmony_ci iwcard->i2c_res = devm_request_region(card->dev, port, 1, 21562306a36Sopenharmony_ci "InterWave (I2C bus)"); 21662306a36Sopenharmony_ci } 21762306a36Sopenharmony_ci if (iwcard->i2c_res == NULL) { 21862306a36Sopenharmony_ci snd_printk(KERN_ERR "interwave: can't grab i2c bus port\n"); 21962306a36Sopenharmony_ci return -ENODEV; 22062306a36Sopenharmony_ci } 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci sprintf(name, "InterWave-%i", card->number); 22362306a36Sopenharmony_ci err = snd_i2c_bus_create(card, name, NULL, &bus); 22462306a36Sopenharmony_ci if (err < 0) 22562306a36Sopenharmony_ci return err; 22662306a36Sopenharmony_ci bus->private_value = port; 22762306a36Sopenharmony_ci bus->hw_ops.bit = &snd_interwave_i2c_bit_ops; 22862306a36Sopenharmony_ci err = snd_tea6330t_detect(bus, 0); 22962306a36Sopenharmony_ci if (err < 0) 23062306a36Sopenharmony_ci return err; 23162306a36Sopenharmony_ci *rbus = bus; 23262306a36Sopenharmony_ci return 0; 23362306a36Sopenharmony_ci} 23462306a36Sopenharmony_ci#endif 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_cistatic int snd_interwave_detect(struct snd_interwave *iwcard, 23762306a36Sopenharmony_ci struct snd_gus_card *gus, 23862306a36Sopenharmony_ci int dev 23962306a36Sopenharmony_ci#ifdef SNDRV_STB 24062306a36Sopenharmony_ci , struct snd_i2c_bus **rbus 24162306a36Sopenharmony_ci#endif 24262306a36Sopenharmony_ci ) 24362306a36Sopenharmony_ci{ 24462306a36Sopenharmony_ci unsigned long flags; 24562306a36Sopenharmony_ci unsigned char rev1, rev2; 24662306a36Sopenharmony_ci int d; 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 0); /* reset GF1 */ 24962306a36Sopenharmony_ci d = snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET); 25062306a36Sopenharmony_ci if ((d & 0x07) != 0) { 25162306a36Sopenharmony_ci snd_printdd("[0x%lx] check 1 failed - 0x%x\n", gus->gf1.port, d); 25262306a36Sopenharmony_ci return -ENODEV; 25362306a36Sopenharmony_ci } 25462306a36Sopenharmony_ci udelay(160); 25562306a36Sopenharmony_ci snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 1); /* release reset */ 25662306a36Sopenharmony_ci udelay(160); 25762306a36Sopenharmony_ci d = snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET); 25862306a36Sopenharmony_ci if ((d & 0x07) != 1) { 25962306a36Sopenharmony_ci snd_printdd("[0x%lx] check 2 failed - 0x%x\n", gus->gf1.port, d); 26062306a36Sopenharmony_ci return -ENODEV; 26162306a36Sopenharmony_ci } 26262306a36Sopenharmony_ci spin_lock_irqsave(&gus->reg_lock, flags); 26362306a36Sopenharmony_ci rev1 = snd_gf1_look8(gus, SNDRV_GF1_GB_VERSION_NUMBER); 26462306a36Sopenharmony_ci snd_gf1_write8(gus, SNDRV_GF1_GB_VERSION_NUMBER, ~rev1); 26562306a36Sopenharmony_ci rev2 = snd_gf1_look8(gus, SNDRV_GF1_GB_VERSION_NUMBER); 26662306a36Sopenharmony_ci snd_gf1_write8(gus, SNDRV_GF1_GB_VERSION_NUMBER, rev1); 26762306a36Sopenharmony_ci spin_unlock_irqrestore(&gus->reg_lock, flags); 26862306a36Sopenharmony_ci snd_printdd("[0x%lx] InterWave check - rev1=0x%x, rev2=0x%x\n", gus->gf1.port, rev1, rev2); 26962306a36Sopenharmony_ci if ((rev1 & 0xf0) == (rev2 & 0xf0) && 27062306a36Sopenharmony_ci (rev1 & 0x0f) != (rev2 & 0x0f)) { 27162306a36Sopenharmony_ci snd_printdd("[0x%lx] InterWave check - passed\n", gus->gf1.port); 27262306a36Sopenharmony_ci gus->interwave = 1; 27362306a36Sopenharmony_ci strcpy(gus->card->shortname, "AMD InterWave"); 27462306a36Sopenharmony_ci gus->revision = rev1 >> 4; 27562306a36Sopenharmony_ci#ifndef SNDRV_STB 27662306a36Sopenharmony_ci return 0; /* ok.. We have an InterWave board */ 27762306a36Sopenharmony_ci#else 27862306a36Sopenharmony_ci return snd_interwave_detect_stb(iwcard, gus, dev, rbus); 27962306a36Sopenharmony_ci#endif 28062306a36Sopenharmony_ci } 28162306a36Sopenharmony_ci snd_printdd("[0x%lx] InterWave check - failed\n", gus->gf1.port); 28262306a36Sopenharmony_ci return -ENODEV; 28362306a36Sopenharmony_ci} 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_cistatic irqreturn_t snd_interwave_interrupt(int irq, void *dev_id) 28662306a36Sopenharmony_ci{ 28762306a36Sopenharmony_ci struct snd_interwave *iwcard = dev_id; 28862306a36Sopenharmony_ci int loop, max = 5; 28962306a36Sopenharmony_ci int handled = 0; 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci do { 29262306a36Sopenharmony_ci loop = 0; 29362306a36Sopenharmony_ci if (inb(iwcard->gus_status_reg)) { 29462306a36Sopenharmony_ci handled = 1; 29562306a36Sopenharmony_ci snd_gus_interrupt(irq, iwcard->gus); 29662306a36Sopenharmony_ci loop++; 29762306a36Sopenharmony_ci } 29862306a36Sopenharmony_ci if (inb(iwcard->pcm_status_reg) & 0x01) { /* IRQ bit is set? */ 29962306a36Sopenharmony_ci handled = 1; 30062306a36Sopenharmony_ci snd_wss_interrupt(irq, iwcard->wss); 30162306a36Sopenharmony_ci loop++; 30262306a36Sopenharmony_ci } 30362306a36Sopenharmony_ci } while (loop && --max > 0); 30462306a36Sopenharmony_ci return IRQ_RETVAL(handled); 30562306a36Sopenharmony_ci} 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_cistatic void snd_interwave_reset(struct snd_gus_card *gus) 30862306a36Sopenharmony_ci{ 30962306a36Sopenharmony_ci snd_gf1_write8(gus, SNDRV_GF1_GB_RESET, 0x00); 31062306a36Sopenharmony_ci udelay(160); 31162306a36Sopenharmony_ci snd_gf1_write8(gus, SNDRV_GF1_GB_RESET, 0x01); 31262306a36Sopenharmony_ci udelay(160); 31362306a36Sopenharmony_ci} 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_cistatic void snd_interwave_bank_sizes(struct snd_gus_card *gus, int *sizes) 31662306a36Sopenharmony_ci{ 31762306a36Sopenharmony_ci unsigned int idx; 31862306a36Sopenharmony_ci unsigned int local; 31962306a36Sopenharmony_ci unsigned char d; 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci for (idx = 0; idx < 4; idx++) { 32262306a36Sopenharmony_ci sizes[idx] = 0; 32362306a36Sopenharmony_ci d = 0x55; 32462306a36Sopenharmony_ci for (local = idx << 22; 32562306a36Sopenharmony_ci local < (idx << 22) + 0x400000; 32662306a36Sopenharmony_ci local += 0x40000, d++) { 32762306a36Sopenharmony_ci snd_gf1_poke(gus, local, d); 32862306a36Sopenharmony_ci snd_gf1_poke(gus, local + 1, d + 1); 32962306a36Sopenharmony_ci#if 0 33062306a36Sopenharmony_ci printk(KERN_DEBUG "d = 0x%x, local = 0x%x, " 33162306a36Sopenharmony_ci "local + 1 = 0x%x, idx << 22 = 0x%x\n", 33262306a36Sopenharmony_ci d, 33362306a36Sopenharmony_ci snd_gf1_peek(gus, local), 33462306a36Sopenharmony_ci snd_gf1_peek(gus, local + 1), 33562306a36Sopenharmony_ci snd_gf1_peek(gus, idx << 22)); 33662306a36Sopenharmony_ci#endif 33762306a36Sopenharmony_ci if (snd_gf1_peek(gus, local) != d || 33862306a36Sopenharmony_ci snd_gf1_peek(gus, local + 1) != d + 1 || 33962306a36Sopenharmony_ci snd_gf1_peek(gus, idx << 22) != 0x55) 34062306a36Sopenharmony_ci break; 34162306a36Sopenharmony_ci sizes[idx]++; 34262306a36Sopenharmony_ci } 34362306a36Sopenharmony_ci } 34462306a36Sopenharmony_ci#if 0 34562306a36Sopenharmony_ci printk(KERN_DEBUG "sizes: %i %i %i %i\n", 34662306a36Sopenharmony_ci sizes[0], sizes[1], sizes[2], sizes[3]); 34762306a36Sopenharmony_ci#endif 34862306a36Sopenharmony_ci} 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_cistruct rom_hdr { 35162306a36Sopenharmony_ci /* 000 */ unsigned char iwave[8]; 35262306a36Sopenharmony_ci /* 008 */ unsigned char rom_hdr_revision; 35362306a36Sopenharmony_ci /* 009 */ unsigned char series_number; 35462306a36Sopenharmony_ci /* 010 */ unsigned char series_name[16]; 35562306a36Sopenharmony_ci /* 026 */ unsigned char date[10]; 35662306a36Sopenharmony_ci /* 036 */ unsigned short vendor_revision_major; 35762306a36Sopenharmony_ci /* 038 */ unsigned short vendor_revision_minor; 35862306a36Sopenharmony_ci /* 040 */ unsigned int rom_size; 35962306a36Sopenharmony_ci /* 044 */ unsigned char copyright[128]; 36062306a36Sopenharmony_ci /* 172 */ unsigned char vendor_name[64]; 36162306a36Sopenharmony_ci /* 236 */ unsigned char rom_description[128]; 36262306a36Sopenharmony_ci /* 364 */ unsigned char pad[147]; 36362306a36Sopenharmony_ci /* 511 */ unsigned char csum; 36462306a36Sopenharmony_ci}; 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_cistatic void snd_interwave_detect_memory(struct snd_gus_card *gus) 36762306a36Sopenharmony_ci{ 36862306a36Sopenharmony_ci static const unsigned int lmc[13] = 36962306a36Sopenharmony_ci { 37062306a36Sopenharmony_ci 0x00000001, 0x00000101, 0x01010101, 0x00000401, 37162306a36Sopenharmony_ci 0x04040401, 0x00040101, 0x04040101, 0x00000004, 37262306a36Sopenharmony_ci 0x00000404, 0x04040404, 0x00000010, 0x00001010, 37362306a36Sopenharmony_ci 0x10101010 37462306a36Sopenharmony_ci }; 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci int bank_pos, pages; 37762306a36Sopenharmony_ci unsigned int i, lmct; 37862306a36Sopenharmony_ci int psizes[4]; 37962306a36Sopenharmony_ci unsigned char iwave[8]; 38062306a36Sopenharmony_ci unsigned char csum; 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci snd_interwave_reset(gus); 38362306a36Sopenharmony_ci snd_gf1_write8(gus, SNDRV_GF1_GB_GLOBAL_MODE, snd_gf1_read8(gus, SNDRV_GF1_GB_GLOBAL_MODE) | 0x01); /* enhanced mode */ 38462306a36Sopenharmony_ci snd_gf1_write8(gus, SNDRV_GF1_GB_MEMORY_CONTROL, 0x01); /* DRAM I/O cycles selected */ 38562306a36Sopenharmony_ci snd_gf1_write16(gus, SNDRV_GF1_GW_MEMORY_CONFIG, (snd_gf1_look16(gus, SNDRV_GF1_GW_MEMORY_CONFIG) & 0xff10) | 0x004c); 38662306a36Sopenharmony_ci /* ok.. simple test of memory size */ 38762306a36Sopenharmony_ci pages = 0; 38862306a36Sopenharmony_ci snd_gf1_poke(gus, 0, 0x55); 38962306a36Sopenharmony_ci snd_gf1_poke(gus, 1, 0xaa); 39062306a36Sopenharmony_ci#if 1 39162306a36Sopenharmony_ci if (snd_gf1_peek(gus, 0) == 0x55 && snd_gf1_peek(gus, 1) == 0xaa) 39262306a36Sopenharmony_ci#else 39362306a36Sopenharmony_ci if (0) /* ok.. for testing of 0k RAM */ 39462306a36Sopenharmony_ci#endif 39562306a36Sopenharmony_ci { 39662306a36Sopenharmony_ci snd_interwave_bank_sizes(gus, psizes); 39762306a36Sopenharmony_ci lmct = (psizes[3] << 24) | (psizes[2] << 16) | 39862306a36Sopenharmony_ci (psizes[1] << 8) | psizes[0]; 39962306a36Sopenharmony_ci#if 0 40062306a36Sopenharmony_ci printk(KERN_DEBUG "lmct = 0x%08x\n", lmct); 40162306a36Sopenharmony_ci#endif 40262306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(lmc); i++) 40362306a36Sopenharmony_ci if (lmct == lmc[i]) { 40462306a36Sopenharmony_ci#if 0 40562306a36Sopenharmony_ci printk(KERN_DEBUG "found !!! %i\n", i); 40662306a36Sopenharmony_ci#endif 40762306a36Sopenharmony_ci snd_gf1_write16(gus, SNDRV_GF1_GW_MEMORY_CONFIG, (snd_gf1_look16(gus, SNDRV_GF1_GW_MEMORY_CONFIG) & 0xfff0) | i); 40862306a36Sopenharmony_ci snd_interwave_bank_sizes(gus, psizes); 40962306a36Sopenharmony_ci break; 41062306a36Sopenharmony_ci } 41162306a36Sopenharmony_ci if (i >= ARRAY_SIZE(lmc) && !gus->gf1.enh_mode) 41262306a36Sopenharmony_ci snd_gf1_write16(gus, SNDRV_GF1_GW_MEMORY_CONFIG, (snd_gf1_look16(gus, SNDRV_GF1_GW_MEMORY_CONFIG) & 0xfff0) | 2); 41362306a36Sopenharmony_ci for (i = 0; i < 4; i++) { 41462306a36Sopenharmony_ci gus->gf1.mem_alloc.banks_8[i].address = 41562306a36Sopenharmony_ci gus->gf1.mem_alloc.banks_16[i].address = i << 22; 41662306a36Sopenharmony_ci gus->gf1.mem_alloc.banks_8[i].size = 41762306a36Sopenharmony_ci gus->gf1.mem_alloc.banks_16[i].size = psizes[i] << 18; 41862306a36Sopenharmony_ci pages += psizes[i]; 41962306a36Sopenharmony_ci } 42062306a36Sopenharmony_ci } 42162306a36Sopenharmony_ci pages <<= 18; 42262306a36Sopenharmony_ci gus->gf1.memory = pages; 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci snd_gf1_write8(gus, SNDRV_GF1_GB_MEMORY_CONTROL, 0x03); /* select ROM */ 42562306a36Sopenharmony_ci snd_gf1_write16(gus, SNDRV_GF1_GW_MEMORY_CONFIG, (snd_gf1_look16(gus, SNDRV_GF1_GW_MEMORY_CONFIG) & 0xff1f) | (4 << 5)); 42662306a36Sopenharmony_ci gus->gf1.rom_banks = 0; 42762306a36Sopenharmony_ci gus->gf1.rom_memory = 0; 42862306a36Sopenharmony_ci for (bank_pos = 0; bank_pos < 16L * 1024L * 1024L; bank_pos += 4L * 1024L * 1024L) { 42962306a36Sopenharmony_ci for (i = 0; i < 8; ++i) 43062306a36Sopenharmony_ci iwave[i] = snd_gf1_peek(gus, bank_pos + i); 43162306a36Sopenharmony_ci if (strncmp(iwave, "INTRWAVE", 8)) 43262306a36Sopenharmony_ci continue; /* first check */ 43362306a36Sopenharmony_ci csum = 0; 43462306a36Sopenharmony_ci for (i = 0; i < sizeof(struct rom_hdr); i++) 43562306a36Sopenharmony_ci csum += snd_gf1_peek(gus, bank_pos + i); 43662306a36Sopenharmony_ci if (csum != 0) 43762306a36Sopenharmony_ci continue; /* not valid rom */ 43862306a36Sopenharmony_ci gus->gf1.rom_banks++; 43962306a36Sopenharmony_ci gus->gf1.rom_present |= 1 << (bank_pos >> 22); 44062306a36Sopenharmony_ci gus->gf1.rom_memory = snd_gf1_peek(gus, bank_pos + 40) | 44162306a36Sopenharmony_ci (snd_gf1_peek(gus, bank_pos + 41) << 8) | 44262306a36Sopenharmony_ci (snd_gf1_peek(gus, bank_pos + 42) << 16) | 44362306a36Sopenharmony_ci (snd_gf1_peek(gus, bank_pos + 43) << 24); 44462306a36Sopenharmony_ci } 44562306a36Sopenharmony_ci#if 0 44662306a36Sopenharmony_ci if (gus->gf1.rom_memory > 0) { 44762306a36Sopenharmony_ci if (gus->gf1.rom_banks == 1 && gus->gf1.rom_present == 8) 44862306a36Sopenharmony_ci gus->card->type = SNDRV_CARD_TYPE_IW_DYNASONIC; 44962306a36Sopenharmony_ci } 45062306a36Sopenharmony_ci#endif 45162306a36Sopenharmony_ci snd_gf1_write8(gus, SNDRV_GF1_GB_MEMORY_CONTROL, 0x00); /* select RAM */ 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci if (!gus->gf1.enh_mode) 45462306a36Sopenharmony_ci snd_interwave_reset(gus); 45562306a36Sopenharmony_ci} 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_cistatic void snd_interwave_init(int dev, struct snd_gus_card *gus) 45862306a36Sopenharmony_ci{ 45962306a36Sopenharmony_ci unsigned long flags; 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci /* ok.. some InterWave specific initialization */ 46262306a36Sopenharmony_ci spin_lock_irqsave(&gus->reg_lock, flags); 46362306a36Sopenharmony_ci snd_gf1_write8(gus, SNDRV_GF1_GB_SOUND_BLASTER_CONTROL, 0x00); 46462306a36Sopenharmony_ci snd_gf1_write8(gus, SNDRV_GF1_GB_COMPATIBILITY, 0x1f); 46562306a36Sopenharmony_ci snd_gf1_write8(gus, SNDRV_GF1_GB_DECODE_CONTROL, 0x49); 46662306a36Sopenharmony_ci snd_gf1_write8(gus, SNDRV_GF1_GB_VERSION_NUMBER, 0x11); 46762306a36Sopenharmony_ci snd_gf1_write8(gus, SNDRV_GF1_GB_MPU401_CONTROL_A, 0x00); 46862306a36Sopenharmony_ci snd_gf1_write8(gus, SNDRV_GF1_GB_MPU401_CONTROL_B, 0x30); 46962306a36Sopenharmony_ci snd_gf1_write8(gus, SNDRV_GF1_GB_EMULATION_IRQ, 0x00); 47062306a36Sopenharmony_ci spin_unlock_irqrestore(&gus->reg_lock, flags); 47162306a36Sopenharmony_ci gus->equal_irq = 1; 47262306a36Sopenharmony_ci gus->codec_flag = 1; 47362306a36Sopenharmony_ci gus->interwave = 1; 47462306a36Sopenharmony_ci gus->max_flag = 1; 47562306a36Sopenharmony_ci gus->joystick_dac = joystick_dac[dev]; 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci} 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_cistatic const struct snd_kcontrol_new snd_interwave_controls[] = { 48062306a36Sopenharmony_ciWSS_DOUBLE("Master Playback Switch", 0, 48162306a36Sopenharmony_ci CS4231_LINE_LEFT_OUTPUT, CS4231_LINE_RIGHT_OUTPUT, 7, 7, 1, 1), 48262306a36Sopenharmony_ciWSS_DOUBLE("Master Playback Volume", 0, 48362306a36Sopenharmony_ci CS4231_LINE_LEFT_OUTPUT, CS4231_LINE_RIGHT_OUTPUT, 0, 0, 31, 1), 48462306a36Sopenharmony_ciWSS_DOUBLE("Mic Playback Switch", 0, 48562306a36Sopenharmony_ci CS4231_LEFT_MIC_INPUT, CS4231_RIGHT_MIC_INPUT, 7, 7, 1, 1), 48662306a36Sopenharmony_ciWSS_DOUBLE("Mic Playback Volume", 0, 48762306a36Sopenharmony_ci CS4231_LEFT_MIC_INPUT, CS4231_RIGHT_MIC_INPUT, 0, 0, 31, 1) 48862306a36Sopenharmony_ci}; 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_cistatic int snd_interwave_mixer(struct snd_wss *chip) 49162306a36Sopenharmony_ci{ 49262306a36Sopenharmony_ci struct snd_card *card = chip->card; 49362306a36Sopenharmony_ci struct snd_ctl_elem_id id1, id2; 49462306a36Sopenharmony_ci unsigned int idx; 49562306a36Sopenharmony_ci int err; 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci memset(&id1, 0, sizeof(id1)); 49862306a36Sopenharmony_ci memset(&id2, 0, sizeof(id2)); 49962306a36Sopenharmony_ci id1.iface = id2.iface = SNDRV_CTL_ELEM_IFACE_MIXER; 50062306a36Sopenharmony_ci#if 0 50162306a36Sopenharmony_ci /* remove mono microphone controls */ 50262306a36Sopenharmony_ci strcpy(id1.name, "Mic Playback Switch"); 50362306a36Sopenharmony_ci err = snd_ctl_remove_id(card, &id1); 50462306a36Sopenharmony_ci if (err < 0) 50562306a36Sopenharmony_ci return err; 50662306a36Sopenharmony_ci strcpy(id1.name, "Mic Playback Volume"); 50762306a36Sopenharmony_ci err = snd_ctl_remove_id(card, &id1); 50862306a36Sopenharmony_ci if (err < 0) 50962306a36Sopenharmony_ci return err; 51062306a36Sopenharmony_ci#endif 51162306a36Sopenharmony_ci /* add new master and mic controls */ 51262306a36Sopenharmony_ci for (idx = 0; idx < ARRAY_SIZE(snd_interwave_controls); idx++) { 51362306a36Sopenharmony_ci err = snd_ctl_add(card, snd_ctl_new1(&snd_interwave_controls[idx], chip)); 51462306a36Sopenharmony_ci if (err < 0) 51562306a36Sopenharmony_ci return err; 51662306a36Sopenharmony_ci } 51762306a36Sopenharmony_ci snd_wss_out(chip, CS4231_LINE_LEFT_OUTPUT, 0x9f); 51862306a36Sopenharmony_ci snd_wss_out(chip, CS4231_LINE_RIGHT_OUTPUT, 0x9f); 51962306a36Sopenharmony_ci snd_wss_out(chip, CS4231_LEFT_MIC_INPUT, 0x9f); 52062306a36Sopenharmony_ci snd_wss_out(chip, CS4231_RIGHT_MIC_INPUT, 0x9f); 52162306a36Sopenharmony_ci /* reassign AUXA to SYNTHESIZER */ 52262306a36Sopenharmony_ci strcpy(id1.name, "Aux Playback Switch"); 52362306a36Sopenharmony_ci strcpy(id2.name, "Synth Playback Switch"); 52462306a36Sopenharmony_ci err = snd_ctl_rename_id(card, &id1, &id2); 52562306a36Sopenharmony_ci if (err < 0) 52662306a36Sopenharmony_ci return err; 52762306a36Sopenharmony_ci strcpy(id1.name, "Aux Playback Volume"); 52862306a36Sopenharmony_ci strcpy(id2.name, "Synth Playback Volume"); 52962306a36Sopenharmony_ci err = snd_ctl_rename_id(card, &id1, &id2); 53062306a36Sopenharmony_ci if (err < 0) 53162306a36Sopenharmony_ci return err; 53262306a36Sopenharmony_ci /* reassign AUXB to CD */ 53362306a36Sopenharmony_ci strcpy(id1.name, "Aux Playback Switch"); id1.index = 1; 53462306a36Sopenharmony_ci strcpy(id2.name, "CD Playback Switch"); 53562306a36Sopenharmony_ci err = snd_ctl_rename_id(card, &id1, &id2); 53662306a36Sopenharmony_ci if (err < 0) 53762306a36Sopenharmony_ci return err; 53862306a36Sopenharmony_ci strcpy(id1.name, "Aux Playback Volume"); 53962306a36Sopenharmony_ci strcpy(id2.name, "CD Playback Volume"); 54062306a36Sopenharmony_ci err = snd_ctl_rename_id(card, &id1, &id2); 54162306a36Sopenharmony_ci if (err < 0) 54262306a36Sopenharmony_ci return err; 54362306a36Sopenharmony_ci return 0; 54462306a36Sopenharmony_ci} 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci#ifdef CONFIG_PNP 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_cistatic int snd_interwave_pnp(int dev, struct snd_interwave *iwcard, 54962306a36Sopenharmony_ci struct pnp_card_link *card, 55062306a36Sopenharmony_ci const struct pnp_card_device_id *id) 55162306a36Sopenharmony_ci{ 55262306a36Sopenharmony_ci struct pnp_dev *pdev; 55362306a36Sopenharmony_ci int err; 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci iwcard->dev = pnp_request_card_device(card, id->devs[0].id, NULL); 55662306a36Sopenharmony_ci if (iwcard->dev == NULL) 55762306a36Sopenharmony_ci return -EBUSY; 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci#ifdef SNDRV_STB 56062306a36Sopenharmony_ci iwcard->devtc = pnp_request_card_device(card, id->devs[1].id, NULL); 56162306a36Sopenharmony_ci if (iwcard->devtc == NULL) 56262306a36Sopenharmony_ci return -EBUSY; 56362306a36Sopenharmony_ci#endif 56462306a36Sopenharmony_ci /* Synth & Codec initialization */ 56562306a36Sopenharmony_ci pdev = iwcard->dev; 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci err = pnp_activate_dev(pdev); 56862306a36Sopenharmony_ci if (err < 0) { 56962306a36Sopenharmony_ci snd_printk(KERN_ERR "InterWave PnP configure failure (out of resources?)\n"); 57062306a36Sopenharmony_ci return err; 57162306a36Sopenharmony_ci } 57262306a36Sopenharmony_ci if (pnp_port_start(pdev, 0) + 0x100 != pnp_port_start(pdev, 1) || 57362306a36Sopenharmony_ci pnp_port_start(pdev, 0) + 0x10c != pnp_port_start(pdev, 2)) { 57462306a36Sopenharmony_ci snd_printk(KERN_ERR "PnP configure failure (wrong ports)\n"); 57562306a36Sopenharmony_ci return -ENOENT; 57662306a36Sopenharmony_ci } 57762306a36Sopenharmony_ci port[dev] = pnp_port_start(pdev, 0); 57862306a36Sopenharmony_ci dma1[dev] = pnp_dma(pdev, 0); 57962306a36Sopenharmony_ci if (dma2[dev] >= 0) 58062306a36Sopenharmony_ci dma2[dev] = pnp_dma(pdev, 1); 58162306a36Sopenharmony_ci irq[dev] = pnp_irq(pdev, 0); 58262306a36Sopenharmony_ci snd_printdd("isapnp IW: sb port=0x%llx, gf1 port=0x%llx, codec port=0x%llx\n", 58362306a36Sopenharmony_ci (unsigned long long)pnp_port_start(pdev, 0), 58462306a36Sopenharmony_ci (unsigned long long)pnp_port_start(pdev, 1), 58562306a36Sopenharmony_ci (unsigned long long)pnp_port_start(pdev, 2)); 58662306a36Sopenharmony_ci snd_printdd("isapnp IW: dma1=%i, dma2=%i, irq=%i\n", dma1[dev], dma2[dev], irq[dev]); 58762306a36Sopenharmony_ci#ifdef SNDRV_STB 58862306a36Sopenharmony_ci /* Tone Control initialization */ 58962306a36Sopenharmony_ci pdev = iwcard->devtc; 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci err = pnp_activate_dev(pdev); 59262306a36Sopenharmony_ci if (err < 0) { 59362306a36Sopenharmony_ci snd_printk(KERN_ERR "InterWave ToneControl PnP configure failure (out of resources?)\n"); 59462306a36Sopenharmony_ci return err; 59562306a36Sopenharmony_ci } 59662306a36Sopenharmony_ci port_tc[dev] = pnp_port_start(pdev, 0); 59762306a36Sopenharmony_ci snd_printdd("isapnp IW: tone control port=0x%lx\n", port_tc[dev]); 59862306a36Sopenharmony_ci#endif 59962306a36Sopenharmony_ci return 0; 60062306a36Sopenharmony_ci} 60162306a36Sopenharmony_ci#endif /* CONFIG_PNP */ 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_cistatic int snd_interwave_card_new(struct device *pdev, int dev, 60462306a36Sopenharmony_ci struct snd_card **cardp) 60562306a36Sopenharmony_ci{ 60662306a36Sopenharmony_ci struct snd_card *card; 60762306a36Sopenharmony_ci struct snd_interwave *iwcard; 60862306a36Sopenharmony_ci int err; 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci err = snd_devm_card_new(pdev, index[dev], id[dev], THIS_MODULE, 61162306a36Sopenharmony_ci sizeof(struct snd_interwave), &card); 61262306a36Sopenharmony_ci if (err < 0) 61362306a36Sopenharmony_ci return err; 61462306a36Sopenharmony_ci iwcard = card->private_data; 61562306a36Sopenharmony_ci iwcard->card = card; 61662306a36Sopenharmony_ci iwcard->irq = -1; 61762306a36Sopenharmony_ci *cardp = card; 61862306a36Sopenharmony_ci return 0; 61962306a36Sopenharmony_ci} 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_cistatic int snd_interwave_probe_gus(struct snd_card *card, int dev, 62262306a36Sopenharmony_ci struct snd_gus_card **gusp) 62362306a36Sopenharmony_ci{ 62462306a36Sopenharmony_ci return snd_gus_create(card, port[dev], -irq[dev], dma1[dev], dma2[dev], 62562306a36Sopenharmony_ci 0, 32, pcm_channels[dev], effect[dev], gusp); 62662306a36Sopenharmony_ci} 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_cistatic int snd_interwave_probe(struct snd_card *card, int dev, 62962306a36Sopenharmony_ci struct snd_gus_card *gus) 63062306a36Sopenharmony_ci{ 63162306a36Sopenharmony_ci int xirq, xdma1, xdma2; 63262306a36Sopenharmony_ci struct snd_interwave *iwcard = card->private_data; 63362306a36Sopenharmony_ci struct snd_wss *wss; 63462306a36Sopenharmony_ci#ifdef SNDRV_STB 63562306a36Sopenharmony_ci struct snd_i2c_bus *i2c_bus; 63662306a36Sopenharmony_ci#endif 63762306a36Sopenharmony_ci char *str; 63862306a36Sopenharmony_ci int err; 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci xirq = irq[dev]; 64162306a36Sopenharmony_ci xdma1 = dma1[dev]; 64262306a36Sopenharmony_ci xdma2 = dma2[dev]; 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci err = snd_interwave_detect(iwcard, gus, dev 64562306a36Sopenharmony_ci#ifdef SNDRV_STB 64662306a36Sopenharmony_ci , &i2c_bus 64762306a36Sopenharmony_ci#endif 64862306a36Sopenharmony_ci ); 64962306a36Sopenharmony_ci if (err < 0) 65062306a36Sopenharmony_ci return err; 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ci iwcard->gus_status_reg = gus->gf1.reg_irqstat; 65362306a36Sopenharmony_ci iwcard->pcm_status_reg = gus->gf1.port + 0x10c + 2; 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_ci snd_interwave_init(dev, gus); 65662306a36Sopenharmony_ci snd_interwave_detect_memory(gus); 65762306a36Sopenharmony_ci err = snd_gus_initialize(gus); 65862306a36Sopenharmony_ci if (err < 0) 65962306a36Sopenharmony_ci return err; 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_ci if (devm_request_irq(card->dev, xirq, snd_interwave_interrupt, 0, 66262306a36Sopenharmony_ci "InterWave", iwcard)) { 66362306a36Sopenharmony_ci snd_printk(KERN_ERR PFX "unable to grab IRQ %d\n", xirq); 66462306a36Sopenharmony_ci return -EBUSY; 66562306a36Sopenharmony_ci } 66662306a36Sopenharmony_ci iwcard->irq = xirq; 66762306a36Sopenharmony_ci card->sync_irq = iwcard->irq; 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_ci err = snd_wss_create(card, 67062306a36Sopenharmony_ci gus->gf1.port + 0x10c, -1, xirq, 67162306a36Sopenharmony_ci xdma2 < 0 ? xdma1 : xdma2, xdma1, 67262306a36Sopenharmony_ci WSS_HW_INTERWAVE, 67362306a36Sopenharmony_ci WSS_HWSHARE_IRQ | 67462306a36Sopenharmony_ci WSS_HWSHARE_DMA1 | 67562306a36Sopenharmony_ci WSS_HWSHARE_DMA2, 67662306a36Sopenharmony_ci &wss); 67762306a36Sopenharmony_ci if (err < 0) 67862306a36Sopenharmony_ci return err; 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_ci err = snd_wss_pcm(wss, 0); 68162306a36Sopenharmony_ci if (err < 0) 68262306a36Sopenharmony_ci return err; 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_ci sprintf(wss->pcm->name + strlen(wss->pcm->name), " rev %c", 68562306a36Sopenharmony_ci gus->revision + 'A'); 68662306a36Sopenharmony_ci strcat(wss->pcm->name, " (codec)"); 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci err = snd_wss_timer(wss, 2); 68962306a36Sopenharmony_ci if (err < 0) 69062306a36Sopenharmony_ci return err; 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ci err = snd_wss_mixer(wss); 69362306a36Sopenharmony_ci if (err < 0) 69462306a36Sopenharmony_ci return err; 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_ci if (pcm_channels[dev] > 0) { 69762306a36Sopenharmony_ci err = snd_gf1_pcm_new(gus, 1, 1); 69862306a36Sopenharmony_ci if (err < 0) 69962306a36Sopenharmony_ci return err; 70062306a36Sopenharmony_ci } 70162306a36Sopenharmony_ci err = snd_interwave_mixer(wss); 70262306a36Sopenharmony_ci if (err < 0) 70362306a36Sopenharmony_ci return err; 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_ci#ifdef SNDRV_STB 70662306a36Sopenharmony_ci { 70762306a36Sopenharmony_ci struct snd_ctl_elem_id id1, id2; 70862306a36Sopenharmony_ci memset(&id1, 0, sizeof(id1)); 70962306a36Sopenharmony_ci memset(&id2, 0, sizeof(id2)); 71062306a36Sopenharmony_ci id1.iface = id2.iface = SNDRV_CTL_ELEM_IFACE_MIXER; 71162306a36Sopenharmony_ci strcpy(id1.name, "Master Playback Switch"); 71262306a36Sopenharmony_ci strcpy(id2.name, id1.name); 71362306a36Sopenharmony_ci id2.index = 1; 71462306a36Sopenharmony_ci err = snd_ctl_rename_id(card, &id1, &id2); 71562306a36Sopenharmony_ci if (err < 0) 71662306a36Sopenharmony_ci return err; 71762306a36Sopenharmony_ci strcpy(id1.name, "Master Playback Volume"); 71862306a36Sopenharmony_ci strcpy(id2.name, id1.name); 71962306a36Sopenharmony_ci err = snd_ctl_rename_id(card, &id1, &id2); 72062306a36Sopenharmony_ci if (err < 0) 72162306a36Sopenharmony_ci return err; 72262306a36Sopenharmony_ci err = snd_tea6330t_update_mixer(card, i2c_bus, 0, 1); 72362306a36Sopenharmony_ci if (err < 0) 72462306a36Sopenharmony_ci return err; 72562306a36Sopenharmony_ci } 72662306a36Sopenharmony_ci#endif 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_ci gus->uart_enable = midi[dev]; 72962306a36Sopenharmony_ci err = snd_gf1_rawmidi_new(gus, 0); 73062306a36Sopenharmony_ci if (err < 0) 73162306a36Sopenharmony_ci return err; 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ci#ifndef SNDRV_STB 73462306a36Sopenharmony_ci str = "AMD InterWave"; 73562306a36Sopenharmony_ci if (gus->gf1.rom_banks == 1 && gus->gf1.rom_present == 8) 73662306a36Sopenharmony_ci str = "Dynasonic 3-D"; 73762306a36Sopenharmony_ci#else 73862306a36Sopenharmony_ci str = "InterWave STB"; 73962306a36Sopenharmony_ci#endif 74062306a36Sopenharmony_ci strcpy(card->driver, str); 74162306a36Sopenharmony_ci strcpy(card->shortname, str); 74262306a36Sopenharmony_ci sprintf(card->longname, "%s at 0x%lx, irq %i, dma %d", 74362306a36Sopenharmony_ci str, 74462306a36Sopenharmony_ci gus->gf1.port, 74562306a36Sopenharmony_ci xirq, 74662306a36Sopenharmony_ci xdma1); 74762306a36Sopenharmony_ci if (xdma2 >= 0) 74862306a36Sopenharmony_ci sprintf(card->longname + strlen(card->longname), "&%d", xdma2); 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_ci err = snd_card_register(card); 75162306a36Sopenharmony_ci if (err < 0) 75262306a36Sopenharmony_ci return err; 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_ci iwcard->wss = wss; 75562306a36Sopenharmony_ci iwcard->gus = gus; 75662306a36Sopenharmony_ci return 0; 75762306a36Sopenharmony_ci} 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_cistatic int snd_interwave_isa_match(struct device *pdev, 76062306a36Sopenharmony_ci unsigned int dev) 76162306a36Sopenharmony_ci{ 76262306a36Sopenharmony_ci if (!enable[dev]) 76362306a36Sopenharmony_ci return 0; 76462306a36Sopenharmony_ci#ifdef CONFIG_PNP 76562306a36Sopenharmony_ci if (isapnp[dev]) 76662306a36Sopenharmony_ci return 0; 76762306a36Sopenharmony_ci#endif 76862306a36Sopenharmony_ci return 1; 76962306a36Sopenharmony_ci} 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_cistatic int snd_interwave_isa_probe(struct device *pdev, 77262306a36Sopenharmony_ci unsigned int dev) 77362306a36Sopenharmony_ci{ 77462306a36Sopenharmony_ci struct snd_card *card; 77562306a36Sopenharmony_ci struct snd_gus_card *gus; 77662306a36Sopenharmony_ci int err; 77762306a36Sopenharmony_ci static const int possible_irqs[] = {5, 11, 12, 9, 7, 15, 3, -1}; 77862306a36Sopenharmony_ci static const int possible_dmas[] = {0, 1, 3, 5, 6, 7, -1}; 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_ci if (irq[dev] == SNDRV_AUTO_IRQ) { 78162306a36Sopenharmony_ci irq[dev] = snd_legacy_find_free_irq(possible_irqs); 78262306a36Sopenharmony_ci if (irq[dev] < 0) { 78362306a36Sopenharmony_ci snd_printk(KERN_ERR PFX "unable to find a free IRQ\n"); 78462306a36Sopenharmony_ci return -EBUSY; 78562306a36Sopenharmony_ci } 78662306a36Sopenharmony_ci } 78762306a36Sopenharmony_ci if (dma1[dev] == SNDRV_AUTO_DMA) { 78862306a36Sopenharmony_ci dma1[dev] = snd_legacy_find_free_dma(possible_dmas); 78962306a36Sopenharmony_ci if (dma1[dev] < 0) { 79062306a36Sopenharmony_ci snd_printk(KERN_ERR PFX "unable to find a free DMA1\n"); 79162306a36Sopenharmony_ci return -EBUSY; 79262306a36Sopenharmony_ci } 79362306a36Sopenharmony_ci } 79462306a36Sopenharmony_ci if (dma2[dev] == SNDRV_AUTO_DMA) { 79562306a36Sopenharmony_ci dma2[dev] = snd_legacy_find_free_dma(possible_dmas); 79662306a36Sopenharmony_ci if (dma2[dev] < 0) { 79762306a36Sopenharmony_ci snd_printk(KERN_ERR PFX "unable to find a free DMA2\n"); 79862306a36Sopenharmony_ci return -EBUSY; 79962306a36Sopenharmony_ci } 80062306a36Sopenharmony_ci } 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_ci err = snd_interwave_card_new(pdev, dev, &card); 80362306a36Sopenharmony_ci if (err < 0) 80462306a36Sopenharmony_ci return err; 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_ci if (port[dev] != SNDRV_AUTO_PORT) 80762306a36Sopenharmony_ci err = snd_interwave_probe_gus(card, dev, &gus); 80862306a36Sopenharmony_ci else { 80962306a36Sopenharmony_ci static const long possible_ports[] = {0x210, 0x220, 0x230, 0x240, 0x250, 0x260}; 81062306a36Sopenharmony_ci int i; 81162306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(possible_ports); i++) { 81262306a36Sopenharmony_ci port[dev] = possible_ports[i]; 81362306a36Sopenharmony_ci err = snd_interwave_probe_gus(card, dev, &gus); 81462306a36Sopenharmony_ci if (! err) 81562306a36Sopenharmony_ci return 0; 81662306a36Sopenharmony_ci } 81762306a36Sopenharmony_ci } 81862306a36Sopenharmony_ci if (err < 0) 81962306a36Sopenharmony_ci return err; 82062306a36Sopenharmony_ci 82162306a36Sopenharmony_ci err = snd_interwave_probe(card, dev, gus); 82262306a36Sopenharmony_ci if (err < 0) 82362306a36Sopenharmony_ci return err; 82462306a36Sopenharmony_ci 82562306a36Sopenharmony_ci dev_set_drvdata(pdev, card); 82662306a36Sopenharmony_ci return 0; 82762306a36Sopenharmony_ci} 82862306a36Sopenharmony_ci 82962306a36Sopenharmony_cistatic struct isa_driver snd_interwave_driver = { 83062306a36Sopenharmony_ci .match = snd_interwave_isa_match, 83162306a36Sopenharmony_ci .probe = snd_interwave_isa_probe, 83262306a36Sopenharmony_ci /* FIXME: suspend,resume */ 83362306a36Sopenharmony_ci .driver = { 83462306a36Sopenharmony_ci .name = INTERWAVE_DRIVER 83562306a36Sopenharmony_ci }, 83662306a36Sopenharmony_ci}; 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_ci#ifdef CONFIG_PNP 83962306a36Sopenharmony_cistatic int snd_interwave_pnp_detect(struct pnp_card_link *pcard, 84062306a36Sopenharmony_ci const struct pnp_card_device_id *pid) 84162306a36Sopenharmony_ci{ 84262306a36Sopenharmony_ci static int dev; 84362306a36Sopenharmony_ci struct snd_card *card; 84462306a36Sopenharmony_ci struct snd_gus_card *gus; 84562306a36Sopenharmony_ci int res; 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_ci for ( ; dev < SNDRV_CARDS; dev++) { 84862306a36Sopenharmony_ci if (enable[dev] && isapnp[dev]) 84962306a36Sopenharmony_ci break; 85062306a36Sopenharmony_ci } 85162306a36Sopenharmony_ci if (dev >= SNDRV_CARDS) 85262306a36Sopenharmony_ci return -ENODEV; 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_ci res = snd_interwave_card_new(&pcard->card->dev, dev, &card); 85562306a36Sopenharmony_ci if (res < 0) 85662306a36Sopenharmony_ci return res; 85762306a36Sopenharmony_ci 85862306a36Sopenharmony_ci res = snd_interwave_pnp(dev, card->private_data, pcard, pid); 85962306a36Sopenharmony_ci if (res < 0) 86062306a36Sopenharmony_ci return res; 86162306a36Sopenharmony_ci res = snd_interwave_probe_gus(card, dev, &gus); 86262306a36Sopenharmony_ci if (res < 0) 86362306a36Sopenharmony_ci return res; 86462306a36Sopenharmony_ci res = snd_interwave_probe(card, dev, gus); 86562306a36Sopenharmony_ci if (res < 0) 86662306a36Sopenharmony_ci return res; 86762306a36Sopenharmony_ci pnp_set_card_drvdata(pcard, card); 86862306a36Sopenharmony_ci dev++; 86962306a36Sopenharmony_ci return 0; 87062306a36Sopenharmony_ci} 87162306a36Sopenharmony_ci 87262306a36Sopenharmony_cistatic struct pnp_card_driver interwave_pnpc_driver = { 87362306a36Sopenharmony_ci .flags = PNP_DRIVER_RES_DISABLE, 87462306a36Sopenharmony_ci .name = INTERWAVE_PNP_DRIVER, 87562306a36Sopenharmony_ci .id_table = snd_interwave_pnpids, 87662306a36Sopenharmony_ci .probe = snd_interwave_pnp_detect, 87762306a36Sopenharmony_ci /* FIXME: suspend,resume */ 87862306a36Sopenharmony_ci}; 87962306a36Sopenharmony_ci 88062306a36Sopenharmony_ci#endif /* CONFIG_PNP */ 88162306a36Sopenharmony_ci 88262306a36Sopenharmony_cistatic int __init alsa_card_interwave_init(void) 88362306a36Sopenharmony_ci{ 88462306a36Sopenharmony_ci int err; 88562306a36Sopenharmony_ci 88662306a36Sopenharmony_ci err = isa_register_driver(&snd_interwave_driver, SNDRV_CARDS); 88762306a36Sopenharmony_ci#ifdef CONFIG_PNP 88862306a36Sopenharmony_ci if (!err) 88962306a36Sopenharmony_ci isa_registered = 1; 89062306a36Sopenharmony_ci 89162306a36Sopenharmony_ci err = pnp_register_card_driver(&interwave_pnpc_driver); 89262306a36Sopenharmony_ci if (!err) 89362306a36Sopenharmony_ci pnp_registered = 1; 89462306a36Sopenharmony_ci 89562306a36Sopenharmony_ci if (isa_registered) 89662306a36Sopenharmony_ci err = 0; 89762306a36Sopenharmony_ci#endif 89862306a36Sopenharmony_ci return err; 89962306a36Sopenharmony_ci} 90062306a36Sopenharmony_ci 90162306a36Sopenharmony_cistatic void __exit alsa_card_interwave_exit(void) 90262306a36Sopenharmony_ci{ 90362306a36Sopenharmony_ci#ifdef CONFIG_PNP 90462306a36Sopenharmony_ci if (pnp_registered) 90562306a36Sopenharmony_ci pnp_unregister_card_driver(&interwave_pnpc_driver); 90662306a36Sopenharmony_ci if (isa_registered) 90762306a36Sopenharmony_ci#endif 90862306a36Sopenharmony_ci isa_unregister_driver(&snd_interwave_driver); 90962306a36Sopenharmony_ci} 91062306a36Sopenharmony_ci 91162306a36Sopenharmony_cimodule_init(alsa_card_interwave_init) 91262306a36Sopenharmony_cimodule_exit(alsa_card_interwave_exit) 913