ad1816.c revision 166426
1130617Smlaier/*-
2126353Smlaier * Copyright (c) 1999 Cameron Grant <cg@freebsd.org>
3126353Smlaier * Copyright (c) 1997,1998 Luigi Rizzo
4126353Smlaier * Copyright (c) 1994,1995 Hannu Savolainen
5126353Smlaier * All rights reserved.
6126353Smlaier *
7126353Smlaier * Redistribution and use in source and binary forms, with or without
8126353Smlaier * modification, are permitted provided that the following conditions
9126353Smlaier * are met:
10126353Smlaier * 1. Redistributions of source code must retain the above copyright
11126353Smlaier *    notice, this list of conditions and the following disclaimer.
12126353Smlaier * 2. Redistributions in binary form must reproduce the above copyright
13126353Smlaier *    notice, this list of conditions and the following disclaimer in the
14126353Smlaier *    documentation and/or other materials provided with the distribution.
15126353Smlaier *
16126353Smlaier * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17126353Smlaier * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18126353Smlaier * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19126353Smlaier * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20126353Smlaier * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21126353Smlaier * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22126353Smlaier * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23126353Smlaier * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24126353Smlaier * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25126353Smlaier * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26126353Smlaier * SUCH DAMAGE.
27126353Smlaier */
28126353Smlaier
29126353Smlaier#include <dev/sound/pcm/sound.h>
30126353Smlaier#include <dev/sound/isa/ad1816.h>
31126353Smlaier
32126353Smlaier#include <isa/isavar.h>
33127082Sobrien
34127082Sobrien#include "mixer_if.h"
35127082Sobrien
36126353SmlaierSND_DECLARE_FILE("$FreeBSD: head/sys/dev/sound/isa/ad1816.c 166426 2007-02-02 13:39:20Z joel $");
37130617Smlaier
38126353Smlaierstruct ad1816_info;
39126353Smlaier
40126353Smlaierstruct ad1816_chinfo {
41126353Smlaier	struct ad1816_info *parent;
42126353Smlaier	struct pcm_channel *channel;
43126353Smlaier	struct snd_dbuf *buffer;
44126353Smlaier	int dir, blksz;
45126353Smlaier};
46126353Smlaier
47126353Smlaierstruct ad1816_info {
48126353Smlaier	struct resource *io_base;	/* primary I/O address for the board */
49126353Smlaier	int io_rid;
50126353Smlaier	struct resource *irq;
51127024Smlaier	int irq_rid;
52126355Smlaier	struct resource *drq1;		/* play */
53126355Smlaier	int drq1_rid;
54126353Smlaier	struct resource *drq2;		/* rec */
55126355Smlaier	int drq2_rid;
56126353Smlaier	void *ih;
57130617Smlaier	bus_dma_tag_t parent_dmat;
58126353Smlaier	struct mtx *lock;
59126353Smlaier
60130617Smlaier	unsigned int bufsize;
61126353Smlaier	struct ad1816_chinfo pch, rch;
62126353Smlaier};
63130617Smlaier
64130617Smlaierstatic u_int32_t ad1816_fmt[] = {
65126353Smlaier	AFMT_U8,
66126353Smlaier	AFMT_STEREO | AFMT_U8,
67126353Smlaier	AFMT_S16_LE,
68126353Smlaier	AFMT_STEREO | AFMT_S16_LE,
69126353Smlaier	AFMT_MU_LAW,
70126353Smlaier	AFMT_STEREO | AFMT_MU_LAW,
71126353Smlaier	AFMT_A_LAW,
72126353Smlaier	AFMT_STEREO | AFMT_A_LAW,
73126353Smlaier	0
74126353Smlaier};
75126353Smlaier
76126353Smlaierstatic struct pcmchan_caps ad1816_caps = {4000, 55200, ad1816_fmt, 0};
77130617Smlaier
78130617Smlaier#define AD1816_MUTE 31		/* value for mute */
79130617Smlaier
80130617Smlaierstatic void
81126353Smlaierad1816_lock(struct ad1816_info *ad1816)
82130617Smlaier{
83130617Smlaier	snd_mtxlock(ad1816->lock);
84126353Smlaier}
85130617Smlaier
86130617Smlaierstatic void
87130617Smlaierad1816_unlock(struct ad1816_info *ad1816)
88126353Smlaier{
89126353Smlaier	snd_mtxunlock(ad1816->lock);
90126353Smlaier}
91126353Smlaier
92126353Smlaierstatic int
93130617Smlaierport_rd(struct resource *port, int off)
94130617Smlaier{
95130617Smlaier	if (port)
96130617Smlaier		return bus_space_read_1(rman_get_bustag(port),
97130617Smlaier					rman_get_bushandle(port),
98130617Smlaier					off);
99126353Smlaier	else
100130617Smlaier		return -1;
101130617Smlaier}
102130617Smlaier
103130617Smlaierstatic void
104130617Smlaierport_wr(struct resource *port, int off, u_int8_t data)
105130617Smlaier{
106130617Smlaier	if (port)
107130617Smlaier		bus_space_write_1(rman_get_bustag(port),
108130617Smlaier				  rman_get_bushandle(port),
109130617Smlaier				  off, data);
110130617Smlaier}
111130617Smlaier
112130617Smlaierstatic int
113130617Smlaierio_rd(struct ad1816_info *ad1816, int reg)
114130617Smlaier{
115126353Smlaier	return port_rd(ad1816->io_base, reg);
116126353Smlaier}
117126353Smlaier
118126353Smlaierstatic void
119126353Smlaierio_wr(struct ad1816_info *ad1816, int reg, u_int8_t data)
120126353Smlaier{
121126353Smlaier	port_wr(ad1816->io_base, reg, data);
122126353Smlaier}
123126353Smlaier
124126353Smlaierstatic void
125126353Smlaierad1816_intr(void *arg)
126126353Smlaier{
127126353Smlaier    	struct ad1816_info *ad1816 = (struct ad1816_info *)arg;
128126353Smlaier    	unsigned char   c, served = 0;
129126353Smlaier
130126353Smlaier	ad1816_lock(ad1816);
131126353Smlaier    	/* get interupt status */
132126353Smlaier    	c = io_rd(ad1816, AD1816_INT);
133126353Smlaier
134126353Smlaier    	/* check for stray interupts */
135126353Smlaier    	if (c & ~(AD1816_INTRCI | AD1816_INTRPI)) {
136126353Smlaier		printf("pcm: stray int (%x)\n", c);
137126353Smlaier		c &= AD1816_INTRCI | AD1816_INTRPI;
138126353Smlaier    	}
139126353Smlaier    	/* check for capture interupt */
140126353Smlaier    	if (sndbuf_runsz(ad1816->rch.buffer) && (c & AD1816_INTRCI)) {
141126353Smlaier		ad1816_unlock(ad1816);
142126353Smlaier		chn_intr(ad1816->rch.channel);
143126353Smlaier		ad1816_lock(ad1816);
144126353Smlaier		served |= AD1816_INTRCI;		/* cp served */
145126353Smlaier    	}
146126353Smlaier    	/* check for playback interupt */
147126353Smlaier    	if (sndbuf_runsz(ad1816->pch.buffer) && (c & AD1816_INTRPI)) {
148126353Smlaier		ad1816_unlock(ad1816);
149126353Smlaier		chn_intr(ad1816->pch.channel);
150126353Smlaier		ad1816_lock(ad1816);
151126353Smlaier		served |= AD1816_INTRPI;		/* pb served */
152126353Smlaier    	}
153126353Smlaier    	if (served == 0) {
154126353Smlaier		/* this probably means this is not a (working) ad1816 chip, */
155127024Smlaier		/* or an error in dma handling                              */
156127024Smlaier		printf("pcm: int without reason (%x)\n", c);
157127024Smlaier		c = 0;
158126353Smlaier    	} else c &= ~served;
159127024Smlaier    	io_wr(ad1816, AD1816_INT, c);
160126353Smlaier    	c = io_rd(ad1816, AD1816_INT);
161126353Smlaier    	if (c != 0) printf("pcm: int clear failed (%x)\n", c);
162130617Smlaier	ad1816_unlock(ad1816);
163126353Smlaier}
164126353Smlaier
165126353Smlaierstatic int
166126353Smlaierad1816_wait_init(struct ad1816_info *ad1816, int x)
167126353Smlaier{
168126353Smlaier    	int             n = 0;	/* to shut up the compiler... */
169126353Smlaier
170126353Smlaier    	for (; x--;)
171126353Smlaier		if ((n = (io_rd(ad1816, AD1816_ALE) & AD1816_BUSY)) == 0) DELAY(10);
172126353Smlaier		else return n;
173126353Smlaier    	printf("ad1816_wait_init failed 0x%02x.\n", n);
174126353Smlaier    	return -1;
175126353Smlaier}
176126353Smlaier
177126353Smlaierstatic unsigned short
178126353Smlaierad1816_read(struct ad1816_info *ad1816, unsigned int reg)
179126353Smlaier{
180126353Smlaier    	u_short         x = 0;
181126353Smlaier
182126353Smlaier    	if (ad1816_wait_init(ad1816, 100) == -1) return 0;
183126353Smlaier    	io_wr(ad1816, AD1816_ALE, 0);
184126353Smlaier    	io_wr(ad1816, AD1816_ALE, (reg & AD1816_ALEMASK));
185130617Smlaier    	if (ad1816_wait_init(ad1816, 100) == -1) return 0;
186130617Smlaier    	x = (io_rd(ad1816, AD1816_HIGH) << 8) | io_rd(ad1816, AD1816_LOW);
187130617Smlaier    	return x;
188130617Smlaier}
189130617Smlaier
190130617Smlaierstatic void
191130617Smlaierad1816_write(struct ad1816_info *ad1816, unsigned int reg, unsigned short data)
192130617Smlaier{
193130617Smlaier    	if (ad1816_wait_init(ad1816, 100) == -1) return;
194130617Smlaier    	io_wr(ad1816, AD1816_ALE, (reg & AD1816_ALEMASK));
195130617Smlaier    	io_wr(ad1816, AD1816_LOW,  (data & 0x000000ff));
196130617Smlaier    	io_wr(ad1816, AD1816_HIGH, (data & 0x0000ff00) >> 8);
197130617Smlaier}
198130617Smlaier
199126353Smlaier/* -------------------------------------------------------------------- */
200126353Smlaier
201126353Smlaierstatic int
202126353Smlaierad1816mix_init(struct snd_mixer *m)
203126353Smlaier{
204126353Smlaier	mix_setdevs(m, AD1816_MIXER_DEVICES);
205126353Smlaier	mix_setrecdevs(m, AD1816_REC_DEVICES);
206126353Smlaier	return 0;
207126353Smlaier}
208126353Smlaier
209126353Smlaierstatic int
210126353Smlaierad1816mix_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right)
211130617Smlaier{
212126353Smlaier	struct ad1816_info *ad1816 = mix_getdevinfo(m);
213126353Smlaier    	u_short reg = 0;
214126353Smlaier
215130617Smlaier    	/* Scale volumes */
216126353Smlaier    	left = AD1816_MUTE - (AD1816_MUTE * left) / 100;
217130617Smlaier    	right = AD1816_MUTE - (AD1816_MUTE * right) / 100;
218130617Smlaier
219130617Smlaier    	reg = (left << 8) | right;
220130617Smlaier
221130617Smlaier    	/* do channel selective muting if volume is zero */
222130617Smlaier    	if (left == AD1816_MUTE)	reg |= 0x8000;
223130617Smlaier    	if (right == AD1816_MUTE)	reg |= 0x0080;
224130617Smlaier
225130617Smlaier	ad1816_lock(ad1816);
226130617Smlaier    	switch (dev) {
227130617Smlaier    	case SOUND_MIXER_VOLUME:	/* Register 14 master volume */
228130617Smlaier		ad1816_write(ad1816, 14, reg);
229126353Smlaier		break;
230126353Smlaier
231126353Smlaier    	case SOUND_MIXER_CD:	/* Register 15 cd */
232126353Smlaier    	case SOUND_MIXER_LINE1:
233130617Smlaier		ad1816_write(ad1816, 15, reg);
234130617Smlaier		break;
235130617Smlaier
236130617Smlaier    	case SOUND_MIXER_SYNTH:	/* Register 16 synth */
237130617Smlaier		ad1816_write(ad1816, 16, reg);
238130617Smlaier		break;
239130617Smlaier
240130617Smlaier    	case SOUND_MIXER_PCM:	/* Register 4 pcm */
241130617Smlaier		ad1816_write(ad1816, 4, reg);
242130617Smlaier		break;
243130617Smlaier
244130617Smlaier    	case SOUND_MIXER_LINE:
245130617Smlaier    	case SOUND_MIXER_LINE3:	/* Register 18 line in */
246130617Smlaier		ad1816_write(ad1816, 18, reg);
247126353Smlaier		break;
248126353Smlaier
249126353Smlaier    	case SOUND_MIXER_MIC:	/* Register 19 mic volume */
250126353Smlaier		ad1816_write(ad1816, 19, reg & ~0xff);	/* mic is mono */
251130617Smlaier		break;
252126353Smlaier
253126353Smlaier    	case SOUND_MIXER_IGAIN:
254126353Smlaier		/* and now to something completely different ... */
255130617Smlaier		ad1816_write(ad1816, 20, ((ad1816_read(ad1816, 20) & ~0x0f0f)
256130617Smlaier	      	| (((AD1816_MUTE - left) / 2) << 8) /* four bits of adc gain */
257126353Smlaier	      	| ((AD1816_MUTE - right) / 2)));
258130617Smlaier		break;
259130617Smlaier
260130617Smlaier    	default:
261126353Smlaier		printf("ad1816_mixer_set(): unknown device.\n");
262126353Smlaier		break;
263126353Smlaier    	}
264126353Smlaier	ad1816_unlock(ad1816);
265126353Smlaier
266126353Smlaier    	left = ((AD1816_MUTE - left) * 100) / AD1816_MUTE;
267130617Smlaier    	right = ((AD1816_MUTE - right) * 100) / AD1816_MUTE;
268130617Smlaier
269130617Smlaier    	return left | (right << 8);
270130617Smlaier}
271130617Smlaier
272130617Smlaierstatic int
273126353Smlaierad1816mix_setrecsrc(struct snd_mixer *m, u_int32_t src)
274130617Smlaier{
275126353Smlaier	struct ad1816_info *ad1816 = mix_getdevinfo(m);
276126353Smlaier    	int dev;
277126353Smlaier
278130617Smlaier    	switch (src) {
279126353Smlaier    	case SOUND_MASK_LINE:
280126353Smlaier    	case SOUND_MASK_LINE3:
281126353Smlaier		dev = 0x00;
282130617Smlaier		break;
283130617Smlaier
284130617Smlaier    	case SOUND_MASK_CD:
285130617Smlaier    	case SOUND_MASK_LINE1:
286130617Smlaier		dev = 0x20;
287126353Smlaier		break;
288126353Smlaier
289126353Smlaier    	case SOUND_MASK_MIC:
290126353Smlaier    	default:
291130617Smlaier		dev = 0x50;
292126353Smlaier		src = SOUND_MASK_MIC;
293130617Smlaier    	}
294130617Smlaier
295130617Smlaier    	dev |= dev << 8;
296126353Smlaier	ad1816_lock(ad1816);
297126353Smlaier    	ad1816_write(ad1816, 20, (ad1816_read(ad1816, 20) & ~0x7070) | dev);
298126353Smlaier	ad1816_unlock(ad1816);
299126353Smlaier    	return src;
300126353Smlaier}
301126353Smlaier
302126353Smlaierstatic kobj_method_t ad1816mixer_methods[] = {
303126353Smlaier    	KOBJMETHOD(mixer_init,		ad1816mix_init),
304126353Smlaier    	KOBJMETHOD(mixer_set,		ad1816mix_set),
305126353Smlaier    	KOBJMETHOD(mixer_setrecsrc,	ad1816mix_setrecsrc),
306126353Smlaier	{ 0, 0 }
307126353Smlaier};
308130617SmlaierMIXER_DECLARE(ad1816mixer);
309126353Smlaier
310130617Smlaier/* -------------------------------------------------------------------- */
311130617Smlaier/* channel interface */
312130617Smlaierstatic void *
313130617Smlaierad1816chan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel *c, int dir)
314126353Smlaier{
315126353Smlaier	struct ad1816_info *ad1816 = devinfo;
316130617Smlaier	struct ad1816_chinfo *ch = (dir == PCMDIR_PLAY)? &ad1816->pch : &ad1816->rch;
317130617Smlaier
318130617Smlaier	ch->parent = ad1816;
319130617Smlaier	ch->channel = c;
320130617Smlaier	ch->buffer = b;
321130617Smlaier	if (sndbuf_alloc(ch->buffer, ad1816->parent_dmat, ad1816->bufsize) != 0)
322130617Smlaier		return NULL;
323130617Smlaier	return ch;
324130617Smlaier}
325130617Smlaier
326130617Smlaierstatic int
327130617Smlaierad1816chan_setdir(kobj_t obj, void *data, int dir)
328134578Smlaier{
329134578Smlaier	struct ad1816_chinfo *ch = data;
330134578Smlaier  	struct ad1816_info *ad1816 = ch->parent;
331130617Smlaier
332134578Smlaier	sndbuf_dmasetup(ch->buffer, (dir == PCMDIR_PLAY)? ad1816->drq1 : ad1816->drq2);
333130617Smlaier	ch->dir = dir;
334130617Smlaier	return 0;
335126353Smlaier}
336130617Smlaier
337126353Smlaierstatic int
338130617Smlaierad1816chan_setformat(kobj_t obj, void *data, u_int32_t format)
339130617Smlaier{
340130617Smlaier	struct ad1816_chinfo *ch = data;
341130617Smlaier  	struct ad1816_info *ad1816 = ch->parent;
342126353Smlaier    	int fmt = AD1816_U8, reg;
343126353Smlaier
344130617Smlaier	ad1816_lock(ad1816);
345130617Smlaier    	if (ch->dir == PCMDIR_PLAY) {
346130617Smlaier        	reg = AD1816_PLAY;
347130617Smlaier        	ad1816_write(ad1816, 8, 0x0000);	/* reset base and current counter */
348130617Smlaier        	ad1816_write(ad1816, 9, 0x0000);	/* for playback and capture */
349130617Smlaier    	} else {
350130617Smlaier        	reg = AD1816_CAPT;
351130617Smlaier        	ad1816_write(ad1816, 10, 0x0000);
352130617Smlaier        	ad1816_write(ad1816, 11, 0x0000);
353130617Smlaier    	}
354130617Smlaier    	switch (format & ~AFMT_STEREO) {
355130617Smlaier    	case AFMT_A_LAW:
356130617Smlaier        	fmt = AD1816_ALAW;
357130617Smlaier		break;
358130617Smlaier
359130617Smlaier    	case AFMT_MU_LAW:
360130617Smlaier		fmt = AD1816_MULAW;
361130617Smlaier		break;
362130617Smlaier
363130617Smlaier    	case AFMT_S16_LE:
364130617Smlaier		fmt = AD1816_S16LE;
365130617Smlaier		break;
366130617Smlaier
367130617Smlaier    	case AFMT_S16_BE:
368130617Smlaier		fmt = AD1816_S16BE;
369130617Smlaier		break;
370130617Smlaier
371130617Smlaier    	case AFMT_U8:
372130617Smlaier		fmt = AD1816_U8;
373130617Smlaier		break;
374130617Smlaier    	}
375130617Smlaier    	if (format & AFMT_STEREO) fmt |= AD1816_STEREO;
376130617Smlaier    	io_wr(ad1816, reg, fmt);
377130617Smlaier	ad1816_unlock(ad1816);
378130617Smlaier#if 0
379130617Smlaier    	return format;
380130617Smlaier#else
381130617Smlaier    	return 0;
382130617Smlaier#endif
383130617Smlaier}
384126353Smlaier
385130617Smlaierstatic int
386130617Smlaierad1816chan_setspeed(kobj_t obj, void *data, u_int32_t speed)
387126353Smlaier{
388126353Smlaier	struct ad1816_chinfo *ch = data;
389126353Smlaier    	struct ad1816_info *ad1816 = ch->parent;
390126353Smlaier
391130617Smlaier    	RANGE(speed, 4000, 55200);
392130617Smlaier	ad1816_lock(ad1816);
393130617Smlaier    	ad1816_write(ad1816, (ch->dir == PCMDIR_PLAY)? 2 : 3, speed);
394130617Smlaier	ad1816_unlock(ad1816);
395126353Smlaier    	return speed;
396126353Smlaier}
397130617Smlaier
398130617Smlaierstatic int
399130617Smlaierad1816chan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize)
400130617Smlaier{
401130617Smlaier	struct ad1816_chinfo *ch = data;
402134578Smlaier
403134578Smlaier	ch->blksz = blocksize;
404134578Smlaier	return ch->blksz;
405130617Smlaier}
406130617Smlaier
407130617Smlaierstatic int
408130617Smlaierad1816chan_trigger(kobj_t obj, void *data, int go)
409130617Smlaier{
410130617Smlaier	struct ad1816_chinfo *ch = data;
411134578Smlaier    	struct ad1816_info *ad1816 = ch->parent;
412134578Smlaier    	int wr, reg;
413134578Smlaier
414134578Smlaier	if (go == PCMTRIG_EMLDMAWR || go == PCMTRIG_EMLDMARD)
415134578Smlaier		return 0;
416134578Smlaier
417134578Smlaier	sndbuf_dma(ch->buffer, go);
418134578Smlaier    	wr = (ch->dir == PCMDIR_PLAY);
419130617Smlaier    	reg = wr? AD1816_PLAY : AD1816_CAPT;
420134578Smlaier	ad1816_lock(ad1816);
421130617Smlaier    	switch (go) {
422130617Smlaier    	case PCMTRIG_START:
423134578Smlaier		/* start only if not already running */
424134578Smlaier		if (!(io_rd(ad1816, reg) & AD1816_ENABLE)) {
425134578Smlaier	    		int cnt = ((ch->blksz) >> 2) - 1;
426134578Smlaier	    		ad1816_write(ad1816, wr? 8 : 10, cnt); /* count */
427130617Smlaier	    		ad1816_write(ad1816, wr? 9 : 11, 0); /* reset cur cnt */
428130617Smlaier	    		ad1816_write(ad1816, 1, ad1816_read(ad1816, 1) |
429134578Smlaier				     (wr? 0x8000 : 0x4000)); /* enable int */
430130617Smlaier	    		/* enable playback */
431130617Smlaier	    		io_wr(ad1816, reg, io_rd(ad1816, reg) | AD1816_ENABLE);
432130617Smlaier	    		if (!(io_rd(ad1816, reg) & AD1816_ENABLE))
433130617Smlaier				printf("ad1816: failed to start %s DMA!\n",
434130617Smlaier				       wr? "play" : "rec");
435130617Smlaier		}
436130617Smlaier		break;
437130617Smlaier
438130617Smlaier    	case PCMTRIG_STOP:
439130617Smlaier    	case PCMTRIG_ABORT:		/* XXX check this... */
440130617Smlaier		/* we don't test here if it is running... */
441130617Smlaier		if (wr) {
442130617Smlaier	    		ad1816_write(ad1816, 1, ad1816_read(ad1816, 1) &
443130617Smlaier				     ~(wr? 0x8000 : 0x4000));
444130617Smlaier	    		/* disable int */
445130617Smlaier	    		io_wr(ad1816, reg, io_rd(ad1816, reg) & ~AD1816_ENABLE);
446130617Smlaier	    		/* disable playback */
447130617Smlaier	    		if (io_rd(ad1816, reg) & AD1816_ENABLE)
448130617Smlaier				printf("ad1816: failed to stop %s DMA!\n",
449126353Smlaier				       wr? "play" : "rec");
450130617Smlaier	    		ad1816_write(ad1816, wr? 8 : 10, 0); /* reset base cnt */
451130617Smlaier	    		ad1816_write(ad1816, wr? 9 : 11, 0); /* reset cur cnt */
452130617Smlaier		}
453130617Smlaier		break;
454130617Smlaier    	}
455130617Smlaier	ad1816_unlock(ad1816);
456130617Smlaier    	return 0;
457130617Smlaier}
458130617Smlaier
459130617Smlaierstatic int
460130617Smlaierad1816chan_getptr(kobj_t obj, void *data)
461130617Smlaier{
462130617Smlaier	struct ad1816_chinfo *ch = data;
463130617Smlaier	return sndbuf_dmaptr(ch->buffer);
464130617Smlaier}
465130617Smlaier
466130617Smlaierstatic struct pcmchan_caps *
467130617Smlaierad1816chan_getcaps(kobj_t obj, void *data)
468130617Smlaier{
469130617Smlaier	return &ad1816_caps;
470130617Smlaier}
471130617Smlaier
472130617Smlaierstatic kobj_method_t ad1816chan_methods[] = {
473130617Smlaier    	KOBJMETHOD(channel_init,		ad1816chan_init),
474130617Smlaier    	KOBJMETHOD(channel_setdir,		ad1816chan_setdir),
475130617Smlaier    	KOBJMETHOD(channel_setformat,		ad1816chan_setformat),
476130617Smlaier    	KOBJMETHOD(channel_setspeed,		ad1816chan_setspeed),
477130617Smlaier    	KOBJMETHOD(channel_setblocksize,	ad1816chan_setblocksize),
478130617Smlaier    	KOBJMETHOD(channel_trigger,		ad1816chan_trigger),
479130617Smlaier    	KOBJMETHOD(channel_getptr,		ad1816chan_getptr),
480130617Smlaier    	KOBJMETHOD(channel_getcaps,		ad1816chan_getcaps),
481130617Smlaier	{ 0, 0 }
482130617Smlaier};
483130617SmlaierCHANNEL_DECLARE(ad1816chan);
484130617Smlaier
485130617Smlaier/* -------------------------------------------------------------------- */
486130617Smlaier
487130617Smlaierstatic void
488130617Smlaierad1816_release_resources(struct ad1816_info *ad1816, device_t dev)
489130617Smlaier{
490130617Smlaier    	if (ad1816->irq) {
491130617Smlaier   		if (ad1816->ih)
492130617Smlaier			bus_teardown_intr(dev, ad1816->irq, ad1816->ih);
493130617Smlaier		bus_release_resource(dev, SYS_RES_IRQ, ad1816->irq_rid, ad1816->irq);
494130617Smlaier		ad1816->irq = 0;
495130617Smlaier    	}
496130617Smlaier    	if (ad1816->drq1) {
497130617Smlaier		isa_dma_release(rman_get_start(ad1816->drq1));
498134578Smlaier		bus_release_resource(dev, SYS_RES_DRQ, ad1816->drq1_rid, ad1816->drq1);
499134578Smlaier		ad1816->drq1 = 0;
500134578Smlaier    	}
501134578Smlaier    	if (ad1816->drq2) {
502130617Smlaier		isa_dma_release(rman_get_start(ad1816->drq2));
503134578Smlaier		bus_release_resource(dev, SYS_RES_DRQ, ad1816->drq2_rid, ad1816->drq2);
504130617Smlaier		ad1816->drq2 = 0;
505130617Smlaier    	}
506130617Smlaier    	if (ad1816->io_base) {
507130617Smlaier		bus_release_resource(dev, SYS_RES_IOPORT, ad1816->io_rid, ad1816->io_base);
508130617Smlaier		ad1816->io_base = 0;
509130617Smlaier    	}
510130617Smlaier    	if (ad1816->parent_dmat) {
511130617Smlaier		bus_dma_tag_destroy(ad1816->parent_dmat);
512130617Smlaier		ad1816->parent_dmat = 0;
513130617Smlaier    	}
514130617Smlaier	if (ad1816->lock)
515130617Smlaier		snd_mtxfree(ad1816->lock);
516130617Smlaier
517130617Smlaier     	free(ad1816, M_DEVBUF);
518130617Smlaier}
519130617Smlaier
520130617Smlaierstatic int
521130617Smlaierad1816_alloc_resources(struct ad1816_info *ad1816, device_t dev)
522130617Smlaier{
523130617Smlaier    	int ok = 1, pdma, rdma;
524130617Smlaier
525130617Smlaier	if (!ad1816->io_base)
526130617Smlaier    		ad1816->io_base = bus_alloc_resource_any(dev,
527130617Smlaier			SYS_RES_IOPORT, &ad1816->io_rid, RF_ACTIVE);
528130617Smlaier	if (!ad1816->irq)
529130617Smlaier    		ad1816->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ,
530130617Smlaier			&ad1816->irq_rid, RF_ACTIVE);
531134578Smlaier	if (!ad1816->drq1)
532134578Smlaier    		ad1816->drq1 = bus_alloc_resource_any(dev, SYS_RES_DRQ,
533134578Smlaier			&ad1816->drq1_rid, RF_ACTIVE);
534134578Smlaier    	if (!ad1816->drq2)
535134578Smlaier        	ad1816->drq2 = bus_alloc_resource_any(dev, SYS_RES_DRQ,
536134578Smlaier			&ad1816->drq2_rid, RF_ACTIVE);
537134578Smlaier
538134578Smlaier    	if (!ad1816->io_base || !ad1816->drq1 || !ad1816->irq) ok = 0;
539134578Smlaier
540130617Smlaier	if (ok) {
541130617Smlaier		pdma = rman_get_start(ad1816->drq1);
542134578Smlaier		isa_dma_acquire(pdma);
543130617Smlaier		isa_dmainit(pdma, ad1816->bufsize);
544130617Smlaier		if (ad1816->drq2) {
545130617Smlaier			rdma = rman_get_start(ad1816->drq2);
546130617Smlaier			isa_dma_acquire(rdma);
547130617Smlaier			isa_dmainit(rdma, ad1816->bufsize);
548130617Smlaier		} else
549130617Smlaier			rdma = pdma;
550130617Smlaier    		if (pdma == rdma)
551130617Smlaier			pcm_setflags(dev, pcm_getflags(dev) | SD_F_SIMPLEX);
552126353Smlaier	}
553126353Smlaier
554126353Smlaier    	return ok;
555130617Smlaier}
556130617Smlaier
557126353Smlaierstatic int
558130617Smlaierad1816_init(struct ad1816_info *ad1816, device_t dev)
559130617Smlaier{
560130617Smlaier    	ad1816_write(ad1816, 1, 0x2);	/* disable interrupts */
561130617Smlaier    	ad1816_write(ad1816, 32, 0x90F0);	/* SoundSys Mode, split fmt */
562130617Smlaier
563130617Smlaier    	ad1816_write(ad1816, 5, 0x8080);	/* FM volume mute */
564130617Smlaier    	ad1816_write(ad1816, 6, 0x8080);	/* I2S1 volume mute */
565126353Smlaier    	ad1816_write(ad1816, 7, 0x8080);	/* I2S0 volume mute */
566126353Smlaier    	ad1816_write(ad1816, 17, 0x8888);	/* VID Volume mute */
567126353Smlaier    	ad1816_write(ad1816, 20, 0x5050);	/* recsrc mic, agc off */
568126353Smlaier    	/* adc gain is set to 0 */
569126353Smlaier
570126353Smlaier	return 0;
571126353Smlaier}
572126353Smlaier
573126353Smlaierstatic int
574126353Smlaierad1816_probe(device_t dev)
575126353Smlaier{
576126353Smlaier    	char *s = NULL;
577126353Smlaier    	u_int32_t logical_id = isa_get_logicalid(dev);
578126353Smlaier
579126353Smlaier    	switch (logical_id) {
580126353Smlaier    	case 0x80719304: /* ADS7180 */
581130617Smlaier 		s = "AD1816";
582130617Smlaier 		break;
583126353Smlaier    	case 0x50719304: /* ADS7150 */
584130617Smlaier 		s = "AD1815";
585130617Smlaier 		break;
586130617Smlaier    	}
587126353Smlaier
588126353Smlaier    	if (s) {
589126353Smlaier		device_set_desc(dev, s);
590126353Smlaier		return BUS_PROBE_DEFAULT;
591126353Smlaier    	}
592126353Smlaier    	return ENXIO;
593126353Smlaier}
594126353Smlaier
595126353Smlaierstatic int
596126353Smlaierad1816_attach(device_t dev)
597126353Smlaier{
598126353Smlaier	struct ad1816_info *ad1816;
599126353Smlaier    	char status[SND_STATUSLEN], status2[SND_STATUSLEN];
600126353Smlaier
601126353Smlaier	ad1816 = (struct ad1816_info *)malloc(sizeof *ad1816, M_DEVBUF, M_NOWAIT | M_ZERO);
602126353Smlaier	if (!ad1816) return ENXIO;
603126353Smlaier
604126353Smlaier	ad1816->lock = snd_mtxcreate(device_get_nameunit(dev), "sound softc");
605126353Smlaier	ad1816->io_rid = 2;
606126353Smlaier	ad1816->irq_rid = 0;
607126353Smlaier	ad1816->drq1_rid = 0;
608130617Smlaier	ad1816->drq2_rid = 1;
609126353Smlaier	ad1816->bufsize = pcm_getbuffersize(dev, 4096, DSP_BUFFSIZE, 65536);
610126353Smlaier
611126353Smlaier    	if (!ad1816_alloc_resources(ad1816, dev)) goto no;
612126353Smlaier    	ad1816_init(ad1816, dev);
613126353Smlaier    	if (mixer_init(dev, &ad1816mixer_class, ad1816)) goto no;
614126353Smlaier
615130617Smlaier	snd_setup_intr(dev, ad1816->irq, 0, ad1816_intr, ad1816, &ad1816->ih);
616126353Smlaier    	if (bus_dma_tag_create(/*parent*/NULL, /*alignment*/2, /*boundary*/0,
617126353Smlaier			/*lowaddr*/BUS_SPACE_MAXADDR_24BIT,
618126353Smlaier			/*highaddr*/BUS_SPACE_MAXADDR,
619126353Smlaier			/*filter*/NULL, /*filterarg*/NULL,
620126353Smlaier			/*maxsize*/ad1816->bufsize, /*nsegments*/1,
621130617Smlaier			/*maxsegz*/0x3ffff,
622130617Smlaier			/*flags*/0, /*lockfunc*/busdma_lock_mutex,
623130617Smlaier			/*lockarg*/ &Giant, &ad1816->parent_dmat) != 0) {
624126353Smlaier		device_printf(dev, "unable to create dma tag\n");
625126353Smlaier		goto no;
626126353Smlaier    	}
627130617Smlaier    	if (ad1816->drq2)
628130617Smlaier		snprintf(status2, SND_STATUSLEN, ":%ld", rman_get_start(ad1816->drq2));
629130617Smlaier	else
630130617Smlaier		status2[0] = '\0';
631130617Smlaier
632130617Smlaier    	snprintf(status, SND_STATUSLEN, "at io 0x%lx irq %ld drq %ld%s bufsz %u %s",
633130617Smlaier    	     	rman_get_start(ad1816->io_base),
634130617Smlaier		rman_get_start(ad1816->irq),
635130617Smlaier		rman_get_start(ad1816->drq1),
636130617Smlaier		status2,
637130617Smlaier		ad1816->bufsize,
638130617Smlaier		PCM_KLDSTRING(snd_ad1816));
639130617Smlaier
640130617Smlaier    	if (pcm_register(dev, ad1816, 1, 1)) goto no;
641130617Smlaier    	pcm_addchan(dev, PCMDIR_REC, &ad1816chan_class, ad1816);
642130617Smlaier    	pcm_addchan(dev, PCMDIR_PLAY, &ad1816chan_class, ad1816);
643130617Smlaier    	pcm_setstatus(dev, status);
644130617Smlaier
645130617Smlaier    	return 0;
646130617Smlaierno:
647130617Smlaier    	ad1816_release_resources(ad1816, dev);
648130617Smlaier
649130617Smlaier    	return ENXIO;
650130617Smlaier
651130617Smlaier}
652130617Smlaier
653130617Smlaierstatic int
654130617Smlaierad1816_detach(device_t dev)
655130617Smlaier{
656126353Smlaier	int r;
657130617Smlaier	struct ad1816_info *ad1816;
658130617Smlaier
659126353Smlaier	r = pcm_unregister(dev);
660126353Smlaier	if (r)
661126353Smlaier		return r;
662126353Smlaier
663126353Smlaier	ad1816 = pcm_getdevinfo(dev);
664126353Smlaier    	ad1816_release_resources(ad1816, dev);
665126353Smlaier	return 0;
666130617Smlaier}
667130617Smlaier
668130617Smlaierstatic device_method_t ad1816_methods[] = {
669126353Smlaier	/* Device interface */
670126353Smlaier	DEVMETHOD(device_probe,		ad1816_probe),
671126353Smlaier	DEVMETHOD(device_attach,	ad1816_attach),
672126353Smlaier	DEVMETHOD(device_detach,	ad1816_detach),
673126353Smlaier
674130617Smlaier	{ 0, 0 }
675130617Smlaier};
676126353Smlaier
677126353Smlaierstatic driver_t ad1816_driver = {
678126353Smlaier	"pcm",
679126353Smlaier	ad1816_methods,
680126353Smlaier	PCM_SOFTC_SIZE,
681130617Smlaier};
682130617Smlaier
683130617SmlaierDRIVER_MODULE(snd_ad1816, isa, ad1816_driver, pcm_devclass, 0, 0);
684130617SmlaierDRIVER_MODULE(snd_ad1816, acpi, ad1816_driver, pcm_devclass, 0, 0);
685130617SmlaierMODULE_DEPEND(snd_ad1816, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER);
686130617SmlaierMODULE_VERSION(snd_ad1816, 1);
687126353Smlaier
688126353Smlaier
689126353Smlaier