sb_common.c 7.2 KB
Newer Older
L
Linus Torvalds 已提交
1
/*
2
 *  Copyright (c) by Jaroslav Kysela <perex@perex.cz>
L
Linus Torvalds 已提交
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
 *                   Uros Bizjak <uros@kss-loka.si>
 *
 *  Lowlevel routines for control of Sound Blaster cards
 *
 *   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/delay.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/slab.h>
#include <linux/ioport.h>
#include <sound/core.h>
#include <sound/sb.h>
#include <sound/initval.h>

#include <asm/io.h>
#include <asm/dma.h>

35
MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
L
Linus Torvalds 已提交
36 37 38 39 40 41 42
MODULE_DESCRIPTION("ALSA lowlevel driver for Sound Blaster cards");
MODULE_LICENSE("GPL");

#define BUSY_LOOPS 100000

#undef IO_DEBUG

43
int snd_sbdsp_command(struct snd_sb *chip, unsigned char val)
L
Linus Torvalds 已提交
44 45 46
{
	int i;
#ifdef IO_DEBUG
47
	snd_printk(KERN_DEBUG "command 0x%x\n", val);
L
Linus Torvalds 已提交
48 49 50 51 52 53
#endif
	for (i = BUSY_LOOPS; i; i--)
		if ((inb(SBP(chip, STATUS)) & 0x80) == 0) {
			outb(val, SBP(chip, COMMAND));
			return 1;
		}
54
	snd_printd("%s [0x%lx]: timeout (0x%x)\n", __func__, chip->port, val);
L
Linus Torvalds 已提交
55 56 57
	return 0;
}

58
int snd_sbdsp_get_byte(struct snd_sb *chip)
L
Linus Torvalds 已提交
59 60 61 62 63 64 65
{
	int val;
	int i;
	for (i = BUSY_LOOPS; i; i--) {
		if (inb(SBP(chip, DATA_AVAIL)) & 0x80) {
			val = inb(SBP(chip, READ));
#ifdef IO_DEBUG
66
			snd_printk(KERN_DEBUG "get_byte 0x%x\n", val);
L
Linus Torvalds 已提交
67 68 69 70
#endif
			return val;
		}
	}
71
	snd_printd("%s [0x%lx]: timeout\n", __func__, chip->port);
L
Linus Torvalds 已提交
72 73 74
	return -ENODEV;
}

75
int snd_sbdsp_reset(struct snd_sb *chip)
L
Linus Torvalds 已提交
76 77 78 79 80 81 82 83 84 85 86 87 88 89
{
	int i;

	outb(1, SBP(chip, RESET));
	udelay(10);
	outb(0, SBP(chip, RESET));
	udelay(30);
	for (i = BUSY_LOOPS; i; i--)
		if (inb(SBP(chip, DATA_AVAIL)) & 0x80) {
			if (inb(SBP(chip, READ)) == 0xaa)
				return 0;
			else
				break;
		}
90
	snd_printdd("%s [0x%lx] failed...\n", __func__, chip->port);
L
Linus Torvalds 已提交
91 92 93
	return -ENODEV;
}

94
static int snd_sbdsp_version(struct snd_sb * chip)
L
Linus Torvalds 已提交
95 96 97 98 99 100 101 102 103
{
	unsigned int result = -ENODEV;

	snd_sbdsp_command(chip, SB_DSP_GET_VERSION);
	result = (short) snd_sbdsp_get_byte(chip) << 8;
	result |= (short) snd_sbdsp_get_byte(chip);
	return result;
}

104
static int snd_sbdsp_probe(struct snd_sb * chip)
L
Linus Torvalds 已提交
105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129
{
	int version;
	int major, minor;
	char *str;
	unsigned long flags;

	/*
	 *  initialization sequence
	 */

	spin_lock_irqsave(&chip->reg_lock, flags);
	if (snd_sbdsp_reset(chip) < 0) {
		spin_unlock_irqrestore(&chip->reg_lock, flags);
		return -ENODEV;
	}
	version = snd_sbdsp_version(chip);
	if (version < 0) {
		spin_unlock_irqrestore(&chip->reg_lock, flags);
		return -ENODEV;
	}
	spin_unlock_irqrestore(&chip->reg_lock, flags);
	major = version >> 8;
	minor = version & 0xff;
	snd_printdd("SB [0x%lx]: DSP chip found, version = %i.%i\n",
		    chip->port, major, minor);
130

L
Linus Torvalds 已提交
131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155
	switch (chip->hardware) {
	case SB_HW_AUTO:
		switch (major) {
		case 1:
			chip->hardware = SB_HW_10;
			str = "1.0";
			break;
		case 2:
			if (minor) {
				chip->hardware = SB_HW_201;
				str = "2.01+";
			} else {
				chip->hardware = SB_HW_20;
				str = "2.0";
			}
			break;
		case 3:
			chip->hardware = SB_HW_PRO;
			str = "Pro";
			break;
		case 4:
			chip->hardware = SB_HW_16;
			str = "16";
			break;
		default:
156
			snd_printk(KERN_INFO "SB [0x%lx]: unknown DSP chip version %i.%i\n",
L
Linus Torvalds 已提交
157 158 159 160 161 162 163 164 165 166 167 168 169
				   chip->port, major, minor);
			return -ENODEV;
		}
		break;
	case SB_HW_ALS100:
		str = "16 (ALS-100)";
		break;
	case SB_HW_ALS4000:
		str = "16 (ALS-4000)";
		break;
	case SB_HW_DT019X:
		str = "(DT019X/ALS007)";
		break;
170 171 172
	case SB_HW_CS5530:
		str = "16 (CS5530)";
		break;
L
Linus Torvalds 已提交
173 174 175 176 177 178 179 180
	default:
		return -ENODEV;
	}
	sprintf(chip->name, "Sound Blaster %s", str);
	chip->version = (major << 8) | minor;
	return 0;
}

181
static int snd_sbdsp_free(struct snd_sb *chip)
L
Linus Torvalds 已提交
182
{
183 184
	if (chip->res_port)
		release_and_free_resource(chip->res_port);
L
Linus Torvalds 已提交
185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200
	if (chip->irq >= 0)
		free_irq(chip->irq, (void *) chip);
#ifdef CONFIG_ISA
	if (chip->dma8 >= 0) {
		disable_dma(chip->dma8);
		free_dma(chip->dma8);
	}
	if (chip->dma16 >= 0 && chip->dma16 != chip->dma8) {
		disable_dma(chip->dma16);
		free_dma(chip->dma16);
	}
#endif
	kfree(chip);
	return 0;
}

201
static int snd_sbdsp_dev_free(struct snd_device *device)
L
Linus Torvalds 已提交
202
{
203
	struct snd_sb *chip = device->device_data;
L
Linus Torvalds 已提交
204 205 206
	return snd_sbdsp_free(chip);
}

207
int snd_sbdsp_create(struct snd_card *card,
L
Linus Torvalds 已提交
208 209
		     unsigned long port,
		     int irq,
210
		     irq_handler_t irq_handler,
L
Linus Torvalds 已提交
211 212 213
		     int dma8,
		     int dma16,
		     unsigned short hardware,
214
		     struct snd_sb **r_chip)
L
Linus Torvalds 已提交
215
{
216
	struct snd_sb *chip;
L
Linus Torvalds 已提交
217
	int err;
218
	static struct snd_device_ops ops = {
L
Linus Torvalds 已提交
219 220 221
		.dev_free =	snd_sbdsp_dev_free,
	};

222 223
	if (snd_BUG_ON(!r_chip))
		return -EINVAL;
L
Linus Torvalds 已提交
224
	*r_chip = NULL;
225
	chip = kzalloc(sizeof(*chip), GFP_KERNEL);
L
Linus Torvalds 已提交
226 227 228 229 230 231 232 233 234 235 236
	if (chip == NULL)
		return -ENOMEM;
	spin_lock_init(&chip->reg_lock);
	spin_lock_init(&chip->open_lock);
	spin_lock_init(&chip->midi_input_lock);
	spin_lock_init(&chip->mixer_lock);
	chip->irq = -1;
	chip->dma8 = -1;
	chip->dma16 = -1;
	chip->port = port;
	
237 238 239
	if (request_irq(irq, irq_handler,
			(hardware == SB_HW_ALS4000 ||
			 hardware == SB_HW_CS5530) ?
240
			IRQF_SHARED : IRQF_DISABLED,
L
Linus Torvalds 已提交
241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300
			"SoundBlaster", (void *) chip)) {
		snd_printk(KERN_ERR "sb: can't grab irq %d\n", irq);
		snd_sbdsp_free(chip);
		return -EBUSY;
	}
	chip->irq = irq;

	if (hardware == SB_HW_ALS4000)
		goto __skip_allocation;
	
	if ((chip->res_port = request_region(port, 16, "SoundBlaster")) == NULL) {
		snd_printk(KERN_ERR "sb: can't grab port 0x%lx\n", port);
		snd_sbdsp_free(chip);
		return -EBUSY;
	}

#ifdef CONFIG_ISA
	if (dma8 >= 0 && request_dma(dma8, "SoundBlaster - 8bit")) {
		snd_printk(KERN_ERR "sb: can't grab DMA8 %d\n", dma8);
		snd_sbdsp_free(chip);
		return -EBUSY;
	}
	chip->dma8 = dma8;
	if (dma16 >= 0) {
		if (hardware != SB_HW_ALS100 && (dma16 < 5 || dma16 > 7)) {
			/* no duplex */
			dma16 = -1;
		} else if (request_dma(dma16, "SoundBlaster - 16bit")) {
			snd_printk(KERN_ERR "sb: can't grab DMA16 %d\n", dma16);
			snd_sbdsp_free(chip);
			return -EBUSY;
		}
	}
	chip->dma16 = dma16;
#endif

      __skip_allocation:
	chip->card = card;
	chip->hardware = hardware;
	if ((err = snd_sbdsp_probe(chip)) < 0) {
		snd_sbdsp_free(chip);
		return err;
	}
	if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
		snd_sbdsp_free(chip);
		return err;
	}
	*r_chip = chip;
	return 0;
}

EXPORT_SYMBOL(snd_sbdsp_command);
EXPORT_SYMBOL(snd_sbdsp_get_byte);
EXPORT_SYMBOL(snd_sbdsp_reset);
EXPORT_SYMBOL(snd_sbdsp_create);
/* sb_mixer.c */
EXPORT_SYMBOL(snd_sbmixer_write);
EXPORT_SYMBOL(snd_sbmixer_read);
EXPORT_SYMBOL(snd_sbmixer_new);
EXPORT_SYMBOL(snd_sbmixer_add_ctl);
301 302 303 304
#ifdef CONFIG_PM
EXPORT_SYMBOL(snd_sbmixer_suspend);
EXPORT_SYMBOL(snd_sbmixer_resume);
#endif
L
Linus Torvalds 已提交
305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320

/*
 *  INIT part
 */

static int __init alsa_sb_common_init(void)
{
	return 0;
}

static void __exit alsa_sb_common_exit(void)
{
}

module_init(alsa_sb_common_init)
module_exit(alsa_sb_common_exit)