ad1816.c revision 168847
1323530Savg/*-
2323530Savg * Copyright (c) 1999 Cameron Grant <cg@freebsd.org>
3323530Savg * Copyright (c) 1997,1998 Luigi Rizzo
4323530Savg * Copyright (c) 1994,1995 Hannu Savolainen
5323530Savg * All rights reserved.
6323530Savg *
7323530Savg * Redistribution and use in source and binary forms, with or without
8323530Savg * modification, are permitted provided that the following conditions
9323530Savg * are met:
10323530Savg * 1. Redistributions of source code must retain the above copyright
11323530Savg *    notice, this list of conditions and the following disclaimer.
12323530Savg * 2. Redistributions in binary form must reproduce the above copyright
13323530Savg *    notice, this list of conditions and the following disclaimer in the
14323530Savg *    documentation and/or other materials provided with the distribution.
15323530Savg *
16323530Savg * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17323530Savg * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18323530Savg * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19323530Savg * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20323530Savg * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21323530Savg * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22323530Savg * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23323530Savg * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24323530Savg * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25323530Savg * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26323530Savg * SUCH DAMAGE.
27323530Savg */
28323530Savg
29323530Savg#include <dev/sound/pcm/sound.h>
30323530Savg#include <dev/sound/isa/ad1816.h>
31323530Savg
32323530Savg#include <isa/isavar.h>
33323530Savg
34323530Savg#include "mixer_if.h"
35323530Savg
36323530SavgSND_DECLARE_FILE("$FreeBSD: head/sys/dev/sound/isa/ad1816.c 168847 2007-04-18 18:26:41Z ariff $");
37323530Savg
38323530Savgstruct ad1816_info;
39323530Savg
40323530Savgstruct ad1816_chinfo {
41323530Savg	struct ad1816_info *parent;
42323530Savg	struct pcm_channel *channel;
43323530Savg	struct snd_dbuf *buffer;
44323530Savg	int dir, blksz;
45323530Savg};
46323530Savg
47323530Savgstruct ad1816_info {
48323530Savg	struct resource *io_base;	/* primary I/O address for the board */
49323530Savg	int io_rid;
50323530Savg	struct resource *irq;
51323530Savg	int irq_rid;
52323530Savg	struct resource *drq1;		/* play */
53323530Savg	int drq1_rid;
54323530Savg	struct resource *drq2;		/* rec */
55323530Savg	int drq2_rid;
56323530Savg	void *ih;
57323530Savg	bus_dma_tag_t parent_dmat;
58323530Savg	struct mtx *lock;
59323530Savg
60323530Savg	unsigned int bufsize;
61323530Savg	struct ad1816_chinfo pch, rch;
62323530Savg};
63323530Savg
64323530Savgstatic u_int32_t ad1816_fmt[] = {
65323530Savg	AFMT_U8,
66323530Savg	AFMT_STEREO | AFMT_U8,
67323530Savg	AFMT_S16_LE,
68323530Savg	AFMT_STEREO | AFMT_S16_LE,
69323530Savg	AFMT_MU_LAW,
70323530Savg	AFMT_STEREO | AFMT_MU_LAW,
71323530Savg	AFMT_A_LAW,
72323530Savg	AFMT_STEREO | AFMT_A_LAW,
73323530Savg	0
74323530Savg};
75323530Savg
76323530Savgstatic struct pcmchan_caps ad1816_caps = {4000, 55200, ad1816_fmt, 0};
77323530Savg
78323530Savg#define AD1816_MUTE 31		/* value for mute */
79323530Savg
80323530Savgstatic void
81323530Savgad1816_lock(struct ad1816_info *ad1816)
82323530Savg{
83323530Savg	snd_mtxlock(ad1816->lock);
84323530Savg}
85323530Savg
86323530Savgstatic void
87323530Savgad1816_unlock(struct ad1816_info *ad1816)
88323530Savg{
89323530Savg	snd_mtxunlock(ad1816->lock);
90323530Savg}
91323530Savg
92323530Savgstatic int
93323530Savgport_rd(struct resource *port, int off)
94323530Savg{
95323530Savg	if (port)
96323530Savg		return bus_space_read_1(rman_get_bustag(port),
97323530Savg					rman_get_bushandle(port),
98323530Savg					off);
99323530Savg	else
100323530Savg		return -1;
101323530Savg}
102323530Savg
103323530Savgstatic void
104323530Savgport_wr(struct resource *port, int off, u_int8_t data)
105323530Savg{
106323530Savg	if (port)
107323530Savg		bus_space_write_1(rman_get_bustag(port),
108323530Savg				  rman_get_bushandle(port),
109323530Savg				  off, data);
110323530Savg}
111323530Savg
112323530Savgstatic int
113323530Savgio_rd(struct ad1816_info *ad1816, int reg)
114323530Savg{
115323530Savg	return port_rd(ad1816->io_base, reg);
116323530Savg}
117323530Savg
118323530Savgstatic void
119323530Savgio_wr(struct ad1816_info *ad1816, int reg, u_int8_t data)
120323530Savg{
121323530Savg	port_wr(ad1816->io_base, reg, data);
122323530Savg}
123323530Savg
124323530Savgstatic void
125323530Savgad1816_intr(void *arg)
126323530Savg{
127323530Savg    	struct ad1816_info *ad1816 = (struct ad1816_info *)arg;
128323530Savg    	unsigned char   c, served = 0;
129323530Savg
130323530Savg	ad1816_lock(ad1816);
131323530Savg    	/* get interupt status */
132323530Savg    	c = io_rd(ad1816, AD1816_INT);
133323530Savg
134323530Savg    	/* check for stray interupts */
135323530Savg    	if (c & ~(AD1816_INTRCI | AD1816_INTRPI)) {
136323530Savg		printf("pcm: stray int (%x)\n", c);
137323530Savg		c &= AD1816_INTRCI | AD1816_INTRPI;
138323530Savg    	}
139323530Savg    	/* check for capture interupt */
140323530Savg    	if (sndbuf_runsz(ad1816->rch.buffer) && (c & AD1816_INTRCI)) {
141323530Savg		ad1816_unlock(ad1816);
142323530Savg		chn_intr(ad1816->rch.channel);
143323530Savg		ad1816_lock(ad1816);
144323530Savg		served |= AD1816_INTRCI;		/* cp served */
145323530Savg    	}
146323530Savg    	/* check for playback interupt */
147323530Savg    	if (sndbuf_runsz(ad1816->pch.buffer) && (c & AD1816_INTRPI)) {
148323530Savg		ad1816_unlock(ad1816);
149323530Savg		chn_intr(ad1816->pch.channel);
150323530Savg		ad1816_lock(ad1816);
151323530Savg		served |= AD1816_INTRPI;		/* pb served */
152323530Savg    	}
153323530Savg    	if (served == 0) {
154323530Savg		/* this probably means this is not a (working) ad1816 chip, */
155323530Savg		/* or an error in dma handling                              */
156323530Savg		printf("pcm: int without reason (%x)\n", c);
157323530Savg		c = 0;
158323530Savg    	} else c &= ~served;
159323530Savg    	io_wr(ad1816, AD1816_INT, c);
160323530Savg    	c = io_rd(ad1816, AD1816_INT);
161323530Savg    	if (c != 0) printf("pcm: int clear failed (%x)\n", c);
162323530Savg	ad1816_unlock(ad1816);
163323530Savg}
164323530Savg
165323530Savgstatic int
166323530Savgad1816_wait_init(struct ad1816_info *ad1816, int x)
167323530Savg{
168323530Savg    	int             n = 0;	/* to shut up the compiler... */
169323530Savg
170323530Savg    	for (; x--;)
171323530Savg		if ((n = (io_rd(ad1816, AD1816_ALE) & AD1816_BUSY)) == 0) DELAY(10);
172323530Savg		else return n;
173323530Savg    	printf("ad1816_wait_init failed 0x%02x.\n", n);
174323530Savg    	return -1;
175323530Savg}
176323530Savg
177323530Savgstatic unsigned short
178323530Savgad1816_read(struct ad1816_info *ad1816, unsigned int reg)
179323530Savg{
180323530Savg    	u_short         x = 0;
181323530Savg
182323530Savg    	if (ad1816_wait_init(ad1816, 100) == -1) return 0;
183323530Savg    	io_wr(ad1816, AD1816_ALE, 0);
184323530Savg    	io_wr(ad1816, AD1816_ALE, (reg & AD1816_ALEMASK));
185323530Savg    	if (ad1816_wait_init(ad1816, 100) == -1) return 0;
186323530Savg    	x = (io_rd(ad1816, AD1816_HIGH) << 8) | io_rd(ad1816, AD1816_LOW);
187323530Savg    	return x;
188323530Savg}
189323530Savg
190323530Savgstatic void
191323530Savgad1816_write(struct ad1816_info *ad1816, unsigned int reg, unsigned short data)
192323530Savg{
193323530Savg    	if (ad1816_wait_init(ad1816, 100) == -1) return;
194323530Savg    	io_wr(ad1816, AD1816_ALE, (reg & AD1816_ALEMASK));
195323530Savg    	io_wr(ad1816, AD1816_LOW,  (data & 0x000000ff));
196323530Savg    	io_wr(ad1816, AD1816_HIGH, (data & 0x0000ff00) >> 8);
197323530Savg}
198323530Savg
199323530Savg/* -------------------------------------------------------------------- */
200323530Savg
201323530Savgstatic int
202323530Savgad1816mix_init(struct snd_mixer *m)
203323530Savg{
204323530Savg	mix_setdevs(m, AD1816_MIXER_DEVICES);
205323530Savg	mix_setrecdevs(m, AD1816_REC_DEVICES);
206323530Savg	return 0;
207323530Savg}
208323530Savg
209323530Savgstatic int
210323530Savgad1816mix_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right)
211323530Savg{
212323530Savg	struct ad1816_info *ad1816 = mix_getdevinfo(m);
213323530Savg    	u_short reg = 0;
214323530Savg
215323530Savg    	/* Scale volumes */
216323530Savg    	left = AD1816_MUTE - (AD1816_MUTE * left) / 100;
217323530Savg    	right = AD1816_MUTE - (AD1816_MUTE * right) / 100;
218323530Savg
219323530Savg    	reg = (left << 8) | right;
220323530Savg
221323530Savg    	/* do channel selective muting if volume is zero */
222323530Savg    	if (left == AD1816_MUTE)	reg |= 0x8000;
223323530Savg    	if (right == AD1816_MUTE)	reg |= 0x0080;
224323530Savg
225323530Savg	ad1816_lock(ad1816);
226323530Savg    	switch (dev) {
227323530Savg    	case SOUND_MIXER_VOLUME:	/* Register 14 master volume */
228323530Savg		ad1816_write(ad1816, 14, reg);
229323530Savg		break;
230323530Savg
231323530Savg    	case SOUND_MIXER_CD:	/* Register 15 cd */
232323530Savg    	case SOUND_MIXER_LINE1:
233323530Savg		ad1816_write(ad1816, 15, reg);
234323530Savg		break;
235323530Savg
236323530Savg    	case SOUND_MIXER_SYNTH:	/* Register 16 synth */
237323530Savg		ad1816_write(ad1816, 16, reg);
238323530Savg		break;
239323530Savg
240323530Savg    	case SOUND_MIXER_PCM:	/* Register 4 pcm */
241323530Savg		ad1816_write(ad1816, 4, reg);
242323530Savg		break;
243323530Savg
244323530Savg    	case SOUND_MIXER_LINE:
245323530Savg    	case SOUND_MIXER_LINE3:	/* Register 18 line in */
246323530Savg		ad1816_write(ad1816, 18, reg);
247323530Savg		break;
248323530Savg
249323530Savg    	case SOUND_MIXER_MIC:	/* Register 19 mic volume */
250323530Savg		ad1816_write(ad1816, 19, reg & ~0xff);	/* mic is mono */
251323530Savg		break;
252323530Savg
253323530Savg    	case SOUND_MIXER_IGAIN:
254323530Savg		/* and now to something completely different ... */
255323530Savg		ad1816_write(ad1816, 20, ((ad1816_read(ad1816, 20) & ~0x0f0f)
256323530Savg	      	| (((AD1816_MUTE - left) / 2) << 8) /* four bits of adc gain */
257323530Savg	      	| ((AD1816_MUTE - right) / 2)));
258323530Savg		break;
259323530Savg
260323530Savg    	default:
261323530Savg		printf("ad1816_mixer_set(): unknown device.\n");
262323530Savg		break;
263323530Savg    	}
264323530Savg	ad1816_unlock(ad1816);
265323530Savg
266323530Savg    	left = ((AD1816_MUTE - left) * 100) / AD1816_MUTE;
267323530Savg    	right = ((AD1816_MUTE - right) * 100) / AD1816_MUTE;
268323530Savg
269323530Savg    	return left | (right << 8);
270323530Savg}
271323530Savg
272323530Savgstatic int
273323530Savgad1816mix_setrecsrc(struct snd_mixer *m, u_int32_t src)
274323530Savg{
275323530Savg	struct ad1816_info *ad1816 = mix_getdevinfo(m);
276323530Savg    	int dev;
277323530Savg
278323530Savg    	switch (src) {
279323530Savg    	case SOUND_MASK_LINE:
280323530Savg    	case SOUND_MASK_LINE3:
281323530Savg		dev = 0x00;
282323530Savg		break;
283323530Savg
284323530Savg    	case SOUND_MASK_CD:
285323530Savg    	case SOUND_MASK_LINE1:
286323530Savg		dev = 0x20;
287323530Savg		break;
288323530Savg
289323530Savg    	case SOUND_MASK_MIC:
290323530Savg    	default:
291323530Savg		dev = 0x50;
292323530Savg		src = SOUND_MASK_MIC;
293323530Savg    	}
294323530Savg
295323530Savg    	dev |= dev << 8;
296323530Savg	ad1816_lock(ad1816);
297323530Savg    	ad1816_write(ad1816, 20, (ad1816_read(ad1816, 20) & ~0x7070) | dev);
298323530Savg	ad1816_unlock(ad1816);
299323530Savg    	return src;
300323530Savg}
301323530Savg
302323530Savgstatic kobj_method_t ad1816mixer_methods[] = {
303323530Savg    	KOBJMETHOD(mixer_init,		ad1816mix_init),
304323530Savg    	KOBJMETHOD(mixer_set,		ad1816mix_set),
305323530Savg    	KOBJMETHOD(mixer_setrecsrc,	ad1816mix_setrecsrc),
306323530Savg	{ 0, 0 }
307323530Savg};
308323530SavgMIXER_DECLARE(ad1816mixer);
309323530Savg
310323530Savg/* -------------------------------------------------------------------- */
311323530Savg/* channel interface */
312323530Savgstatic void *
313323530Savgad1816chan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel *c, int dir)
314323530Savg{
315323530Savg	struct ad1816_info *ad1816 = devinfo;
316323530Savg	struct ad1816_chinfo *ch = (dir == PCMDIR_PLAY)? &ad1816->pch : &ad1816->rch;
317323530Savg
318323530Savg	ch->parent = ad1816;
319323530Savg	ch->channel = c;
320323530Savg	ch->buffer = b;
321323530Savg	if (sndbuf_alloc(ch->buffer, ad1816->parent_dmat, 0, ad1816->bufsize) != 0)
322323530Savg		return NULL;
323323530Savg	return ch;
324323530Savg}
325323530Savg
326323530Savgstatic int
327323530Savgad1816chan_setdir(kobj_t obj, void *data, int dir)
328323530Savg{
329323530Savg	struct ad1816_chinfo *ch = data;
330323530Savg  	struct ad1816_info *ad1816 = ch->parent;
331323530Savg
332323530Savg	sndbuf_dmasetup(ch->buffer, (dir == PCMDIR_PLAY)? ad1816->drq1 : ad1816->drq2);
333323530Savg	ch->dir = dir;
334323530Savg	return 0;
335323530Savg}
336323530Savg
337323530Savgstatic int
338323530Savgad1816chan_setformat(kobj_t obj, void *data, u_int32_t format)
339323530Savg{
340323530Savg	struct ad1816_chinfo *ch = data;
341323530Savg  	struct ad1816_info *ad1816 = ch->parent;
342323530Savg    	int fmt = AD1816_U8, reg;
343323530Savg
344323530Savg	ad1816_lock(ad1816);
345323530Savg    	if (ch->dir == PCMDIR_PLAY) {
346323530Savg        	reg = AD1816_PLAY;
347323530Savg        	ad1816_write(ad1816, 8, 0x0000);	/* reset base and current counter */
348323530Savg        	ad1816_write(ad1816, 9, 0x0000);	/* for playback and capture */
349323530Savg    	} else {
350323530Savg        	reg = AD1816_CAPT;
351323530Savg        	ad1816_write(ad1816, 10, 0x0000);
352323530Savg        	ad1816_write(ad1816, 11, 0x0000);
353323530Savg    	}
354323530Savg    	switch (format & ~AFMT_STEREO) {
355323530Savg    	case AFMT_A_LAW:
356323530Savg        	fmt = AD1816_ALAW;
357323530Savg		break;
358323530Savg
359323530Savg    	case AFMT_MU_LAW:
360323530Savg		fmt = AD1816_MULAW;
361323530Savg		break;
362323530Savg
363323530Savg    	case AFMT_S16_LE:
364323530Savg		fmt = AD1816_S16LE;
365323530Savg		break;
366323530Savg
367323530Savg    	case AFMT_S16_BE:
368323530Savg		fmt = AD1816_S16BE;
369323530Savg		break;
370323530Savg
371323530Savg    	case AFMT_U8:
372323530Savg		fmt = AD1816_U8;
373323530Savg		break;
374323530Savg    	}
375323530Savg    	if (format & AFMT_STEREO) fmt |= AD1816_STEREO;
376323530Savg    	io_wr(ad1816, reg, fmt);
377323530Savg	ad1816_unlock(ad1816);
378323530Savg#if 0
379323530Savg    	return format;
380323530Savg#else
381323530Savg    	return 0;
382323530Savg#endif
383323530Savg}
384323530Savg
385323530Savgstatic int
386323530Savgad1816chan_setspeed(kobj_t obj, void *data, u_int32_t speed)
387323530Savg{
388323530Savg	struct ad1816_chinfo *ch = data;
389323530Savg    	struct ad1816_info *ad1816 = ch->parent;
390323530Savg
391323530Savg    	RANGE(speed, 4000, 55200);
392323530Savg	ad1816_lock(ad1816);
393323530Savg    	ad1816_write(ad1816, (ch->dir == PCMDIR_PLAY)? 2 : 3, speed);
394323530Savg	ad1816_unlock(ad1816);
395323530Savg    	return speed;
396323530Savg}
397323530Savg
398323530Savgstatic int
399323530Savgad1816chan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize)
400323530Savg{
401323530Savg	struct ad1816_chinfo *ch = data;
402323530Savg
403323530Savg	ch->blksz = blocksize;
404323530Savg	return ch->blksz;
405323530Savg}
406323530Savg
407323530Savgstatic int
408323530Savgad1816chan_trigger(kobj_t obj, void *data, int go)
409323530Savg{
410323530Savg	struct ad1816_chinfo *ch = data;
411323530Savg    	struct ad1816_info *ad1816 = ch->parent;
412323530Savg    	int wr, reg;
413323530Savg
414323530Savg	if (go == PCMTRIG_EMLDMAWR || go == PCMTRIG_EMLDMARD)
415323530Savg		return 0;
416323530Savg
417323530Savg	sndbuf_dma(ch->buffer, go);
418323530Savg    	wr = (ch->dir == PCMDIR_PLAY);
419323530Savg    	reg = wr? AD1816_PLAY : AD1816_CAPT;
420323530Savg	ad1816_lock(ad1816);
421323530Savg    	switch (go) {
422323530Savg    	case PCMTRIG_START:
423323530Savg		/* start only if not already running */
424323530Savg		if (!(io_rd(ad1816, reg) & AD1816_ENABLE)) {
425323530Savg	    		int cnt = ((ch->blksz) >> 2) - 1;
426323530Savg	    		ad1816_write(ad1816, wr? 8 : 10, cnt); /* count */
427323530Savg	    		ad1816_write(ad1816, wr? 9 : 11, 0); /* reset cur cnt */
428323530Savg	    		ad1816_write(ad1816, 1, ad1816_read(ad1816, 1) |
429323530Savg				     (wr? 0x8000 : 0x4000)); /* enable int */
430323530Savg	    		/* enable playback */
431323530Savg	    		io_wr(ad1816, reg, io_rd(ad1816, reg) | AD1816_ENABLE);
432323530Savg	    		if (!(io_rd(ad1816, reg) & AD1816_ENABLE))
433323530Savg				printf("ad1816: failed to start %s DMA!\n",
434323530Savg				       wr? "play" : "rec");
435323530Savg		}
436323530Savg		break;
437323530Savg
438323530Savg    	case PCMTRIG_STOP:
439323530Savg    	case PCMTRIG_ABORT:		/* XXX check this... */
440323530Savg		/* we don't test here if it is running... */
441323530Savg		if (wr) {
442323530Savg	    		ad1816_write(ad1816, 1, ad1816_read(ad1816, 1) &
443323530Savg				     ~(wr? 0x8000 : 0x4000));
444323530Savg	    		/* disable int */
445323530Savg	    		io_wr(ad1816, reg, io_rd(ad1816, reg) & ~AD1816_ENABLE);
446323530Savg	    		/* disable playback */
447323530Savg	    		if (io_rd(ad1816, reg) & AD1816_ENABLE)
448323530Savg				printf("ad1816: failed to stop %s DMA!\n",
449323530Savg				       wr? "play" : "rec");
450323530Savg	    		ad1816_write(ad1816, wr? 8 : 10, 0); /* reset base cnt */
451323530Savg	    		ad1816_write(ad1816, wr? 9 : 11, 0); /* reset cur cnt */
452323530Savg		}
453323530Savg		break;
454323530Savg    	}
455323530Savg	ad1816_unlock(ad1816);
456323530Savg    	return 0;
457323530Savg}
458323530Savg
459323530Savgstatic int
460323530Savgad1816chan_getptr(kobj_t obj, void *data)
461323530Savg{
462323530Savg	struct ad1816_chinfo *ch = data;
463323530Savg	return sndbuf_dmaptr(ch->buffer);
464323530Savg}
465323530Savg
466323530Savgstatic struct pcmchan_caps *
467323530Savgad1816chan_getcaps(kobj_t obj, void *data)
468323530Savg{
469323530Savg	return &ad1816_caps;
470323530Savg}
471323530Savg
472323530Savgstatic kobj_method_t ad1816chan_methods[] = {
473323530Savg    	KOBJMETHOD(channel_init,		ad1816chan_init),
474323530Savg    	KOBJMETHOD(channel_setdir,		ad1816chan_setdir),
475323530Savg    	KOBJMETHOD(channel_setformat,		ad1816chan_setformat),
476323530Savg    	KOBJMETHOD(channel_setspeed,		ad1816chan_setspeed),
477323530Savg    	KOBJMETHOD(channel_setblocksize,	ad1816chan_setblocksize),
478323530Savg    	KOBJMETHOD(channel_trigger,		ad1816chan_trigger),
479323530Savg    	KOBJMETHOD(channel_getptr,		ad1816chan_getptr),
480323530Savg    	KOBJMETHOD(channel_getcaps,		ad1816chan_getcaps),
481323530Savg	{ 0, 0 }
482323530Savg};
483323530SavgCHANNEL_DECLARE(ad1816chan);
484323530Savg
485323530Savg/* -------------------------------------------------------------------- */
486323530Savg
487323530Savgstatic void
488323530Savgad1816_release_resources(struct ad1816_info *ad1816, device_t dev)
489323530Savg{
490323530Savg    	if (ad1816->irq) {
491323530Savg   		if (ad1816->ih)
492323530Savg			bus_teardown_intr(dev, ad1816->irq, ad1816->ih);
493323530Savg		bus_release_resource(dev, SYS_RES_IRQ, ad1816->irq_rid, ad1816->irq);
494323530Savg		ad1816->irq = 0;
495323530Savg    	}
496323530Savg    	if (ad1816->drq1) {
497323530Savg		isa_dma_release(rman_get_start(ad1816->drq1));
498323530Savg		bus_release_resource(dev, SYS_RES_DRQ, ad1816->drq1_rid, ad1816->drq1);
499323530Savg		ad1816->drq1 = 0;
500323530Savg    	}
501323530Savg    	if (ad1816->drq2) {
502323530Savg		isa_dma_release(rman_get_start(ad1816->drq2));
503323530Savg		bus_release_resource(dev, SYS_RES_DRQ, ad1816->drq2_rid, ad1816->drq2);
504323530Savg		ad1816->drq2 = 0;
505323530Savg    	}
506323530Savg    	if (ad1816->io_base) {
507323530Savg		bus_release_resource(dev, SYS_RES_IOPORT, ad1816->io_rid, ad1816->io_base);
508323530Savg		ad1816->io_base = 0;
509323530Savg    	}
510323530Savg    	if (ad1816->parent_dmat) {
511323530Savg		bus_dma_tag_destroy(ad1816->parent_dmat);
512323530Savg		ad1816->parent_dmat = 0;
513323530Savg    	}
514323530Savg	if (ad1816->lock)
515323530Savg		snd_mtxfree(ad1816->lock);
516323530Savg
517323530Savg     	free(ad1816, M_DEVBUF);
518323530Savg}
519323530Savg
520323530Savgstatic int
521323530Savgad1816_alloc_resources(struct ad1816_info *ad1816, device_t dev)
522323530Savg{
523323530Savg    	int ok = 1, pdma, rdma;
524323530Savg
525323530Savg	if (!ad1816->io_base)
526323530Savg    		ad1816->io_base = bus_alloc_resource_any(dev,
527323530Savg			SYS_RES_IOPORT, &ad1816->io_rid, RF_ACTIVE);
528323530Savg	if (!ad1816->irq)
529323530Savg    		ad1816->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ,
530323530Savg			&ad1816->irq_rid, RF_ACTIVE);
531323530Savg	if (!ad1816->drq1)
532323530Savg    		ad1816->drq1 = bus_alloc_resource_any(dev, SYS_RES_DRQ,
533323530Savg			&ad1816->drq1_rid, RF_ACTIVE);
534323530Savg    	if (!ad1816->drq2)
535323530Savg        	ad1816->drq2 = bus_alloc_resource_any(dev, SYS_RES_DRQ,
536323530Savg			&ad1816->drq2_rid, RF_ACTIVE);
537323530Savg
538323530Savg    	if (!ad1816->io_base || !ad1816->drq1 || !ad1816->irq) ok = 0;
539323530Savg
540323530Savg	if (ok) {
541323530Savg		pdma = rman_get_start(ad1816->drq1);
542323530Savg		isa_dma_acquire(pdma);
543323530Savg		isa_dmainit(pdma, ad1816->bufsize);
544323530Savg		if (ad1816->drq2) {
545323530Savg			rdma = rman_get_start(ad1816->drq2);
546323530Savg			isa_dma_acquire(rdma);
547323530Savg			isa_dmainit(rdma, ad1816->bufsize);
548323530Savg		} else
549323530Savg			rdma = pdma;
550323530Savg    		if (pdma == rdma)
551323530Savg			pcm_setflags(dev, pcm_getflags(dev) | SD_F_SIMPLEX);
552323530Savg	}
553323530Savg
554323530Savg    	return ok;
555323530Savg}
556323530Savg
557323530Savgstatic int
558323530Savgad1816_init(struct ad1816_info *ad1816, device_t dev)
559323530Savg{
560323530Savg    	ad1816_write(ad1816, 1, 0x2);	/* disable interrupts */
561323530Savg    	ad1816_write(ad1816, 32, 0x90F0);	/* SoundSys Mode, split fmt */
562323530Savg
563323530Savg    	ad1816_write(ad1816, 5, 0x8080);	/* FM volume mute */
564323530Savg    	ad1816_write(ad1816, 6, 0x8080);	/* I2S1 volume mute */
565323530Savg    	ad1816_write(ad1816, 7, 0x8080);	/* I2S0 volume mute */
566323530Savg    	ad1816_write(ad1816, 17, 0x8888);	/* VID Volume mute */
567323530Savg    	ad1816_write(ad1816, 20, 0x5050);	/* recsrc mic, agc off */
568323530Savg    	/* adc gain is set to 0 */
569323530Savg
570323530Savg	return 0;
571323530Savg}
572323530Savg
573323530Savgstatic int
574323530Savgad1816_probe(device_t dev)
575323530Savg{
576323530Savg    	char *s = NULL;
577323530Savg    	u_int32_t logical_id = isa_get_logicalid(dev);
578323530Savg
579323530Savg    	switch (logical_id) {
580323530Savg    	case 0x80719304: /* ADS7180 */
581323530Savg 		s = "AD1816";
582323530Savg 		break;
583323530Savg    	case 0x50719304: /* ADS7150 */
584323530Savg 		s = "AD1815";
585323530Savg 		break;
586323530Savg    	}
587323530Savg
588323530Savg    	if (s) {
589323530Savg		device_set_desc(dev, s);
590323530Savg		return BUS_PROBE_DEFAULT;
591323530Savg    	}
592323530Savg    	return ENXIO;
593323530Savg}
594323530Savg
595323530Savgstatic int
596323530Savgad1816_attach(device_t dev)
597323530Savg{
598323530Savg	struct ad1816_info *ad1816;
599323530Savg    	char status[SND_STATUSLEN], status2[SND_STATUSLEN];
600323530Savg
601323530Savg	ad1816 = (struct ad1816_info *)malloc(sizeof *ad1816, M_DEVBUF, M_NOWAIT | M_ZERO);
602323530Savg	if (!ad1816) return ENXIO;
603323530Savg
604323530Savg	ad1816->lock = snd_mtxcreate(device_get_nameunit(dev),
605323530Savg	    "snd_ad1816 softc");
606323530Savg	ad1816->io_rid = 2;
607323530Savg	ad1816->irq_rid = 0;
608323530Savg	ad1816->drq1_rid = 0;
609323530Savg	ad1816->drq2_rid = 1;
610323530Savg	ad1816->bufsize = pcm_getbuffersize(dev, 4096, DSP_BUFFSIZE, 65536);
611323530Savg
612323530Savg    	if (!ad1816_alloc_resources(ad1816, dev)) goto no;
613323530Savg    	ad1816_init(ad1816, dev);
614323530Savg    	if (mixer_init(dev, &ad1816mixer_class, ad1816)) goto no;
615323530Savg
616323530Savg	snd_setup_intr(dev, ad1816->irq, 0, ad1816_intr, ad1816, &ad1816->ih);
617323530Savg    	if (bus_dma_tag_create(/*parent*/bus_get_dma_tag(dev), /*alignment*/2,
618323530Savg			/*boundary*/0,
619323530Savg			/*lowaddr*/BUS_SPACE_MAXADDR_24BIT,
620323530Savg			/*highaddr*/BUS_SPACE_MAXADDR,
621323530Savg			/*filter*/NULL, /*filterarg*/NULL,
622323530Savg			/*maxsize*/ad1816->bufsize, /*nsegments*/1,
623323530Savg			/*maxsegz*/0x3ffff,
624323530Savg			/*flags*/0, /*lockfunc*/busdma_lock_mutex,
625323530Savg			/*lockarg*/ &Giant, &ad1816->parent_dmat) != 0) {
626323530Savg		device_printf(dev, "unable to create dma tag\n");
627323530Savg		goto no;
628323530Savg    	}
629323530Savg    	if (ad1816->drq2)
630323530Savg		snprintf(status2, SND_STATUSLEN, ":%ld", rman_get_start(ad1816->drq2));
631323530Savg	else
632323530Savg		status2[0] = '\0';
633323530Savg
634323530Savg    	snprintf(status, SND_STATUSLEN, "at io 0x%lx irq %ld drq %ld%s bufsz %u %s",
635323530Savg    	     	rman_get_start(ad1816->io_base),
636323530Savg		rman_get_start(ad1816->irq),
637323530Savg		rman_get_start(ad1816->drq1),
638323530Savg		status2,
639323530Savg		ad1816->bufsize,
640323530Savg		PCM_KLDSTRING(snd_ad1816));
641323530Savg
642323530Savg    	if (pcm_register(dev, ad1816, 1, 1)) goto no;
643323530Savg    	pcm_addchan(dev, PCMDIR_REC, &ad1816chan_class, ad1816);
644323530Savg    	pcm_addchan(dev, PCMDIR_PLAY, &ad1816chan_class, ad1816);
645323530Savg    	pcm_setstatus(dev, status);
646323530Savg
647323530Savg    	return 0;
648323530Savgno:
649323530Savg    	ad1816_release_resources(ad1816, dev);
650323530Savg
651323530Savg    	return ENXIO;
652323530Savg
653323530Savg}
654323530Savg
655323530Savgstatic int
656323530Savgad1816_detach(device_t dev)
657323530Savg{
658323530Savg	int r;
659323530Savg	struct ad1816_info *ad1816;
660323530Savg
661323530Savg	r = pcm_unregister(dev);
662323530Savg	if (r)
663323530Savg		return r;
664323530Savg
665323530Savg	ad1816 = pcm_getdevinfo(dev);
666323530Savg    	ad1816_release_resources(ad1816, dev);
667323530Savg	return 0;
668323530Savg}
669323530Savg
670323530Savgstatic device_method_t ad1816_methods[] = {
671323530Savg	/* Device interface */
672323530Savg	DEVMETHOD(device_probe,		ad1816_probe),
673323530Savg	DEVMETHOD(device_attach,	ad1816_attach),
674323530Savg	DEVMETHOD(device_detach,	ad1816_detach),
675323530Savg
676323530Savg	{ 0, 0 }
677323530Savg};
678323530Savg
679323530Savgstatic driver_t ad1816_driver = {
680323530Savg	"pcm",
681323530Savg	ad1816_methods,
682323530Savg	PCM_SOFTC_SIZE,
683323530Savg};
684323530Savg
685323530SavgDRIVER_MODULE(snd_ad1816, isa, ad1816_driver, pcm_devclass, 0, 0);
686323530SavgDRIVER_MODULE(snd_ad1816, acpi, ad1816_driver, pcm_devclass, 0, 0);
687323530SavgMODULE_DEPEND(snd_ad1816, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER);
688323530SavgMODULE_VERSION(snd_ad1816, 1);
689323530Savg
690323530Savg
691323530Savg