sb8.c 6.7 KB
Newer Older
L
Linus Torvalds 已提交
1 2
/*
 *  Driver for SoundBlaster 1.0/2.0/Pro soundcards and compatible
3
 *  Copyright (c) by Jaroslav Kysela <perex@perex.cz>
L
Linus Torvalds 已提交
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
 *
 *
 *   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 <linux/init.h>
23
#include <linux/err.h>
24
#include <linux/isa.h>
L
Linus Torvalds 已提交
25
#include <linux/ioport.h>
26
#include <linux/module.h>
L
Linus Torvalds 已提交
27 28 29 30 31
#include <sound/core.h>
#include <sound/sb.h>
#include <sound/opl3.h>
#include <sound/initval.h>

32
MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
L
Linus Torvalds 已提交
33 34 35 36 37 38
MODULE_DESCRIPTION("Sound Blaster 1.0/2.0/Pro");
MODULE_LICENSE("GPL");
MODULE_SUPPORTED_DEVICE("{{Creative Labs,SB 1.0/SB 2.0/SB Pro}}");

static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	/* Index 0-MAX */
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	/* ID for this card */
39
static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE;	/* Enable this card */
L
Linus Torvalds 已提交
40 41 42 43 44 45 46 47 48 49
static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;	/* 0x220,0x240,0x260 */
static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;	/* 5,7,9,10 */
static int dma8[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;	/* 1,3 */

module_param_array(index, int, NULL, 0444);
MODULE_PARM_DESC(index, "Index value for Sound Blaster soundcard.");
module_param_array(id, charp, NULL, 0444);
MODULE_PARM_DESC(id, "ID string for Sound Blaster soundcard.");
module_param_array(enable, bool, NULL, 0444);
MODULE_PARM_DESC(enable, "Enable Sound Blaster soundcard.");
50
module_param_hw_array(port, long, ioport, NULL, 0444);
L
Linus Torvalds 已提交
51
MODULE_PARM_DESC(port, "Port # for SB8 driver.");
52
module_param_hw_array(irq, int, irq, NULL, 0444);
L
Linus Torvalds 已提交
53
MODULE_PARM_DESC(irq, "IRQ # for SB8 driver.");
54
module_param_hw_array(dma8, int, dma, NULL, 0444);
L
Linus Torvalds 已提交
55 56 57 58
MODULE_PARM_DESC(dma8, "8-bit DMA # for SB8 driver.");

struct snd_sb8 {
	struct resource *fm_res;	/* used to block FM i/o region for legacy cards */
59
	struct snd_sb *chip;
L
Linus Torvalds 已提交
60 61
};

62
static irqreturn_t snd_sb8_interrupt(int irq, void *dev_id)
L
Linus Torvalds 已提交
63
{
64
	struct snd_sb *chip = dev_id;
L
Linus Torvalds 已提交
65 66 67 68 69 70 71 72

	if (chip->open & SB_OPEN_PCM) {
		return snd_sb8dsp_interrupt(chip);
	} else {
		return snd_sb8dsp_midi_interrupt(chip);
	}
}

73
static void snd_sb8_free(struct snd_card *card)
L
Linus Torvalds 已提交
74
{
75
	struct snd_sb8 *acard = card->private_data;
L
Linus Torvalds 已提交
76 77 78

	if (acard == NULL)
		return;
79
	release_and_free_resource(acard->fm_res);
L
Linus Torvalds 已提交
80 81
}

82
static int snd_sb8_match(struct device *pdev, unsigned int dev)
83 84 85 86
{
	if (!enable[dev])
		return 0;
	if (irq[dev] == SNDRV_AUTO_IRQ) {
87
		dev_err(pdev, "please specify irq\n");
88 89 90
		return 0;
	}
	if (dma8[dev] == SNDRV_AUTO_DMA) {
91
		dev_err(pdev, "please specify dma8\n");
92 93 94 95 96
		return 0;
	}
	return 1;
}

97
static int snd_sb8_probe(struct device *pdev, unsigned int dev)
L
Linus Torvalds 已提交
98
{
99 100
	struct snd_sb *chip;
	struct snd_card *card;
L
Linus Torvalds 已提交
101
	struct snd_sb8 *acard;
102
	struct snd_opl3 *opl3;
L
Linus Torvalds 已提交
103 104
	int err;

105 106
	err = snd_card_new(pdev, index[dev], id[dev], THIS_MODULE,
			   sizeof(struct snd_sb8), &card);
107 108
	if (err < 0)
		return err;
109
	acard = card->private_data;
L
Linus Torvalds 已提交
110 111 112 113 114
	card->private_free = snd_sb8_free;

	/* block the 0x388 port to avoid PnP conflicts */
	acard->fm_res = request_region(0x388, 4, "SoundBlaster FM");

115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141
	if (port[dev] != SNDRV_AUTO_PORT) {
		if ((err = snd_sbdsp_create(card, port[dev], irq[dev],
					    snd_sb8_interrupt,
					    dma8[dev],
					    -1,
					    SB_HW_AUTO,
					    &chip)) < 0)
			goto _err;
	} else {
		/* auto-probe legacy ports */
		static unsigned long possible_ports[] = {
			0x220, 0x240, 0x260,
		};
		int i;
		for (i = 0; i < ARRAY_SIZE(possible_ports); i++) {
			err = snd_sbdsp_create(card, possible_ports[i],
					       irq[dev],
					       snd_sb8_interrupt,
					       dma8[dev],
					       -1,
					       SB_HW_AUTO,
					       &chip);
			if (err >= 0) {
				port[dev] = possible_ports[i];
				break;
			}
		}
142 143
		if (i >= ARRAY_SIZE(possible_ports)) {
			err = -EINVAL;
144
			goto _err;
145
		}
146 147 148
	}
	acard->chip = chip;
			
L
Linus Torvalds 已提交
149 150
	if (chip->hardware >= SB_HW_16) {
		if (chip->hardware == SB_HW_ALS100)
151
			snd_printk(KERN_WARNING "ALS100 chip detected at 0x%lx, try snd-als100 module\n",
L
Linus Torvalds 已提交
152 153
				    port[dev]);
		else
154 155 156 157
			snd_printk(KERN_WARNING "SB 16 chip detected at 0x%lx, try snd-sb16 module\n",
				   port[dev]);
		err = -ENODEV;
		goto _err;
L
Linus Torvalds 已提交
158 159
	}

160
	if ((err = snd_sb8dsp_pcm(chip, 0)) < 0)
161 162 163 164 165
		goto _err;

	if ((err = snd_sbmixer_new(chip)) < 0)
		goto _err;

L
Linus Torvalds 已提交
166 167 168 169
	if (chip->hardware == SB_HW_10 || chip->hardware == SB_HW_20) {
		if ((err = snd_opl3_create(card, chip->port + 8, 0,
					   OPL3_HW_AUTO, 1,
					   &opl3)) < 0) {
170
			snd_printk(KERN_WARNING "sb8: no OPL device at 0x%lx\n", chip->port + 8);
L
Linus Torvalds 已提交
171 172 173 174 175
		}
	} else {
		if ((err = snd_opl3_create(card, chip->port, chip->port + 2,
					   OPL3_HW_AUTO, 1,
					   &opl3)) < 0) {
176
			snd_printk(KERN_WARNING "sb8: no OPL device at 0x%lx-0x%lx\n",
L
Linus Torvalds 已提交
177 178 179 180
				   chip->port, chip->port + 2);
		}
	}
	if (err >= 0) {
181 182
		if ((err = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0)
			goto _err;
L
Linus Torvalds 已提交
183 184
	}

185
	if ((err = snd_sb8dsp_midi(chip, 0)) < 0)
186
		goto _err;
L
Linus Torvalds 已提交
187 188 189 190 191 192 193

	strcpy(card->driver, chip->hardware == SB_HW_PRO ? "SB Pro" : "SB8");
	strcpy(card->shortname, chip->name);
	sprintf(card->longname, "%s at 0x%lx, irq %d, dma %d",
		chip->name,
		chip->port,
		irq[dev], dma8[dev]);
194 195 196 197

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

198
	dev_set_drvdata(pdev, card);
L
Linus Torvalds 已提交
199
	return 0;
200 201 202 203

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

206
static int snd_sb8_remove(struct device *pdev, unsigned int dev)
207
{
208
	snd_card_free(dev_get_drvdata(pdev));
209 210 211 212
	return 0;
}

#ifdef CONFIG_PM
213 214
static int snd_sb8_suspend(struct device *dev, unsigned int n,
			   pm_message_t state)
215
{
216
	struct snd_card *card = dev_get_drvdata(dev);
217 218 219 220 221 222 223 224
	struct snd_sb8 *acard = card->private_data;
	struct snd_sb *chip = acard->chip;

	snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
	snd_sbmixer_suspend(chip);
	return 0;
}

225
static int snd_sb8_resume(struct device *dev, unsigned int n)
L
Linus Torvalds 已提交
226
{
227
	struct snd_card *card = dev_get_drvdata(dev);
228 229 230 231 232 233 234
	struct snd_sb8 *acard = card->private_data;
	struct snd_sb *chip = acard->chip;

	snd_sbdsp_reset(chip);
	snd_sbmixer_resume(chip);
	snd_power_change_state(card, SNDRV_CTL_POWER_D0);
	return 0;
L
Linus Torvalds 已提交
235
}
236 237
#endif

238
#define DEV_NAME "sb8"
239

240 241
static struct isa_driver snd_sb8_driver = {
	.match		= snd_sb8_match,
242
	.probe		= snd_sb8_probe,
243
	.remove		= snd_sb8_remove,
244 245 246 247 248
#ifdef CONFIG_PM
	.suspend	= snd_sb8_suspend,
	.resume		= snd_sb8_resume,
#endif
	.driver		= {
249
		.name	= DEV_NAME 
250 251
	},
};
L
Linus Torvalds 已提交
252

253
module_isa_driver(snd_sb8_driver, SNDRV_CARDS);