ad1816.c revision 78564
1203954Srdivacky/*
2203954Srdivacky * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk>
3203954Srdivacky * Copyright Luigi Rizzo, 1997,1998
4203954Srdivacky * Copyright by Hannu Savolainen 1994, 1995
5203954Srdivacky * All rights reserved.
6203954Srdivacky *
7203954Srdivacky * Redistribution and use in source and binary forms, with or without
8203954Srdivacky * modification, are permitted provided that the following conditions
9203954Srdivacky * are met:
10203954Srdivacky * 1. Redistributions of source code must retain the above copyright
11203954Srdivacky *    notice, this list of conditions and the following disclaimer.
12221345Sdim * 2. Redistributions in binary form must reproduce the above copyright
13203954Srdivacky *    notice, this list of conditions and the following disclaimer in the
14221345Sdim *    documentation and/or other materials provided with the distribution.
15204642Srdivacky *
16203954Srdivacky * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17204642Srdivacky * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18203954Srdivacky * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19203954Srdivacky * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20204642Srdivacky * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21204642Srdivacky * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22204642Srdivacky * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23204642Srdivacky * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24204642Srdivacky * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25204642Srdivacky * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26204642Srdivacky * SUCH DAMAGE.
27204642Srdivacky *
28204642Srdivacky * $FreeBSD: head/sys/dev/sound/isa/ad1816.c 78564 2001-06-21 19:45:59Z greid $
29204642Srdivacky */
30218893Sdim
31204642Srdivacky#include <dev/sound/pcm/sound.h>
32204642Srdivacky#include <dev/sound/isa/ad1816.h>
33204642Srdivacky
34204642Srdivacky#include "mixer_if.h"
35218893Sdim
36204642Srdivackystruct ad1816_info;
37204642Srdivacky
38204642Srdivackystruct ad1816_chinfo {
39204642Srdivacky	struct ad1816_info *parent;
40204642Srdivacky	struct pcm_channel *channel;
41204642Srdivacky	struct snd_dbuf *buffer;
42204642Srdivacky	int dir, blksz;
43204642Srdivacky};
44204642Srdivacky
45204642Srdivackystruct ad1816_info {
46204642Srdivacky    struct resource *io_base;	/* primary I/O address for the board */
47204642Srdivacky    int		     io_rid;
48204642Srdivacky    struct resource *irq;
49203954Srdivacky    int		     irq_rid;
50203954Srdivacky    struct resource *drq1; /* play */
51203954Srdivacky    int		     drq1_rid;
52203954Srdivacky    struct resource *drq2; /* rec */
53218893Sdim    int		     drq2_rid;
54203954Srdivacky    void 	    *ih;
55203954Srdivacky    bus_dma_tag_t    parent_dmat;
56203954Srdivacky    void 	    *lock;
57203954Srdivacky
58218893Sdim    struct ad1816_chinfo pch, rch;
59203954Srdivacky};
60203954Srdivacky
61203954Srdivackystatic driver_intr_t 	ad1816_intr;
62203954Srdivackystatic int 		ad1816_probe(device_t dev);
63218893Sdimstatic int 		ad1816_attach(device_t dev);
64204642Srdivacky
65204642Srdivacky/* IO primitives */
66204642Srdivackystatic int      	ad1816_wait_init(struct ad1816_info *ad1816, int x);
67203954Srdivackystatic u_short		ad1816_read(struct ad1816_info *ad1816, u_int reg);
68218893Sdimstatic void     	ad1816_write(struct ad1816_info *ad1816, u_int reg, u_short data);
69204642Srdivacky
70204642Srdivackystatic u_int32_t ad1816_fmt[] = {
71204642Srdivacky	AFMT_U8,
72204642Srdivacky	AFMT_STEREO | AFMT_U8,
73218893Sdim	AFMT_S16_LE,
74218893Sdim	AFMT_STEREO | AFMT_S16_LE,
75218893Sdim	AFMT_MU_LAW,
76218893Sdim	AFMT_STEREO | AFMT_MU_LAW,
77204792Srdivacky	AFMT_A_LAW,
78204792Srdivacky	AFMT_STEREO | AFMT_A_LAW,
79204792Srdivacky	0
80204792Srdivacky};
81204792Srdivacky
82204792Srdivackystatic struct pcmchan_caps ad1816_caps = {4000, 55200, ad1816_fmt, 0};
83204792Srdivacky
84218893Sdim#define AD1816_MUTE 31		/* value for mute */
85204642Srdivacky
86204642Srdivackystatic void
87204642Srdivackyad1816_lock(struct ad1816_info *ad1816)
88204642Srdivacky{
89218893Sdim	snd_mtxlock(ad1816->lock);
90204642Srdivacky}
91204642Srdivacky
92218893Sdimstatic void
93204642Srdivackyad1816_unlock(struct ad1816_info *ad1816)
94204642Srdivacky{
95204642Srdivacky	snd_mtxunlock(ad1816->lock);
96221345Sdim}
97221345Sdim
98221345Sdimstatic int
99221345Sdimport_rd(struct resource *port, int off)
100203954Srdivacky{
101203954Srdivacky	if (port)
102218893Sdim		return bus_space_read_1(rman_get_bustag(port),
103203954Srdivacky					rman_get_bushandle(port),
104203954Srdivacky					off);
105203954Srdivacky	else
106218893Sdim		return -1;
107204642Srdivacky}
108204642Srdivacky
109218893Sdimstatic void
110204642Srdivackyport_wr(struct resource *port, int off, u_int8_t data)
111203954Srdivacky{
112204642Srdivacky	if (port)
113203954Srdivacky		return bus_space_write_1(rman_get_bustag(port),
114218893Sdim					 rman_get_bushandle(port),
115204642Srdivacky					 off, data);
116203954Srdivacky}
117203954Srdivacky
118203954Srdivackystatic int
119203954Srdivackyio_rd(struct ad1816_info *ad1816, int reg)
120218893Sdim{
121204642Srdivacky	return port_rd(ad1816->io_base, reg);
122204642Srdivacky}
123204642Srdivacky
124204642Srdivackystatic void
125204642Srdivackyio_wr(struct ad1816_info *ad1816, int reg, u_int8_t data)
126204642Srdivacky{
127204642Srdivacky	return port_wr(ad1816->io_base, reg, data);
128204642Srdivacky}
129204642Srdivacky
130204642Srdivackystatic void
131204642Srdivackyad1816_intr(void *arg)
132218893Sdim{
133204642Srdivacky    	struct ad1816_info *ad1816 = (struct ad1816_info *)arg;
134204642Srdivacky    	unsigned char   c, served = 0;
135204642Srdivacky
136204642Srdivacky	ad1816_lock(ad1816);
137204642Srdivacky    	/* get interupt status */
138204642Srdivacky    	c = io_rd(ad1816, AD1816_INT);
139204642Srdivacky
140204642Srdivacky    	/* check for stray interupts */
141204642Srdivacky    	if (c & ~(AD1816_INTRCI | AD1816_INTRPI)) {
142204642Srdivacky		printf("pcm: stray int (%x)\n", c);
143204642Srdivacky		c &= AD1816_INTRCI | AD1816_INTRPI;
144218893Sdim    	}
145203954Srdivacky    	/* check for capture interupt */
146203954Srdivacky    	if (sndbuf_runsz(ad1816->rch.buffer) && (c & AD1816_INTRCI)) {
147203954Srdivacky		chn_intr(ad1816->rch.channel);
148203954Srdivacky		served |= AD1816_INTRCI;		/* cp served */
149203954Srdivacky    	}
150204642Srdivacky    	/* check for playback interupt */
151203954Srdivacky    	if (sndbuf_runsz(ad1816->pch.buffer) && (c & AD1816_INTRPI)) {
152203954Srdivacky		chn_intr(ad1816->pch.channel);
153203954Srdivacky		served |= AD1816_INTRPI;		/* pb served */
154203954Srdivacky    	}
155203954Srdivacky    	if (served == 0) {
156203954Srdivacky		/* this probably means this is not a (working) ad1816 chip, */
157203954Srdivacky		/* or an error in dma handling                              */
158203954Srdivacky		printf("pcm: int without reason (%x)\n", c);
159203954Srdivacky		c = 0;
160203954Srdivacky    	} else c &= ~served;
161203954Srdivacky    	io_wr(ad1816, AD1816_INT, c);
162203954Srdivacky    	c = io_rd(ad1816, AD1816_INT);
163203954Srdivacky    	if (c != 0) printf("pcm: int clear failed (%x)\n", c);
164203954Srdivacky	ad1816_unlock(ad1816);
165218893Sdim}
166203954Srdivacky
167203954Srdivackystatic int
168221345Sdimad1816_wait_init(struct ad1816_info *ad1816, int x)
169221345Sdim{
170221345Sdim    	int             n = 0;	/* to shut up the compiler... */
171221345Sdim
172221345Sdim    	for (; x--;)
173221345Sdim		if ((n = (io_rd(ad1816, AD1816_ALE) & AD1816_BUSY)) == 0) DELAY(10);
174203954Srdivacky		else return n;
175203954Srdivacky    	printf("ad1816_wait_init failed 0x%02x.\n", n);
176203954Srdivacky    	return -1;
177203954Srdivacky}
178203954Srdivacky
179203954Srdivackystatic unsigned short
180203954Srdivackyad1816_read(struct ad1816_info *ad1816, unsigned int reg)
181203954Srdivacky{
182203954Srdivacky    	u_short         x = 0;
183203954Srdivacky
184218893Sdim    	if (ad1816_wait_init(ad1816, 100) == -1) return 0;
185203954Srdivacky    	io_wr(ad1816, AD1816_ALE, 0);
186203954Srdivacky    	io_wr(ad1816, AD1816_ALE, (reg & AD1816_ALEMASK));
187203954Srdivacky    	if (ad1816_wait_init(ad1816, 100) == -1) return 0;
188203954Srdivacky    	x = (io_rd(ad1816, AD1816_HIGH) << 8) | io_rd(ad1816, AD1816_LOW);
189203954Srdivacky    	return x;
190203954Srdivacky}
191203954Srdivacky
192203954Srdivackystatic void
193203954Srdivackyad1816_write(struct ad1816_info *ad1816, unsigned int reg, unsigned short data)
194203954Srdivacky{
195203954Srdivacky    	if (ad1816_wait_init(ad1816, 100) == -1) return;
196203954Srdivacky    	io_wr(ad1816, AD1816_ALE, (reg & AD1816_ALEMASK));
197218893Sdim    	io_wr(ad1816, AD1816_LOW,  (data & 0x000000ff));
198204642Srdivacky    	io_wr(ad1816, AD1816_HIGH, (data & 0x0000ff00) >> 8);
199203954Srdivacky}
200204642Srdivacky
201203954Srdivacky/* -------------------------------------------------------------------- */
202204642Srdivacky
203203954Srdivackystatic int
204203954Srdivackyad1816mix_init(struct snd_mixer *m)
205203954Srdivacky{
206203954Srdivacky	mix_setdevs(m, AD1816_MIXER_DEVICES);
207204642Srdivacky	mix_setrecdevs(m, AD1816_REC_DEVICES);
208204642Srdivacky	return 0;
209204642Srdivacky}
210203954Srdivacky
211203954Srdivackystatic int
212203954Srdivackyad1816mix_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right)
213203954Srdivacky{
214218893Sdim	struct ad1816_info *ad1816 = mix_getdevinfo(m);
215203954Srdivacky    	u_short reg = 0;
216204642Srdivacky
217204642Srdivacky    	/* Scale volumes */
218204642Srdivacky    	left = AD1816_MUTE - (AD1816_MUTE * left) / 100;
219204642Srdivacky    	right = AD1816_MUTE - (AD1816_MUTE * right) / 100;
220204642Srdivacky
221204642Srdivacky    	reg = (left << 8) | right;
222204642Srdivacky
223204642Srdivacky    	/* do channel selective muting if volume is zero */
224204642Srdivacky    	if (left == AD1816_MUTE)	reg |= 0x8000;
225204642Srdivacky    	if (right == AD1816_MUTE)	reg |= 0x0080;
226204642Srdivacky
227218893Sdim	ad1816_lock(ad1816);
228203954Srdivacky    	switch (dev) {
229203954Srdivacky    	case SOUND_MIXER_VOLUME:	/* Register 14 master volume */
230203954Srdivacky		ad1816_write(ad1816, 14, reg);
231203954Srdivacky		break;
232203954Srdivacky
233218893Sdim    	case SOUND_MIXER_CD:	/* Register 15 cd */
234203954Srdivacky    	case SOUND_MIXER_LINE1:
235203954Srdivacky		ad1816_write(ad1816, 15, reg);
236218893Sdim		break;
237203954Srdivacky
238208599Srdivacky    	case SOUND_MIXER_SYNTH:	/* Register 16 synth */
239203954Srdivacky		ad1816_write(ad1816, 16, reg);
240203954Srdivacky		break;
241203954Srdivacky
242204642Srdivacky    	case SOUND_MIXER_PCM:	/* Register 4 pcm */
243204642Srdivacky		ad1816_write(ad1816, 4, reg);
244218893Sdim		break;
245204642Srdivacky
246204642Srdivacky    	case SOUND_MIXER_LINE:
247204642Srdivacky    	case SOUND_MIXER_LINE3:	/* Register 18 line in */
248204642Srdivacky		ad1816_write(ad1816, 18, reg);
249204642Srdivacky		break;
250204642Srdivacky
251218893Sdim    	case SOUND_MIXER_MIC:	/* Register 19 mic volume */
252203954Srdivacky		ad1816_write(ad1816, 19, reg & ~0xff);	/* mic is mono */
253204642Srdivacky		break;
254218893Sdim
255203954Srdivacky    	case SOUND_MIXER_IGAIN:
256204642Srdivacky		/* and now to something completely different ... */
257218893Sdim		ad1816_write(ad1816, 20, ((ad1816_read(ad1816, 20) & ~0x0f0f)
258203954Srdivacky	      	| (((AD1816_MUTE - left) / 2) << 8) /* four bits of adc gain */
259204642Srdivacky	      	| ((AD1816_MUTE - right) / 2)));
260204642Srdivacky		break;
261204642Srdivacky
262204642Srdivacky    	default:
263204642Srdivacky		printf("ad1816_mixer_set(): unknown device.\n");
264204642Srdivacky		break;
265204642Srdivacky    	}
266204792Srdivacky	ad1816_unlock(ad1816);
267204792Srdivacky
268204792Srdivacky    	left = ((AD1816_MUTE - left) * 100) / AD1816_MUTE;
269204642Srdivacky    	right = ((AD1816_MUTE - right) * 100) / AD1816_MUTE;
270203954Srdivacky
271218893Sdim    	return left | (right << 8);
272203954Srdivacky}
273203954Srdivacky
274203954Srdivackystatic int
275203954Srdivackyad1816mix_setrecsrc(struct snd_mixer *m, u_int32_t src)
276203954Srdivacky{
277203954Srdivacky	struct ad1816_info *ad1816 = mix_getdevinfo(m);
278203954Srdivacky    	int dev;
279203954Srdivacky
280218893Sdim    	switch (src) {
281203954Srdivacky    	case SOUND_MASK_LINE:
282203954Srdivacky    	case SOUND_MASK_LINE3:
283203954Srdivacky		dev = 0x00;
284203954Srdivacky		break;
285203954Srdivacky
286203954Srdivacky    	case SOUND_MASK_CD:
287203954Srdivacky    	case SOUND_MASK_LINE1:
288203954Srdivacky		dev = 0x20;
289203954Srdivacky		break;
290203954Srdivacky
291218893Sdim    	case SOUND_MASK_MIC:
292203954Srdivacky    	default:
293204642Srdivacky		dev = 0x50;
294204642Srdivacky		src = SOUND_MASK_MIC;
295203954Srdivacky    	}
296203954Srdivacky
297204642Srdivacky    	dev |= dev << 8;
298204642Srdivacky	ad1816_lock(ad1816);
299204642Srdivacky    	ad1816_write(ad1816, 20, (ad1816_read(ad1816, 20) & ~0x7070) | dev);
300204642Srdivacky	ad1816_unlock(ad1816);
301204642Srdivacky    	return src;
302204642Srdivacky}
303204642Srdivacky
304203954Srdivackystatic kobj_method_t ad1816mixer_methods[] = {
305204642Srdivacky    	KOBJMETHOD(mixer_init,		ad1816mix_init),
306203954Srdivacky    	KOBJMETHOD(mixer_set,		ad1816mix_set),
307204642Srdivacky    	KOBJMETHOD(mixer_setrecsrc,	ad1816mix_setrecsrc),
308203954Srdivacky	{ 0, 0 }
309203954Srdivacky};
310204642SrdivackyMIXER_DECLARE(ad1816mixer);
311203954Srdivacky
312204642Srdivacky/* -------------------------------------------------------------------- */
313203954Srdivacky/* channel interface */
314203954Srdivackystatic void *
315203954Srdivackyad1816chan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel *c, int dir)
316203954Srdivacky{
317218893Sdim	struct ad1816_info *ad1816 = devinfo;
318203954Srdivacky	struct ad1816_chinfo *ch = (dir == PCMDIR_PLAY)? &ad1816->pch : &ad1816->rch;
319204642Srdivacky
320218893Sdim	ch->parent = ad1816;
321204642Srdivacky	ch->channel = c;
322204642Srdivacky	ch->buffer = b;
323204642Srdivacky	if (sndbuf_alloc(ch->buffer, ad1816->parent_dmat, DSP_BUFFSIZE) == -1) return NULL;
324204642Srdivacky	return ch;
325218893Sdim}
326203954Srdivacky
327203954Srdivackystatic int
328203954Srdivackyad1816chan_setdir(kobj_t obj, void *data, int dir)
329204642Srdivacky{
330204642Srdivacky	struct ad1816_chinfo *ch = data;
331204642Srdivacky  	struct ad1816_info *ad1816 = ch->parent;
332204642Srdivacky
333204642Srdivacky	sndbuf_isadmasetup(ch->buffer, (dir == PCMDIR_PLAY)? ad1816->drq1 : ad1816->drq2);
334204642Srdivacky	ch->dir = dir;
335204642Srdivacky	return 0;
336218893Sdim}
337204642Srdivacky
338204642Srdivackystatic int
339203954Srdivackyad1816chan_setformat(kobj_t obj, void *data, u_int32_t format)
340203954Srdivacky{
341204642Srdivacky	struct ad1816_chinfo *ch = data;
342204642Srdivacky  	struct ad1816_info *ad1816 = ch->parent;
343204642Srdivacky    	int fmt = AD1816_U8, reg;
344204642Srdivacky
345204642Srdivacky	ad1816_lock(ad1816);
346204642Srdivacky    	if (ch->dir == PCMDIR_PLAY) {
347204642Srdivacky        	reg = AD1816_PLAY;
348204642Srdivacky        	ad1816_write(ad1816, 8, 0x0000);	/* reset base and current counter */
349204642Srdivacky        	ad1816_write(ad1816, 9, 0x0000);	/* for playback and capture */
350204642Srdivacky    	} else {
351204642Srdivacky        	reg = AD1816_CAPT;
352204642Srdivacky        	ad1816_write(ad1816, 10, 0x0000);
353204642Srdivacky        	ad1816_write(ad1816, 11, 0x0000);
354204642Srdivacky    	}
355204642Srdivacky    	switch (format & ~AFMT_STEREO) {
356204642Srdivacky    	case AFMT_A_LAW:
357204642Srdivacky        	fmt = AD1816_ALAW;
358204642Srdivacky		break;
359204642Srdivacky
360204642Srdivacky    	case AFMT_MU_LAW:
361204642Srdivacky		fmt = AD1816_MULAW;
362204642Srdivacky		break;
363204642Srdivacky
364203954Srdivacky    	case AFMT_S16_LE:
365203954Srdivacky		fmt = AD1816_S16LE;
366203954Srdivacky		break;
367218893Sdim
368203954Srdivacky    	case AFMT_S16_BE:
369203954Srdivacky		fmt = AD1816_S16BE;
370203954Srdivacky		break;
371218893Sdim
372203954Srdivacky    	case AFMT_U8:
373203954Srdivacky		fmt = AD1816_U8;
374203954Srdivacky		break;
375203954Srdivacky    	}
376203954Srdivacky    	if (format & AFMT_STEREO) fmt |= AD1816_STEREO;
377203954Srdivacky    	io_wr(ad1816, reg, fmt);
378203954Srdivacky	ad1816_unlock(ad1816);
379203954Srdivacky    	return format;
380218893Sdim}
381218893Sdim
382203954Srdivackystatic int
383218893Sdimad1816chan_setspeed(kobj_t obj, void *data, u_int32_t speed)
384203954Srdivacky{
385204642Srdivacky	struct ad1816_chinfo *ch = data;
386203954Srdivacky    	struct ad1816_info *ad1816 = ch->parent;
387203954Srdivacky
388204642Srdivacky    	RANGE(speed, 4000, 55200);
389218893Sdim	ad1816_lock(ad1816);
390218893Sdim    	ad1816_write(ad1816, (ch->dir == PCMDIR_PLAY)? 2 : 3, speed);
391204642Srdivacky	ad1816_unlock(ad1816);
392218893Sdim    	return speed;
393218893Sdim}
394204642Srdivacky
395204642Srdivackystatic int
396218893Sdimad1816chan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize)
397204642Srdivacky{
398218893Sdim	struct ad1816_chinfo *ch = data;
399218893Sdim
400204642Srdivacky	ch->blksz = blocksize;
401218893Sdim	return ch->blksz;
402218893Sdim}
403218893Sdim
404218893Sdimstatic int
405218893Sdimad1816chan_trigger(kobj_t obj, void *data, int go)
406218893Sdim{
407218893Sdim	struct ad1816_chinfo *ch = data;
408203954Srdivacky    	struct ad1816_info *ad1816 = ch->parent;
409203954Srdivacky    	int wr, reg;
410203954Srdivacky
411204642Srdivacky	if (go == PCMTRIG_EMLDMAWR || go == PCMTRIG_EMLDMARD)
412203954Srdivacky		return 0;
413204642Srdivacky
414203954Srdivacky	sndbuf_isadma(ch->buffer, go);
415203954Srdivacky    	wr = (ch->dir == PCMDIR_PLAY);
416203954Srdivacky    	reg = wr? AD1816_PLAY : AD1816_CAPT;
417203954Srdivacky	ad1816_lock(ad1816);
418203954Srdivacky    	switch (go) {
419203954Srdivacky    	case PCMTRIG_START:
420203954Srdivacky		/* start only if not already running */
421203954Srdivacky		if (!(io_rd(ad1816, reg) & AD1816_ENABLE)) {
422203954Srdivacky	    		int cnt = ((ch->blksz) >> 2) - 1;
423206083Srdivacky	    		ad1816_write(ad1816, wr? 8 : 10, cnt); /* count */
424218893Sdim	    		ad1816_write(ad1816, wr? 9 : 11, 0); /* reset cur cnt */
425206083Srdivacky	    		ad1816_write(ad1816, 1, ad1816_read(ad1816, 1) |
426206083Srdivacky				     (wr? 0x8000 : 0x4000)); /* enable int */
427206083Srdivacky	    		/* enable playback */
428203954Srdivacky	    		io_wr(ad1816, reg, io_rd(ad1816, reg) | AD1816_ENABLE);
429206083Srdivacky	    		if (!(io_rd(ad1816, reg) & AD1816_ENABLE))
430203954Srdivacky				printf("ad1816: failed to start %s DMA!\n",
431218893Sdim				       wr? "play" : "rec");
432203954Srdivacky		}
433203954Srdivacky		break;
434203954Srdivacky
435203954Srdivacky    	case PCMTRIG_STOP:
436203954Srdivacky    	case PCMTRIG_ABORT:		/* XXX check this... */
437204642Srdivacky		/* we don't test here if it is running... */
438204642Srdivacky		if (wr) {
439203954Srdivacky	    		ad1816_write(ad1816, 1, ad1816_read(ad1816, 1) &
440203954Srdivacky				     ~(wr? 0x8000 : 0x4000));
441203954Srdivacky	    		/* disable int */
442203954Srdivacky	    		io_wr(ad1816, reg, io_rd(ad1816, reg) & ~AD1816_ENABLE);
443203954Srdivacky	    		/* disable playback */
444203954Srdivacky	    		if (io_rd(ad1816, reg) & AD1816_ENABLE)
445204642Srdivacky				printf("ad1816: failed to stop %s DMA!\n",
446203954Srdivacky				       wr? "play" : "rec");
447203954Srdivacky	    		ad1816_write(ad1816, wr? 8 : 10, 0); /* reset base cnt */
448203954Srdivacky	    		ad1816_write(ad1816, wr? 9 : 11, 0); /* reset cur cnt */
449218893Sdim		}
450203954Srdivacky		break;
451203954Srdivacky    	}
452203954Srdivacky	ad1816_unlock(ad1816);
453203954Srdivacky    	return 0;
454218893Sdim}
455204961Srdivacky
456204961Srdivackystatic int
457204961Srdivackyad1816chan_getptr(kobj_t obj, void *data)
458218893Sdim{
459206083Srdivacky	struct ad1816_chinfo *ch = data;
460206083Srdivacky	return sndbuf_isadmaptr(ch->buffer);
461206083Srdivacky}
462203954Srdivacky
463203954Srdivackystatic struct pcmchan_caps *
464204642Srdivackyad1816chan_getcaps(kobj_t obj, void *data)
465204642Srdivacky{
466204642Srdivacky	return &ad1816_caps;
467204642Srdivacky}
468204642Srdivacky
469204642Srdivackystatic kobj_method_t ad1816chan_methods[] = {
470204642Srdivacky    	KOBJMETHOD(channel_init,		ad1816chan_init),
471204642Srdivacky    	KOBJMETHOD(channel_setdir,		ad1816chan_setdir),
472204642Srdivacky    	KOBJMETHOD(channel_setformat,		ad1816chan_setformat),
473204642Srdivacky    	KOBJMETHOD(channel_setspeed,		ad1816chan_setspeed),
474204642Srdivacky    	KOBJMETHOD(channel_setblocksize,	ad1816chan_setblocksize),
475204642Srdivacky    	KOBJMETHOD(channel_trigger,		ad1816chan_trigger),
476204642Srdivacky    	KOBJMETHOD(channel_getptr,		ad1816chan_getptr),
477218893Sdim    	KOBJMETHOD(channel_getcaps,		ad1816chan_getcaps),
478204642Srdivacky	{ 0, 0 }
479204642Srdivacky};
480204642SrdivackyCHANNEL_DECLARE(ad1816chan);
481204642Srdivacky
482218893Sdim/* -------------------------------------------------------------------- */
483204792Srdivacky
484204792Srdivackystatic void
485218893Sdimad1816_release_resources(struct ad1816_info *ad1816, device_t dev)
486203954Srdivacky{
487203954Srdivacky    	if (ad1816->irq) {
488203954Srdivacky   		if (ad1816->ih)
489204642Srdivacky			bus_teardown_intr(dev, ad1816->irq, ad1816->ih);
490218893Sdim		bus_release_resource(dev, SYS_RES_IRQ, ad1816->irq_rid,
491204792Srdivacky				     ad1816->irq);
492204792Srdivacky		ad1816->irq = 0;
493204792Srdivacky    	}
494204792Srdivacky    	if (ad1816->drq1) {
495204792Srdivacky		bus_release_resource(dev, SYS_RES_DRQ, ad1816->drq1_rid,
496204792Srdivacky				     ad1816->drq1);
497218893Sdim		ad1816->drq1 = 0;
498204792Srdivacky    	}
499204792Srdivacky    	if (ad1816->drq2) {
500204642Srdivacky		bus_release_resource(dev, SYS_RES_DRQ, ad1816->drq2_rid,
501204792Srdivacky				     ad1816->drq2);
502204792Srdivacky		ad1816->drq2 = 0;
503204792Srdivacky    	}
504204792Srdivacky    	if (ad1816->io_base) {
505204792Srdivacky		bus_release_resource(dev, SYS_RES_IOPORT, ad1816->io_rid,
506218893Sdim				     ad1816->io_base);
507204792Srdivacky		ad1816->io_base = 0;
508204792Srdivacky    	}
509218893Sdim    	if (ad1816->parent_dmat) {
510204792Srdivacky		bus_dma_tag_destroy(ad1816->parent_dmat);
511204792Srdivacky		ad1816->parent_dmat = 0;
512204792Srdivacky    	}
513204792Srdivacky	if (ad1816->lock) snd_mtxfree(ad1816->lock);
514218893Sdim     	free(ad1816, M_DEVBUF);
515204792Srdivacky}
516204792Srdivacky
517204792Srdivackystatic int
518204792Srdivackyad1816_alloc_resources(struct ad1816_info *ad1816, device_t dev)
519204792Srdivacky{
520204792Srdivacky    	int ok = 1, pdma, rdma;
521204792Srdivacky	if (!ad1816->io_base)
522218893Sdim    		ad1816->io_base = bus_alloc_resource(dev, SYS_RES_IOPORT, &ad1816->io_rid,
523204792Srdivacky						  0, ~0, 1, RF_ACTIVE);
524204792Srdivacky	if (!ad1816->irq)
525204792Srdivacky    		ad1816->irq = bus_alloc_resource(dev, SYS_RES_IRQ, &ad1816->irq_rid,
526204792Srdivacky					      0, ~0, 1, RF_ACTIVE);
527204792Srdivacky	if (!ad1816->drq1)
528218893Sdim    		ad1816->drq1 = bus_alloc_resource(dev, SYS_RES_DRQ, &ad1816->drq1_rid,
529218893Sdim					       0, ~0, 1, RF_ACTIVE);
530204792Srdivacky    	if (!ad1816->drq2)
531204792Srdivacky        	ad1816->drq2 = bus_alloc_resource(dev, SYS_RES_DRQ, &ad1816->drq2_rid,
532218893Sdim					       0, ~0, 1, RF_ACTIVE);
533204642Srdivacky
534203954Srdivacky    	if (!ad1816->io_base || !ad1816->drq1 || !ad1816->irq) ok = 0;
535203954Srdivacky
536203954Srdivacky	if (ok) {
537204642Srdivacky		pdma = rman_get_start(ad1816->drq1);
538204642Srdivacky		isa_dma_acquire(pdma);
539204642Srdivacky		isa_dmainit(pdma, DSP_BUFFSIZE);
540204642Srdivacky		if (ad1816->drq2) {
541204642Srdivacky			rdma = rman_get_start(ad1816->drq2);
542204642Srdivacky			isa_dma_acquire(rdma);
543204642Srdivacky			isa_dmainit(rdma, DSP_BUFFSIZE);
544218893Sdim		} else rdma = pdma;
545204642Srdivacky    		if (pdma == rdma)
546204642Srdivacky			pcm_setflags(dev, pcm_getflags(dev) | SD_F_SIMPLEX);
547204642Srdivacky	}
548204792Srdivacky    	return ok;
549204792Srdivacky}
550204792Srdivacky
551204792Srdivackystatic int
552204792Srdivackyad1816_init(struct ad1816_info *ad1816, device_t dev)
553204792Srdivacky{
554204792Srdivacky    	ad1816_write(ad1816, 1, 0x2);	/* disable interrupts */
555218893Sdim    	ad1816_write(ad1816, 32, 0x90F0);	/* SoundSys Mode, split fmt */
556204642Srdivacky
557204642Srdivacky    	ad1816_write(ad1816, 5, 0x8080);	/* FM volume mute */
558204642Srdivacky    	ad1816_write(ad1816, 6, 0x8080);	/* I2S1 volume mute */
559204792Srdivacky    	ad1816_write(ad1816, 7, 0x8080);	/* I2S0 volume mute */
560204642Srdivacky    	ad1816_write(ad1816, 17, 0x8888);	/* VID Volume mute */
561204642Srdivacky    	ad1816_write(ad1816, 20, 0x5050);	/* recsrc mic, agc off */
562204642Srdivacky    	/* adc gain is set to 0 */
563204792Srdivacky
564204792Srdivacky	return 0;
565204642Srdivacky}
566204642Srdivacky
567204642Srdivackystatic int
568204642Srdivackyad1816_probe(device_t dev)
569204642Srdivacky{
570204642Srdivacky    	char *s = NULL;
571204642Srdivacky    	u_int32_t logical_id = isa_get_logicalid(dev);
572204642Srdivacky
573204642Srdivacky    	switch (logical_id) {
574204642Srdivacky    	case 0x80719304: /* ADS7180 */
575204642Srdivacky 		s = "AD1816";
576218893Sdim 		break;
577204642Srdivacky    	}
578204642Srdivacky
579204642Srdivacky    	if (s) {
580204642Srdivacky		device_set_desc(dev, s);
581204642Srdivacky		return 0;
582204642Srdivacky    	}
583218893Sdim    	return ENXIO;
584204642Srdivacky}
585205407Srdivacky
586204642Srdivackystatic int
587204642Srdivackyad1816_attach(device_t dev)
588204642Srdivacky{
589218893Sdim	struct ad1816_info *ad1816;
590204642Srdivacky    	char status[SND_STATUSLEN];
591204642Srdivacky
592204642Srdivacky	ad1816 = (struct ad1816_info *)malloc(sizeof *ad1816, M_DEVBUF, M_NOWAIT | M_ZERO);
593221345Sdim	if (!ad1816) return ENXIO;
594221345Sdim
595204642Srdivacky	ad1816->lock = snd_mtxcreate(device_get_nameunit(dev));
596204642Srdivacky	ad1816->io_rid = 2;
597204642Srdivacky	ad1816->irq_rid = 0;
598218893Sdim	ad1816->drq1_rid = 0;
599204642Srdivacky	ad1816->drq2_rid = 1;
600205407Srdivacky
601204642Srdivacky    	if (!ad1816_alloc_resources(ad1816, dev)) goto no;
602204642Srdivacky    	ad1816_init(ad1816, dev);
603204642Srdivacky    	if (mixer_init(dev, &ad1816mixer_class, ad1816)) goto no;
604218893Sdim
605204642Srdivacky	snd_setup_intr(dev, ad1816->irq, INTR_MPSAFE, ad1816_intr, ad1816, &ad1816->ih);
606204642Srdivacky    	if (bus_dma_tag_create(/*parent*/NULL, /*alignment*/2, /*boundary*/0,
607204642Srdivacky			/*lowaddr*/BUS_SPACE_MAXADDR_24BIT,
608204642Srdivacky			/*highaddr*/BUS_SPACE_MAXADDR,
609204642Srdivacky			/*filter*/NULL, /*filterarg*/NULL,
610204642Srdivacky			/*maxsize*/DSP_BUFFSIZE, /*nsegments*/1,
611204642Srdivacky			/*maxsegz*/0x3ffff,
612204642Srdivacky			/*flags*/0, &ad1816->parent_dmat) != 0) {
613208599Srdivacky		device_printf(dev, "unable to create dma tag\n");
614208599Srdivacky		goto no;
615208599Srdivacky    	}
616208599Srdivacky    	snprintf(status, SND_STATUSLEN, "at io 0x%lx irq %ld drq %ld",
617208599Srdivacky    	     	rman_get_start(ad1816->io_base),
618208599Srdivacky		rman_get_start(ad1816->irq),
619208599Srdivacky		rman_get_start(ad1816->drq1));
620208599Srdivacky    	if (ad1816->drq2) snprintf(status + strlen(status),
621204642Srdivacky        	SND_STATUSLEN - strlen(status), ":%ld",
622218893Sdim		rman_get_start(ad1816->drq2));
623204642Srdivacky
624204642Srdivacky    	if (pcm_register(dev, ad1816, 1, 1)) goto no;
625204642Srdivacky    	pcm_addchan(dev, PCMDIR_REC, &ad1816chan_class, ad1816);
626204642Srdivacky    	pcm_addchan(dev, PCMDIR_PLAY, &ad1816chan_class, ad1816);
627204642Srdivacky    	pcm_setstatus(dev, status);
628218893Sdim
629204642Srdivacky    	return 0;
630204642Srdivackyno:
631204642Srdivacky    	ad1816_release_resources(ad1816, dev);
632218893Sdim
633204642Srdivacky    	return ENXIO;
634204642Srdivacky
635204642Srdivacky}
636204642Srdivacky
637204642Srdivackystatic int
638204642Srdivackyad1816_detach(device_t dev)
639204642Srdivacky{
640204642Srdivacky	int r;
641218893Sdim	struct ad1816_info *ad1816;
642204642Srdivacky
643204642Srdivacky	r = pcm_unregister(dev);
644204642Srdivacky	if (r)
645218893Sdim		return r;
646204642Srdivacky
647204642Srdivacky	ad1816 = pcm_getdevinfo(dev);
648204642Srdivacky    	ad1816_release_resources(ad1816, dev);
649223017Sdim	return 0;
650223017Sdim}
651223017Sdim
652223017Sdimstatic device_method_t ad1816_methods[] = {
653223017Sdim	/* Device interface */
654223017Sdim	DEVMETHOD(device_probe,		ad1816_probe),
655223017Sdim	DEVMETHOD(device_attach,	ad1816_attach),
656223017Sdim	DEVMETHOD(device_detach,	ad1816_detach),
657223017Sdim
658223017Sdim	{ 0, 0 }
659223017Sdim};
660223017Sdim
661223017Sdimstatic driver_t ad1816_driver = {
662223017Sdim	"pcm",
663223017Sdim	ad1816_methods,
664223017Sdim	sizeof(struct snddev_info),
665223017Sdim};
666223017Sdim
667223017SdimDRIVER_MODULE(snd_ad1816, isa, ad1816_driver, pcm_devclass, 0, 0);
668223017SdimMODULE_DEPEND(snd_ad1816, snd_pcm, PCM_MINVER, PCM_PREFVER, PCM_MAXVER);
669223017SdimMODULE_VERSION(snd_ad1816, 1);
670223017Sdim
671223017Sdim
672223017Sdim