📄 interwave.c
字号:
/* * Driver for AMD InterWave soundcard * Copyright (c) by Jaroslav Kysela <perex@suse.cz> * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * 1999/07/22 Erik Inge Bolso <knan@mo.himolde.no> * * mixer group handlers * */#include <sound/driver.h>#include <asm/dma.h>#include <linux/delay.h>#include <linux/init.h>#include <linux/slab.h>#include <linux/pnp.h>#include <linux/moduleparam.h>#include <sound/core.h>#include <sound/gus.h>#include <sound/cs4231.h>#ifdef SNDRV_STB#include <sound/tea6330t.h>#endif#define SNDRV_LEGACY_AUTO_PROBE#define SNDRV_LEGACY_FIND_FREE_IRQ#define SNDRV_LEGACY_FIND_FREE_DMA#include <sound/initval.h>MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");MODULE_LICENSE("GPL");#ifndef SNDRV_STBMODULE_DESCRIPTION("AMD InterWave");MODULE_SUPPORTED_DEVICE("{{Gravis,UltraSound Plug & Play}," "{STB,SoundRage32}," "{MED,MED3210}," "{Dynasonix,Dynasonix Pro}," "{Panasonic,PCA761AW}}");#elseMODULE_DESCRIPTION("AMD InterWave STB with TEA6330T");MODULE_SUPPORTED_DEVICE("{{AMD,InterWave STB with TEA6330T}}");#endifstatic int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP; /* Enable this card */#ifdef CONFIG_PNPstatic int isapnp[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1};#endifstatic long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x210,0x220,0x230,0x240,0x250,0x260 */#ifdef SNDRV_STBstatic long port_tc[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x350,0x360,0x370,0x380 */#endifstatic int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 2,3,5,9,11,12,15 */static int dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 0,1,3,5,6,7 */static int dma2[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 0,1,3,5,6,7 */static int joystick_dac[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 29}; /* 0 to 31, (0.59V-4.52V or 0.389V-2.98V) */static int midi[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0};static int pcm_channels[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 2};static int effect[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0};#ifdef SNDRV_STB#define PFX "interwave-stb: "#else#define PFX "interwave: "#endifmodule_param_array(index, int, NULL, 0444);MODULE_PARM_DESC(index, "Index value for InterWave soundcard.");module_param_array(id, charp, NULL, 0444);MODULE_PARM_DESC(id, "ID string for InterWave soundcard.");module_param_array(enable, bool, NULL, 0444);MODULE_PARM_DESC(enable, "Enable InterWave soundcard.");#ifdef CONFIG_PNPmodule_param_array(isapnp, bool, NULL, 0444);MODULE_PARM_DESC(isapnp, "ISA PnP detection for specified soundcard.");#endifmodule_param_array(port, long, NULL, 0444);MODULE_PARM_DESC(port, "Port # for InterWave driver.");#ifdef SNDRV_STBmodule_param_array(port_tc, long, NULL, 0444);MODULE_PARM_DESC(port_tc, "Tone control (TEA6330T - i2c bus) port # for InterWave driver.");#endifmodule_param_array(irq, int, NULL, 0444);MODULE_PARM_DESC(irq, "IRQ # for InterWave driver.");module_param_array(dma1, int, NULL, 0444);MODULE_PARM_DESC(dma1, "DMA1 # for InterWave driver.");module_param_array(dma2, int, NULL, 0444);MODULE_PARM_DESC(dma2, "DMA2 # for InterWave driver.");module_param_array(joystick_dac, int, NULL, 0444);MODULE_PARM_DESC(joystick_dac, "Joystick DAC level 0.59V-4.52V or 0.389V-2.98V for InterWave driver.");module_param_array(midi, int, NULL, 0444);MODULE_PARM_DESC(midi, "MIDI UART enable for InterWave driver.");module_param_array(pcm_channels, int, NULL, 0444);MODULE_PARM_DESC(pcm_channels, "Reserved PCM channels for InterWave driver.");module_param_array(effect, int, NULL, 0444);MODULE_PARM_DESC(effect, "Effects enable for InterWave driver.");struct snd_interwave { int irq; snd_card_t *card; snd_gus_card_t *gus; cs4231_t *cs4231;#ifdef SNDRV_STB struct resource *i2c_res;#endif unsigned short gus_status_reg; unsigned short pcm_status_reg;#ifdef CONFIG_PNP struct pnp_dev *dev;#ifdef SNDRV_STB struct pnp_dev *devtc;#endif#endif};static snd_card_t *snd_interwave_legacy[SNDRV_CARDS] = SNDRV_DEFAULT_PTR;#ifdef CONFIG_PNPstatic struct pnp_card_device_id snd_interwave_pnpids[] = {#ifndef SNDRV_STB /* Gravis UltraSound Plug & Play */ { .id = "GRV0001", .devs = { { .id = "GRV0000" } } }, /* STB SoundRage32 */ { .id = "STB011a", .devs = { { .id = "STB0010" } } }, /* MED3210 */ { .id = "DXP3201", .devs = { { .id = "DXP0010" } } }, /* Dynasonic Pro */ /* This device also have CDC1117:DynaSonix Pro Audio Effects Processor */ { .id = "CDC1111", .devs = { { .id = "CDC1112" } } }, /* Panasonic PCA761AW Audio Card */ { .id = "ADV55ff", .devs = { { .id = "ADV0010" } } }, /* InterWave STB without TEA6330T */ { .id = "ADV550a", .devs = { { .id = "ADV0010" } } },#else /* InterWave STB with TEA6330T */ { .id = "ADV550a", .devs = { { .id = "ADV0010" }, { .id = "ADV0015" } } },#endif { .id = "" }};MODULE_DEVICE_TABLE(pnp_card, snd_interwave_pnpids);#endif /* CONFIG_PNP */#ifdef SNDRV_STBstatic void snd_interwave_i2c_setlines(snd_i2c_bus_t *bus, int ctrl, int data){ unsigned long port = bus->private_value;#if 0 printk("i2c_setlines - 0x%lx <- %i,%i\n", port, ctrl, data);#endif outb((data << 1) | ctrl, port); udelay(10);}static int snd_interwave_i2c_getclockline(snd_i2c_bus_t *bus){ unsigned long port = bus->private_value; unsigned char res; res = inb(port) & 1;#if 0 printk("i2c_getclockline - 0x%lx -> %i\n", port, res);#endif return res;}static int snd_interwave_i2c_getdataline(snd_i2c_bus_t *bus, int ack){ unsigned long port = bus->private_value; unsigned char res; if (ack) udelay(10); res = (inb(port) & 2) >> 1;#if 0 printk("i2c_getdataline - 0x%lx -> %i\n", port, res);#endif return res;}static snd_i2c_bit_ops_t snd_interwave_i2c_bit_ops = { .setlines = snd_interwave_i2c_setlines, .getclock = snd_interwave_i2c_getclockline, .getdata = snd_interwave_i2c_getdataline,};static int __devinit snd_interwave_detect_stb(struct snd_interwave *iwcard, snd_gus_card_t * gus, int dev, snd_i2c_bus_t **rbus){ unsigned long port; snd_i2c_bus_t *bus; snd_card_t *card = iwcard->card; char name[32]; int err; *rbus = NULL; port = port_tc[dev]; if (port == SNDRV_AUTO_PORT) { port = 0x350; if (gus->gf1.port == 0x250) { port = 0x360; } while (port <= 0x380) { if ((iwcard->i2c_res = request_region(port, 1, "InterWave (I2C bus)")) != NULL) break; port += 0x10; } } else { iwcard->i2c_res = request_region(port, 1, "InterWave (I2C bus)"); } if (iwcard->i2c_res == NULL) { snd_printk(KERN_ERR "interwave: can't grab i2c bus port\n"); return -ENODEV; } sprintf(name, "InterWave-%i", card->number); if ((err = snd_i2c_bus_create(card, name, NULL, &bus)) < 0) return err; bus->private_value = port; bus->hw_ops.bit = &snd_interwave_i2c_bit_ops; if ((err = snd_tea6330t_detect(bus, 0)) < 0) return err; *rbus = bus; return 0;}#endifstatic int __devinit snd_interwave_detect(struct snd_interwave *iwcard, snd_gus_card_t * gus, int dev#ifdef SNDRV_STB , snd_i2c_bus_t **rbus#endif ){ unsigned long flags; unsigned char rev1, rev2; int d; snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 0); /* reset GF1 */ if (((d = snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET)) & 0x07) != 0) { snd_printdd("[0x%lx] check 1 failed - 0x%x\n", gus->gf1.port, d); return -ENODEV; } udelay(160); snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 1); /* release reset */ udelay(160); if (((d = snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET)) & 0x07) != 1) { snd_printdd("[0x%lx] check 2 failed - 0x%x\n", gus->gf1.port, d); return -ENODEV; } spin_lock_irqsave(&gus->reg_lock, flags); rev1 = snd_gf1_look8(gus, SNDRV_GF1_GB_VERSION_NUMBER); snd_gf1_write8(gus, SNDRV_GF1_GB_VERSION_NUMBER, ~rev1); rev2 = snd_gf1_look8(gus, SNDRV_GF1_GB_VERSION_NUMBER); snd_gf1_write8(gus, SNDRV_GF1_GB_VERSION_NUMBER, rev1); spin_unlock_irqrestore(&gus->reg_lock, flags); snd_printdd("[0x%lx] InterWave check - rev1=0x%x, rev2=0x%x\n", gus->gf1.port, rev1, rev2); if ((rev1 & 0xf0) == (rev2 & 0xf0) && (rev1 & 0x0f) != (rev2 & 0x0f)) { snd_printdd("[0x%lx] InterWave check - passed\n", gus->gf1.port); gus->interwave = 1; strcpy(gus->card->shortname, "AMD InterWave"); gus->revision = rev1 >> 4;#ifndef SNDRV_STB return 0; /* ok.. We have an InterWave board */#else return snd_interwave_detect_stb(iwcard, gus, dev, rbus);#endif } snd_printdd("[0x%lx] InterWave check - failed\n", gus->gf1.port); return -ENODEV;}static irqreturn_t snd_interwave_interrupt(int irq, void *dev_id, struct pt_regs *regs){ struct snd_interwave *iwcard = (struct snd_interwave *) dev_id; int loop, max = 5; int handled = 0; do { loop = 0; if (inb(iwcard->gus_status_reg)) { handled = 1; snd_gus_interrupt(irq, iwcard->gus, regs); loop++; } if (inb(iwcard->pcm_status_reg) & 0x01) { /* IRQ bit is set? */ handled = 1; snd_cs4231_interrupt(irq, iwcard->cs4231, regs); loop++; } } while (loop && --max > 0); return IRQ_RETVAL(handled);}static void __devinit snd_interwave_reset(snd_gus_card_t * gus){ snd_gf1_write8(gus, SNDRV_GF1_GB_RESET, 0x00); udelay(160); snd_gf1_write8(gus, SNDRV_GF1_GB_RESET, 0x01); udelay(160);}static void __devinit snd_interwave_bank_sizes(snd_gus_card_t * gus, int *sizes){ unsigned int idx; unsigned int local; unsigned char d; for (idx = 0; idx < 4; idx++) { sizes[idx] = 0; d = 0x55; for (local = idx << 22; local < (idx << 22) + 0x400000; local += 0x40000, d++) { snd_gf1_poke(gus, local, d); snd_gf1_poke(gus, local + 1, d + 1);#if 0 printk("d = 0x%x, local = 0x%x, local + 1 = 0x%x, idx << 22 = 0x%x\n", d, snd_gf1_peek(gus, local), snd_gf1_peek(gus, local + 1), snd_gf1_peek(gus, idx << 22));#endif if (snd_gf1_peek(gus, local) != d || snd_gf1_peek(gus, local + 1) != d + 1 || snd_gf1_peek(gus, idx << 22) != 0x55) break; sizes[idx]++; } }#if 0 printk("sizes: %i %i %i %i\n", sizes[0], sizes[1], sizes[2], sizes[3]);#endif}struct rom_hdr { /* 000 */ unsigned char iwave[8]; /* 008 */ unsigned char rom_hdr_revision; /* 009 */ unsigned char series_number; /* 010 */ unsigned char series_name[16]; /* 026 */ unsigned char date[10]; /* 036 */ unsigned short vendor_revision_major; /* 038 */ unsigned short vendor_revision_minor; /* 040 */ unsigned int rom_size; /* 044 */ unsigned char copyright[128]; /* 172 */ unsigned char vendor_name[64]; /* 236 */ unsigned char rom_description[128]; /* 364 */ unsigned char pad[147]; /* 511 */ unsigned char csum;};static void __devinit snd_interwave_detect_memory(snd_gus_card_t * gus){ static unsigned int lmc[13] = { 0x00000001, 0x00000101, 0x01010101, 0x00000401, 0x04040401, 0x00040101, 0x04040101, 0x00000004, 0x00000404, 0x04040404, 0x00000010, 0x00001010, 0x10101010 }; int bank_pos, pages; unsigned int i, lmct; int psizes[4]; unsigned char iwave[8]; unsigned char csum; snd_interwave_reset(gus); snd_gf1_write8(gus, SNDRV_GF1_GB_GLOBAL_MODE, snd_gf1_read8(gus, SNDRV_GF1_GB_GLOBAL_MODE) | 0x01); /* enhanced mode */ snd_gf1_write8(gus, SNDRV_GF1_GB_MEMORY_CONTROL, 0x01); /* DRAM I/O cycles selected */ snd_gf1_write16(gus, SNDRV_GF1_GW_MEMORY_CONFIG, (snd_gf1_look16(gus, SNDRV_GF1_GW_MEMORY_CONFIG) & 0xff10) | 0x004c); /* ok.. simple test of memory size */ pages = 0; snd_gf1_poke(gus, 0, 0x55); snd_gf1_poke(gus, 1, 0xaa);#if 1 if (snd_gf1_peek(gus, 0) == 0x55 && snd_gf1_peek(gus, 1) == 0xaa)#else if (0) /* ok.. for testing of 0k RAM */#endif { snd_interwave_bank_sizes(gus, psizes); lmct = (psizes[3] << 24) | (psizes[2] << 16) | (psizes[1] << 8) | psizes[0];#if 0 printk("lmct = 0x%08x\n", lmct);#endif for (i = 0; i < ARRAY_SIZE(lmc); i++) if (lmct == lmc[i]) {#if 0 printk("found !!! %i\n", i);#endif snd_gf1_write16(gus, SNDRV_GF1_GW_MEMORY_CONFIG, (snd_gf1_look16(gus, SNDRV_GF1_GW_MEMORY_CONFIG) & 0xfff0) | i); snd_interwave_bank_sizes(gus, psizes); break; } if (i >= ARRAY_SIZE(lmc) && !gus->gf1.enh_mode) snd_gf1_write16(gus, SNDRV_GF1_GW_MEMORY_CONFIG, (snd_gf1_look16(gus, SNDRV_GF1_GW_MEMORY_CONFIG) & 0xfff0) | 2); for (i = 0; i < 4; i++) { gus->gf1.mem_alloc.banks_8[i].address = gus->gf1.mem_alloc.banks_16[i].address = i << 22; gus->gf1.mem_alloc.banks_8[i].size = gus->gf1.mem_alloc.banks_16[i].size = psizes[i] << 18; pages += psizes[i]; } } pages <<= 18; gus->gf1.memory = pages; snd_gf1_write8(gus, SNDRV_GF1_GB_MEMORY_CONTROL, 0x03); /* select ROM */ snd_gf1_write16(gus, SNDRV_GF1_GW_MEMORY_CONFIG, (snd_gf1_look16(gus, SNDRV_GF1_GW_MEMORY_CONFIG) & 0xff1f) | (4 << 5)); gus->gf1.rom_banks = 0; gus->gf1.rom_memory = 0; for (bank_pos = 0; bank_pos < 16L * 1024L * 1024L; bank_pos += 4L * 1024L * 1024L) { for (i = 0; i < 8; ++i) iwave[i] = snd_gf1_peek(gus, bank_pos + i);#ifdef CONFIG_SND_DEBUG_ROM printk(KERN_DEBUG "ROM at 0x%06x = %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n", bank_pos, iwave[0], iwave[1], iwave[2], iwave[3], iwave[4], iwave[5], iwave[6], iwave[7]);#endif if (strncmp(iwave, "INTRWAVE", 8)) continue; /* first check */ csum = 0; for (i = 0; i < sizeof(struct rom_hdr); i++) csum += snd_gf1_peek(gus, bank_pos + i);#ifdef CONFIG_SND_DEBUG_ROM printk(KERN_DEBUG "ROM checksum = 0x%x (computed)\n", csum);#endif if (csum != 0) continue; /* not valid rom */ gus->gf1.rom_banks++; gus->gf1.rom_present |= 1 << (bank_pos >> 22); gus->gf1.rom_memory = snd_gf1_peek(gus, bank_pos + 40) | (snd_gf1_peek(gus, bank_pos + 41) << 8) | (snd_gf1_peek(gus, bank_pos + 42) << 16) | (snd_gf1_peek(gus, bank_pos + 43) << 24); }#if 0 if (gus->gf1.rom_memory > 0) { if (gus->gf1.rom_banks == 1 && gus->gf1.rom_present == 8) gus->card->type = SNDRV_CARD_TYPE_IW_DYNASONIC; }#endif snd_gf1_write8(gus, SNDRV_GF1_GB_MEMORY_CONTROL, 0x00); /* select RAM */ if (!gus->gf1.enh_mode) snd_interwave_reset(gus);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -