sb16.c revision 84111
1262395Sbapt/*
2262395Sbapt * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk>
3262395Sbapt * Copyright 1997,1998 Luigi Rizzo.
4262395Sbapt *
5262395Sbapt * Derived from files in the Voxware 3.5 distribution,
6262395Sbapt * Copyright by Hannu Savolainen 1994, under the same copyright
7262395Sbapt * conditions.
8262395Sbapt * All rights reserved.
9262395Sbapt *
10262395Sbapt * Redistribution and use in source and binary forms, with or without
11262395Sbapt * modification, are permitted provided that the following conditions
12262395Sbapt * are met:
13262395Sbapt * 1. Redistributions of source code must retain the above copyright
14262395Sbapt *    notice, this list of conditions and the following disclaimer.
15262395Sbapt * 2. Redistributions in binary form must reproduce the above copyright
16262395Sbapt *    notice, this list of conditions and the following disclaimer in the
17262395Sbapt *    documentation and/or other materials provided with the distribution.
18262395Sbapt *
19262395Sbapt * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20262395Sbapt * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21262395Sbapt * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22262395Sbapt * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23262395Sbapt * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24262395Sbapt * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25262395Sbapt * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26262395Sbapt * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27262395Sbapt * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28262395Sbapt * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29262395Sbapt * SUCH DAMAGE.
30262395Sbapt */
31262395Sbapt
32262395Sbapt#include <dev/sound/pcm/sound.h>
33262395Sbapt
34262395Sbapt#include  <dev/sound/isa/sb.h>
35262395Sbapt#include  <dev/sound/chip.h>
36262395Sbapt
37262395Sbapt#include "mixer_if.h"
38262395Sbapt
39262395SbaptSND_DECLARE_FILE("$FreeBSD: head/sys/dev/sound/isa/sb16.c 84111 2001-09-29 07:57:07Z cg $");
40262395Sbapt
41262395Sbapt#define SB16_BUFFSIZE	4096
42262395Sbapt#define PLAIN_SB16(x) ((((x)->bd_flags) & (BD_F_SB16|BD_F_SB16X)) == BD_F_SB16)
43262395Sbapt
44262395Sbaptstatic u_int32_t sb16_fmt8[] = {
45262395Sbapt	AFMT_U8,
46262395Sbapt	AFMT_STEREO | AFMT_U8,
47262395Sbapt	0
48262395Sbapt};
49262395Sbaptstatic struct pcmchan_caps sb16_caps8 = {5000, 45000, sb16_fmt8, 0};
50262395Sbapt
51262395Sbaptstatic u_int32_t sb16_fmt16[] = {
52262395Sbapt	AFMT_S16_LE,
53262395Sbapt	AFMT_STEREO | AFMT_S16_LE,
54262395Sbapt	0
55262395Sbapt};
56262395Sbaptstatic struct pcmchan_caps sb16_caps16 = {5000, 45000, sb16_fmt16, 0};
57262395Sbapt
58262395Sbaptstatic u_int32_t sb16x_fmt[] = {
59262395Sbapt	AFMT_U8,
60262395Sbapt	AFMT_STEREO | AFMT_U8,
61262395Sbapt	AFMT_S16_LE,
62262395Sbapt	AFMT_STEREO | AFMT_S16_LE,
63262395Sbapt	0
64262395Sbapt};
65262395Sbaptstatic struct pcmchan_caps sb16x_caps = {5000, 49000, sb16x_fmt, 0};
66262395Sbapt
67262395Sbaptstruct sb_info;
68262395Sbapt
69262395Sbaptstruct sb_chinfo {
70262395Sbapt	struct sb_info *parent;
71262395Sbapt	struct pcm_channel *channel;
72262395Sbapt	struct snd_dbuf *buffer;
73262395Sbapt	int dir, run, dch;
74262395Sbapt	u_int32_t fmt, spd, blksz;
75262395Sbapt};
76262395Sbapt
77262395Sbaptstruct sb_info {
78262395Sbapt    	struct resource *io_base;	/* I/O address for the board */
79262395Sbapt    	struct resource *irq;
80262395Sbapt   	struct resource *drq1;
81262395Sbapt    	struct resource *drq2;
82262395Sbapt    	void *ih;
83262395Sbapt    	bus_dma_tag_t parent_dmat;
84262395Sbapt
85262395Sbapt	unsigned int bufsize;
86262395Sbapt    	int bd_id;
87262395Sbapt    	u_long bd_flags;       /* board-specific flags */
88262395Sbapt	int prio, prio16;
89262395Sbapt    	struct sb_chinfo pch, rch;
90262395Sbapt	device_t parent_dev;
91262395Sbapt};
92262395Sbapt
93262395Sbapt#if 0
94262395Sbaptstatic void sb_lock(struct sb_info *sb);
95262395Sbaptstatic void sb_unlock(struct sb_info *sb);
96262395Sbaptstatic int sb_rd(struct sb_info *sb, int reg);
97262395Sbaptstatic void sb_wr(struct sb_info *sb, int reg, u_int8_t val);
98262395Sbaptstatic int sb_cmd(struct sb_info *sb, u_char val);
99262395Sbapt/* static int sb_cmd1(struct sb_info *sb, u_char cmd, int val); */
100262395Sbaptstatic int sb_cmd2(struct sb_info *sb, u_char cmd, int val);
101262395Sbaptstatic u_int sb_get_byte(struct sb_info *sb);
102262395Sbaptstatic void sb_setmixer(struct sb_info *sb, u_int port, u_int value);
103262395Sbaptstatic int sb_getmixer(struct sb_info *sb, u_int port);
104262395Sbaptstatic int sb_reset_dsp(struct sb_info *sb);
105262395Sbapt
106262395Sbaptstatic void sb_intr(void *arg);
107262395Sbapt#endif
108262395Sbapt
109262395Sbapt/*
110262395Sbapt * Common code for the midi and pcm functions
111262395Sbapt *
112262395Sbapt * sb_cmd write a single byte to the CMD port.
113262395Sbapt * sb_cmd1 write a CMD + 1 byte arg
114262395Sbapt * sb_cmd2 write a CMD + 2 byte arg
115262395Sbapt * sb_get_byte returns a single byte from the DSP data port
116262395Sbapt */
117262395Sbapt
118262395Sbaptstatic void
119262395Sbaptsb_lock(struct sb_info *sb) {
120262395Sbapt
121262395Sbapt	sbc_lock(device_get_softc(sb->parent_dev));
122262395Sbapt}
123262395Sbapt
124262395Sbaptstatic void
125262395Sbaptsb_unlock(struct sb_info *sb) {
126262395Sbapt
127262395Sbapt	sbc_unlock(device_get_softc(sb->parent_dev));
128262395Sbapt}
129262395Sbapt
130262395Sbaptstatic int
131262395Sbaptport_rd(struct resource *port, int off)
132262395Sbapt{
133262395Sbapt	return bus_space_read_1(rman_get_bustag(port), rman_get_bushandle(port), off);
134262395Sbapt}
135262395Sbapt
136262395Sbaptstatic void
137262395Sbaptport_wr(struct resource *port, int off, u_int8_t data)
138262395Sbapt{
139262395Sbapt	return bus_space_write_1(rman_get_bustag(port), rman_get_bushandle(port), off, data);
140262395Sbapt}
141262395Sbapt
142262395Sbaptstatic int
143262395Sbaptsb_rd(struct sb_info *sb, int reg)
144262395Sbapt{
145262395Sbapt	return port_rd(sb->io_base, reg);
146262395Sbapt}
147262395Sbapt
148262395Sbaptstatic void
149262395Sbaptsb_wr(struct sb_info *sb, int reg, u_int8_t val)
150262395Sbapt{
151262395Sbapt	port_wr(sb->io_base, reg, val);
152262395Sbapt}
153262395Sbapt
154262395Sbaptstatic int
155262395Sbaptsb_dspwr(struct sb_info *sb, u_char val)
156262395Sbapt{
157262395Sbapt    	int  i;
158262395Sbapt
159262395Sbapt    	for (i = 0; i < 1000; i++) {
160262395Sbapt		if ((sb_rd(sb, SBDSP_STATUS) & 0x80))
161262395Sbapt	    		DELAY((i > 100)? 1000 : 10);
162262395Sbapt	    	else {
163262395Sbapt			sb_wr(sb, SBDSP_CMD, val);
164262395Sbapt			return 1;
165262395Sbapt		}
166262395Sbapt    	}
167262395Sbapt#if __FreeBSD_version > 500000
168262395Sbapt	if (curthread->td_intr_nesting_level == 0)
169262395Sbapt		printf("sb_dspwr(0x%02x) timed out.\n", val);
170262395Sbapt#endif
171262395Sbapt    	return 0;
172262395Sbapt}
173262395Sbapt
174262395Sbaptstatic int
175262395Sbaptsb_cmd(struct sb_info *sb, u_char val)
176262395Sbapt{
177262395Sbapt#if 0
178262395Sbapt	printf("sb_cmd: %x\n", val);
179262395Sbapt#endif
180262395Sbapt    	return sb_dspwr(sb, val);
181262395Sbapt}
182262395Sbapt
183262395Sbapt/*
184262395Sbaptstatic int
185262395Sbaptsb_cmd1(struct sb_info *sb, u_char cmd, int val)
186262395Sbapt{
187262395Sbapt#if 0
188262395Sbapt    	printf("sb_cmd1: %x, %x\n", cmd, val);
189262395Sbapt#endif
190262395Sbapt    	if (sb_dspwr(sb, cmd)) {
191262395Sbapt		return sb_dspwr(sb, val & 0xff);
192262395Sbapt    	} else return 0;
193262395Sbapt}
194262395Sbapt*/
195262395Sbapt
196262395Sbaptstatic int
197262395Sbaptsb_cmd2(struct sb_info *sb, u_char cmd, int val)
198262395Sbapt{
199262395Sbapt	int r;
200262395Sbapt
201262395Sbapt#if 0
202262395Sbapt    	printf("sb_cmd2: %x, %x\n", cmd, val);
203262395Sbapt#endif
204262395Sbapt	sb_lock(sb);
205262395Sbapt	r = 0;
206262395Sbapt    	if (sb_dspwr(sb, cmd)) {
207262395Sbapt		if (sb_dspwr(sb, val & 0xff)) {
208262395Sbapt			if (sb_dspwr(sb, (val >> 8) & 0xff)) {
209262395Sbapt				r = 1;
210262395Sbapt			}
211262395Sbapt		}
212262395Sbapt    	}
213262395Sbapt	sb_unlock(sb);
214262395Sbapt
215262395Sbapt	return r;
216262395Sbapt}
217262395Sbapt
218262395Sbapt/*
219262395Sbapt * in the SB, there is a set of indirect "mixer" registers with
220262395Sbapt * address at offset 4, data at offset 5
221262395Sbapt */
222262395Sbaptstatic void
223262395Sbaptsb_setmixer(struct sb_info *sb, u_int port, u_int value)
224262395Sbapt{
225262395Sbapt	sb_lock(sb);
226262395Sbapt    	sb_wr(sb, SB_MIX_ADDR, (u_char) (port & 0xff)); /* Select register */
227262395Sbapt    	DELAY(10);
228262395Sbapt    	sb_wr(sb, SB_MIX_DATA, (u_char) (value & 0xff));
229262395Sbapt    	DELAY(10);
230262395Sbapt	sb_unlock(sb);
231262395Sbapt}
232262395Sbapt
233262395Sbaptstatic int
234262395Sbaptsb_getmixer(struct sb_info *sb, u_int port)
235262395Sbapt{
236262395Sbapt    	int val;
237262395Sbapt
238262395Sbapt	sb_lock(sb);
239266636Sbapt    	sb_wr(sb, SB_MIX_ADDR, (u_char) (port & 0xff)); /* Select register */
240266636Sbapt    	DELAY(10);
241266636Sbapt    	val = sb_rd(sb, SB_MIX_DATA);
242262395Sbapt    	DELAY(10);
243262395Sbapt	sb_unlock(sb);
244262395Sbapt
245262395Sbapt    	return val;
246262395Sbapt}
247262395Sbapt
248262395Sbaptstatic u_int
249262395Sbaptsb_get_byte(struct sb_info *sb)
250262395Sbapt{
251262395Sbapt    	int i;
252262395Sbapt
253262395Sbapt    	for (i = 1000; i > 0; i--) {
254262395Sbapt		if (sb_rd(sb, DSP_DATA_AVAIL) & 0x80)
255262395Sbapt			return sb_rd(sb, DSP_READ);
256262395Sbapt		else
257262395Sbapt			DELAY(20);
258262395Sbapt    	}
259262395Sbapt    	return 0xffff;
260262395Sbapt}
261262395Sbapt
262262395Sbaptstatic int
263262395Sbaptsb_reset_dsp(struct sb_info *sb)
264266636Sbapt{
265266636Sbapt	u_char b;
266266636Sbapt
267266636Sbapt	sb_lock(sb);
268266636Sbapt    	sb_wr(sb, SBDSP_RST, 3);
269266636Sbapt    	DELAY(100);
270266636Sbapt    	sb_wr(sb, SBDSP_RST, 0);
271266636Sbapt	b = sb_get_byte(sb);
272266636Sbapt	sb_unlock(sb);
273266636Sbapt    	if (b != 0xAA) {
274266636Sbapt        	DEB(printf("sb_reset_dsp 0x%lx failed\n",
275266636Sbapt			   rman_get_start(d->io_base)));
276266636Sbapt		return ENXIO;	/* Sorry */
277262395Sbapt    	}
278262395Sbapt    	return 0;
279262395Sbapt}
280262395Sbapt
281262395Sbapt/************************************************************/
282262395Sbapt
283262395Sbaptstruct sb16_mixent {
284262395Sbapt	int reg;
285262395Sbapt	int bits;
286262395Sbapt	int ofs;
287262395Sbapt	int stereo;
288262395Sbapt};
289262395Sbapt
290266636Sbaptstatic const struct sb16_mixent sb16_mixtab[32] = {
291266636Sbapt    	[SOUND_MIXER_VOLUME]	= { 0x30, 5, 3, 1 },
292262395Sbapt    	[SOUND_MIXER_PCM]	= { 0x32, 5, 3, 1 },
293262395Sbapt    	[SOUND_MIXER_SYNTH]	= { 0x34, 5, 3, 1 },
294262395Sbapt    	[SOUND_MIXER_CD]	= { 0x36, 5, 3, 1 },
295262395Sbapt    	[SOUND_MIXER_LINE]	= { 0x38, 5, 3, 1 },
296262395Sbapt    	[SOUND_MIXER_MIC]	= { 0x3a, 5, 3, 0 },
297262395Sbapt       	[SOUND_MIXER_SPEAKER]	= { 0x3b, 5, 3, 0 },
298262395Sbapt    	[SOUND_MIXER_IGAIN]	= { 0x3f, 2, 6, 1 },
299262395Sbapt    	[SOUND_MIXER_OGAIN]	= { 0x41, 2, 6, 1 },
300262395Sbapt	[SOUND_MIXER_TREBLE]	= { 0x44, 4, 4, 1 },
301262395Sbapt    	[SOUND_MIXER_BASS]	= { 0x46, 4, 4, 1 },
302266636Sbapt};
303266636Sbapt
304262395Sbaptstatic int
305262395Sbaptsb16mix_init(struct snd_mixer *m)
306262395Sbapt{
307262395Sbapt    	struct sb_info *sb = mix_getdevinfo(m);
308262395Sbapt
309262395Sbapt	mix_setdevs(m, SOUND_MASK_SYNTH | SOUND_MASK_PCM | SOUND_MASK_SPEAKER |
310262395Sbapt     		       SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD |
311262395Sbapt     		       SOUND_MASK_IGAIN | SOUND_MASK_OGAIN |
312262395Sbapt     		       SOUND_MASK_VOLUME | SOUND_MASK_BASS | SOUND_MASK_TREBLE);
313262395Sbapt
314262395Sbapt	mix_setrecdevs(m, SOUND_MASK_SYNTH | SOUND_MASK_LINE |
315262395Sbapt			  SOUND_MASK_MIC | SOUND_MASK_CD);
316262395Sbapt
317262395Sbapt	sb_setmixer(sb, 0x3c, 0x1f); /* make all output active */
318262395Sbapt
319262395Sbapt	sb_setmixer(sb, 0x3d, 0); /* make all inputs-l off */
320262395Sbapt	sb_setmixer(sb, 0x3e, 0); /* make all inputs-r off */
321262395Sbapt
322262395Sbapt	return 0;
323262395Sbapt}
324262395Sbapt
325262395Sbaptstatic int
326262395Sbaptsb16mix_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right)
327262395Sbapt{
328262395Sbapt    	struct sb_info *sb = mix_getdevinfo(m);
329262395Sbapt    	const struct sb16_mixent *e;
330262395Sbapt    	int max;
331262395Sbapt
332262395Sbapt	e = &sb16_mixtab[dev];
333262395Sbapt	max = (1 << e->bits) - 1;
334262395Sbapt
335262395Sbapt	left = (left * max) / 100;
336262395Sbapt	right = (right * max) / 100;
337262395Sbapt
338262395Sbapt	sb_setmixer(sb, e->reg, left << e->ofs);
339262395Sbapt	if (e->stereo)
340262395Sbapt		sb_setmixer(sb, e->reg + 1, right << e->ofs);
341262395Sbapt	else
342262395Sbapt		right = left;
343262395Sbapt
344262395Sbapt	left = (left * 100) / max;
345262395Sbapt	right = (right * 100) / max;
346262395Sbapt
347262395Sbapt    	return left | (right << 8);
348262395Sbapt}
349266636Sbapt
350262395Sbaptstatic int
351262395Sbaptsb16mix_setrecsrc(struct snd_mixer *m, u_int32_t src)
352266636Sbapt{
353266636Sbapt    	struct sb_info *sb = mix_getdevinfo(m);
354262395Sbapt    	u_char recdev;
355266636Sbapt
356262395Sbapt	recdev = 0;
357262395Sbapt	if (src & SOUND_MASK_MIC)
358262395Sbapt		recdev |= 0x01; /* mono mic */
359262395Sbapt
360262395Sbapt	if (src & SOUND_MASK_CD)
361262395Sbapt		recdev |= 0x06; /* l+r cd */
362262395Sbapt
363262395Sbapt	if (src & SOUND_MASK_LINE)
364262395Sbapt		recdev |= 0x18; /* l+r line */
365262395Sbapt
366262395Sbapt	if (src & SOUND_MASK_SYNTH)
367266636Sbapt		recdev |= 0x60; /* l+r midi */
368262395Sbapt
369262395Sbapt	sb_setmixer(sb, SB16_IMASK_L, recdev);
370262395Sbapt	sb_setmixer(sb, SB16_IMASK_R, recdev);
371262395Sbapt
372262395Sbapt	/*
373262395Sbapt	 * since the same volume controls apply to the input and
374262395Sbapt	 * output sections, the best approach to have a consistent
375262395Sbapt	 * behaviour among cards would be to disable the output path
376262395Sbapt	 * on devices which are used to record.
377262395Sbapt	 * However, since users like to have feedback, we only disable
378262395Sbapt	 * the mic -- permanently.
379262395Sbapt	 */
380262395Sbapt        sb_setmixer(sb, SB16_OMASK, 0x1f & ~1);
381262395Sbapt
382262395Sbapt	return src;
383262395Sbapt}
384266636Sbapt
385266636Sbaptstatic kobj_method_t sb16mix_mixer_methods[] = {
386266636Sbapt    	KOBJMETHOD(mixer_init,		sb16mix_init),
387266636Sbapt    	KOBJMETHOD(mixer_set,		sb16mix_set),
388266636Sbapt    	KOBJMETHOD(mixer_setrecsrc,	sb16mix_setrecsrc),
389266636Sbapt	{ 0, 0 }
390266636Sbapt};
391266636SbaptMIXER_DECLARE(sb16mix_mixer);
392266636Sbapt
393266636Sbapt/************************************************************/
394266636Sbapt
395266636Sbaptstatic void
396268831Sbaptsb16_release_resources(struct sb_info *sb, device_t dev)
397268831Sbapt{
398268831Sbapt    	if (sb->irq) {
399268831Sbapt    		if (sb->ih)
400268831Sbapt			bus_teardown_intr(dev, sb->irq, sb->ih);
401268831Sbapt 		bus_release_resource(dev, SYS_RES_IRQ, 0, sb->irq);
402268831Sbapt		sb->irq = 0;
403268831Sbapt    	}
404268831Sbapt    	if (sb->drq2) {
405268831Sbapt		if (sb->drq2 != sb->drq1) {
406266636Sbapt			isa_dma_release(rman_get_start(sb->drq2));
407262395Sbapt			bus_release_resource(dev, SYS_RES_DRQ, 1, sb->drq2);
408262395Sbapt		}
409262395Sbapt		sb->drq2 = 0;
410262395Sbapt    	}
411262395Sbapt     	if (sb->drq1) {
412262395Sbapt		isa_dma_release(rman_get_start(sb->drq1));
413262395Sbapt		bus_release_resource(dev, SYS_RES_DRQ, 0, sb->drq1);
414262395Sbapt		sb->drq1 = 0;
415262395Sbapt    	}
416262395Sbapt   	if (sb->io_base) {
417262395Sbapt		bus_release_resource(dev, SYS_RES_IOPORT, 0, sb->io_base);
418262395Sbapt		sb->io_base = 0;
419262395Sbapt    	}
420262395Sbapt    	if (sb->parent_dmat) {
421262395Sbapt		bus_dma_tag_destroy(sb->parent_dmat);
422262395Sbapt		sb->parent_dmat = 0;
423262395Sbapt    	}
424262395Sbapt     	free(sb, M_DEVBUF);
425262395Sbapt}
426262395Sbapt
427262395Sbaptstatic int
428262395Sbaptsb16_alloc_resources(struct sb_info *sb, device_t dev)
429262395Sbapt{
430262395Sbapt	int rid;
431262395Sbapt
432262395Sbapt	rid = 0;
433262395Sbapt	if (!sb->io_base)
434262395Sbapt    		sb->io_base = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0, ~0, 1, RF_ACTIVE);
435262395Sbapt
436262395Sbapt	rid = 0;
437262395Sbapt	if (!sb->irq)
438262395Sbapt    		sb->irq = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0, ~0, 1, RF_ACTIVE);
439262395Sbapt
440262395Sbapt	rid = 0;
441262395Sbapt	if (!sb->drq1)
442262395Sbapt    		sb->drq1 = bus_alloc_resource(dev, SYS_RES_DRQ, &rid, 0, ~0, 1, RF_ACTIVE);
443262395Sbapt
444262395Sbapt	rid = 1;
445262395Sbapt	if (!sb->drq2)
446262395Sbapt        	sb->drq2 = bus_alloc_resource(dev, SYS_RES_DRQ, &rid, 0, ~0, 1, RF_ACTIVE);
447262395Sbapt
448262395Sbapt    	if (sb->io_base && sb->drq1 && sb->irq) {
449262395Sbapt		isa_dma_acquire(rman_get_start(sb->drq1));
450262395Sbapt		isa_dmainit(rman_get_start(sb->drq1), sb->bufsize);
451262395Sbapt
452262395Sbapt		if (sb->drq2) {
453262395Sbapt			isa_dma_acquire(rman_get_start(sb->drq2));
454262395Sbapt			isa_dmainit(rman_get_start(sb->drq2), sb->bufsize);
455262395Sbapt		} else {
456262395Sbapt			sb->drq2 = sb->drq1;
457262395Sbapt			pcm_setflags(dev, pcm_getflags(dev) | SD_F_SIMPLEX);
458262395Sbapt		}
459262395Sbapt		return 0;
460262395Sbapt	} else return ENXIO;
461262395Sbapt}
462262395Sbapt
463262395Sbapt/* sbc does locking for us */
464262395Sbaptstatic void
465262395Sbaptsb_intr(void *arg)
466262395Sbapt{
467262395Sbapt    	struct sb_info *sb = (struct sb_info *)arg;
468262395Sbapt    	int reason = 3, c;
469262395Sbapt
470262395Sbapt    	/*
471262395Sbapt     	 * The Vibra16X has separate flags for 8 and 16 bit transfers, but
472262395Sbapt     	 * I have no idea how to tell capture from playback interrupts...
473262395Sbapt     	 */
474262395Sbapt
475262395Sbapt	reason = 0;
476262395Sbapt	sb_lock(sb);
477262395Sbapt    	c = sb_getmixer(sb, IRQ_STAT);
478262395Sbapt    	if (c & 1)
479262395Sbapt		sb_rd(sb, DSP_DATA_AVAIL); /* 8-bit int ack */
480262395Sbapt
481262395Sbapt    	if (c & 2)
482262395Sbapt		sb_rd(sb, DSP_DATA_AVL16); /* 16-bit int ack */
483262395Sbapt	sb_unlock(sb);
484262395Sbapt
485262395Sbapt	/*
486262395Sbapt	 * this tells us if the source is 8-bit or 16-bit dma. We
487262395Sbapt     	 * have to check the io channel to map it to read or write...
488262395Sbapt     	 */
489262395Sbapt
490262395Sbapt	if (sb->bd_flags & BD_F_SB16X) {
491262395Sbapt    		if (c & 1) { /* 8-bit format */
492262395Sbapt			if (sb->pch.fmt & AFMT_8BIT)
493262395Sbapt				reason |= 1;
494262395Sbapt			if (sb->rch.fmt & AFMT_8BIT)
495262395Sbapt				reason |= 2;
496262395Sbapt    		}
497262395Sbapt    		if (c & 2) { /* 16-bit format */
498262395Sbapt			if (sb->pch.fmt & AFMT_16BIT)
499262395Sbapt				reason |= 1;
500262395Sbapt			if (sb->rch.fmt & AFMT_16BIT)
501262395Sbapt				reason |= 2;
502262395Sbapt    		}
503262395Sbapt	} else {
504262395Sbapt    		if (c & 1) { /* 8-bit dma */
505262395Sbapt			if (sb->pch.dch == 1)
506262395Sbapt				reason |= 1;
507262395Sbapt			if (sb->rch.dch == 1)
508262395Sbapt				reason |= 2;
509262395Sbapt    		}
510262395Sbapt    		if (c & 2) { /* 16-bit dma */
511262395Sbapt			if (sb->pch.dch == 2)
512262395Sbapt				reason |= 1;
513262395Sbapt			if (sb->rch.dch == 2)
514262395Sbapt				reason |= 2;
515262395Sbapt    		}
516262395Sbapt	}
517262395Sbapt#if 0
518262395Sbapt    	printf("sb_intr: reason=%d c=0x%x\n", reason, c);
519262395Sbapt#endif
520262395Sbapt    	if ((reason & 1) && (sb->pch.run))
521262395Sbapt		chn_intr(sb->pch.channel);
522262395Sbapt
523262395Sbapt    	if ((reason & 2) && (sb->rch.run))
524262395Sbapt		chn_intr(sb->rch.channel);
525262395Sbapt}
526262395Sbapt
527262395Sbaptstatic int
528262395Sbaptsb_setup(struct sb_info *sb)
529262395Sbapt{
530262395Sbapt	struct sb_chinfo *ch;
531262395Sbapt	u_int8_t v;
532262395Sbapt	int l, pprio;
533262395Sbapt
534262395Sbapt	sb_lock(sb);
535262395Sbapt	if (sb->bd_flags & BD_F_DMARUN)
536262395Sbapt		sndbuf_isadma(sb->pch.buffer, PCMTRIG_STOP);
537262395Sbapt	if (sb->bd_flags & BD_F_DMARUN2)
538262395Sbapt		sndbuf_isadma(sb->rch.buffer, PCMTRIG_STOP);
539262395Sbapt	sb->bd_flags &= ~(BD_F_DMARUN | BD_F_DMARUN2);
540262395Sbapt
541262395Sbapt	sb_reset_dsp(sb);
542262395Sbapt
543262395Sbapt	if (sb->bd_flags & BD_F_SB16X) {
544262395Sbapt		pprio = sb->pch.run? 1 : 0;
545262395Sbapt		sndbuf_isadmasetup(sb->pch.buffer, pprio? sb->drq1 : NULL);
546262395Sbapt		sb->pch.dch = pprio? 1 : 0;
547262395Sbapt		sndbuf_isadmasetup(sb->rch.buffer, pprio? sb->drq2 : sb->drq1);
548262395Sbapt		sb->rch.dch = pprio? 2 : 1;
549262395Sbapt	} else {
550262395Sbapt		if (sb->pch.run && sb->rch.run) {
551262395Sbapt			pprio = (sb->rch.fmt & AFMT_16BIT)? 0 : 1;
552262395Sbapt			sndbuf_isadmasetup(sb->pch.buffer, pprio? sb->drq2 : sb->drq1);
553262395Sbapt			sb->pch.dch = pprio? 2 : 1;
554262395Sbapt			sndbuf_isadmasetup(sb->rch.buffer, pprio? sb->drq1 : sb->drq2);
555262395Sbapt			sb->rch.dch = pprio? 1 : 2;
556262395Sbapt		} else {
557262395Sbapt			if (sb->pch.run) {
558262395Sbapt				sndbuf_isadmasetup(sb->pch.buffer, (sb->pch.fmt & AFMT_16BIT)? sb->drq2 : sb->drq1);
559262395Sbapt				sb->pch.dch = (sb->pch.fmt & AFMT_16BIT)? 2 : 1;
560262395Sbapt				sndbuf_isadmasetup(sb->rch.buffer, (sb->pch.fmt & AFMT_16BIT)? sb->drq1 : sb->drq2);
561263648Sbapt				sb->rch.dch = (sb->pch.fmt & AFMT_16BIT)? 1 : 2;
562263648Sbapt			} else if (sb->rch.run) {
563263648Sbapt				sndbuf_isadmasetup(sb->pch.buffer, (sb->rch.fmt & AFMT_16BIT)? sb->drq1 : sb->drq2);
564263648Sbapt				sb->pch.dch = (sb->rch.fmt & AFMT_16BIT)? 1 : 2;
565262395Sbapt				sndbuf_isadmasetup(sb->rch.buffer, (sb->rch.fmt & AFMT_16BIT)? sb->drq2 : sb->drq1);
566262395Sbapt				sb->rch.dch = (sb->rch.fmt & AFMT_16BIT)? 2 : 1;
567262395Sbapt			}
568262395Sbapt		}
569262395Sbapt	}
570262395Sbapt
571262395Sbapt	sndbuf_isadmasetdir(sb->pch.buffer, PCMDIR_PLAY);
572262395Sbapt	sndbuf_isadmasetdir(sb->rch.buffer, PCMDIR_REC);
573262395Sbapt
574262395Sbapt	/*
575263648Sbapt	printf("setup: [pch = %d, pfmt = %d, pgo = %d] [rch = %d, rfmt = %d, rgo = %d]\n",
576263648Sbapt	       sb->pch.dch, sb->pch.fmt, sb->pch.run, sb->rch.dch, sb->rch.fmt, sb->rch.run);
577262395Sbapt	*/
578262395Sbapt
579262395Sbapt	ch = &sb->pch;
580262395Sbapt	if (ch->run) {
581263648Sbapt		l = ch->blksz;
582262395Sbapt		if (ch->fmt & AFMT_16BIT)
583262395Sbapt			l >>= 1;
584262395Sbapt		l--;
585262395Sbapt
586262395Sbapt		/* play speed */
587262395Sbapt		RANGE(ch->spd, 5000, 45000);
588262395Sbapt		sb_cmd(sb, DSP_CMD_OUT16);
589262395Sbapt    		sb_cmd(sb, ch->spd >> 8);
590262395Sbapt		sb_cmd(sb, ch->spd & 0xff);
591262395Sbapt
592262395Sbapt		/* play format, length */
593262395Sbapt		v = DSP_F16_AUTO | DSP_F16_FIFO_ON | DSP_F16_DAC;
594262395Sbapt		v |= (ch->fmt & AFMT_16BIT)? DSP_DMA16 : DSP_DMA8;
595262395Sbapt		sb_cmd(sb, v);
596262395Sbapt
597262395Sbapt		v = (ch->fmt & AFMT_STEREO)? DSP_F16_STEREO : 0;
598262395Sbapt		v |= (ch->fmt & AFMT_SIGNED)? DSP_F16_SIGNED : 0;
599262395Sbapt		sb_cmd2(sb, v, l);
600262395Sbapt		sndbuf_isadma(ch->buffer, PCMTRIG_START);
601262395Sbapt		sb->bd_flags |= BD_F_DMARUN;
602262395Sbapt	}
603262395Sbapt
604262395Sbapt	ch = &sb->rch;
605262395Sbapt	if (ch->run) {
606262395Sbapt		l = ch->blksz;
607262395Sbapt		if (ch->fmt & AFMT_16BIT)
608262395Sbapt			l >>= 1;
609262395Sbapt		l--;
610262395Sbapt
611262395Sbapt		/* record speed */
612262395Sbapt		RANGE(ch->spd, 5000, 45000);
613262395Sbapt		sb_cmd(sb, DSP_CMD_IN16);
614262395Sbapt    		sb_cmd(sb, ch->spd >> 8);
615262395Sbapt		sb_cmd(sb, ch->spd & 0xff);
616262395Sbapt
617262395Sbapt		/* record format, length */
618262395Sbapt		v = DSP_F16_AUTO | DSP_F16_FIFO_ON | DSP_F16_ADC;
619262395Sbapt		v |= (ch->fmt & AFMT_16BIT)? DSP_DMA16 : DSP_DMA8;
620262395Sbapt		sb_cmd(sb, v);
621262395Sbapt
622262395Sbapt		v = (ch->fmt & AFMT_STEREO)? DSP_F16_STEREO : 0;
623262395Sbapt		v |= (ch->fmt & AFMT_SIGNED)? DSP_F16_SIGNED : 0;
624262395Sbapt		sb_cmd2(sb, v, l);
625262395Sbapt		sndbuf_isadma(ch->buffer, PCMTRIG_START);
626262395Sbapt		sb->bd_flags |= BD_F_DMARUN2;
627262395Sbapt	}
628262395Sbapt	sb_unlock(sb);
629262395Sbapt
630262395Sbapt    	return 0;
631262395Sbapt}
632262395Sbapt
633262395Sbapt/* channel interface */
634262395Sbaptstatic void *
635262395Sbaptsb16chan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel *c, int dir)
636262395Sbapt{
637262395Sbapt	struct sb_info *sb = devinfo;
638262395Sbapt	struct sb_chinfo *ch = (dir == PCMDIR_PLAY)? &sb->pch : &sb->rch;
639262395Sbapt
640262395Sbapt	ch->parent = sb;
641262395Sbapt	ch->channel = c;
642262395Sbapt	ch->buffer = b;
643262395Sbapt	ch->dir = dir;
644262395Sbapt
645262395Sbapt	if (sndbuf_alloc(ch->buffer, sb->parent_dmat, sb->bufsize) == -1)
646262395Sbapt		return NULL;
647262395Sbapt
648262395Sbapt	return ch;
649262395Sbapt}
650262395Sbapt
651262395Sbaptstatic int
652262395Sbaptsb16chan_setformat(kobj_t obj, void *data, u_int32_t format)
653262395Sbapt{
654262395Sbapt	struct sb_chinfo *ch = data;
655262395Sbapt	struct sb_info *sb = ch->parent;
656262395Sbapt
657262395Sbapt	ch->fmt = format;
658262395Sbapt	sb->prio = ch->dir;
659262395Sbapt	sb->prio16 = (ch->fmt & AFMT_16BIT)? 1 : 0;
660262395Sbapt
661262395Sbapt	return 0;
662262395Sbapt}
663262395Sbapt
664262395Sbaptstatic int
665262395Sbaptsb16chan_setspeed(kobj_t obj, void *data, u_int32_t speed)
666262395Sbapt{
667262395Sbapt	struct sb_chinfo *ch = data;
668262395Sbapt
669262395Sbapt	ch->spd = speed;
670262395Sbapt	return speed;
671262395Sbapt}
672262395Sbapt
673262395Sbaptstatic int
674262395Sbaptsb16chan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize)
675262395Sbapt{
676262395Sbapt	struct sb_chinfo *ch = data;
677262395Sbapt
678262395Sbapt	ch->blksz = blocksize;
679263648Sbapt	return ch->blksz;
680263648Sbapt}
681262395Sbapt
682262395Sbaptstatic int
683262395Sbaptsb16chan_trigger(kobj_t obj, void *data, int go)
684262395Sbapt{
685262395Sbapt	struct sb_chinfo *ch = data;
686262395Sbapt	struct sb_info *sb = ch->parent;
687262395Sbapt
688262395Sbapt	if (go == PCMTRIG_EMLDMAWR || go == PCMTRIG_EMLDMARD)
689262395Sbapt		return 0;
690262395Sbapt
691262395Sbapt	if (go == PCMTRIG_START)
692262395Sbapt		ch->run = 1;
693262395Sbapt	else
694262395Sbapt		ch->run = 0;
695262395Sbapt
696262395Sbapt	sb_setup(sb);
697262395Sbapt
698262395Sbapt	return 0;
699262395Sbapt}
700262395Sbapt
701263648Sbaptstatic int
702262395Sbaptsb16chan_getptr(kobj_t obj, void *data)
703262395Sbapt{
704262395Sbapt	struct sb_chinfo *ch = data;
705262395Sbapt
706262395Sbapt	return sndbuf_isadmaptr(ch->buffer);
707262395Sbapt}
708262395Sbapt
709262395Sbaptstatic struct pcmchan_caps *
710262395Sbaptsb16chan_getcaps(kobj_t obj, void *data)
711262395Sbapt{
712262395Sbapt	struct sb_chinfo *ch = data;
713262395Sbapt	struct sb_info *sb = ch->parent;
714262395Sbapt
715262395Sbapt	if ((sb->prio == 0) || (sb->prio == ch->dir))
716262395Sbapt		return &sb16x_caps;
717262395Sbapt	else
718262395Sbapt		return sb->prio16? &sb16_caps8 : &sb16_caps16;
719262395Sbapt}
720262395Sbapt
721262395Sbaptstatic int
722262395Sbaptsb16chan_resetdone(kobj_t obj, void *data)
723262395Sbapt{
724262395Sbapt	struct sb_chinfo *ch = data;
725262395Sbapt	struct sb_info *sb = ch->parent;
726262395Sbapt
727262395Sbapt	sb->prio = 0;
728262395Sbapt
729262395Sbapt	return 0;
730262395Sbapt}
731263648Sbapt
732262395Sbaptstatic kobj_method_t sb16chan_methods[] = {
733262395Sbapt    	KOBJMETHOD(channel_init,		sb16chan_init),
734262395Sbapt    	KOBJMETHOD(channel_resetdone,		sb16chan_resetdone),
735262395Sbapt    	KOBJMETHOD(channel_setformat,		sb16chan_setformat),
736262395Sbapt    	KOBJMETHOD(channel_setspeed,		sb16chan_setspeed),
737262395Sbapt    	KOBJMETHOD(channel_setblocksize,	sb16chan_setblocksize),
738262395Sbapt    	KOBJMETHOD(channel_trigger,		sb16chan_trigger),
739262395Sbapt    	KOBJMETHOD(channel_getptr,		sb16chan_getptr),
740263648Sbapt    	KOBJMETHOD(channel_getcaps,		sb16chan_getcaps),
741262395Sbapt	{ 0, 0 }
742262395Sbapt};
743262395SbaptCHANNEL_DECLARE(sb16chan);
744262395Sbapt
745262395Sbapt/************************************************************/
746262395Sbapt
747262395Sbaptstatic int
748262395Sbaptsb16_probe(device_t dev)
749262395Sbapt{
750262395Sbapt    	char buf[64];
751262395Sbapt	uintptr_t func, ver, r, f;
752262395Sbapt
753262395Sbapt	/* The parent device has already been probed. */
754262395Sbapt	r = BUS_READ_IVAR(device_get_parent(dev), dev, 0, &func);
755262395Sbapt	if (func != SCF_PCM)
756262395Sbapt		return (ENXIO);
757262395Sbapt
758262395Sbapt	r = BUS_READ_IVAR(device_get_parent(dev), dev, 1, &ver);
759262395Sbapt	f = (ver & 0xffff0000) >> 16;
760263648Sbapt	ver &= 0x0000ffff;
761263648Sbapt	if (f & BD_F_SB16) {
762262395Sbapt		snprintf(buf, sizeof buf, "SB16 DSP %d.%02d%s", (int) ver >> 8, (int) ver & 0xff,
763262395Sbapt			 (f & BD_F_SB16X)? " (ViBRA16X)" : "");
764262395Sbapt    		device_set_desc_copy(dev, buf);
765262395Sbapt		return 0;
766262395Sbapt	} else
767263648Sbapt		return (ENXIO);
768262395Sbapt}
769262395Sbapt
770262395Sbaptstatic int
771262395Sbaptsb16_attach(device_t dev)
772262395Sbapt{
773262395Sbapt    	struct sb_info *sb;
774262395Sbapt	uintptr_t ver;
775262395Sbapt    	char status[SND_STATUSLEN], status2[SND_STATUSLEN];
776262395Sbapt
777262395Sbapt    	sb = (struct sb_info *)malloc(sizeof *sb, M_DEVBUF, M_NOWAIT | M_ZERO);
778262395Sbapt    	if (!sb)
779263648Sbapt		return ENXIO;
780263648Sbapt
781262395Sbapt	sb->parent_dev = device_get_parent(dev);
782262395Sbapt	BUS_READ_IVAR(sb->parent_dev, dev, 1, &ver);
783262395Sbapt	sb->bd_id = ver & 0x0000ffff;
784262395Sbapt	sb->bd_flags = (ver & 0xffff0000) >> 16;
785263648Sbapt	sb->bufsize = pcm_getbuffersize(dev, 4096, SB16_BUFFSIZE, 65536);
786262395Sbapt
787262395Sbapt    	if (sb16_alloc_resources(sb, dev))
788262395Sbapt		goto no;
789262395Sbapt    	if (sb_reset_dsp(sb))
790262395Sbapt		goto no;
791262395Sbapt	if (mixer_init(dev, &sb16mix_mixer_class, sb))
792262395Sbapt		goto no;
793262395Sbapt	if (snd_setup_intr(dev, sb->irq, INTR_MPSAFE, sb_intr, sb, &sb->ih))
794262395Sbapt		goto no;
795262395Sbapt
796262395Sbapt	if (sb->bd_flags & BD_F_SB16X)
797262395Sbapt		pcm_setflags(dev, pcm_getflags(dev) | SD_F_SIMPLEX);
798263648Sbapt
799263648Sbapt	sb->prio = 0;
800262395Sbapt
801262395Sbapt    	if (bus_dma_tag_create(/*parent*/NULL, /*alignment*/2, /*boundary*/0,
802262395Sbapt			/*lowaddr*/BUS_SPACE_MAXADDR_24BIT,
803262395Sbapt			/*highaddr*/BUS_SPACE_MAXADDR,
804262395Sbapt			/*filter*/NULL, /*filterarg*/NULL,
805262395Sbapt			/*maxsize*/sb->bufsize, /*nsegments*/1,
806262395Sbapt			/*maxsegz*/0x3ffff,
807262395Sbapt			/*flags*/0, &sb->parent_dmat) != 0) {
808262395Sbapt		device_printf(dev, "unable to create dma tag\n");
809262395Sbapt		goto no;
810262395Sbapt    	}
811262395Sbapt
812262395Sbapt    	if (!(pcm_getflags(dev) & SD_F_SIMPLEX))
813262395Sbapt		snprintf(status2, SND_STATUSLEN, ":%ld", rman_get_start(sb->drq2));
814262395Sbapt	else
815262395Sbapt		status2[0] = '\0';
816262395Sbapt
817262395Sbapt    	snprintf(status, SND_STATUSLEN, "at io 0x%lx irq %ld drq %ld%s bufsz %ud",
818262395Sbapt    	     	rman_get_start(sb->io_base), rman_get_start(sb->irq),
819262395Sbapt		rman_get_start(sb->drq1), status2, sb->bufsize);
820262395Sbapt
821262395Sbapt    	if (pcm_register(dev, sb, 1, 1))
822262395Sbapt		goto no;
823262395Sbapt	pcm_addchan(dev, PCMDIR_REC, &sb16chan_class, sb);
824262395Sbapt	pcm_addchan(dev, PCMDIR_PLAY, &sb16chan_class, sb);
825262395Sbapt
826262395Sbapt    	pcm_setstatus(dev, status);
827262395Sbapt
828263648Sbapt    	return 0;
829263648Sbapt
830262395Sbaptno:
831262395Sbapt    	sb16_release_resources(sb, dev);
832262395Sbapt    	return ENXIO;
833262395Sbapt}
834262395Sbapt
835262395Sbaptstatic int
836262395Sbaptsb16_detach(device_t dev)
837262395Sbapt{
838262395Sbapt	int r;
839262395Sbapt	struct sb_info *sb;
840262395Sbapt
841262395Sbapt	r = pcm_unregister(dev);
842262395Sbapt	if (r)
843262395Sbapt		return r;
844262395Sbapt
845262395Sbapt	sb = pcm_getdevinfo(dev);
846262395Sbapt    	sb16_release_resources(sb, dev);
847262395Sbapt	return 0;
848262395Sbapt}
849262395Sbapt
850262395Sbaptstatic device_method_t sb16_methods[] = {
851262395Sbapt	/* Device interface */
852262395Sbapt	DEVMETHOD(device_probe,		sb16_probe),
853262395Sbapt	DEVMETHOD(device_attach,	sb16_attach),
854262395Sbapt	DEVMETHOD(device_detach,	sb16_detach),
855262395Sbapt
856262395Sbapt	{ 0, 0 }
857262395Sbapt};
858262395Sbapt
859262395Sbaptstatic driver_t sb16_driver = {
860262395Sbapt	"pcm",
861262395Sbapt	sb16_methods,
862262395Sbapt	PCM_SOFTC_SIZE,
863262395Sbapt};
864262395Sbapt
865262395SbaptDRIVER_MODULE(snd_sb16, sbc, sb16_driver, pcm_devclass, 0, 0);
866262395SbaptMODULE_DEPEND(snd_sb16, snd_pcm, PCM_MINVER, PCM_PREFVER, PCM_MAXVER);
867262395SbaptMODULE_DEPEND(snd_sb16, snd_sbc, 1, 1, 1);
868262395SbaptMODULE_VERSION(snd_sb16, 1);
869262395Sbapt