sb8.c revision 74763
1/*
2 * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk>
3 * Copyright 1997,1998 Luigi Rizzo.
4 *
5 * Derived from files in the Voxware 3.5 distribution,
6 * Copyright by Hannu Savolainen 1994, under the same copyright
7 * conditions.
8 * All rights reserved.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 *
31 * $FreeBSD: head/sys/dev/sound/isa/sb8.c 74763 2001-03-24 23:10:29Z cg $
32 */
33
34#include <dev/sound/pcm/sound.h>
35
36#include  <dev/sound/isa/sb.h>
37#include  <dev/sound/chip.h>
38
39#include "mixer_if.h"
40
41#define SB_BUFFSIZE	4096
42
43static u_int32_t sb_fmt[] = {
44	AFMT_U8,
45	0
46};
47static struct pcmchan_caps sb200_playcaps = {4000, 23000, sb_fmt, 0};
48static struct pcmchan_caps sb200_reccaps = {4000, 13000, sb_fmt, 0};
49static struct pcmchan_caps sb201_playcaps = {4000, 44100, sb_fmt, 0};
50static struct pcmchan_caps sb201_reccaps = {4000, 15000, sb_fmt, 0};
51
52static u_int32_t sbpro_fmt[] = {
53	AFMT_U8,
54	AFMT_STEREO | AFMT_U8,
55	0
56};
57static struct pcmchan_caps sbpro_playcaps = {4000, 44100, sbpro_fmt, 0};
58static struct pcmchan_caps sbpro_reccaps = {4000, 44100, sbpro_fmt, 0};
59
60struct sb_info;
61
62struct sb_chinfo {
63	struct sb_info *parent;
64	struct pcm_channel *channel;
65	struct snd_dbuf *buffer;
66	int dir;
67	u_int32_t fmt, spd, blksz;
68};
69
70struct sb_info {
71	device_t parent_dev;
72    	struct resource *io_base;	/* I/O address for the board */
73    	struct resource *irq;
74   	struct resource *drq;
75    	void *ih;
76    	bus_dma_tag_t parent_dmat;
77
78    	int bd_id;
79    	u_long bd_flags;       /* board-specific flags */
80    	struct sb_chinfo pch, rch;
81};
82
83static int sb_rd(struct sb_info *sb, int reg);
84static void sb_wr(struct sb_info *sb, int reg, u_int8_t val);
85static int sb_dspready(struct sb_info *sb);
86static int sb_cmd(struct sb_info *sb, u_char val);
87static int sb_cmd1(struct sb_info *sb, u_char cmd, int val);
88static int sb_cmd2(struct sb_info *sb, u_char cmd, int val);
89static u_int sb_get_byte(struct sb_info *sb);
90static void sb_setmixer(struct sb_info *sb, u_int port, u_int value);
91static int sb_getmixer(struct sb_info *sb, u_int port);
92static int sb_reset_dsp(struct sb_info *sb);
93
94static void sb_intr(void *arg);
95static int sb_speed(struct sb_chinfo *ch);
96static int sb_start(struct sb_chinfo *ch);
97static int sb_stop(struct sb_chinfo *ch);
98
99static devclass_t pcm_devclass;
100
101/*
102 * Common code for the midi and pcm functions
103 *
104 * sb_cmd write a single byte to the CMD port.
105 * sb_cmd1 write a CMD + 1 byte arg
106 * sb_cmd2 write a CMD + 2 byte arg
107 * sb_get_byte returns a single byte from the DSP data port
108 */
109
110static void
111sb_lock(struct sb_info *sb) {
112
113	sbc_lock(device_get_softc(sb->parent_dev));
114}
115
116static void
117sb_unlock(struct sb_info *sb) {
118
119	sbc_unlock(device_get_softc(sb->parent_dev));
120}
121
122static int
123port_rd(struct resource *port, int off)
124{
125	return bus_space_read_1(rman_get_bustag(port), rman_get_bushandle(port), off);
126}
127
128static void
129port_wr(struct resource *port, int off, u_int8_t data)
130{
131	return bus_space_write_1(rman_get_bustag(port), rman_get_bushandle(port), off, data);
132}
133
134static int
135sb_rd(struct sb_info *sb, int reg)
136{
137	return port_rd(sb->io_base, reg);
138}
139
140static void
141sb_wr(struct sb_info *sb, int reg, u_int8_t val)
142{
143	port_wr(sb->io_base, reg, val);
144}
145
146static int
147sb_dspready(struct sb_info *sb)
148{
149	return ((sb_rd(sb, SBDSP_STATUS) & 0x80) == 0);
150}
151
152static int
153sb_dspwr(struct sb_info *sb, u_char val)
154{
155    	int  i;
156
157    	for (i = 0; i < 1000; i++) {
158		if (sb_dspready(sb)) {
159	    		sb_wr(sb, SBDSP_CMD, val);
160	    		return 1;
161		}
162		if (i > 10) DELAY((i > 100)? 1000 : 10);
163    	}
164    	printf("sb_dspwr(0x%02x) timed out.\n", val);
165    	return 0;
166}
167
168static int
169sb_cmd(struct sb_info *sb, u_char val)
170{
171#if 0
172	printf("sb_cmd: %x\n", val);
173#endif
174    	return sb_dspwr(sb, val);
175}
176
177static int
178sb_cmd1(struct sb_info *sb, u_char cmd, int val)
179{
180#if 0
181    	printf("sb_cmd1: %x, %x\n", cmd, val);
182#endif
183    	if (sb_dspwr(sb, cmd)) {
184		return sb_dspwr(sb, val & 0xff);
185    	} else return 0;
186}
187
188static int
189sb_cmd2(struct sb_info *sb, u_char cmd, int val)
190{
191#if 0
192    	printf("sb_cmd2: %x, %x\n", cmd, val);
193#endif
194    	if (sb_dspwr(sb, cmd)) {
195		return sb_dspwr(sb, val & 0xff) &&
196		       sb_dspwr(sb, (val >> 8) & 0xff);
197    	} else return 0;
198}
199
200/*
201 * in the SB, there is a set of indirect "mixer" registers with
202 * address at offset 4, data at offset 5
203 *
204 * we don't need to interlock these, the mixer lock will suffice.
205 */
206static void
207sb_setmixer(struct sb_info *sb, u_int port, u_int value)
208{
209    	sb_wr(sb, SB_MIX_ADDR, (u_char) (port & 0xff)); /* Select register */
210    	DELAY(10);
211    	sb_wr(sb, SB_MIX_DATA, (u_char) (value & 0xff));
212    	DELAY(10);
213}
214
215static int
216sb_getmixer(struct sb_info *sb, u_int port)
217{
218    	int val;
219
220    	sb_wr(sb, SB_MIX_ADDR, (u_char) (port & 0xff)); /* Select register */
221    	DELAY(10);
222    	val = sb_rd(sb, SB_MIX_DATA);
223    	DELAY(10);
224
225    	return val;
226}
227
228static u_int
229sb_get_byte(struct sb_info *sb)
230{
231    	int i;
232
233    	for (i = 1000; i > 0; i--) {
234		if (sb_rd(sb, DSP_DATA_AVAIL) & 0x80)
235			return sb_rd(sb, DSP_READ);
236		else
237			DELAY(20);
238    	}
239    	return 0xffff;
240}
241
242static int
243sb_reset_dsp(struct sb_info *sb)
244{
245    	sb_wr(sb, SBDSP_RST, 3);
246    	DELAY(100);
247    	sb_wr(sb, SBDSP_RST, 0);
248    	if (sb_get_byte(sb) != 0xAA) {
249        	DEB(printf("sb_reset_dsp 0x%lx failed\n",
250			   rman_get_start(d->io_base)));
251		return ENXIO;	/* Sorry */
252    	}
253    	return 0;
254}
255
256static void
257sb_release_resources(struct sb_info *sb, device_t dev)
258{
259    	if (sb->irq) {
260    		if (sb->ih)
261			bus_teardown_intr(dev, sb->irq, sb->ih);
262 		bus_release_resource(dev, SYS_RES_IRQ, 0, sb->irq);
263		sb->irq = 0;
264    	}
265    	if (sb->drq) {
266		bus_release_resource(dev, SYS_RES_DRQ, 0, sb->drq);
267		sb->drq = 0;
268    	}
269    	if (sb->io_base) {
270		bus_release_resource(dev, SYS_RES_IOPORT, 0, sb->io_base);
271		sb->io_base = 0;
272    	}
273    	if (sb->parent_dmat) {
274		bus_dma_tag_destroy(sb->parent_dmat);
275		sb->parent_dmat = 0;
276    	}
277     	free(sb, M_DEVBUF);
278}
279
280static int
281sb_alloc_resources(struct sb_info *sb, device_t dev)
282{
283	int rid;
284
285	rid = 0;
286	if (!sb->io_base)
287    		sb->io_base = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0, ~0, 1, RF_ACTIVE);
288	rid = 0;
289	if (!sb->irq)
290    		sb->irq = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0, ~0, 1, RF_ACTIVE);
291	rid = 0;
292	if (!sb->drq)
293    		sb->drq = bus_alloc_resource(dev, SYS_RES_DRQ, &rid, 0, ~0, 1, RF_ACTIVE);
294
295	if (sb->io_base && sb->drq && sb->irq) {
296		int bs = SB_BUFFSIZE;
297
298		isa_dma_acquire(rman_get_start(sb->drq));
299		isa_dmainit(rman_get_start(sb->drq), bs);
300
301		return 0;
302	} else return ENXIO;
303}
304
305/************************************************************/
306
307static int
308sbpromix_init(struct snd_mixer *m)
309{
310    	struct sb_info *sb = mix_getdevinfo(m);
311
312	mix_setdevs(m, SOUND_MASK_SYNTH | SOUND_MASK_PCM | SOUND_MASK_LINE |
313		       SOUND_MASK_MIC | SOUND_MASK_CD | SOUND_MASK_VOLUME);
314
315	mix_setrecdevs(m, SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD);
316
317	sb_setmixer(sb, 0, 1); /* reset mixer */
318
319    	return 0;
320}
321
322static int
323sbpromix_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right)
324{
325    	struct sb_info *sb = mix_getdevinfo(m);
326    	int reg, max;
327    	u_char val;
328
329	max = 7;
330	switch (dev) {
331	case SOUND_MASK_PCM:
332		reg = 0x04;
333		break;
334
335	case SOUND_MASK_MIC:
336		reg = 0x0a;
337		max = 3;
338		break;
339
340	case SOUND_MASK_VOLUME:
341		reg = 0x22;
342		break;
343
344	case SOUND_MASK_SYNTH:
345		reg = 0x26;
346		break;
347
348	case SOUND_MASK_CD:
349		reg = 0x28;
350		break;
351
352	case SOUND_MASK_LINE:
353		reg = 0x2e;
354		break;
355
356	default:
357		return -1;
358	}
359
360	left = (left * max) / 100;
361	right = (dev == SOUND_MIXER_MIC)? left : ((right * max) / 100);
362
363	val = (dev == SOUND_MIXER_MIC)? (left << 1) : (left << 5 | right << 1);
364	sb_setmixer(sb, reg, val);
365
366	left = (left * 100) / max;
367	right = (right * 100) / max;
368
369    	return left | (right << 8);
370}
371
372static int
373sbpromix_setrecsrc(struct snd_mixer *m, u_int32_t src)
374{
375    	struct sb_info *sb = mix_getdevinfo(m);
376    	u_char recdev;
377
378	if      (src == SOUND_MASK_LINE)
379		recdev = 0x06;
380	else if (src == SOUND_MASK_CD)
381		recdev = 0x02;
382	else { /* default: mic */
383	    	src = SOUND_MASK_MIC;
384	    	recdev = 0;
385	}
386	sb_setmixer(sb, RECORD_SRC, recdev | (sb_getmixer(sb, RECORD_SRC) & ~0x07));
387
388	return src;
389}
390
391static kobj_method_t sbpromix_mixer_methods[] = {
392    	KOBJMETHOD(mixer_init,		sbpromix_init),
393    	KOBJMETHOD(mixer_set,		sbpromix_set),
394    	KOBJMETHOD(mixer_setrecsrc,	sbpromix_setrecsrc),
395	{ 0, 0 }
396};
397MIXER_DECLARE(sbpromix_mixer);
398
399/************************************************************/
400
401static int
402sbmix_init(struct snd_mixer *m)
403{
404    	struct sb_info *sb = mix_getdevinfo(m);
405
406	mix_setdevs(m, SOUND_MASK_SYNTH | SOUND_MASK_PCM | SOUND_MASK_CD | SOUND_MASK_VOLUME);
407
408	mix_setrecdevs(m, 0);
409
410	sb_setmixer(sb, 0, 1); /* reset mixer */
411
412    	return 0;
413}
414
415static int
416sbmix_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right)
417{
418    	struct sb_info *sb = mix_getdevinfo(m);
419    	int reg, max;
420
421	max = 7;
422	switch (dev) {
423	case SOUND_MASK_VOLUME:
424		reg = 0x2;
425		break;
426
427	case SOUND_MASK_SYNTH:
428		reg = 0x6;
429		break;
430
431	case SOUND_MASK_CD:
432		reg = 0x8;
433		break;
434
435	case SOUND_MASK_PCM:
436		reg = 0x0a;
437		max = 3;
438		break;
439
440	default:
441		return -1;
442	}
443
444	left = (left * max) / 100;
445
446	sb_setmixer(sb, reg, left << 1);
447
448	left = (left * 100) / max;
449
450    	return left | (left << 8);
451}
452
453static int
454sbmix_setrecsrc(struct snd_mixer *m, u_int32_t src)
455{
456	return 0;
457}
458
459static kobj_method_t sbmix_mixer_methods[] = {
460    	KOBJMETHOD(mixer_init,		sbmix_init),
461    	KOBJMETHOD(mixer_set,		sbmix_set),
462    	KOBJMETHOD(mixer_setrecsrc,	sbmix_setrecsrc),
463	{ 0, 0 }
464};
465MIXER_DECLARE(sbmix_mixer);
466
467/************************************************************/
468
469static void
470sb_intr(void *arg)
471{
472    	struct sb_info *sb = (struct sb_info *)arg;
473
474	sb_lock(sb);
475    	if (sndbuf_runsz(sb->pch.buffer) > 0)
476		chn_intr(sb->pch.channel);
477
478    	if (sndbuf_runsz(sb->rch.buffer) > 0)
479		chn_intr(sb->rch.channel);
480
481	sb_rd(sb, DSP_DATA_AVAIL); /* int ack */
482	sb_unlock(sb);
483}
484
485static int
486sb_speed(struct sb_chinfo *ch)
487{
488	struct sb_info *sb = ch->parent;
489    	int play = (ch->dir == PCMDIR_PLAY)? 1 : 0;
490	int stereo = (ch->fmt & AFMT_STEREO)? 1 : 0;
491	int speed, tmp, thresh, max;
492	u_char tconst;
493
494	if (sb->bd_id >= 0x300) {
495		thresh = stereo? 11025 : 23000;
496		max = stereo? 22050 : 44100;
497	} else if (sb->bd_id > 0x200) {
498		thresh = play? 23000 : 13000;
499		max = play? 44100 : 15000;
500	} else {
501		thresh = 999999;
502		max = play? 23000 : 13000;
503	}
504
505	speed = ch->spd;
506	if (speed > max)
507		speed = max;
508
509	sb_lock(sb);
510	sb->bd_flags &= ~BD_F_HISPEED;
511	if (speed > thresh)
512		sb->bd_flags |= BD_F_HISPEED;
513
514	tmp = 65536 - (256000000 / (speed << stereo));
515	tconst = tmp >> 8;
516
517	sb_cmd1(sb, 0x40, tconst); /* set time constant */
518
519	speed = (256000000 / (65536 - tmp)) >> stereo;
520
521	ch->spd = speed;
522	sb_unlock(sb);
523	return speed;
524}
525
526static int
527sb_start(struct sb_chinfo *ch)
528{
529	struct sb_info *sb = ch->parent;
530    	int play = (ch->dir == PCMDIR_PLAY)? 1 : 0;
531    	int stereo = (ch->fmt & AFMT_STEREO)? 1 : 0;
532	int l = ch->blksz;
533	u_char i;
534
535	l--;
536
537	sb_lock(sb);
538	if (play)
539		sb_cmd(sb, DSP_CMD_SPKON);
540
541	if (sb->bd_flags & BD_F_HISPEED)
542		i = play? 0x90 : 0x98;
543	else
544		i = play? 0x1c : 0x2c;
545
546	sb_setmixer(sb, 0x0e, stereo? 2 : 0);
547	sb_cmd2(sb, 0x48, l);
548       	sb_cmd(sb, i);
549
550	sb->bd_flags |= BD_F_DMARUN;
551	sb_unlock(sb);
552	return 0;
553}
554
555static int
556sb_stop(struct sb_chinfo *ch)
557{
558	struct sb_info *sb = ch->parent;
559    	int play = (ch->dir == PCMDIR_PLAY)? 1 : 0;
560
561	sb_lock(sb);
562    	if (sb->bd_flags & BD_F_HISPEED)
563		sb_reset_dsp(sb);
564	else
565		sb_cmd(sb, DSP_CMD_DMAEXIT_8);
566
567	if (play)
568		sb_cmd(sb, DSP_CMD_SPKOFF); /* speaker off */
569	sb_unlock(sb);
570	sb->bd_flags &= ~BD_F_DMARUN;
571	return 0;
572}
573
574/* channel interface */
575static void *
576sbchan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel *c, int dir)
577{
578	struct sb_info *sb = devinfo;
579	struct sb_chinfo *ch = (dir == PCMDIR_PLAY)? &sb->pch : &sb->rch;
580
581	ch->parent = sb;
582	ch->channel = c;
583	ch->dir = dir;
584	ch->buffer = b;
585	if (sndbuf_alloc(ch->buffer, sb->parent_dmat, SB_BUFFSIZE) == -1)
586		return NULL;
587	sndbuf_isadmasetup(ch->buffer, sb->drq);
588	return ch;
589}
590
591static int
592sbchan_setformat(kobj_t obj, void *data, u_int32_t format)
593{
594	struct sb_chinfo *ch = data;
595
596	ch->fmt = format;
597	return 0;
598}
599
600static int
601sbchan_setspeed(kobj_t obj, void *data, u_int32_t speed)
602{
603	struct sb_chinfo *ch = data;
604
605	ch->spd = speed;
606	return sb_speed(ch);
607}
608
609static int
610sbchan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize)
611{
612	struct sb_chinfo *ch = data;
613
614	ch->blksz = blocksize;
615	return ch->blksz;
616}
617
618static int
619sbchan_trigger(kobj_t obj, void *data, int go)
620{
621	struct sb_chinfo *ch = data;
622
623	if (go == PCMTRIG_EMLDMAWR || go == PCMTRIG_EMLDMARD)
624		return 0;
625
626	sndbuf_isadma(ch->buffer, go);
627	if (go == PCMTRIG_START)
628		sb_start(ch);
629	else
630		sb_stop(ch);
631	return 0;
632}
633
634static int
635sbchan_getptr(kobj_t obj, void *data)
636{
637	struct sb_chinfo *ch = data;
638
639	return sndbuf_isadmaptr(ch->buffer);
640}
641
642static struct pcmchan_caps *
643sbchan_getcaps(kobj_t obj, void *data)
644{
645	struct sb_chinfo *ch = data;
646	int p = (ch->dir == PCMDIR_PLAY)? 1 : 0;
647
648	if (ch->parent->bd_id == 0x200)
649		return p? &sb200_playcaps : &sb200_reccaps;
650	if (ch->parent->bd_id < 0x300)
651		return p? &sb201_playcaps : &sb201_reccaps;
652	return p? &sbpro_playcaps : &sbpro_reccaps;
653}
654
655static kobj_method_t sbchan_methods[] = {
656    	KOBJMETHOD(channel_init,		sbchan_init),
657    	KOBJMETHOD(channel_setformat,		sbchan_setformat),
658    	KOBJMETHOD(channel_setspeed,		sbchan_setspeed),
659    	KOBJMETHOD(channel_setblocksize,	sbchan_setblocksize),
660    	KOBJMETHOD(channel_trigger,		sbchan_trigger),
661    	KOBJMETHOD(channel_getptr,		sbchan_getptr),
662    	KOBJMETHOD(channel_getcaps,		sbchan_getcaps),
663	{ 0, 0 }
664};
665CHANNEL_DECLARE(sbchan);
666
667/************************************************************/
668
669static int
670sb_probe(device_t dev)
671{
672    	char buf[64];
673	uintptr_t func, ver, r, f;
674
675	/* The parent device has already been probed. */
676	r = BUS_READ_IVAR(device_get_parent(dev), dev, 0, &func);
677	if (func != SCF_PCM)
678		return (ENXIO);
679
680	r = BUS_READ_IVAR(device_get_parent(dev), dev, 1, &ver);
681	f = (ver & 0xffff0000) >> 16;
682	ver &= 0x0000ffff;
683	if ((f & BD_F_ESS) || (ver >= 0x400))
684		return (ENXIO);
685
686	snprintf(buf, sizeof buf, "SB DSP %d.%02d", (int) ver >> 8, (int) ver & 0xff);
687
688    	device_set_desc_copy(dev, buf);
689
690	return 0;
691}
692
693static int
694sb_attach(device_t dev)
695{
696    	struct sb_info *sb;
697    	char status[SND_STATUSLEN];
698	int bs = SB_BUFFSIZE;
699	uintptr_t ver;
700
701    	sb = (struct sb_info *)malloc(sizeof *sb, M_DEVBUF, M_NOWAIT);
702    	if (!sb)
703		return ENXIO;
704    	bzero(sb, sizeof *sb);
705
706	sb->parent_dev = device_get_parent(dev);
707	BUS_READ_IVAR(device_get_parent(dev), dev, 1, &ver);
708	sb->bd_id = ver & 0x0000ffff;
709	sb->bd_flags = (ver & 0xffff0000) >> 16;
710
711    	if (sb_alloc_resources(sb, dev))
712		goto no;
713    	if (sb_reset_dsp(sb))
714		goto no;
715    	if (mixer_init(dev, (sb->bd_id < 0x300)? &sbmix_mixer_class : &sbpromix_mixer_class, sb))
716		goto no;
717	if (snd_setup_intr(dev, sb->irq, INTR_MPSAFE, sb_intr, sb, &sb->ih))
718		goto no;
719
720	pcm_setflags(dev, pcm_getflags(dev) | SD_F_SIMPLEX);
721
722    	if (bus_dma_tag_create(/*parent*/NULL, /*alignment*/2, /*boundary*/0,
723			/*lowaddr*/BUS_SPACE_MAXADDR_24BIT,
724			/*highaddr*/BUS_SPACE_MAXADDR,
725			/*filter*/NULL, /*filterarg*/NULL,
726			/*maxsize*/bs, /*nsegments*/1,
727			/*maxsegz*/0x3ffff,
728			/*flags*/0, &sb->parent_dmat) != 0) {
729		device_printf(dev, "unable to create dma tag\n");
730		goto no;
731    	}
732
733    	snprintf(status, SND_STATUSLEN, "at io 0x%lx irq %ld drq %ld",
734    	     	rman_get_start(sb->io_base), rman_get_start(sb->irq), rman_get_start(sb->drq));
735
736    	if (pcm_register(dev, sb, 1, 1))
737		goto no;
738	pcm_addchan(dev, PCMDIR_REC, &sbchan_class, sb);
739	pcm_addchan(dev, PCMDIR_PLAY, &sbchan_class, sb);
740
741    	pcm_setstatus(dev, status);
742
743    	return 0;
744
745no:
746    	sb_release_resources(sb, dev);
747    	return ENXIO;
748}
749
750static int
751sb_detach(device_t dev)
752{
753	int r;
754	struct sb_info *sb;
755
756	r = pcm_unregister(dev);
757	if (r)
758		return r;
759
760	sb = pcm_getdevinfo(dev);
761    	sb_release_resources(sb, dev);
762	return 0;
763}
764
765static device_method_t sb_methods[] = {
766	/* Device interface */
767	DEVMETHOD(device_probe,		sb_probe),
768	DEVMETHOD(device_attach,	sb_attach),
769	DEVMETHOD(device_detach,	sb_detach),
770
771	{ 0, 0 }
772};
773
774static driver_t sb_driver = {
775	"pcm",
776	sb_methods,
777	sizeof(struct snddev_info),
778};
779
780DRIVER_MODULE(snd_sb8, sbc, sb_driver, pcm_devclass, 0, 0);
781MODULE_DEPEND(snd_sb8, snd_pcm, PCM_MINVER, PCM_PREFVER, PCM_MAXVER);
782MODULE_DEPEND(snd_sb8, snd_sbc, 1, 1, 1);
783MODULE_VERSION(snd_sb8, 1);
784
785
786
787
788