sb16.c revision 119853
1101704Smjacob/*
2101704Smjacob * Copyright (c) 1999 Cameron Grant <cg@freebsd.org>
3101704Smjacob * Copyright 1997,1998 Luigi Rizzo.
4101704Smjacob *
5101704Smjacob * Derived from files in the Voxware 3.5 distribution,
6101704Smjacob * Copyright by Hannu Savolainen 1994, under the same copyright
7101704Smjacob * conditions.
8101704Smjacob * All rights reserved.
9101704Smjacob *
10101704Smjacob * Redistribution and use in source and binary forms, with or without
11101704Smjacob * modification, are permitted provided that the following conditions
12101704Smjacob * are met:
13101704Smjacob * 1. Redistributions of source code must retain the above copyright
14101704Smjacob *    notice, this list of conditions and the following disclaimer.
15101704Smjacob * 2. Redistributions in binary form must reproduce the above copyright
16101704Smjacob *    notice, this list of conditions and the following disclaimer in the
17101704Smjacob *    documentation and/or other materials provided with the distribution.
18101704Smjacob *
19101704Smjacob * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20101704Smjacob * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21101704Smjacob * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22101704Smjacob * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23101704Smjacob * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24101704Smjacob * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25101704Smjacob * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26101704Smjacob * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27101704Smjacob * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28101704Smjacob * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29101704Smjacob * SUCH DAMAGE.
30101704Smjacob */
31101704Smjacob
32134123Sobrien#include <dev/sound/pcm/sound.h>
33134123Sobrien
34134123Sobrien#include  <dev/sound/isa/sb.h>
35101704Smjacob#include  <dev/sound/chip.h>
36102199Smjacob
37101704Smjacob#include <isa/isavar.h>
38101704Smjacob
39101704Smjacob#include "mixer_if.h"
40101704Smjacob
41101704SmjacobSND_DECLARE_FILE("$FreeBSD: head/sys/dev/sound/isa/sb16.c 119853 2003-09-07 16:28:03Z cg $");
42101704Smjacob
43101704Smjacob#define SB16_BUFFSIZE	4096
44103914Smjacob#define PLAIN_SB16(x) ((((x)->bd_flags) & (BD_F_SB16|BD_F_SB16X)) == BD_F_SB16)
45103914Smjacob
46101704Smjacobstatic u_int32_t sb16_fmt8[] = {
47103914Smjacob	AFMT_U8,
48102199Smjacob	AFMT_STEREO | AFMT_U8,
49101704Smjacob	0
50101704Smjacob};
51101704Smjacobstatic struct pcmchan_caps sb16_caps8 = {5000, 45000, sb16_fmt8, 0};
52101704Smjacob
53103914Smjacobstatic u_int32_t sb16_fmt16[] = {
54102199Smjacob	AFMT_S16_LE,
55101704Smjacob	AFMT_STEREO | AFMT_S16_LE,
56101704Smjacob	0
57101704Smjacob};
58101704Smjacobstatic struct pcmchan_caps sb16_caps16 = {5000, 45000, sb16_fmt16, 0};
59101704Smjacob
60101704Smjacobstatic u_int32_t sb16x_fmt[] = {
61102199Smjacob	AFMT_U8,
62101704Smjacob	AFMT_STEREO | AFMT_U8,
63101704Smjacob	AFMT_S16_LE,
64101704Smjacob	AFMT_STEREO | AFMT_S16_LE,
65101704Smjacob	0
66101704Smjacob};
67101704Smjacobstatic struct pcmchan_caps sb16x_caps = {5000, 49000, sb16x_fmt, 0};
68101704Smjacob
69101704Smjacobstruct sb_info;
70101704Smjacob
71101704Smjacobstruct sb_chinfo {
72101704Smjacob	struct sb_info *parent;
73101704Smjacob	struct pcm_channel *channel;
74101704Smjacob	struct snd_dbuf *buffer;
75101704Smjacob	int dir, run, dch;
76101704Smjacob	u_int32_t fmt, spd, blksz;
77102199Smjacob};
78101704Smjacob
79101704Smjacobstruct sb_info {
80101704Smjacob    	struct resource *io_base;	/* I/O address for the board */
81101704Smjacob    	struct resource *irq;
82101704Smjacob   	struct resource *drq1;
83101704Smjacob    	struct resource *drq2;
84101704Smjacob    	void *ih;
85101704Smjacob    	bus_dma_tag_t parent_dmat;
86101704Smjacob
87101704Smjacob	unsigned int bufsize;
88101704Smjacob    	int bd_id;
89101704Smjacob    	u_long bd_flags;       /* board-specific flags */
90101704Smjacob	int prio, prio16;
91101704Smjacob    	struct sb_chinfo pch, rch;
92102199Smjacob	device_t parent_dev;
93101704Smjacob};
94101704Smjacob
95101704Smjacob#if 0
96103914Smjacobstatic void sb_lock(struct sb_info *sb);
97101704Smjacobstatic void sb_unlock(struct sb_info *sb);
98101704Smjacobstatic int sb_rd(struct sb_info *sb, int reg);
99101704Smjacobstatic void sb_wr(struct sb_info *sb, int reg, u_int8_t val);
100101704Smjacobstatic int sb_cmd(struct sb_info *sb, u_char val);
101101704Smjacob/* static int sb_cmd1(struct sb_info *sb, u_char cmd, int val); */
102101704Smjacobstatic int sb_cmd2(struct sb_info *sb, u_char cmd, int val);
103102199Smjacobstatic u_int sb_get_byte(struct sb_info *sb);
104101704Smjacobstatic void sb_setmixer(struct sb_info *sb, u_int port, u_int value);
105101704Smjacobstatic int sb_getmixer(struct sb_info *sb, u_int port);
106101704Smjacobstatic int sb_reset_dsp(struct sb_info *sb);
107101704Smjacob
108101704Smjacobstatic void sb_intr(void *arg);
109101704Smjacob#endif
110101704Smjacob
111101704Smjacob/*
112101704Smjacob * Common code for the midi and pcm functions
113101704Smjacob *
114101704Smjacob * sb_cmd write a single byte to the CMD port.
115101704Smjacob * sb_cmd1 write a CMD + 1 byte arg
116101704Smjacob * sb_cmd2 write a CMD + 2 byte arg
117101704Smjacob * sb_get_byte returns a single byte from the DSP data port
118101704Smjacob */
119101704Smjacob
120101704Smjacobstatic void
121102199Smjacobsb_lock(struct sb_info *sb) {
122101704Smjacob
123101704Smjacob	sbc_lock(device_get_softc(sb->parent_dev));
124103914Smjacob}
125101704Smjacob
126101704Smjacobstatic void
127101704Smjacobsb_unlock(struct sb_info *sb) {
128101704Smjacob
129103914Smjacob	sbc_unlock(device_get_softc(sb->parent_dev));
130101704Smjacob}
131101704Smjacob
132101704Smjacobstatic int
133101704Smjacobport_rd(struct resource *port, int off)
134101704Smjacob{
135101704Smjacob	return bus_space_read_1(rman_get_bustag(port), rman_get_bushandle(port), off);
136101704Smjacob}
137101704Smjacob
138103914Smjacobstatic void
139101704Smjacobport_wr(struct resource *port, int off, u_int8_t data)
140101704Smjacob{
141101704Smjacob	bus_space_write_1(rman_get_bustag(port), rman_get_bushandle(port), off, data);
142101704Smjacob}
143101704Smjacob
144101704Smjacobstatic int
145101704Smjacobsb_rd(struct sb_info *sb, int reg)
146103914Smjacob{
147101704Smjacob	return port_rd(sb->io_base, reg);
148101704Smjacob}
149101704Smjacob
150101704Smjacobstatic void
151101704Smjacobsb_wr(struct sb_info *sb, int reg, u_int8_t val)
152103914Smjacob{
153101704Smjacob	port_wr(sb->io_base, reg, val);
154101704Smjacob}
155101704Smjacob
156101704Smjacobstatic int
157101704Smjacobsb_dspwr(struct sb_info *sb, u_char val)
158101704Smjacob{
159101704Smjacob    	int  i;
160101704Smjacob
161101704Smjacob    	for (i = 0; i < 1000; i++) {
162101704Smjacob		if ((sb_rd(sb, SBDSP_STATUS) & 0x80))
163102199Smjacob	    		DELAY((i > 100)? 1000 : 10);
164101704Smjacob	    	else {
165101704Smjacob			sb_wr(sb, SBDSP_CMD, val);
166101704Smjacob			return 1;
167101704Smjacob		}
168101704Smjacob    	}
169103914Smjacob#if __FreeBSD_version > 500000
170101704Smjacob	if (curthread->td_intr_nesting_level == 0)
171101704Smjacob		printf("sb_dspwr(0x%02x) timed out.\n", val);
172101704Smjacob#endif
173101704Smjacob    	return 0;
174101704Smjacob}
175101704Smjacob
176101704Smjacobstatic int
177101704Smjacobsb_cmd(struct sb_info *sb, u_char val)
178101704Smjacob{
179101704Smjacob#if 0
180101704Smjacob	printf("sb_cmd: %x\n", val);
181101704Smjacob#endif
182101704Smjacob    	return sb_dspwr(sb, val);
183101704Smjacob}
184101704Smjacob
185101704Smjacob/*
186101704Smjacobstatic int
187101704Smjacobsb_cmd1(struct sb_info *sb, u_char cmd, int val)
188101704Smjacob{
189101704Smjacob#if 0
190101704Smjacob    	printf("sb_cmd1: %x, %x\n", cmd, val);
191101704Smjacob#endif
192101704Smjacob    	if (sb_dspwr(sb, cmd)) {
193101704Smjacob		return sb_dspwr(sb, val & 0xff);
194101704Smjacob    	} else return 0;
195101704Smjacob}
196101704Smjacob*/
197101704Smjacob
198101704Smjacobstatic int
199101704Smjacobsb_cmd2(struct sb_info *sb, u_char cmd, int val)
200101704Smjacob{
201101704Smjacob	int r;
202101704Smjacob
203101704Smjacob#if 0
204101704Smjacob    	printf("sb_cmd2: %x, %x\n", cmd, val);
205101704Smjacob#endif
206101704Smjacob	sb_lock(sb);
207102199Smjacob	r = 0;
208101704Smjacob    	if (sb_dspwr(sb, cmd)) {
209101704Smjacob		if (sb_dspwr(sb, val & 0xff)) {
210101704Smjacob			if (sb_dspwr(sb, (val >> 8) & 0xff)) {
211101704Smjacob				r = 1;
212101704Smjacob			}
213101704Smjacob		}
214101704Smjacob    	}
215101704Smjacob	sb_unlock(sb);
216101704Smjacob
217101704Smjacob	return r;
218101704Smjacob}
219103914Smjacob
220101704Smjacob/*
221101704Smjacob * in the SB, there is a set of indirect "mixer" registers with
222101704Smjacob * address at offset 4, data at offset 5
223101704Smjacob */
224101704Smjacobstatic void
225101704Smjacobsb_setmixer(struct sb_info *sb, u_int port, u_int value)
226101704Smjacob{
227101704Smjacob	sb_lock(sb);
228102199Smjacob    	sb_wr(sb, SB_MIX_ADDR, (u_char) (port & 0xff)); /* Select register */
229101704Smjacob    	DELAY(10);
230103871Smjacob    	sb_wr(sb, SB_MIX_DATA, (u_char) (value & 0xff));
231101704Smjacob    	DELAY(10);
232101704Smjacob	sb_unlock(sb);
233101704Smjacob}
234103827Smjacob
235101704Smjacobstatic int
236101704Smjacobsb_getmixer(struct sb_info *sb, u_int port)
237101704Smjacob{
238101704Smjacob    	int val;
239101704Smjacob
240101704Smjacob	sb_lock(sb);
241101704Smjacob    	sb_wr(sb, SB_MIX_ADDR, (u_char) (port & 0xff)); /* Select register */
242102199Smjacob    	DELAY(10);
243101704Smjacob    	val = sb_rd(sb, SB_MIX_DATA);
244101704Smjacob    	DELAY(10);
245101704Smjacob	sb_unlock(sb);
246101704Smjacob
247103871Smjacob    	return val;
248101704Smjacob}
249101704Smjacob
250101704Smjacobstatic u_int
251101704Smjacobsb_get_byte(struct sb_info *sb)
252101704Smjacob{
253101704Smjacob    	int i;
254101704Smjacob
255101704Smjacob    	for (i = 1000; i > 0; i--) {
256101704Smjacob		if (sb_rd(sb, DSP_DATA_AVAIL) & 0x80)
257101704Smjacob			return sb_rd(sb, DSP_READ);
258101704Smjacob		else
259101704Smjacob			DELAY(20);
260101704Smjacob    	}
261102199Smjacob    	return 0xffff;
262101704Smjacob}
263101704Smjacob
264101704Smjacobstatic int
265101704Smjacobsb_reset_dsp(struct sb_info *sb)
266101704Smjacob{
267103914Smjacob	u_char b;
268103914Smjacob
269103914Smjacob	sb_lock(sb);
270101704Smjacob    	sb_wr(sb, SBDSP_RST, 3);
271103914Smjacob    	DELAY(100);
272101704Smjacob    	sb_wr(sb, SBDSP_RST, 0);
273103914Smjacob	b = sb_get_byte(sb);
274101704Smjacob	sb_unlock(sb);
275103914Smjacob    	if (b != 0xAA) {
276101704Smjacob        	DEB(printf("sb_reset_dsp 0x%lx failed\n",
277101704Smjacob			   rman_get_start(sb->io_base)));
278101704Smjacob		return ENXIO;	/* Sorry */
279103914Smjacob    	}
280101704Smjacob    	return 0;
281101704Smjacob}
282101704Smjacob
283101704Smjacob/************************************************************/
284101704Smjacob
285101704Smjacobstruct sb16_mixent {
286101704Smjacob	int reg;
287101704Smjacob	int bits;
288101704Smjacob	int ofs;
289102199Smjacob	int stereo;
290101704Smjacob};
291101704Smjacob
292101704Smjacobstatic const struct sb16_mixent sb16_mixtab[32] = {
293101704Smjacob    	[SOUND_MIXER_VOLUME]	= { 0x30, 5, 3, 1 },
294101704Smjacob    	[SOUND_MIXER_PCM]	= { 0x32, 5, 3, 1 },
295101704Smjacob    	[SOUND_MIXER_SYNTH]	= { 0x34, 5, 3, 1 },
296102199Smjacob    	[SOUND_MIXER_CD]	= { 0x36, 5, 3, 1 },
297101704Smjacob    	[SOUND_MIXER_LINE]	= { 0x38, 5, 3, 1 },
298101704Smjacob    	[SOUND_MIXER_MIC]	= { 0x3a, 5, 3, 0 },
299101704Smjacob       	[SOUND_MIXER_SPEAKER]	= { 0x3b, 5, 3, 0 },
300101704Smjacob    	[SOUND_MIXER_IGAIN]	= { 0x3f, 2, 6, 1 },
301101704Smjacob    	[SOUND_MIXER_OGAIN]	= { 0x41, 2, 6, 1 },
302101704Smjacob	[SOUND_MIXER_TREBLE]	= { 0x44, 4, 4, 1 },
303101704Smjacob    	[SOUND_MIXER_BASS]	= { 0x46, 4, 4, 1 },
304101704Smjacob	[SOUND_MIXER_LINE1]	= { 0x52, 5, 3, 1 }
305101704Smjacob};
306101704Smjacob
307101704Smjacobstatic int
308102199Smjacobsb16mix_init(struct snd_mixer *m)
309101704Smjacob{
310101704Smjacob    	struct sb_info *sb = mix_getdevinfo(m);
311101704Smjacob
312101704Smjacob	mix_setdevs(m, SOUND_MASK_SYNTH | SOUND_MASK_PCM | SOUND_MASK_SPEAKER |
313101704Smjacob     		       SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD |
314101704Smjacob     		       SOUND_MASK_IGAIN | SOUND_MASK_OGAIN | SOUND_MASK_LINE1 |
315101704Smjacob     		       SOUND_MASK_VOLUME | SOUND_MASK_BASS | SOUND_MASK_TREBLE);
316101704Smjacob
317101704Smjacob	mix_setrecdevs(m, SOUND_MASK_SYNTH | SOUND_MASK_LINE |
318101704Smjacob			  SOUND_MASK_LINE1 | SOUND_MASK_MIC | SOUND_MASK_CD);
319103914Smjacob
320101704Smjacob	sb_setmixer(sb, 0x3c, 0x1f); /* make all output active */
321101704Smjacob
322101704Smjacob	sb_setmixer(sb, 0x3d, 0); /* make all inputs-l off */
323101704Smjacob	sb_setmixer(sb, 0x3e, 0); /* make all inputs-r off */
324101704Smjacob
325101704Smjacob	return 0;
326101704Smjacob}
327101704Smjacob
328101704Smjacobstatic int
329101704Smjacobsb16mix_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right)
330101704Smjacob{
331101704Smjacob    	struct sb_info *sb = mix_getdevinfo(m);
332101704Smjacob    	const struct sb16_mixent *e;
333101704Smjacob    	int max;
334101704Smjacob
335101704Smjacob	e = &sb16_mixtab[dev];
336101704Smjacob	max = (1 << e->bits) - 1;
337101704Smjacob
338101704Smjacob	left = (left * max) / 100;
339101704Smjacob	right = (right * max) / 100;
340101704Smjacob
341101704Smjacob	sb_setmixer(sb, e->reg, left << e->ofs);
342103914Smjacob	if (e->stereo)
343101704Smjacob		sb_setmixer(sb, e->reg + 1, right << e->ofs);
344101704Smjacob	else
345101704Smjacob		right = left;
346101704Smjacob
347101704Smjacob	left = (left * 100) / max;
348101704Smjacob	right = (right * 100) / max;
349101704Smjacob
350103914Smjacob    	return left | (right << 8);
351101704Smjacob}
352101704Smjacob
353101704Smjacobstatic int
354101704Smjacobsb16mix_setrecsrc(struct snd_mixer *m, u_int32_t src)
355101704Smjacob{
356101704Smjacob    	struct sb_info *sb = mix_getdevinfo(m);
357101704Smjacob    	u_char recdev;
358103914Smjacob
359103914Smjacob	recdev = 0;
360101704Smjacob	if (src & SOUND_MASK_MIC)
361101704Smjacob		recdev |= 0x01; /* mono mic */
362101704Smjacob
363101704Smjacob	if (src & SOUND_MASK_CD)
364101704Smjacob		recdev |= 0x06; /* l+r cd */
365101704Smjacob
366101704Smjacob	if (src & SOUND_MASK_LINE)
367101704Smjacob		recdev |= 0x18; /* l+r line */
368102199Smjacob
369101704Smjacob	if (src & SOUND_MASK_SYNTH)
370101704Smjacob		recdev |= 0x60; /* l+r midi */
371101704Smjacob
372101704Smjacob	sb_setmixer(sb, SB16_IMASK_L, recdev);
373101704Smjacob	sb_setmixer(sb, SB16_IMASK_R, recdev);
374101704Smjacob
375101704Smjacob	/* Switch on/off FM tuner source */
376101704Smjacob	if (src & SOUND_MASK_LINE1)
377101704Smjacob		sb_setmixer(sb, 0x4a, 0x0c);
378101704Smjacob	else
379101704Smjacob		sb_setmixer(sb, 0x4a, 0x00);
380101704Smjacob
381101704Smjacob	/*
382103914Smjacob	 * since the same volume controls apply to the input and
383101704Smjacob	 * output sections, the best approach to have a consistent
384101704Smjacob	 * behaviour among cards would be to disable the output path
385101704Smjacob	 * on devices which are used to record.
386101704Smjacob	 * However, since users like to have feedback, we only disable
387101704Smjacob	 * the mic -- permanently.
388101704Smjacob	 */
389101704Smjacob        sb_setmixer(sb, SB16_OMASK, 0x1f & ~1);
390103914Smjacob
391101704Smjacob	return src;
392101704Smjacob}
393101704Smjacob
394101704Smjacobstatic kobj_method_t sb16mix_mixer_methods[] = {
395101704Smjacob    	KOBJMETHOD(mixer_init,		sb16mix_init),
396101704Smjacob    	KOBJMETHOD(mixer_set,		sb16mix_set),
397101704Smjacob    	KOBJMETHOD(mixer_setrecsrc,	sb16mix_setrecsrc),
398103914Smjacob	{ 0, 0 }
399103914Smjacob};
400103914SmjacobMIXER_DECLARE(sb16mix_mixer);
401101704Smjacob
402101704Smjacob/************************************************************/
403101704Smjacob
404101704Smjacobstatic void
405101704Smjacobsb16_release_resources(struct sb_info *sb, device_t dev)
406101704Smjacob{
407101704Smjacob    	if (sb->irq) {
408101704Smjacob    		if (sb->ih)
409101704Smjacob			bus_teardown_intr(dev, sb->irq, sb->ih);
410103914Smjacob 		bus_release_resource(dev, SYS_RES_IRQ, 0, sb->irq);
411101704Smjacob		sb->irq = 0;
412101704Smjacob    	}
413101704Smjacob    	if (sb->drq2) {
414101704Smjacob		if (sb->drq2 != sb->drq1) {
415101704Smjacob			isa_dma_release(rman_get_start(sb->drq2));
416101704Smjacob			bus_release_resource(dev, SYS_RES_DRQ, 1, sb->drq2);
417101704Smjacob		}
418101704Smjacob		sb->drq2 = 0;
419101704Smjacob    	}
420101704Smjacob     	if (sb->drq1) {
421101704Smjacob		isa_dma_release(rman_get_start(sb->drq1));
422101704Smjacob		bus_release_resource(dev, SYS_RES_DRQ, 0, sb->drq1);
423103914Smjacob		sb->drq1 = 0;
424101704Smjacob    	}
425101704Smjacob   	if (sb->io_base) {
426101704Smjacob		bus_release_resource(dev, SYS_RES_IOPORT, 0, sb->io_base);
427101704Smjacob		sb->io_base = 0;
428101704Smjacob    	}
429101704Smjacob    	if (sb->parent_dmat) {
430101704Smjacob		bus_dma_tag_destroy(sb->parent_dmat);
431101704Smjacob		sb->parent_dmat = 0;
432101704Smjacob    	}
433101704Smjacob     	free(sb, M_DEVBUF);
434101704Smjacob}
435101704Smjacob
436101704Smjacobstatic int
437101704Smjacobsb16_alloc_resources(struct sb_info *sb, device_t dev)
438102199Smjacob{
439101704Smjacob	int rid;
440101704Smjacob
441101704Smjacob	rid = 0;
442101704Smjacob	if (!sb->io_base)
443101704Smjacob    		sb->io_base = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0, ~0, 1, RF_ACTIVE);
444101704Smjacob
445101704Smjacob	rid = 0;
446101704Smjacob	if (!sb->irq)
447101704Smjacob    		sb->irq = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0, ~0, 1, RF_ACTIVE);
448101704Smjacob
449101704Smjacob	rid = 0;
450101704Smjacob	if (!sb->drq1)
451101704Smjacob    		sb->drq1 = bus_alloc_resource(dev, SYS_RES_DRQ, &rid, 0, ~0, 1, RF_ACTIVE);
452101704Smjacob
453102199Smjacob	rid = 1;
454102199Smjacob	if (!sb->drq2)
455102199Smjacob        	sb->drq2 = bus_alloc_resource(dev, SYS_RES_DRQ, &rid, 0, ~0, 1, RF_ACTIVE);
456102199Smjacob
457102199Smjacob    	if (sb->io_base && sb->drq1 && sb->irq) {
458102199Smjacob		isa_dma_acquire(rman_get_start(sb->drq1));
459102199Smjacob		isa_dmainit(rman_get_start(sb->drq1), sb->bufsize);
460102199Smjacob
461102199Smjacob		if (sb->drq2) {
462102199Smjacob			isa_dma_acquire(rman_get_start(sb->drq2));
463102199Smjacob			isa_dmainit(rman_get_start(sb->drq2), sb->bufsize);
464102199Smjacob		} else {
465102199Smjacob			sb->drq2 = sb->drq1;
466102199Smjacob			pcm_setflags(dev, pcm_getflags(dev) | SD_F_SIMPLEX);
467102199Smjacob		}
468102199Smjacob		return 0;
469102199Smjacob	} else return ENXIO;
470101704Smjacob}
471101704Smjacob
472101704Smjacob/* sbc does locking for us */
473101704Smjacobstatic void
474101704Smjacobsb_intr(void *arg)
475101704Smjacob{
476101704Smjacob    	struct sb_info *sb = (struct sb_info *)arg;
477102199Smjacob    	int reason = 3, c;
478101704Smjacob
479101704Smjacob    	/*
480101704Smjacob     	 * The Vibra16X has separate flags for 8 and 16 bit transfers, but
481101704Smjacob     	 * I have no idea how to tell capture from playback interrupts...
482101704Smjacob     	 */
483101704Smjacob
484101704Smjacob	reason = 0;
485101704Smjacob	sb_lock(sb);
486101704Smjacob    	c = sb_getmixer(sb, IRQ_STAT);
487101704Smjacob    	if (c & 1)
488101704Smjacob		sb_rd(sb, DSP_DATA_AVAIL); /* 8-bit int ack */
489101704Smjacob
490101704Smjacob    	if (c & 2)
491101704Smjacob		sb_rd(sb, DSP_DATA_AVL16); /* 16-bit int ack */
492101704Smjacob	sb_unlock(sb);
493101704Smjacob
494101704Smjacob	/*
495101704Smjacob	 * this tells us if the source is 8-bit or 16-bit dma. We
496101704Smjacob     	 * have to check the io channel to map it to read or write...
497101704Smjacob     	 */
498101704Smjacob
499101704Smjacob	if (sb->bd_flags & BD_F_SB16X) {
500101704Smjacob    		if (c & 1) { /* 8-bit format */
501101704Smjacob			if (sb->pch.fmt & AFMT_8BIT)
502101704Smjacob				reason |= 1;
503102199Smjacob			if (sb->rch.fmt & AFMT_8BIT)
504102199Smjacob				reason |= 2;
505102199Smjacob    		}
506102199Smjacob    		if (c & 2) { /* 16-bit format */
507102199Smjacob			if (sb->pch.fmt & AFMT_16BIT)
508101704Smjacob				reason |= 1;
509115778Smjacob			if (sb->rch.fmt & AFMT_16BIT)
510102199Smjacob				reason |= 2;
511102199Smjacob    		}
512102199Smjacob	} else {
513115778Smjacob    		if (c & 1) { /* 8-bit dma */
514101704Smjacob			if (sb->pch.dch == 1)
515101704Smjacob				reason |= 1;
516101704Smjacob			if (sb->rch.dch == 1)
517102199Smjacob				reason |= 2;
518102199Smjacob    		}
519102199Smjacob    		if (c & 2) { /* 16-bit dma */
520102199Smjacob			if (sb->pch.dch == 2)
521102199Smjacob				reason |= 1;
522102199Smjacob			if (sb->rch.dch == 2)
523102199Smjacob				reason |= 2;
524102199Smjacob    		}
525102199Smjacob	}
526102199Smjacob#if 0
527102199Smjacob    	printf("sb_intr: reason=%d c=0x%x\n", reason, c);
528102199Smjacob#endif
529102199Smjacob    	if ((reason & 1) && (sb->pch.run))
530102199Smjacob		chn_intr(sb->pch.channel);
531102199Smjacob
532102199Smjacob    	if ((reason & 2) && (sb->rch.run))
533102199Smjacob		chn_intr(sb->rch.channel);
534102199Smjacob}
535102199Smjacob
536102199Smjacobstatic int
537102199Smjacobsb_setup(struct sb_info *sb)
538102199Smjacob{
539102199Smjacob	struct sb_chinfo *ch;
540102199Smjacob	u_int8_t v;
541102199Smjacob	int l, pprio;
542103914Smjacob
543102199Smjacob	sb_lock(sb);
544102199Smjacob	if (sb->bd_flags & BD_F_DMARUN)
545102199Smjacob		sndbuf_dma(sb->pch.buffer, PCMTRIG_STOP);
546102199Smjacob	if (sb->bd_flags & BD_F_DMARUN2)
547102199Smjacob		sndbuf_dma(sb->rch.buffer, PCMTRIG_STOP);
548102199Smjacob	sb->bd_flags &= ~(BD_F_DMARUN | BD_F_DMARUN2);
549103914Smjacob
550102199Smjacob	sb_reset_dsp(sb);
551103914Smjacob
552102199Smjacob	if (sb->bd_flags & BD_F_SB16X) {
553102199Smjacob		pprio = sb->pch.run? 1 : 0;
554115778Smjacob		sndbuf_dmasetup(sb->pch.buffer, pprio? sb->drq1 : NULL);
555102199Smjacob		sb->pch.dch = pprio? 1 : 0;
556102199Smjacob		sndbuf_dmasetup(sb->rch.buffer, pprio? sb->drq2 : sb->drq1);
557102199Smjacob		sb->rch.dch = pprio? 2 : 1;
558102199Smjacob	} else {
559102199Smjacob		if (sb->pch.run && sb->rch.run) {
560103827Smjacob			pprio = (sb->rch.fmt & AFMT_16BIT)? 0 : 1;
561102199Smjacob			sndbuf_dmasetup(sb->pch.buffer, pprio? sb->drq2 : sb->drq1);
562102822Smjacob			sb->pch.dch = pprio? 2 : 1;
563115778Smjacob			sndbuf_dmasetup(sb->rch.buffer, pprio? sb->drq1 : sb->drq2);
564102199Smjacob			sb->rch.dch = pprio? 1 : 2;
565102199Smjacob		} else {
566102199Smjacob			if (sb->pch.run) {
567102199Smjacob				sndbuf_dmasetup(sb->pch.buffer, (sb->pch.fmt & AFMT_16BIT)? sb->drq2 : sb->drq1);
568102199Smjacob				sb->pch.dch = (sb->pch.fmt & AFMT_16BIT)? 2 : 1;
569102199Smjacob				sndbuf_dmasetup(sb->rch.buffer, (sb->pch.fmt & AFMT_16BIT)? sb->drq1 : sb->drq2);
570102199Smjacob				sb->rch.dch = (sb->pch.fmt & AFMT_16BIT)? 1 : 2;
571102199Smjacob			} else if (sb->rch.run) {
572102199Smjacob				sndbuf_dmasetup(sb->pch.buffer, (sb->rch.fmt & AFMT_16BIT)? sb->drq1 : sb->drq2);
573102199Smjacob				sb->pch.dch = (sb->rch.fmt & AFMT_16BIT)? 1 : 2;
574102199Smjacob				sndbuf_dmasetup(sb->rch.buffer, (sb->rch.fmt & AFMT_16BIT)? sb->drq2 : sb->drq1);
575103827Smjacob				sb->rch.dch = (sb->rch.fmt & AFMT_16BIT)? 2 : 1;
576102199Smjacob			}
577102199Smjacob		}
578102199Smjacob	}
579103914Smjacob
580102199Smjacob	sndbuf_dmasetdir(sb->pch.buffer, PCMDIR_PLAY);
581102199Smjacob	sndbuf_dmasetdir(sb->rch.buffer, PCMDIR_REC);
582102199Smjacob
583102199Smjacob	/*
584102199Smjacob	printf("setup: [pch = %d, pfmt = %d, pgo = %d] [rch = %d, rfmt = %d, rgo = %d]\n",
585102199Smjacob	       sb->pch.dch, sb->pch.fmt, sb->pch.run, sb->rch.dch, sb->rch.fmt, sb->rch.run);
586102199Smjacob	*/
587102199Smjacob
588102199Smjacob	ch = &sb->pch;
589102199Smjacob	if (ch->run) {
590102199Smjacob		l = ch->blksz;
591102199Smjacob		if (ch->fmt & AFMT_16BIT)
592102199Smjacob			l >>= 1;
593102199Smjacob		l--;
594102199Smjacob
595102199Smjacob		/* play speed */
596102199Smjacob		RANGE(ch->spd, 5000, 45000);
597102199Smjacob		sb_cmd(sb, DSP_CMD_OUT16);
598103914Smjacob    		sb_cmd(sb, ch->spd >> 8);
599102199Smjacob		sb_cmd(sb, ch->spd & 0xff);
600102199Smjacob
601102199Smjacob		/* play format, length */
602102199Smjacob		v = DSP_F16_AUTO | DSP_F16_FIFO_ON | DSP_F16_DAC;
603102199Smjacob		v |= (ch->fmt & AFMT_16BIT)? DSP_DMA16 : DSP_DMA8;
604102199Smjacob		sb_cmd(sb, v);
605103914Smjacob
606102199Smjacob		v = (ch->fmt & AFMT_STEREO)? DSP_F16_STEREO : 0;
607103914Smjacob		v |= (ch->fmt & AFMT_SIGNED)? DSP_F16_SIGNED : 0;
608102199Smjacob		sb_cmd2(sb, v, l);
609102199Smjacob		sndbuf_dma(ch->buffer, PCMTRIG_START);
610102199Smjacob		sb->bd_flags |= BD_F_DMARUN;
611102199Smjacob	}
612102199Smjacob
613102199Smjacob	ch = &sb->rch;
614102199Smjacob	if (ch->run) {
615115778Smjacob		l = ch->blksz;
616102199Smjacob		if (ch->fmt & AFMT_16BIT)
617102199Smjacob			l >>= 1;
618115778Smjacob		l--;
619102199Smjacob
620102199Smjacob		/* record speed */
621115778Smjacob		RANGE(ch->spd, 5000, 45000);
622102199Smjacob		sb_cmd(sb, DSP_CMD_IN16);
623102199Smjacob    		sb_cmd(sb, ch->spd >> 8);
624115778Smjacob		sb_cmd(sb, ch->spd & 0xff);
625102199Smjacob
626102199Smjacob		/* record format, length */
627115778Smjacob		v = DSP_F16_AUTO | DSP_F16_FIFO_ON | DSP_F16_ADC;
628102199Smjacob		v |= (ch->fmt & AFMT_16BIT)? DSP_DMA16 : DSP_DMA8;
629102199Smjacob		sb_cmd(sb, v);
630102199Smjacob
631102199Smjacob		v = (ch->fmt & AFMT_STEREO)? DSP_F16_STEREO : 0;
632102199Smjacob		v |= (ch->fmt & AFMT_SIGNED)? DSP_F16_SIGNED : 0;
633102199Smjacob		sb_cmd2(sb, v, l);
634102822Smjacob		sndbuf_dma(ch->buffer, PCMTRIG_START);
635115778Smjacob		sb->bd_flags |= BD_F_DMARUN2;
636102199Smjacob	}
637102199Smjacob	sb_unlock(sb);
638102199Smjacob
639102199Smjacob    	return 0;
640102199Smjacob}
641102199Smjacob
642102199Smjacob/* channel interface */
643102199Smjacobstatic void *
644102199Smjacobsb16chan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel *c, int dir)
645102199Smjacob{
646102199Smjacob	struct sb_info *sb = devinfo;
647102199Smjacob	struct sb_chinfo *ch = (dir == PCMDIR_PLAY)? &sb->pch : &sb->rch;
648102199Smjacob
649102199Smjacob	ch->parent = sb;
650102199Smjacob	ch->channel = c;
651102199Smjacob	ch->buffer = b;
652103914Smjacob	ch->dir = dir;
653102199Smjacob
654102199Smjacob	if (sndbuf_alloc(ch->buffer, sb->parent_dmat, sb->bufsize) == -1)
655102199Smjacob		return NULL;
656102199Smjacob
657102199Smjacob	return ch;
658102199Smjacob}
659102199Smjacob
660102199Smjacobstatic int
661103914Smjacobsb16chan_setformat(kobj_t obj, void *data, u_int32_t format)
662102199Smjacob{
663102199Smjacob	struct sb_chinfo *ch = data;
664102199Smjacob	struct sb_info *sb = ch->parent;
665102199Smjacob
666102199Smjacob	ch->fmt = format;
667102199Smjacob	sb->prio = ch->dir;
668102199Smjacob	sb->prio16 = (ch->fmt & AFMT_16BIT)? 1 : 0;
669102199Smjacob
670102199Smjacob	return 0;
671102199Smjacob}
672102199Smjacob
673102199Smjacobstatic int
674102199Smjacobsb16chan_setspeed(kobj_t obj, void *data, u_int32_t speed)
675115778Smjacob{
676102199Smjacob	struct sb_chinfo *ch = data;
677102199Smjacob
678115778Smjacob	ch->spd = speed;
679102199Smjacob	return speed;
680102199Smjacob}
681115778Smjacob
682102199Smjacobstatic int
683102199Smjacobsb16chan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize)
684115778Smjacob{
685102199Smjacob	struct sb_chinfo *ch = data;
686102199Smjacob
687115778Smjacob	ch->blksz = blocksize;
688102199Smjacob	return ch->blksz;
689102199Smjacob}
690103914Smjacob
691103914Smjacobstatic int
692102199Smjacobsb16chan_trigger(kobj_t obj, void *data, int go)
693102199Smjacob{
694102199Smjacob	struct sb_chinfo *ch = data;
695102199Smjacob	struct sb_info *sb = ch->parent;
696102199Smjacob
697102199Smjacob	if (go == PCMTRIG_EMLDMAWR || go == PCMTRIG_EMLDMARD)
698102199Smjacob		return 0;
699102199Smjacob
700102199Smjacob	if (go == PCMTRIG_START)
701103914Smjacob		ch->run = 1;
702102199Smjacob	else
703102199Smjacob		ch->run = 0;
704102199Smjacob
705102199Smjacob	sb_setup(sb);
706102199Smjacob
707102199Smjacob	return 0;
708103914Smjacob}
709102199Smjacob
710103914Smjacobstatic int
711102199Smjacobsb16chan_getptr(kobj_t obj, void *data)
712102199Smjacob{
713102199Smjacob	struct sb_chinfo *ch = data;
714102199Smjacob
715102199Smjacob	return sndbuf_dmaptr(ch->buffer);
716102199Smjacob}
717102199Smjacob
718102199Smjacobstatic struct pcmchan_caps *
719102199Smjacobsb16chan_getcaps(kobj_t obj, void *data)
720102199Smjacob{
721102199Smjacob	struct sb_chinfo *ch = data;
722102199Smjacob	struct sb_info *sb = ch->parent;
723102199Smjacob
724102199Smjacob	if ((sb->prio == 0) || (sb->prio == ch->dir))
725102199Smjacob		return &sb16x_caps;
726102199Smjacob	else
727102199Smjacob		return sb->prio16? &sb16_caps8 : &sb16_caps16;
728102199Smjacob}
729102199Smjacob
730102199Smjacobstatic int
731102199Smjacobsb16chan_resetdone(kobj_t obj, void *data)
732102199Smjacob{
733103914Smjacob	struct sb_chinfo *ch = data;
734102199Smjacob	struct sb_info *sb = ch->parent;
735102199Smjacob
736102199Smjacob	sb->prio = 0;
737102199Smjacob
738102199Smjacob	return 0;
739102199Smjacob}
740102199Smjacob
741102199Smjacobstatic kobj_method_t sb16chan_methods[] = {
742102199Smjacob    	KOBJMETHOD(channel_init,		sb16chan_init),
743102199Smjacob    	KOBJMETHOD(channel_resetdone,		sb16chan_resetdone),
744102199Smjacob    	KOBJMETHOD(channel_setformat,		sb16chan_setformat),
745102199Smjacob    	KOBJMETHOD(channel_setspeed,		sb16chan_setspeed),
746103914Smjacob    	KOBJMETHOD(channel_setblocksize,	sb16chan_setblocksize),
747102199Smjacob    	KOBJMETHOD(channel_trigger,		sb16chan_trigger),
748102199Smjacob    	KOBJMETHOD(channel_getptr,		sb16chan_getptr),
749102199Smjacob    	KOBJMETHOD(channel_getcaps,		sb16chan_getcaps),
750102199Smjacob	{ 0, 0 }
751102199Smjacob};
752102199SmjacobCHANNEL_DECLARE(sb16chan);
753102199Smjacob
754102199Smjacob/************************************************************/
755102199Smjacob
756102199Smjacobstatic int
757102199Smjacobsb16_probe(device_t dev)
758102199Smjacob{
759102199Smjacob    	char buf[64];
760103914Smjacob	uintptr_t func, ver, r, f;
761102199Smjacob
762102199Smjacob	/* The parent device has already been probed. */
763102199Smjacob	r = BUS_READ_IVAR(device_get_parent(dev), dev, 0, &func);
764102199Smjacob	if (func != SCF_PCM)
765102199Smjacob		return (ENXIO);
766102199Smjacob
767102199Smjacob	r = BUS_READ_IVAR(device_get_parent(dev), dev, 1, &ver);
768102199Smjacob	f = (ver & 0xffff0000) >> 16;
769102199Smjacob	ver &= 0x0000ffff;
770102199Smjacob	if (f & BD_F_SB16) {
771102199Smjacob		snprintf(buf, sizeof buf, "SB16 DSP %d.%02d%s", (int) ver >> 8, (int) ver & 0xff,
772102199Smjacob			 (f & BD_F_SB16X)? " (ViBRA16X)" : "");
773102199Smjacob    		device_set_desc_copy(dev, buf);
774103914Smjacob		return 0;
775103914Smjacob	} else
776102199Smjacob		return (ENXIO);
777102199Smjacob}
778102199Smjacob
779102199Smjacobstatic int
780102199Smjacobsb16_attach(device_t dev)
781102199Smjacob{
782102199Smjacob    	struct sb_info *sb;
783102199Smjacob	uintptr_t ver;
784102199Smjacob    	char status[SND_STATUSLEN], status2[SND_STATUSLEN];
785102199Smjacob
786102199Smjacob    	sb = (struct sb_info *)malloc(sizeof *sb, M_DEVBUF, M_NOWAIT | M_ZERO);
787102199Smjacob    	if (!sb)
788103914Smjacob		return ENXIO;
789103914Smjacob
790102199Smjacob	sb->parent_dev = device_get_parent(dev);
791102199Smjacob	BUS_READ_IVAR(sb->parent_dev, dev, 1, &ver);
792102199Smjacob	sb->bd_id = ver & 0x0000ffff;
793102199Smjacob	sb->bd_flags = (ver & 0xffff0000) >> 16;
794102199Smjacob	sb->bufsize = pcm_getbuffersize(dev, 4096, SB16_BUFFSIZE, 65536);
795102199Smjacob
796102199Smjacob    	if (sb16_alloc_resources(sb, dev))
797102199Smjacob		goto no;
798102199Smjacob    	if (sb_reset_dsp(sb))
799102199Smjacob		goto no;
800102199Smjacob	if (mixer_init(dev, &sb16mix_mixer_class, sb))
801102199Smjacob		goto no;
802102199Smjacob	if (snd_setup_intr(dev, sb->irq, INTR_MPSAFE, sb_intr, sb, &sb->ih))
803102199Smjacob		goto no;
804102199Smjacob
805103914Smjacob	if (sb->bd_flags & BD_F_SB16X)
806102199Smjacob		pcm_setflags(dev, pcm_getflags(dev) | SD_F_SIMPLEX);
807103914Smjacob
808103914Smjacob	sb->prio = 0;
809102199Smjacob
810102199Smjacob    	if (bus_dma_tag_create(/*parent*/NULL, /*alignment*/2, /*boundary*/0,
811102199Smjacob			/*lowaddr*/BUS_SPACE_MAXADDR_24BIT,
812102199Smjacob			/*highaddr*/BUS_SPACE_MAXADDR,
813102199Smjacob			/*filter*/NULL, /*filterarg*/NULL,
814102199Smjacob			/*maxsize*/sb->bufsize, /*nsegments*/1,
815103914Smjacob			/*maxsegz*/0x3ffff, /*flags*/0,
816102199Smjacob			/*lockfunc*/busdma_lock_mutex, /*lockarg*/&Giant,
817103914Smjacob			&sb->parent_dmat) != 0) {
818103914Smjacob		device_printf(dev, "unable to create dma tag\n");
819102199Smjacob		goto no;
820102199Smjacob    	}
821102199Smjacob
822102199Smjacob    	if (!(pcm_getflags(dev) & SD_F_SIMPLEX))
823102199Smjacob		snprintf(status2, SND_STATUSLEN, ":%ld", rman_get_start(sb->drq2));
824102199Smjacob	else
825103914Smjacob		status2[0] = '\0';
826102199Smjacob
827103914Smjacob    	snprintf(status, SND_STATUSLEN, "at io 0x%lx irq %ld drq %ld%s bufsz %ud",
828103914Smjacob    	     	rman_get_start(sb->io_base), rman_get_start(sb->irq),
829102199Smjacob		rman_get_start(sb->drq1), status2, sb->bufsize);
830102199Smjacob
831102199Smjacob    	if (pcm_register(dev, sb, 1, 1))
832103914Smjacob		goto no;
833103914Smjacob	pcm_addchan(dev, PCMDIR_REC, &sb16chan_class, sb);
834102199Smjacob	pcm_addchan(dev, PCMDIR_PLAY, &sb16chan_class, sb);
835102199Smjacob
836102199Smjacob    	pcm_setstatus(dev, status);
837102199Smjacob
838102199Smjacob    	return 0;
839102199Smjacob
840102199Smjacobno:
841102199Smjacob    	sb16_release_resources(sb, dev);
842102199Smjacob    	return ENXIO;
843103914Smjacob}
844102199Smjacob
845102199Smjacobstatic int
846102199Smjacobsb16_detach(device_t dev)
847103914Smjacob{
848103914Smjacob	int r;
849102199Smjacob	struct sb_info *sb;
850102199Smjacob
851102199Smjacob	r = pcm_unregister(dev);
852102199Smjacob	if (r)
853102199Smjacob		return r;
854103914Smjacob
855102199Smjacob	sb = pcm_getdevinfo(dev);
856102199Smjacob    	sb16_release_resources(sb, dev);
857102199Smjacob	return 0;
858103914Smjacob}
859103914Smjacob
860102199Smjacobstatic device_method_t sb16_methods[] = {
861102199Smjacob	/* Device interface */
862102199Smjacob	DEVMETHOD(device_probe,		sb16_probe),
863102199Smjacob	DEVMETHOD(device_attach,	sb16_attach),
864102199Smjacob	DEVMETHOD(device_detach,	sb16_detach),
865102199Smjacob
866102199Smjacob	{ 0, 0 }
867102199Smjacob};
868102199Smjacob
869102199Smjacobstatic driver_t sb16_driver = {
870102199Smjacob	"pcm",
871102199Smjacob	sb16_methods,
872102199Smjacob	PCM_SOFTC_SIZE,
873102199Smjacob};
874102199Smjacob
875102199SmjacobDRIVER_MODULE(snd_sb16, sbc, sb16_driver, pcm_devclass, 0, 0);
876102199SmjacobMODULE_DEPEND(snd_sb16, snd_pcm, PCM_MINVER, PCM_PREFVER, PCM_MAXVER);
877102822SmjacobMODULE_DEPEND(snd_sb16, snd_sbc, 1, 1, 1);
878102822SmjacobMODULE_VERSION(snd_sb16, 1);
879102822Smjacob