gusclassic.c 8.0 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
#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.");

74 75
static struct platform_device *devices[SNDRV_CARDS];

L
Linus Torvalds 已提交
76

77
#define PFX	"gusclassic: "
L
Linus Torvalds 已提交
78

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

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

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

T
Takashi Iwai 已提交
106
static int __init snd_gusclassic_probe(struct platform_device *pdev)
L
Linus Torvalds 已提交
107
{
T
Takashi Iwai 已提交
108
	int dev = pdev->id;
L
Linus Torvalds 已提交
109 110 111
	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;
112 113
	struct snd_card *card;
	struct snd_gus_card *gus = NULL;
L
Linus Torvalds 已提交
114 115 116 117 118 119 120 121 122 123 124
	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) {
125 126 127
			snd_printk(KERN_ERR PFX "unable to find a free IRQ\n");
			err = -EBUSY;
			goto _err;
L
Linus Torvalds 已提交
128 129 130 131 132
		}
	}
	xdma1 = dma1[dev];
	if (xdma1 == SNDRV_AUTO_DMA) {
		if ((xdma1 = snd_legacy_find_free_dma(possible_dmas)) < 0) {
133 134 135
			snd_printk(KERN_ERR PFX "unable to find a free DMA1\n");
			err = -EBUSY;
			goto _err;
L
Linus Torvalds 已提交
136 137 138 139 140
		}
	}
	xdma2 = dma2[dev];
	if (xdma2 == SNDRV_AUTO_DMA) {
		if ((xdma2 = snd_legacy_find_free_dma(possible_dmas)) < 0) {
141 142 143
			snd_printk(KERN_ERR PFX "unable to find a free DMA2\n");
			err = -EBUSY;
			goto _err;
L
Linus Torvalds 已提交
144 145 146
		}
	}

T
Takashi Iwai 已提交
147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171
	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)
172 173 174 175 176
		goto _err;

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

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

L
Linus Torvalds 已提交
181
	if (gus->max_flag || gus->ess_flag) {
182 183 184
		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 已提交
185
	}
186 187 188 189 190 191 192

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

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

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

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

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

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

T
Takashi Iwai 已提交
221 222 223 224 225 226 227 228 229 230 231
#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
	},
};

232 233 234 235 236 237 238 239 240
static void __init_or_module snd_gusclassic_unregister_all(void)
{
	int i;

	for (i = 0; i < ARRAY_SIZE(devices); ++i)
		platform_device_unregister(devices[i]);
	platform_driver_unregister(&snd_gusclassic_driver);
}

L
Linus Torvalds 已提交
241 242
static int __init alsa_card_gusclassic_init(void)
{
T
Takashi Iwai 已提交
243 244 245 246 247
	int i, cards, err;

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

T
Takashi Iwai 已提交
249 250 251 252 253 254 255 256 257
	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;
		}
258
		devices[i] = device;
T
Takashi Iwai 已提交
259 260
		cards++;
	}
L
Linus Torvalds 已提交
261 262 263 264
	if (!cards) {
#ifdef MODULE
		printk(KERN_ERR "GUS Classic soundcard not found or device busy\n");
#endif
T
Takashi Iwai 已提交
265 266
		err = -ENODEV;
		goto errout;
L
Linus Torvalds 已提交
267 268
	}
	return 0;
T
Takashi Iwai 已提交
269 270

 errout:
271
	snd_gusclassic_unregister_all();
T
Takashi Iwai 已提交
272
	return err;
L
Linus Torvalds 已提交
273 274 275 276
}

static void __exit alsa_card_gusclassic_exit(void)
{
277
	snd_gusclassic_unregister_all();
L
Linus Torvalds 已提交
278 279 280 281
}

module_init(alsa_card_gusclassic_init)
module_exit(alsa_card_gusclassic_exit)