gusextreme.c 11.1 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
/*
 *  Driver for Gravis UltraSound Extreme soundcards
 *  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
 *
 */

#include <sound/driver.h>
#include <linux/init.h>
T
Takashi Iwai 已提交
24
#include <linux/err.h>
R
Rene Herman 已提交
25
#include <linux/isa.h>
L
Linus Torvalds 已提交
26 27 28
#include <linux/delay.h>
#include <linux/time.h>
#include <linux/moduleparam.h>
T
Takashi Iwai 已提交
29
#include <asm/dma.h>
L
Linus Torvalds 已提交
30 31 32 33 34 35 36 37 38 39
#include <sound/core.h>
#include <sound/gus.h>
#include <sound/es1688.h>
#include <sound/mpu401.h>
#include <sound/opl3.h>
#define SNDRV_LEGACY_AUTO_PROBE
#define SNDRV_LEGACY_FIND_FREE_IRQ
#define SNDRV_LEGACY_FIND_FREE_DMA
#include <sound/initval.h>

R
Rene Herman 已提交
40 41 42 43
#define CRD_NAME "Gravis UltraSound Extreme"
#define DEV_NAME "gusextreme"

MODULE_DESCRIPTION(CRD_NAME);
L
Linus Torvalds 已提交
44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
MODULE_LICENSE("GPL");
MODULE_SUPPORTED_DEVICE("{{Gravis,UltraSound Extreme}}");

static 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;	/* Enable this card */
static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;	/* 0x220,0x240,0x260 */
static long gf1_port[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS) - 1] = -1}; /* 0x210,0x220,0x230,0x240,0x250,0x260,0x270 */
static long mpu_port[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS) - 1] = -1}; /* 0x300,0x310,0x320 */
static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;	/* 5,7,9,10 */
static int mpu_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;	/* 5,7,9,10 */
static int gf1_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;	/* 2,3,5,9,11,12,15 */
static int dma8[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;	/* 0,1,3 */
static int dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;
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 channels[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 24};
static int pcm_channels[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 2};

module_param_array(index, int, NULL, 0444);
R
Rene Herman 已提交
65
MODULE_PARM_DESC(index, "Index value for " CRD_NAME " soundcard.");
L
Linus Torvalds 已提交
66
module_param_array(id, charp, NULL, 0444);
R
Rene Herman 已提交
67
MODULE_PARM_DESC(id, "ID string for " CRD_NAME " soundcard.");
L
Linus Torvalds 已提交
68
module_param_array(enable, bool, NULL, 0444);
R
Rene Herman 已提交
69
MODULE_PARM_DESC(enable, "Enable " CRD_NAME " soundcard.");
L
Linus Torvalds 已提交
70
module_param_array(port, long, NULL, 0444);
R
Rene Herman 已提交
71
MODULE_PARM_DESC(port, "Port # for " CRD_NAME " driver.");
L
Linus Torvalds 已提交
72
module_param_array(gf1_port, long, NULL, 0444);
R
Rene Herman 已提交
73
MODULE_PARM_DESC(gf1_port, "GF1 port # for " CRD_NAME " driver (optional).");
L
Linus Torvalds 已提交
74
module_param_array(mpu_port, long, NULL, 0444);
R
Rene Herman 已提交
75
MODULE_PARM_DESC(mpu_port, "MPU-401 port # for " CRD_NAME " driver.");
L
Linus Torvalds 已提交
76
module_param_array(irq, int, NULL, 0444);
R
Rene Herman 已提交
77
MODULE_PARM_DESC(irq, "IRQ # for " CRD_NAME " driver.");
L
Linus Torvalds 已提交
78
module_param_array(mpu_irq, int, NULL, 0444);
R
Rene Herman 已提交
79
MODULE_PARM_DESC(mpu_irq, "MPU-401 IRQ # for " CRD_NAME " driver.");
L
Linus Torvalds 已提交
80
module_param_array(gf1_irq, int, NULL, 0444);
R
Rene Herman 已提交
81
MODULE_PARM_DESC(gf1_irq, "GF1 IRQ # for " CRD_NAME " driver.");
L
Linus Torvalds 已提交
82
module_param_array(dma8, int, NULL, 0444);
R
Rene Herman 已提交
83
MODULE_PARM_DESC(dma8, "8-bit DMA # for " CRD_NAME " driver.");
L
Linus Torvalds 已提交
84
module_param_array(dma1, int, NULL, 0444);
R
Rene Herman 已提交
85
MODULE_PARM_DESC(dma1, "GF1 DMA # for " CRD_NAME " driver.");
L
Linus Torvalds 已提交
86
module_param_array(joystick_dac, int, NULL, 0444);
R
Rene Herman 已提交
87
MODULE_PARM_DESC(joystick_dac, "Joystick DAC level 0.59V-4.52V or 0.389V-2.98V for " CRD_NAME " driver.");
L
Linus Torvalds 已提交
88
module_param_array(channels, int, NULL, 0444);
R
Rene Herman 已提交
89
MODULE_PARM_DESC(channels, "GF1 channels for " CRD_NAME " driver.");
L
Linus Torvalds 已提交
90
module_param_array(pcm_channels, int, NULL, 0444);
R
Rene Herman 已提交
91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122
MODULE_PARM_DESC(pcm_channels, "Reserved PCM channels for " CRD_NAME " driver.");

static int __devinit snd_gusextreme_match(struct device *dev, unsigned int n)
{
	return enable[n];
}

static int __devinit snd_gusextreme_es1688_create(struct snd_card *card,
		struct device *dev, unsigned int n, struct snd_es1688 **rchip)
{
	static long possible_ports[] = {0x220, 0x240, 0x260};
	static int possible_irqs[] = {5, 9, 10, 7, -1};
	static int possible_dmas[] = {1, 3, 0, -1};

	int i, error;

	if (irq[n] == SNDRV_AUTO_IRQ) {
		irq[n] = snd_legacy_find_free_irq(possible_irqs);
		if (irq[n] < 0) {
			snd_printk(KERN_ERR "%s: unable to find a free IRQ "
				"for ES1688\n", dev->bus_id);
			return -EBUSY;
		}
	}
	if (dma8[n] == SNDRV_AUTO_DMA) {
		dma8[n] = snd_legacy_find_free_dma(possible_dmas);
		if (dma8[n] < 0) {
			snd_printk(KERN_ERR "%s: unable to find a free DMA "
				"for ES1688\n", dev->bus_id);
			return -EBUSY;
		}
	}
L
Linus Torvalds 已提交
123

R
Rene Herman 已提交
124 125 126
	if (port[n] != SNDRV_AUTO_PORT)
		return snd_es1688_create(card, port[n], mpu_port[n], irq[n],
				mpu_irq[n], dma8[n], ES1688_HW_1688, rchip);
127

R
Rene Herman 已提交
128 129 130 131 132 133
	i = 0;
	do {
		port[n] = possible_ports[i];
		error = snd_es1688_create(card, port[n], mpu_port[n], irq[n],
				mpu_irq[n], dma8[n], ES1688_HW_1688, rchip);
	} while (error < 0 && ++i < ARRAY_SIZE(possible_ports));
L
Linus Torvalds 已提交
134

R
Rene Herman 已提交
135 136
	return error;
}
L
Linus Torvalds 已提交
137

R
Rene Herman 已提交
138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165
static int __devinit snd_gusextreme_gus_card_create(struct snd_card *card,
		struct device *dev, unsigned int n, struct snd_gus_card **rgus)
{
	static int possible_irqs[] = {11, 12, 15, 9, 5, 7, 3, -1};
	static int possible_dmas[] = {5, 6, 7, 3, 1, -1};

	if (gf1_irq[n] == SNDRV_AUTO_IRQ) {
		gf1_irq[n] = snd_legacy_find_free_irq(possible_irqs);
		if (gf1_irq[n] < 0) {
			snd_printk(KERN_ERR "%s: unable to find a free IRQ "
				"for GF1\n", dev->bus_id);
			return -EBUSY;
		}
	}
	if (dma1[n] == SNDRV_AUTO_DMA) {
		dma1[n] = snd_legacy_find_free_dma(possible_dmas);
		if (dma1[n] < 0) {
			snd_printk(KERN_ERR "%s: unable to find a free DMA "
				"for GF1\n", dev->bus_id);
			return -EBUSY;
		}
	}
	return snd_gus_create(card, gf1_port[n], gf1_irq[n], dma1[n], -1,
			0, channels[n], pcm_channels[n], 0, rgus);
}

static int __devinit snd_gusextreme_detect(struct snd_gus_card *gus,
	struct snd_es1688 *es1688)
L
Linus Torvalds 已提交
166 167
{
	unsigned long flags;
168
	unsigned char d;
L
Linus Torvalds 已提交
169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186

	/*
	 * This is main stuff - enable access to GF1 chip...
	 * I'm not sure, if this will work for card which have
	 * ES1688 chip in another place than 0x220.
         *
         * I used reverse-engineering in DOSEMU. [--jk]
	 *
	 * ULTRINIT.EXE:
	 * 0x230 = 0,2,3
	 * 0x240 = 2,0,1
	 * 0x250 = 2,0,3
	 * 0x260 = 2,2,1
	 */

	spin_lock_irqsave(&es1688->mixer_lock, flags);
	snd_es1688_mixer_write(es1688, 0x40, 0x0b);	/* don't change!!! */
	spin_unlock_irqrestore(&es1688->mixer_lock, flags);
R
Rene Herman 已提交
187

L
Linus Torvalds 已提交
188
	spin_lock_irqsave(&es1688->reg_lock, flags);
R
Rene Herman 已提交
189
	outb(gus->gf1.port & 0x040 ? 2 : 0, ES1688P(es1688, INIT1));
L
Linus Torvalds 已提交
190
	outb(0, 0x201);
R
Rene Herman 已提交
191
	outb(gus->gf1.port & 0x020 ? 2 : 0, ES1688P(es1688, INIT1));
L
Linus Torvalds 已提交
192
	outb(0, 0x201);
R
Rene Herman 已提交
193
	outb(gus->gf1.port & 0x010 ? 3 : 1, ES1688P(es1688, INIT1));
L
Linus Torvalds 已提交
194 195 196 197 198
	spin_unlock_irqrestore(&es1688->reg_lock, flags);

	udelay(100);

	snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 0);	/* reset GF1 */
199 200
	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);
L
Linus Torvalds 已提交
201
		return -EIO;
202
	}
L
Linus Torvalds 已提交
203 204 205
	udelay(160);
	snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 1);	/* release reset */
	udelay(160);
206 207
	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);
L
Linus Torvalds 已提交
208
		return -EIO;
209
	}
L
Linus Torvalds 已提交
210

R
Rene Herman 已提交
211
	return 0;
L
Linus Torvalds 已提交
212 213
}

214
static int __devinit snd_gusextreme_mixer(struct snd_es1688 *chip)
L
Linus Torvalds 已提交
215
{
216 217
	struct snd_card *card = chip->card;
	struct snd_ctl_elem_id id1, id2;
R
Rene Herman 已提交
218
	int error;
L
Linus Torvalds 已提交
219 220 221 222

	memset(&id1, 0, sizeof(id1));
	memset(&id2, 0, sizeof(id2));
	id1.iface = id2.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
R
Rene Herman 已提交
223

L
Linus Torvalds 已提交
224 225 226
	/* reassign AUX to SYNTHESIZER */
	strcpy(id1.name, "Aux Playback Volume");
	strcpy(id2.name, "Synth Playback Volume");
R
Rene Herman 已提交
227 228 229 230
	error = snd_ctl_rename_id(card, &id1, &id2);
	if (error < 0)
		return error;

L
Linus Torvalds 已提交
231 232 233
	/* reassign Master Playback Switch to Synth Playback Switch */
	strcpy(id1.name, "Master Playback Switch");
	strcpy(id2.name, "Synth Playback Switch");
R
Rene Herman 已提交
234 235 236 237
	error = snd_ctl_rename_id(card, &id1, &id2);
	if (error < 0)
		return error;

L
Linus Torvalds 已提交
238 239 240
	return 0;
}

R
Rene Herman 已提交
241
static int __devinit snd_gusextreme_probe(struct device *dev, unsigned int n)
L
Linus Torvalds 已提交
242
{
243 244 245 246
	struct snd_card *card;
	struct snd_gus_card *gus;
	struct snd_es1688 *es1688;
	struct snd_opl3 *opl3;
R
Rene Herman 已提交
247
	int error;
L
Linus Torvalds 已提交
248

R
Rene Herman 已提交
249 250 251
	card = snd_card_new(index[n], id[n], THIS_MODULE, 0);
	if (!card)
		return -EINVAL;
L
Linus Torvalds 已提交
252

R
Rene Herman 已提交
253 254
	if (mpu_port[n] == SNDRV_AUTO_PORT)
		mpu_port[n] = 0;
L
Linus Torvalds 已提交
255

R
Rene Herman 已提交
256 257 258 259 260
	if (mpu_irq[n] > 15)
		mpu_irq[n] = -1;

	error = snd_gusextreme_es1688_create(card, dev, n, &es1688);
	if (error < 0)
L
Linus Torvalds 已提交
261
		goto out;
T
Takashi Iwai 已提交
262

R
Rene Herman 已提交
263 264 265 266 267
	if (gf1_port[n] < 0)
		gf1_port[n] = es1688->port + 0x20;

	error = snd_gusextreme_gus_card_create(card, dev, n, &gus);
	if (error < 0)
L
Linus Torvalds 已提交
268 269
		goto out;

R
Rene Herman 已提交
270 271
	error = snd_gusextreme_detect(gus, es1688);
	if (error < 0)
L
Linus Torvalds 已提交
272 273
		goto out;

R
Rene Herman 已提交
274 275 276 277
	gus->joystick_dac = joystick_dac[n];

	error = snd_gus_initialize(gus);
	if (error < 0)
L
Linus Torvalds 已提交
278 279
		goto out;

R
Rene Herman 已提交
280
	error = -ENODEV;
L
Linus Torvalds 已提交
281
	if (!gus->ess_flag) {
R
Rene Herman 已提交
282 283
		snd_printk(KERN_ERR "%s: GUS Extreme soundcard was not "
			"detected at 0x%lx\n", dev->bus_id, gus->gf1.port);
L
Linus Torvalds 已提交
284 285
		goto out;
	}
R
Rene Herman 已提交
286 287 288

	error = snd_es1688_pcm(es1688, 0, NULL);
	if (error < 0)
L
Linus Torvalds 已提交
289 290
		goto out;

R
Rene Herman 已提交
291 292
	error = snd_es1688_mixer(es1688);
	if (error < 0)
L
Linus Torvalds 已提交
293 294 295
		goto out;

	snd_component_add(card, "ES1688");
R
Rene Herman 已提交
296 297 298 299

	if (pcm_channels[n] > 0) {
		error = snd_gf1_pcm_new(gus, 1, 1, NULL);
		if (error < 0)
L
Linus Torvalds 已提交
300 301
			goto out;
	}
R
Rene Herman 已提交
302 303 304

	error = snd_gf1_new_mixer(gus);
	if (error < 0)
L
Linus Torvalds 已提交
305 306
		goto out;

R
Rene Herman 已提交
307 308
	error = snd_gusextreme_mixer(es1688);
	if (error < 0)
L
Linus Torvalds 已提交
309 310 311
		goto out;

	if (snd_opl3_create(card, es1688->port, es1688->port + 2,
R
Rene Herman 已提交
312 313 314 315 316 317
			OPL3_HW_OPL3, 0, &opl3) < 0)
		printk(KERN_ERR "%s: opl3 not detected at 0x%lx\n",
			dev->bus_id, es1688->port);
	else {
		error = snd_opl3_hwdep_new(opl3, 0, 2, NULL);
		if (error < 0)
L
Linus Torvalds 已提交
318 319 320
			goto out;
	}

R
Rene Herman 已提交
321 322 323 324 325 326 327
	if (es1688->mpu_port >= 0x300) {
		error = snd_mpu401_uart_new(card, 0, MPU401_HW_ES1688,
				es1688->mpu_port, 0,
				mpu_irq[n], IRQF_DISABLED, NULL);
		if (error < 0)
			goto out;
	}
L
Linus Torvalds 已提交
328

R
Rene Herman 已提交
329 330 331
	sprintf(card->longname, "Gravis UltraSound Extreme at 0x%lx, "
		"irq %i&%i, dma %i&%i", es1688->port,
		gus->gf1.irq, es1688->irq, gus->gf1.dma1, es1688->dma8);
332

R
Rene Herman 已提交
333
	snd_card_set_dev(card, dev);
334

R
Rene Herman 已提交
335 336
	error = snd_card_register(card);
	if (error < 0)
L
Linus Torvalds 已提交
337 338
		goto out;

R
Rene Herman 已提交
339
	dev_set_drvdata(dev, card);
L
Linus Torvalds 已提交
340 341
	return 0;

R
Rene Herman 已提交
342 343
out:	snd_card_free(card);
	return error;
L
Linus Torvalds 已提交
344 345
}

R
Rene Herman 已提交
346
static int __devexit snd_gusextreme_remove(struct device *dev, unsigned int n)
L
Linus Torvalds 已提交
347
{
R
Rene Herman 已提交
348 349
	snd_card_free(dev_get_drvdata(dev));
	dev_set_drvdata(dev, NULL);
T
Takashi Iwai 已提交
350
	return 0;
L
Linus Torvalds 已提交
351 352
}

R
Rene Herman 已提交
353 354
static struct isa_driver snd_gusextreme_driver = {
	.match		= snd_gusextreme_match,
T
Takashi Iwai 已提交
355
	.probe		= snd_gusextreme_probe,
R
Rene Herman 已提交
356 357 358 359 360
	.remove		= snd_gusextreme_remove,
#if 0	/* FIXME */
	.suspend	= snd_gusextreme_suspend,
	.resume		= snd_gusextreme_resume,
#endif
T
Takashi Iwai 已提交
361
	.driver		= {
R
Rene Herman 已提交
362 363
		.name	= DEV_NAME
	}
T
Takashi Iwai 已提交
364 365
};

L
Linus Torvalds 已提交
366 367
static int __init alsa_card_gusextreme_init(void)
{
R
Rene Herman 已提交
368
	return isa_register_driver(&snd_gusextreme_driver, SNDRV_CARDS);
L
Linus Torvalds 已提交
369 370 371 372
}

static void __exit alsa_card_gusextreme_exit(void)
{
R
Rene Herman 已提交
373
	isa_unregister_driver(&snd_gusextreme_driver);
L
Linus Torvalds 已提交
374 375
}

R
Rene Herman 已提交
376 377
module_init(alsa_card_gusextreme_init);
module_exit(alsa_card_gusextreme_exit);