gusclassic.c 7.8 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 Classic 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
 *
 */

#include <sound/driver.h>
#include <linux/init.h>
T
Takashi Iwai 已提交
24 25
#include <linux/err.h>
#include <linux/platform_device.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 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74
#include <sound/core.h>
#include <sound/gus.h>
#define SNDRV_LEGACY_FIND_FREE_IRQ
#define SNDRV_LEGACY_FIND_FREE_DMA
#include <sound/initval.h>

MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
MODULE_DESCRIPTION("Gravis UltraSound Classic");
MODULE_LICENSE("GPL");
MODULE_SUPPORTED_DEVICE("{{Gravis,UltraSound Classic}}");

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,0x230,0x240,0x250,0x260 */
static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;	/* 3,5,9,11,12,15 */
static int dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;	/* 1,3,5,6,7 */
static int dma2[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;	/* 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 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);
MODULE_PARM_DESC(index, "Index value for GUS Classic soundcard.");
module_param_array(id, charp, NULL, 0444);
MODULE_PARM_DESC(id, "ID string for GUS Classic soundcard.");
module_param_array(enable, bool, NULL, 0444);
MODULE_PARM_DESC(enable, "Enable GUS Classic soundcard.");
module_param_array(port, long, NULL, 0444);
MODULE_PARM_DESC(port, "Port # for GUS Classic driver.");
module_param_array(irq, int, NULL, 0444);
MODULE_PARM_DESC(irq, "IRQ # for GUS Classic driver.");
module_param_array(dma1, int, NULL, 0444);
MODULE_PARM_DESC(dma1, "DMA1 # for GUS Classic driver.");
module_param_array(dma2, int, NULL, 0444);
MODULE_PARM_DESC(dma2, "DMA2 # for GUS Classic 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 GUS Classic driver.");
module_param_array(channels, int, NULL, 0444);
MODULE_PARM_DESC(channels, "GF1 channels for GUS Classic driver.");
module_param_array(pcm_channels, int, NULL, 0444);
MODULE_PARM_DESC(pcm_channels, "Reserved PCM channels for GUS Classic driver.");


75
#define PFX	"gusclassic: "
L
Linus Torvalds 已提交
76

77
static int __init snd_gusclassic_detect(struct snd_gus_card * gus)
L
Linus Torvalds 已提交
78
{
79
	unsigned char d;
L
Linus Torvalds 已提交
80

81 82 83
	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);
L
Linus Torvalds 已提交
84
		return -ENODEV;
85
	}
L
Linus Torvalds 已提交
86 87 88
	udelay(160);
	snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 1);	/* release reset */
	udelay(160);
89 90
	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 已提交
91
		return -ENODEV;
92
	}
L
Linus Torvalds 已提交
93 94 95
	return 0;
}

96
static void __init snd_gusclassic_init(int dev, struct snd_gus_card * gus)
L
Linus Torvalds 已提交
97 98 99 100 101 102 103
{
	gus->equal_irq = 0;
	gus->codec_flag = 0;
	gus->max_flag = 0;
	gus->joystick_dac = joystick_dac[dev];
}

T
Takashi Iwai 已提交
104
static int __init snd_gusclassic_probe(struct platform_device *pdev)
L
Linus Torvalds 已提交
105
{
T
Takashi Iwai 已提交
106
	int dev = pdev->id;
L
Linus Torvalds 已提交
107 108 109
	static int possible_irqs[] = {5, 11, 12, 9, 7, 15, 3, 4, -1};
	static int possible_dmas[] = {5, 6, 7, 1, 3, -1};
	int xirq, xdma1, xdma2;
110 111
	struct snd_card *card;
	struct snd_gus_card *gus = NULL;
L
Linus Torvalds 已提交
112 113 114 115 116 117 118 119 120 121 122
	int err;

	card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0);
	if (card == NULL)
		return -ENOMEM;
	if (pcm_channels[dev] < 2)
		pcm_channels[dev] = 2;

	xirq = irq[dev];
	if (xirq == SNDRV_AUTO_IRQ) {
		if ((xirq = snd_legacy_find_free_irq(possible_irqs)) < 0) {
123 124 125
			snd_printk(KERN_ERR PFX "unable to find a free IRQ\n");
			err = -EBUSY;
			goto _err;
L
Linus Torvalds 已提交
126 127 128 129 130
		}
	}
	xdma1 = dma1[dev];
	if (xdma1 == SNDRV_AUTO_DMA) {
		if ((xdma1 = snd_legacy_find_free_dma(possible_dmas)) < 0) {
131 132 133
			snd_printk(KERN_ERR PFX "unable to find a free DMA1\n");
			err = -EBUSY;
			goto _err;
L
Linus Torvalds 已提交
134 135 136 137 138
		}
	}
	xdma2 = dma2[dev];
	if (xdma2 == SNDRV_AUTO_DMA) {
		if ((xdma2 = snd_legacy_find_free_dma(possible_dmas)) < 0) {
139 140 141
			snd_printk(KERN_ERR PFX "unable to find a free DMA2\n");
			err = -EBUSY;
			goto _err;
L
Linus Torvalds 已提交
142 143 144
		}
	}

T
Takashi Iwai 已提交
145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169
	if (port[dev] != SNDRV_AUTO_PORT) {
		err = snd_gus_create(card,
				     port[dev],
				     xirq, xdma1, xdma2,
				     0, channels[dev], pcm_channels[dev],
				     0, &gus);
	} else {
		/* auto-probe legacy ports */
		static unsigned long possible_ports[] = {
			0x220, 0x230, 0x240, 0x250, 0x260,
		};
		int i;
		for (i = 0; i < ARRAY_SIZE(possible_ports); i++) {
			err = snd_gus_create(card,
					     possible_ports[i],
					     xirq, xdma1, xdma2,
					     0, channels[dev], pcm_channels[dev],
					     0, &gus);
			if (err >= 0) {
				port[dev] = possible_ports[i];
				break;
			}
		}
	}
	if (err < 0)
170 171 172 173 174
		goto _err;

	if ((err = snd_gusclassic_detect(gus)) < 0)
		goto _err;

L
Linus Torvalds 已提交
175
	snd_gusclassic_init(dev, gus);
176 177 178
	if ((err = snd_gus_initialize(gus)) < 0)
		goto _err;

L
Linus Torvalds 已提交
179
	if (gus->max_flag || gus->ess_flag) {
180 181 182
		snd_printk(KERN_ERR PFX "GUS Classic or ACE soundcard was not detected at 0x%lx\n", gus->gf1.port);
		err = -ENODEV;
		goto _err;
L
Linus Torvalds 已提交
183
	}
184 185 186 187 188 189 190

	if ((err = snd_gf1_new_mixer(gus)) < 0)
		goto _err;

	if ((err = snd_gf1_pcm_new(gus, 0, 0, NULL)) < 0)
		goto _err;

L
Linus Torvalds 已提交
191
	if (!gus->ace_flag) {
192 193
		if ((err = snd_gf1_rawmidi_new(gus, 0, NULL)) < 0)
			goto _err;
L
Linus Torvalds 已提交
194 195 196 197
	}
	sprintf(card->longname + strlen(card->longname), " at 0x%lx, irq %d, dma %d", gus->gf1.port, xirq, xdma1);
	if (dma2 >= 0)
		sprintf(card->longname + strlen(card->longname), "&%d", xdma2);
198

T
Takashi Iwai 已提交
199
	snd_card_set_dev(card, &pdev->dev);
200 201 202 203

	if ((err = snd_card_register(card)) < 0)
		goto _err;

T
Takashi Iwai 已提交
204
	platform_set_drvdata(pdev, card);
L
Linus Torvalds 已提交
205
	return 0;
206 207 208 209

 _err:
	snd_card_free(card);
	return err;
L
Linus Torvalds 已提交
210 211
}

T
Takashi Iwai 已提交
212
static int snd_gusclassic_remove(struct platform_device *devptr)
L
Linus Torvalds 已提交
213
{
T
Takashi Iwai 已提交
214 215 216
	snd_card_free(platform_get_drvdata(devptr));
	platform_set_drvdata(devptr, NULL);
	return 0;
L
Linus Torvalds 已提交
217 218
}

T
Takashi Iwai 已提交
219 220 221 222 223 224 225 226 227 228 229
#define GUSCLASSIC_DRIVER	"snd_gusclassic"

static struct platform_driver snd_gusclassic_driver = {
	.probe		= snd_gusclassic_probe,
	.remove		= snd_gusclassic_remove,
	/* FIXME: suspend/resume */
	.driver		= {
		.name	= GUSCLASSIC_DRIVER
	},
};

L
Linus Torvalds 已提交
230 231
static int __init alsa_card_gusclassic_init(void)
{
T
Takashi Iwai 已提交
232 233 234 235 236
	int i, cards, err;

	err = platform_driver_register(&snd_gusclassic_driver);
	if (err < 0)
		return err;
L
Linus Torvalds 已提交
237

T
Takashi Iwai 已提交
238 239 240 241 242 243 244 245 246 247 248
	cards = 0;
	for (i = 0; i < SNDRV_CARDS && enable[i]; i++) {
		struct platform_device *device;
		device = platform_device_register_simple(GUSCLASSIC_DRIVER,
							 i, NULL, 0);
		if (IS_ERR(device)) {
			err = PTR_ERR(device);
			goto errout;
		}
		cards++;
	}
L
Linus Torvalds 已提交
249 250 251 252
	if (!cards) {
#ifdef MODULE
		printk(KERN_ERR "GUS Classic soundcard not found or device busy\n");
#endif
T
Takashi Iwai 已提交
253 254
		err = -ENODEV;
		goto errout;
L
Linus Torvalds 已提交
255 256
	}
	return 0;
T
Takashi Iwai 已提交
257 258 259 260

 errout:
	platform_driver_unregister(&snd_gusclassic_driver);
	return err;
L
Linus Torvalds 已提交
261 262 263 264
}

static void __exit alsa_card_gusclassic_exit(void)
{
T
Takashi Iwai 已提交
265
	platform_driver_unregister(&snd_gusclassic_driver);
L
Linus Torvalds 已提交
266 267 268 269
}

module_init(alsa_card_gusclassic_init)
module_exit(alsa_card_gusclassic_exit)