ad1816.c revision 53465
1193326Sed/*
2193326Sed * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk>
3193326Sed * Copyright Luigi Rizzo, 1997,1998
4193326Sed * Copyright by Hannu Savolainen 1994, 1995
5193326Sed * All rights reserved.
6193326Sed *
7193326Sed * Redistribution and use in source and binary forms, with or without
8193326Sed * modification, are permitted provided that the following conditions
9193326Sed * are met:
10193326Sed * 1. Redistributions of source code must retain the above copyright
11193326Sed *    notice, this list of conditions and the following disclaimer.
12193326Sed * 2. Redistributions in binary form must reproduce the above copyright
13193326Sed *    notice, this list of conditions and the following disclaimer in the
14193326Sed *    documentation and/or other materials provided with the distribution.
15205219Srdivacky *
16193326Sed * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17252723Sdim * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18218893Sdim * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19226890Sdim * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20252723Sdim * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21252723Sdim * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22193326Sed * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23193326Sed * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24252723Sdim * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25198092Srdivacky * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26193326Sed * SUCH DAMAGE.
27252723Sdim *
28205219Srdivacky * $FreeBSD: head/sys/dev/sound/isa/ad1816.c 53465 1999-11-20 16:50:33Z cg $
29218893Sdim */
30205219Srdivacky
31193326Sed#include <dev/sound/pcm/sound.h>
32193326Sed
33193326Sed#if NPCM > 0 && NPNP > 0
34193326Sed
35193326Sed#include <dev/sound/isa/ad1816.h>
36193326Sed
37193326Sedstruct ad1816_info;
38193326Sed
39193326Sedstruct ad1816_chinfo {
40212904Sdim	struct ad1816_info *parent;
41212904Sdim	pcm_channel *channel;
42193326Sed	snd_dbuf *buffer;
43193326Sed	int dir;
44226890Sdim};
45226890Sdim
46193326Sedstruct ad1816_info {
47205408Srdivacky    struct resource *io_base;	/* primary I/O address for the board */
48193326Sed    int		     io_rid;
49193326Sed    struct resource *irq;
50221345Sdim    int		     irq_rid;
51221345Sdim    struct resource *drq1; /* play */
52221345Sdim    int		     drq1_rid;
53221345Sdim    struct resource *drq2; /* rec */
54221345Sdim    int		     drq2_rid;
55221345Sdim    bus_dma_tag_t    parent_dmat;
56221345Sdim
57221345Sdim    struct ad1816_chinfo pch, rch;
58221345Sdim};
59221345Sdim
60221345Sdimstatic driver_intr_t 	ad1816_intr;
61221345Sdimstatic int 		ad1816_probe(device_t dev);
62221345Sdimstatic int 		ad1816_attach(device_t dev);
63193326Sed
64193326Sed/* IO primitives */
65193326Sedstatic int      	ad1816_wait_init(struct ad1816_info *ad1816, int x);
66200583Srdivackystatic u_short		ad1816_read(struct ad1816_info *ad1816, u_int reg);
67193326Sedstatic void     	ad1816_write(struct ad1816_info *ad1816, u_int reg, u_short data);
68205408Srdivacky
69221345Sdimstatic int ad1816mix_init(snd_mixer *m);
70193326Sedstatic int ad1816mix_set(snd_mixer *m, unsigned dev, unsigned left, unsigned right);
71193326Sedstatic int ad1816mix_setrecsrc(snd_mixer *m, u_int32_t src);
72212904Sdimstatic snd_mixer ad1816_mixer = {
73212904Sdim    "ad1816 mixer",
74245431Sdim    ad1816mix_init,
75235633Sdim    ad1816mix_set,
76235633Sdim    ad1816mix_setrecsrc,
77235633Sdim};
78235633Sdim
79200583Srdivackystatic devclass_t pcm_devclass;
80212904Sdim
81212904Sdim/* channel interface */
82205408Srdivackystatic void *ad1816chan_init(void *devinfo, snd_dbuf *b, pcm_channel *c, int dir);
83212904Sdimstatic int ad1816chan_setdir(void *data, int dir);
84200583Srdivackystatic int ad1816chan_setformat(void *data, u_int32_t format);
85200583Srdivackystatic int ad1816chan_setspeed(void *data, u_int32_t speed);
86226890Sdimstatic int ad1816chan_setblocksize(void *data, u_int32_t blocksize);
87207619Srdivackystatic int ad1816chan_trigger(void *data, int go);
88207619Srdivackystatic int ad1816chan_getptr(void *data);
89205219Srdivackystatic pcmchan_caps *ad1816chan_getcaps(void *data);
90218893Sdim
91221345Sdimstatic pcmchan_caps ad1816_caps = {
92221345Sdim	4000, 55200,
93218893Sdim	AFMT_STEREO | AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW,
94218893Sdim	AFMT_STEREO | AFMT_S16_LE
95205408Srdivacky};
96218893Sdim
97218893Sdimstatic pcm_channel ad1816_chantemplate = {
98206084Srdivacky	ad1816chan_init,
99218893Sdim	ad1816chan_setdir,
100245431Sdim	ad1816chan_setformat,
101245431Sdim	ad1816chan_setspeed,
102245431Sdim	ad1816chan_setblocksize,
103245431Sdim	ad1816chan_trigger,
104206084Srdivacky	ad1816chan_getptr,
105218893Sdim	ad1816chan_getcaps,
106218893Sdim};
107218893Sdim
108218893Sdim#define FULL_DUPLEX(x) (pcm_getflags(x) & SD_F_SIMPLEX)
109218893Sdim#define AD1816_MUTE 31		/* value for mute */
110218893Sdim
111218893Sdimstatic int
112218893Sdimport_rd(struct resource *port, int off)
113218893Sdim{
114218893Sdim	if (port)
115218893Sdim		return bus_space_read_1(rman_get_bustag(port),
116226890Sdim					rman_get_bushandle(port),
117221345Sdim					off);
118218893Sdim	else
119218893Sdim		return -1;
120221345Sdim}
121218893Sdim
122207619Srdivackystatic void
123218893Sdimport_wr(struct resource *port, int off, u_int8_t data)
124218893Sdim{
125221345Sdim	if (port)
126218893Sdim		return bus_space_write_1(rman_get_bustag(port),
127218893Sdim					 rman_get_bushandle(port),
128221345Sdim					 off, data);
129206084Srdivacky}
130218893Sdim
131207619Srdivackystatic int
132218893Sdimio_rd(struct ad1816_info *ad1816, int reg)
133218893Sdim{
134198092Srdivacky	return port_rd(ad1816->io_base, reg);
135205219Srdivacky}
136218893Sdim
137218893Sdimstatic void
138221345Sdimio_wr(struct ad1816_info *ad1816, int reg, u_int8_t data)
139218893Sdim{
140218893Sdim	return port_wr(ad1816->io_base, reg, data);
141221345Sdim}
142218893Sdim
143218893Sdimstatic void
144221345Sdimad1816_intr(void *arg)
145218893Sdim{
146218893Sdim    	struct ad1816_info *ad1816 = (struct ad1816_info *)arg;
147218893Sdim    	unsigned char   c, served = 0;
148218893Sdim
149218893Sdim    	/* get interupt status */
150221345Sdim    	c = io_rd(ad1816, AD1816_INT);
151218893Sdim
152221345Sdim    	/* check for stray interupts */
153218893Sdim    	if (c & ~(AD1816_INTRCI | AD1816_INTRPI)) {
154226890Sdim		printf("pcm: stray int (%x)\n", c);
155221345Sdim		c &= AD1816_INTRCI | AD1816_INTRPI;
156218893Sdim    	}
157218893Sdim    	/* check for capture interupt */
158218893Sdim    	if (ad1816->rch.buffer->dl && (c & AD1816_INTRCI)) {
159218893Sdim		chn_intr(ad1816->rch.channel);
160218893Sdim		served |= AD1816_INTRCI;		/* cp served */
161218893Sdim    	}
162218893Sdim    	/* check for playback interupt */
163218893Sdim    	if (ad1816->pch.buffer->dl && (c & AD1816_INTRPI)) {
164218893Sdim		chn_intr(ad1816->pch.channel);
165218893Sdim		served |= AD1816_INTRPI;		/* pb served */
166218893Sdim    	}
167218893Sdim    	if (served == 0) {
168221345Sdim		/* this probably means this is not a (working) ad1816 chip, */
169218893Sdim		/* or an error in dma handling                              */
170221345Sdim		printf("pcm: int without reason (%x)\n", c);
171218893Sdim		c = 0;
172218893Sdim    	} else c &= ~served;
173218893Sdim    	io_wr(ad1816, AD1816_INT, c);
174205408Srdivacky    	c = io_rd(ad1816, AD1816_INT);
175212904Sdim    	if (c != 0) printf("pcm: int clear failed (%x)\n", c);
176205408Srdivacky}
177205408Srdivacky
178198092Srdivackystatic int
179198092Srdivackyad1816_wait_init(struct ad1816_info *ad1816, int x)
180226890Sdim{
181193326Sed    	int             n = 0;	/* to shut up the compiler... */
182193326Sed
183198092Srdivacky    	for (; x--;)
184224145Sdim		if ((n = (io_rd(ad1816, AD1816_ALE) & AD1816_BUSY)) == 0) DELAY(10);
185193326Sed		else return n;
186193326Sed    	printf("ad1816_wait_init failed 0x%02x.\n", n);
187198092Srdivacky    	return -1;
188193326Sed}
189193326Sed
190193326Sedstatic unsigned short
191193326Sedad1816_read(struct ad1816_info *ad1816, unsigned int reg)
192193326Sed{
193193326Sed    	int             flags;
194193326Sed    	u_short         x = 0;
195245431Sdim
196193326Sed    	/* we don't want to be blocked here */
197245431Sdim    	flags = spltty();
198193326Sed    	if (ad1816_wait_init(ad1816, 100) == -1) return 0;
199193326Sed    	io_wr(ad1816, AD1816_ALE, 0);
200198092Srdivacky    	io_wr(ad1816, AD1816_ALE, (reg & AD1816_ALEMASK));
201193326Sed    	if (ad1816_wait_init(ad1816, 100) == -1) return 0;
202193326Sed    	x = (io_rd(ad1816, AD1816_HIGH) << 8) | io_rd(ad1816, AD1816_LOW);
203198092Srdivacky    	splx(flags);
204193326Sed    	return x;
205193326Sed}
206198092Srdivacky
207193326Sedstatic void
208193326Sedad1816_write(struct ad1816_info *ad1816, unsigned int reg, unsigned short data)
209193326Sed{
210193326Sed    	int             flags;
211193326Sed
212198092Srdivacky    	flags = spltty();
213193326Sed    	if (ad1816_wait_init(ad1816, 100) == -1) return;
214193326Sed    	io_wr(ad1816, AD1816_ALE, (reg & AD1816_ALEMASK));
215193326Sed    	io_wr(ad1816, AD1816_LOW,  (data & 0x000000ff));
216193326Sed    	io_wr(ad1816, AD1816_HIGH, (data & 0x0000ff00) >> 8);
217193326Sed    	splx(flags);
218198092Srdivacky}
219193326Sed
220193326Sedstatic int
221193326Sedad1816mix_init(snd_mixer *m)
222193326Sed{
223193326Sed	mix_setdevs(m, AD1816_MIXER_DEVICES);
224193326Sed	mix_setrecdevs(m, AD1816_REC_DEVICES);
225245431Sdim	return 0;
226193326Sed}
227193326Sed
228245431Sdimstatic int
229193326Sedad1816mix_set(snd_mixer *m, unsigned dev, unsigned left, unsigned right)
230193326Sed{
231193326Sed	struct ad1816_info *ad1816 = mix_getdevinfo(m);
232193326Sed    	u_short reg = 0;
233198092Srdivacky
234193326Sed    	/* Scale volumes */
235198092Srdivacky    	left = AD1816_MUTE - (AD1816_MUTE * left) / 100;
236193326Sed    	right = AD1816_MUTE - (AD1816_MUTE * right) / 100;
237193326Sed
238193326Sed    	reg = (left << 8) | right;
239193326Sed
240193326Sed    	/* do channel selective muting if volume is zero */
241193326Sed    	if (left == AD1816_MUTE)	reg |= 0x8000;
242193326Sed    	if (right == AD1816_MUTE)	reg |= 0x0080;
243193326Sed
244193326Sed    	switch (dev) {
245193326Sed    	case SOUND_MIXER_VOLUME:	/* Register 14 master volume */
246193326Sed		ad1816_write(ad1816, 14, reg);
247198092Srdivacky		break;
248193326Sed
249193326Sed    	case SOUND_MIXER_CD:	/* Register 15 cd */
250193326Sed    	case SOUND_MIXER_LINE1:
251193326Sed		ad1816_write(ad1816, 15, reg);
252193326Sed		break;
253193326Sed
254198092Srdivacky    	case SOUND_MIXER_SYNTH:	/* Register 16 synth */
255193326Sed		ad1816_write(ad1816, 16, reg);
256193326Sed		break;
257193326Sed
258193326Sed    	case SOUND_MIXER_PCM:	/* Register 4 pcm */
259193326Sed		ad1816_write(ad1816, 4, reg);
260193326Sed		break;
261193326Sed
262245431Sdim    	case SOUND_MIXER_LINE:
263193326Sed    	case SOUND_MIXER_LINE3:	/* Register 18 line in */
264193326Sed		ad1816_write(ad1816, 18, reg);
265193326Sed		break;
266193326Sed
267193326Sed    	case SOUND_MIXER_MIC:	/* Register 19 mic volume */
268193326Sed		ad1816_write(ad1816, 19, reg & ~0xff);	/* mic is mono */
269193326Sed		break;
270193326Sed
271193326Sed    	case SOUND_MIXER_IGAIN:
272193326Sed		/* and now to something completely different ... */
273193326Sed		ad1816_write(ad1816, 20, ((ad1816_read(ad1816, 20) & ~0x0f0f)
274193326Sed	      	| (((AD1816_MUTE - left) / 2) << 8) /* four bits of adc gain */
275193326Sed	      	| ((AD1816_MUTE - right) / 2)));
276193326Sed		break;
277193326Sed
278193326Sed    	default:
279193326Sed		printf("ad1816_mixer_set(): unknown device.\n");
280193326Sed		break;
281245431Sdim    	}
282193326Sed
283193326Sed    	return left | (right << 8);
284193326Sed}
285193326Sed
286193326Sedstatic int
287198092Srdivackyad1816mix_setrecsrc(snd_mixer *m, u_int32_t src)
288226890Sdim{
289193326Sed	struct ad1816_info *ad1816 = mix_getdevinfo(m);
290193326Sed    	int dev;
291224145Sdim
292193326Sed    	switch (src) {
293193326Sed    	case SOUND_MASK_LINE:
294193326Sed    	case SOUND_MASK_LINE3:
295193326Sed		dev = 0x00;
296193326Sed		break;
297193326Sed
298193326Sed    	case SOUND_MASK_CD:
299193326Sed    	case SOUND_MASK_LINE1:
300226890Sdim		dev = 0x20;
301198092Srdivacky		break;
302221345Sdim
303221345Sdim    	case SOUND_MASK_MIC:
304221345Sdim    	default:
305221345Sdim		dev = 0x50;
306221345Sdim		src = SOUND_MASK_MIC;
307221345Sdim    	}
308193326Sed
309193326Sed    	dev |= dev << 8;
310193326Sed    	ad1816_write(ad1816, 20, (ad1816_read(ad1816, 20) & ~0x7070) | dev);
311198092Srdivacky    	return src;
312193326Sed}
313193326Sed
314245431Sdim/* channel interface */
315193326Sedstatic void *
316193326Sedad1816chan_init(void *devinfo, snd_dbuf *b, pcm_channel *c, int dir)
317193326Sed{
318193326Sed	struct ad1816_info *ad1816 = devinfo;
319193326Sed	struct ad1816_chinfo *ch = (dir == PCMDIR_PLAY)? &ad1816->pch : &ad1816->rch;
320193326Sed
321193326Sed	ch->parent = ad1816;
322193326Sed	ch->channel = c;
323193326Sed	ch->buffer = b;
324193326Sed	ch->buffer->bufsize = DSP_BUFFSIZE;
325193326Sed	if (chn_allocbuf(ch->buffer, ad1816->parent_dmat) == -1) return NULL;
326193326Sed	return ch;
327193326Sed}
328193326Sed
329198092Srdivackystatic int
330226890Sdimad1816chan_setdir(void *data, int dir)
331198092Srdivacky{
332221345Sdim	struct ad1816_chinfo *ch = data;
333221345Sdim  	struct ad1816_info *ad1816 = ch->parent;
334221345Sdim
335221345Sdim	ch->buffer->chan = rman_get_start((dir == PCMDIR_PLAY)?
336221345Sdim		ad1816->drq1 : ad1816->drq2);
337221345Sdim	ch->dir = dir;
338221345Sdim	return 0;
339193326Sed}
340193326Sed
341198092Srdivackystatic int
342193326Sedad1816chan_setformat(void *data, u_int32_t format)
343193326Sed{
344198092Srdivacky	struct ad1816_chinfo *ch = data;
345193326Sed  	struct ad1816_info *ad1816 = ch->parent;
346193326Sed
347193326Sed    	int fmt = AD1816_U8, reg;
348193326Sed    	if (ch->dir == PCMDIR_PLAY) {
349193326Sed        	reg = AD1816_PLAY;
350193326Sed        	ad1816_write(ad1816, 8, 0x0000);	/* reset base and current counter */
351193326Sed        	ad1816_write(ad1816, 9, 0x0000);	/* for playback and capture */
352198092Srdivacky    	} else {
353193326Sed        	reg = AD1816_CAPT;
354193326Sed        	ad1816_write(ad1816, 10, 0x0000);
355193326Sed        	ad1816_write(ad1816, 11, 0x0000);
356193326Sed    	}
357193326Sed    	switch (format & ~AFMT_STEREO) {
358198092Srdivacky    	case AFMT_A_LAW:
359245431Sdim        	fmt = AD1816_ALAW;
360193326Sed		break;
361193326Sed
362193326Sed    	case AFMT_MU_LAW:
363193326Sed		fmt = AD1816_MULAW;
364193326Sed		break;
365193326Sed
366193326Sed    	case AFMT_S16_LE:
367193326Sed		fmt = AD1816_S16LE;
368193326Sed		break;
369193326Sed
370193326Sed    	case AFMT_S16_BE:
371193326Sed		fmt = AD1816_S16BE;
372193326Sed		break;
373245431Sdim
374245431Sdim    	case AFMT_U8:
375221345Sdim		fmt = AD1816_U8;
376245431Sdim		break;
377218893Sdim    	}
378235633Sdim    	if (format & AFMT_STEREO) fmt |= AD1816_STEREO;
379235633Sdim    	io_wr(ad1816, reg, fmt);
380218893Sdim    	return format;
381218893Sdim}
382218893Sdim
383218893Sdimstatic int
384193326Sedad1816chan_setspeed(void *data, u_int32_t speed)
385193326Sed{
386198092Srdivacky	struct ad1816_chinfo *ch = data;
387193326Sed    	struct ad1816_info *ad1816 = ch->parent;
388193326Sed
389193326Sed    	RANGE(speed, 4000, 55200);
390193326Sed    	ad1816_write(ad1816, (ch->dir == PCMDIR_PLAY)? 2 : 3, speed);
391235633Sdim    	return speed;
392235633Sdim}
393235633Sdim
394235633Sdimstatic int
395193326Sedad1816chan_setblocksize(void *data, u_int32_t blocksize)
396193326Sed{
397193326Sed	return blocksize;
398235633Sdim}
399235633Sdim
400235633Sdimstatic int
401235633Sdimad1816chan_trigger(void *data, int go)
402193326Sed{
403221345Sdim	struct ad1816_chinfo *ch = data;
404221345Sdim    	struct ad1816_info *ad1816 = ch->parent;
405235633Sdim    	int wr, reg;
406226890Sdim
407226890Sdim	buf_isadma(ch->buffer, go);
408226890Sdim    	wr = (ch->dir == PCMDIR_PLAY);
409226890Sdim    	reg = wr? AD1816_PLAY : AD1816_CAPT;
410226890Sdim    	switch (go) {
411193326Sed    	case PCMTRIG_START:
412193326Sed		/* start only if not already running */
413193326Sed		if (!(io_rd(ad1816, reg) & AD1816_ENABLE)) {
414193326Sed	    		int cnt = ((ch->buffer->dl) >> 2) - 1;
415226890Sdim	    		ad1816_write(ad1816, wr? 8 : 10, cnt); /* count */
416226890Sdim	    		ad1816_write(ad1816, 1, ad1816_read(ad1816, 1) |
417226890Sdim				     (wr? 0x8000 : 0x4000)); /* enable int */
418193326Sed	    		/* enable playback */
419193326Sed	    		io_wr(ad1816, reg, io_rd(ad1816, reg) | AD1816_ENABLE);
420193326Sed	    		if (!(io_rd(ad1816, reg) & AD1816_ENABLE))
421198092Srdivacky				printf("ad1816: failed to start %s DMA!\n",
422193326Sed				       wr? "play" : "rec");
423193326Sed		}
424198092Srdivacky		break;
425226890Sdim
426226890Sdim    	case PCMTRIG_STOP:
427226890Sdim    	case PCMTRIG_ABORT:		/* XXX check this... */
428226890Sdim		/* we don't test here if it is running... */
429193326Sed		if (wr) {
430193326Sed	    		ad1816_write(ad1816, 1, ad1816_read(ad1816, 1) &
431193326Sed				     ~(wr? 0x8000 : 0x4000));
432193326Sed	    		/* disable int */
433193326Sed	    		io_wr(ad1816, reg, io_rd(ad1816, reg) & ~AD1816_ENABLE);
434245431Sdim	    		/* disable playback */
435245431Sdim	    		if (io_rd(ad1816, reg) & AD1816_ENABLE)
436193326Sed				printf("ad1816: failed to stop %s DMA!\n",
437198092Srdivacky				       wr? "play" : "rec");
438193326Sed	    		ad1816_write(ad1816, wr? 8 : 10, 0); /* reset base cnt */
439193326Sed	    		ad1816_write(ad1816, wr? 9 : 11, 0); /* reset cur cnt */
440193326Sed		}
441198092Srdivacky		break;
442193326Sed    	}
443193326Sed    	return 0;
444193326Sed}
445193326Sed
446193326Sedstatic int
447193326Sedad1816chan_getptr(void *data)
448221345Sdim{
449245431Sdim	struct ad1816_chinfo *ch = data;
450245431Sdim	return buf_isadmaptr(ch->buffer);
451245431Sdim}
452245431Sdim
453245431Sdimstatic pcmchan_caps *
454245431Sdimad1816chan_getcaps(void *data)
455245431Sdim{
456245431Sdim	return &ad1816_caps;
457245431Sdim}
458245431Sdim
459245431Sdimstatic void
460245431Sdimad1816_release_resources(struct ad1816_info *ad1816, device_t dev)
461221345Sdim{
462245431Sdim    	if (ad1816->irq) {
463221345Sdim		bus_release_resource(dev, SYS_RES_IRQ, ad1816->irq_rid,
464245431Sdim				     ad1816->irq);
465245431Sdim		ad1816->irq = 0;
466193326Sed    	}
467193326Sed    	if (ad1816->drq1) {
468193326Sed		bus_release_resource(dev, SYS_RES_DRQ, ad1816->drq1_rid,
469193326Sed				     ad1816->drq1);
470193326Sed		ad1816->drq1 = 0;
471193326Sed    	}
472193326Sed    	if (ad1816->drq2) {
473193326Sed		bus_release_resource(dev, SYS_RES_DRQ, ad1816->drq2_rid,
474193326Sed				     ad1816->drq2);
475193326Sed		ad1816->drq2 = 0;
476193326Sed    	}
477193326Sed    	if (ad1816->io_base) {
478193326Sed		bus_release_resource(dev, SYS_RES_IOPORT, ad1816->io_rid,
479193326Sed				     ad1816->io_base);
480193326Sed		ad1816->io_base = 0;
481193326Sed    	}
482193326Sed    	free(ad1816, M_DEVBUF);
483193326Sed}
484193326Sed
485193326Sedstatic int
486235633Sdimad1816_alloc_resources(struct ad1816_info *ad1816, device_t dev)
487235633Sdim{
488235633Sdim    	int ok = 1, pdma, rdma;
489235633Sdim	if (!ad1816->io_base)
490235633Sdim    		ad1816->io_base = bus_alloc_resource(dev, SYS_RES_IOPORT, &ad1816->io_rid,
491235633Sdim						  0, ~0, 1, RF_ACTIVE);
492235633Sdim	if (!ad1816->irq)
493235633Sdim    		ad1816->irq = bus_alloc_resource(dev, SYS_RES_IRQ, &ad1816->irq_rid,
494235633Sdim					      0, ~0, 1, RF_ACTIVE);
495235633Sdim	if (!ad1816->drq1)
496235633Sdim    		ad1816->drq1 = bus_alloc_resource(dev, SYS_RES_DRQ, &ad1816->drq1_rid,
497235633Sdim					       0, ~0, 1, RF_ACTIVE);
498235633Sdim    	if (ad1816->drq2_rid >= 0 && !ad1816->drq2)
499235633Sdim        	ad1816->drq2 = bus_alloc_resource(dev, SYS_RES_DRQ, &ad1816->drq2_rid,
500235633Sdim					       0, ~0, 1, RF_ACTIVE);
501235633Sdim
502235633Sdim    	if (!ad1816->io_base || !ad1816->drq1 || !ad1816->irq) ok = 0;
503235633Sdim    	if (ad1816->drq2_rid >= 0 && !ad1816->drq2) ok = 0;
504235633Sdim
505226890Sdim	if (ok) {
506226890Sdim		pdma = rman_get_start(ad1816->drq1);
507226890Sdim		isa_dma_acquire(pdma);
508226890Sdim		isa_dmainit(pdma, DSP_BUFFSIZE);
509226890Sdim		if (ad1816->drq2) {
510226890Sdim			rdma = rman_get_start(ad1816->drq2);
511226890Sdim			isa_dma_acquire(rdma);
512226890Sdim			isa_dmainit(rdma, DSP_BUFFSIZE);
513226890Sdim		} else rdma = pdma;
514226890Sdim    		if (pdma == rdma)
515193326Sed			pcm_setflags(dev, pcm_getflags(dev) | SD_F_SIMPLEX);
516193326Sed	}
517221345Sdim    	return ok;
518221345Sdim}
519221345Sdim
520221345Sdimstatic int
521221345Sdimad1816_init(struct ad1816_info *ad1816, device_t dev)
522221345Sdim{
523221345Sdim    	ad1816_write(ad1816, 1, 0x2);	/* disable interrupts */
524221345Sdim    	ad1816_write(ad1816, 32, 0x90F0);	/* SoundSys Mode, split fmt */
525221345Sdim
526193326Sed    	ad1816_write(ad1816, 5, 0x8080);	/* FM volume mute */
527235633Sdim    	ad1816_write(ad1816, 6, 0x8080);	/* I2S1 volume mute */
528235633Sdim    	ad1816_write(ad1816, 7, 0x8080);	/* I2S0 volume mute */
529235633Sdim    	ad1816_write(ad1816, 17, 0x8888);	/* VID Volume mute */
530235633Sdim    	ad1816_write(ad1816, 20, 0x5050);	/* recsrc mic, agc off */
531235633Sdim    	/* adc gain is set to 0 */
532235633Sdim
533235633Sdim	return 0;
534235633Sdim}
535235633Sdim
536235633Sdimstatic int
537235633Sdimad1816_probe(device_t dev)
538235633Sdim{
539263509Sdim    	char *s = NULL;
540263509Sdim    	u_int32_t logical_id = isa_get_logicalid(dev);
541263509Sdim
542263509Sdim    	switch (logical_id) {
543263509Sdim    	case 0x80719304: /* ADS7180 */
544263509Sdim 		s = "Terratec Soundsystem BASE 1";
545263509Sdim 		break;
546263509Sdim    	}
547263509Sdim
548263509Sdim    	if (s) {
549263509Sdim		device_set_desc(dev, s);
550263509Sdim		return 0;
551263509Sdim    	}
552263509Sdim    	return ENXIO;
553263509Sdim}
554263509Sdim
555263509Sdimstatic int
556263509Sdimad1816_attach(device_t dev)
557263509Sdim{
558263509Sdim	struct ad1816_info *ad1816;
559263509Sdim    	snddev_info *d = device_get_softc(dev);
560263509Sdim    	void *ih;
561263509Sdim    	char status[SND_STATUSLEN];
562263509Sdim
563263509Sdim	ad1816 = (struct ad1816_info *)malloc(sizeof *ad1816, M_DEVBUF, M_NOWAIT);
564263509Sdim	if (!ad1816) return ENXIO;
565263509Sdim	bzero(ad1816, sizeof *ad1816);
566263509Sdim
567263509Sdim	ad1816->io_rid = 2;
568263509Sdim	ad1816->irq_rid = 0;
569263509Sdim	ad1816->drq1_rid = 0;
570263509Sdim	ad1816->drq2_rid = 1;
571263509Sdim
572263509Sdim    	if (!ad1816_alloc_resources(ad1816, dev)) goto no;
573263509Sdim    	ad1816_init(ad1816, dev);
574263509Sdim    	mixer_init(d, &ad1816_mixer, ad1816);
575263509Sdim	bus_setup_intr(dev, ad1816->irq, INTR_TYPE_TTY, ad1816_intr, ad1816, &ih);
576193326Sed    	if (bus_dma_tag_create(/*parent*/NULL, /*alignment*/2, /*boundary*/0,
577226890Sdim			/*lowaddr*/BUS_SPACE_MAXADDR_24BIT,
578193326Sed			/*highaddr*/BUS_SPACE_MAXADDR,
579193326Sed			/*filter*/NULL, /*filterarg*/NULL,
580212904Sdim			/*maxsize*/DSP_BUFFSIZE, /*nsegments*/1,
581193326Sed			/*maxsegz*/0x3ffff,
582193326Sed			/*flags*/0, &ad1816->parent_dmat) != 0) {
583193326Sed		device_printf(dev, "unable to create dma tag\n");
584193326Sed		goto no;
585193326Sed    	}
586226890Sdim    	snprintf(status, SND_STATUSLEN, "at io 0x%lx irq %ld drq %ld",
587226890Sdim    	     	rman_get_start(ad1816->io_base),
588226890Sdim		rman_get_start(ad1816->irq),
589226890Sdim		rman_get_start(ad1816->drq1));
590226890Sdim    	if (FULL_DUPLEX(dev)) snprintf(status + strlen(status),
591226890Sdim        	SND_STATUSLEN - strlen(status), ":%ld",
592226890Sdim		rman_get_start(ad1816->drq1));
593226890Sdim
594226890Sdim    	if (pcm_register(dev, ad1816, 1, 1)) goto no;
595226890Sdim    	pcm_addchan(dev, PCMDIR_REC, &ad1816_chantemplate, ad1816);
596193326Sed    	pcm_addchan(dev, PCMDIR_PLAY, &ad1816_chantemplate, ad1816);
597226890Sdim    	pcm_setstatus(dev, status);
598226890Sdim
599226890Sdim    	return 0;
600193326Sedno:
601226890Sdim    	ad1816_release_resources(ad1816, dev);
602226890Sdim    	return ENXIO;
603226890Sdim
604226890Sdim}
605226890Sdim
606226890Sdimstatic device_method_t ad1816_methods[] = {
607198092Srdivacky	/* Device interface */
608193326Sed	DEVMETHOD(device_probe,		ad1816_probe),
609193326Sed	DEVMETHOD(device_attach,	ad1816_attach),
610226890Sdim
611194711Sed	{ 0, 0 }
612193326Sed};
613193326Sed
614224145Sdimstatic driver_t ad1816_driver = {
615226890Sdim	"pcm",
616226890Sdim	ad1816_methods,
617226890Sdim	sizeof(snddev_info),
618226890Sdim};
619226890Sdim
620226890SdimDRIVER_MODULE(ad1816, isa, ad1816_driver, pcm_devclass, 0, 0);
621224145Sdim
622224145Sdim#endif
623226890Sdim