xref: /netbsd-src/sys/dev/ic/am7930.c (revision fdecd6a253f999ae92b139670d9e15cc9df4497c)
1 /*	$NetBSD: am7930.c,v 1.22 1997/05/24 20:15:59 pk Exp $	*/
2 
3 /*
4  * Copyright (c) 1995 Rolf Grossmann
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. All advertising materials mentioning features or use of this software
16  *    must display the following acknowledgement:
17  *      This product includes software developed by Rolf Grossmann.
18  * 4. The name of the author may not be used to endorse or promote products
19  *    derived from this software without specific prior written permission
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
26  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32 
33 #include "audio.h"
34 #if NAUDIO > 0
35 
36 #include <sys/param.h>
37 #include <sys/systm.h>
38 #include <sys/errno.h>
39 #include <sys/ioctl.h>
40 #include <sys/device.h>
41 #include <sys/proc.h>
42 
43 #include <machine/autoconf.h>
44 #include <machine/cpu.h>
45 
46 #include <sys/audioio.h>
47 #include <dev/audio_if.h>
48 
49 #include <dev/ic/am7930reg.h>
50 #include <sparc/dev/amd7930var.h>
51 
52 #ifdef AUDIO_DEBUG
53 extern void Dprintf __P((const char *, ...));
54 
55 int     amd7930debug = 0;
56 #define DPRINTF(x)      if (amd7930debug) Dprintf x
57 #else
58 #define DPRINTF(x)
59 #endif
60 
61 /*
62  * Software state, per AMD79C30 audio chip.
63  */
64 struct amd7930_softc {
65 	struct	device sc_dev;		/* base device */
66 	struct	intrhand sc_hwih;	/* hardware interrupt vector */
67 	struct	intrhand sc_swih;	/* software interrupt vector */
68 
69 	int	sc_open;		/* single use device */
70 	int	sc_locked;		/* true when transfering data */
71 	struct	mapreg sc_map;		/* current contents of map registers */
72 
73 	u_char	sc_rlevel;		/* record level */
74 	u_char	sc_plevel;		/* play level */
75 	u_char	sc_mlevel;		/* monitor level */
76 	u_char	sc_out_port;		/* output port */
77 
78 	/* interfacing with the interrupt handlers */
79 	void	(*sc_rintr)(void*);	/* input completion intr handler */
80 	void	*sc_rarg;		/* arg for sc_rintr() */
81 	void	(*sc_pintr)(void*);	/* output completion intr handler */
82 	void	*sc_parg;		/* arg for sc_pintr() */
83 
84         /* sc_au is special in that the hardware interrupt handler uses it */
85         struct  auio sc_au;		/* recv and xmit buffers, etc */
86 #define sc_intrcnt	sc_au.au_intrcnt	/* statistics */
87 };
88 
89 /* interrupt interfaces */
90 #ifdef AUDIO_C_HANDLER
91 int	amd7930hwintr __P((void *));
92 #if defined(SUN4M)
93 #define AUDIO_SET_SWINTR do {		\
94 	if (CPU_ISSUN4M)		\
95 		raise(0, 4);		\
96 	else				\
97 		ienab_bis(IE_L4);	\
98 } while(0);
99 #else
100 #define AUDIO_SET_SWINTR ienab_bis(IE_L4)
101 #endif /* defined(SUN4M) */
102 #else
103 struct auio *auiop;
104 #endif /* AUDIO_C_HANDLER */
105 int	amd7930swintr __P((void *));
106 
107 /* forward declarations */
108 void	audio_setmap __P((volatile struct amd7930 *, struct mapreg *));
109 static void init_amd __P((volatile struct amd7930 *));
110 
111 /* autoconfiguration driver */
112 void	amd7930attach __P((struct device *, struct device *, void *));
113 int	amd7930match __P((struct device *, struct cfdata *, void *));
114 
115 struct cfattach audio_ca = {
116 	sizeof(struct amd7930_softc), amd7930match, amd7930attach
117 };
118 
119 struct	cfdriver audio_cd = {
120 	NULL, "audio", DV_DULL
121 };
122 
123 struct audio_device amd7930_device = {
124 	"amd7930",
125 	"x",
126 	"audio"
127 };
128 
129 /* Write 16 bits of data from variable v to the data port of the audio chip */
130 #define	WAMD16(amd, v) ((amd)->dr = (v), (amd)->dr = (v) >> 8)
131 
132 /* The following tables stolen from former (4.4Lite's) sys/sparc/bsd_audio.c */
133 
134 /*
135  * gx, gr & stg gains.  this table must contain 256 elements with
136  * the 0th being "infinity" (the magic value 9008).  The remaining
137  * elements match sun's gain curve (but with higher resolution):
138  * -18 to 0dB in .16dB steps then 0 to 12dB in .08dB steps.
139  */
140 static const u_short gx_coeff[256] = {
141 	0x9008, 0x8e7c, 0x8e51, 0x8e45, 0x8d42, 0x8d3b, 0x8c36, 0x8c33,
142 	0x8b32, 0x8b2a, 0x8b2b, 0x8b2c, 0x8b25, 0x8b23, 0x8b22, 0x8b22,
143 	0x9122, 0x8b1a, 0x8aa3, 0x8aa3, 0x8b1c, 0x8aa6, 0x912d, 0x912b,
144 	0x8aab, 0x8b12, 0x8aaa, 0x8ab2, 0x9132, 0x8ab4, 0x913c, 0x8abb,
145 	0x9142, 0x9144, 0x9151, 0x8ad5, 0x8aeb, 0x8a79, 0x8a5a, 0x8a4a,
146 	0x8b03, 0x91c2, 0x91bb, 0x8a3f, 0x8a33, 0x91b2, 0x9212, 0x9213,
147 	0x8a2c, 0x921d, 0x8a23, 0x921a, 0x9222, 0x9223, 0x922d, 0x9231,
148 	0x9234, 0x9242, 0x925b, 0x92dd, 0x92c1, 0x92b3, 0x92ab, 0x92a4,
149 	0x92a2, 0x932b, 0x9341, 0x93d3, 0x93b2, 0x93a2, 0x943c, 0x94b2,
150 	0x953a, 0x9653, 0x9782, 0x9e21, 0x9d23, 0x9cd2, 0x9c23, 0x9baa,
151 	0x9bde, 0x9b33, 0x9b22, 0x9b1d, 0x9ab2, 0xa142, 0xa1e5, 0x9a3b,
152 	0xa213, 0xa1a2, 0xa231, 0xa2eb, 0xa313, 0xa334, 0xa421, 0xa54b,
153 	0xada4, 0xac23, 0xab3b, 0xaaab, 0xaa5c, 0xb1a3, 0xb2ca, 0xb3bd,
154 	0xbe24, 0xbb2b, 0xba33, 0xc32b, 0xcb5a, 0xd2a2, 0xe31d, 0x0808,
155 	0x72ba, 0x62c2, 0x5c32, 0x52db, 0x513e, 0x4cce, 0x43b2, 0x4243,
156 	0x41b4, 0x3b12, 0x3bc3, 0x3df2, 0x34bd, 0x3334, 0x32c2, 0x3224,
157 	0x31aa, 0x2a7b, 0x2aaa, 0x2b23, 0x2bba, 0x2c42, 0x2e23, 0x25bb,
158 	0x242b, 0x240f, 0x231a, 0x22bb, 0x2241, 0x2223, 0x221f, 0x1a33,
159 	0x1a4a, 0x1acd, 0x2132, 0x1b1b, 0x1b2c, 0x1b62, 0x1c12, 0x1c32,
160 	0x1d1b, 0x1e71, 0x16b1, 0x1522, 0x1434, 0x1412, 0x1352, 0x1323,
161 	0x1315, 0x12bc, 0x127a, 0x1235, 0x1226, 0x11a2, 0x1216, 0x0a2a,
162 	0x11bc, 0x11d1, 0x1163, 0x0ac2, 0x0ab2, 0x0aab, 0x0b1b, 0x0b23,
163 	0x0b33, 0x0c0f, 0x0bb3, 0x0c1b, 0x0c3e, 0x0cb1, 0x0d4c, 0x0ec1,
164 	0x079a, 0x0614, 0x0521, 0x047c, 0x0422, 0x03b1, 0x03e3, 0x0333,
165 	0x0322, 0x031c, 0x02aa, 0x02ba, 0x02f2, 0x0242, 0x0232, 0x0227,
166 	0x0222, 0x021b, 0x01ad, 0x0212, 0x01b2, 0x01bb, 0x01cb, 0x01f6,
167 	0x0152, 0x013a, 0x0133, 0x0131, 0x012c, 0x0123, 0x0122, 0x00a2,
168 	0x011b, 0x011e, 0x0114, 0x00b1, 0x00aa, 0x00b3, 0x00bd, 0x00ba,
169 	0x00c5, 0x00d3, 0x00f3, 0x0062, 0x0051, 0x0042, 0x003b, 0x0033,
170 	0x0032, 0x002a, 0x002c, 0x0025, 0x0023, 0x0022, 0x001a, 0x0021,
171 	0x001b, 0x001b, 0x001d, 0x0015, 0x0013, 0x0013, 0x0012, 0x0012,
172 	0x000a, 0x000a, 0x0011, 0x0011, 0x000b, 0x000b, 0x000c, 0x000e,
173 };
174 
175 /*
176  * second stage play gain.
177  */
178 static const u_short ger_coeff[] = {
179 	0x431f, /* 5. dB */
180 	0x331f, /* 5.5 dB */
181 	0x40dd, /* 6. dB */
182 	0x11dd, /* 6.5 dB */
183 	0x440f, /* 7. dB */
184 	0x411f, /* 7.5 dB */
185 	0x311f, /* 8. dB */
186 	0x5520, /* 8.5 dB */
187 	0x10dd, /* 9. dB */
188 	0x4211, /* 9.5 dB */
189 	0x410f, /* 10. dB */
190 	0x111f, /* 10.5 dB */
191 	0x600b, /* 11. dB */
192 	0x00dd, /* 11.5 dB */
193 	0x4210, /* 12. dB */
194 	0x110f, /* 13. dB */
195 	0x7200, /* 14. dB */
196 	0x2110, /* 15. dB */
197 	0x2200, /* 15.9 dB */
198 	0x000b, /* 16.9 dB */
199 	0x000f  /* 18. dB */
200 #define NGER (sizeof(ger_coeff) / sizeof(ger_coeff[0]))
201 };
202 
203 /*
204  * Define our interface to the higher level audio driver.
205  */
206 int	amd7930_open __P((dev_t, int));
207 void	amd7930_close __P((void *));
208 int	amd7930_query_encoding __P((void *, struct audio_encoding *));
209 int	amd7930_set_params __P((void *, int, struct audio_params *, struct audio_params *));
210 int	amd7930_round_blocksize __P((void *, int));
211 int	amd7930_set_out_port __P((void *, int));
212 int	amd7930_get_out_port __P((void *));
213 int	amd7930_set_in_port __P((void *, int));
214 int	amd7930_get_in_port __P((void *));
215 int	amd7930_commit_settings __P((void *));
216 int	amd7930_start_output __P((void *, void *, int, void (*)(void *),
217 				  void *));
218 int	amd7930_start_input __P((void *, void *, int, void (*)(void *),
219 				 void *));
220 int	amd7930_halt_output __P((void *));
221 int	amd7930_halt_input __P((void *));
222 int	amd7930_cont_output __P((void *));
223 int	amd7930_cont_input __P((void *));
224 int	amd7930_getdev __P((void *, struct audio_device *));
225 int	amd7930_setfd __P((void *, int));
226 int	amd7930_set_port __P((void *, mixer_ctrl_t *));
227 int	amd7930_get_port __P((void *, mixer_ctrl_t *));
228 int	amd7930_query_devinfo __P((void *, mixer_devinfo_t *));
229 
230 
231 struct audio_hw_if sa_hw_if = {
232 	amd7930_open,
233 	amd7930_close,
234 	NULL,
235 	amd7930_query_encoding,
236 	amd7930_set_params,
237 	amd7930_round_blocksize,
238 	amd7930_set_out_port,
239 	amd7930_get_out_port,
240 	amd7930_set_in_port,
241 	amd7930_get_in_port,
242 	amd7930_commit_settings,
243 	amd7930_start_output,
244 	amd7930_start_input,
245 	amd7930_halt_output,
246 	amd7930_halt_input,
247 	amd7930_cont_output,
248 	amd7930_cont_input,
249 	NULL,
250 	amd7930_getdev,
251 	amd7930_setfd,
252 	amd7930_set_port,
253 	amd7930_get_port,
254 	amd7930_query_devinfo,
255 	1,
256 	0
257 };
258 
259 /* autoconfig routines */
260 
261 int
262 amd7930match(parent, cf, aux)
263 	struct device *parent;
264 	struct cfdata *cf;
265 	void *aux;
266 {
267 	register struct confargs *ca = aux;
268 	register struct romaux *ra = &ca->ca_ra;
269 
270 	if (CPU_ISSUN4)
271 		return (0);
272 	return (strcmp(cf->cf_driver->cd_name, ra->ra_name) == 0);
273 }
274 
275 /*
276  * Audio chip found.
277  */
278 void
279 amd7930attach(parent, self, args)
280 	struct device *parent, *self;
281 	void *args;
282 {
283 	register struct amd7930_softc *sc = (struct amd7930_softc *)self;
284 	register struct confargs *ca = args;
285 	register struct romaux *ra = &ca->ca_ra;
286 	register volatile struct amd7930 *amd;
287 	register int pri;
288 
289 	if (ra->ra_nintr != 1) {
290 		printf(": expected 1 interrupt, got %d\n", ra->ra_nintr);
291 		return;
292 	}
293 	pri = ra->ra_intr[0].int_pri;
294 	printf(" pri %d, softpri %d\n", pri, PIL_AUSOFT);
295 	amd = (volatile struct amd7930 *)(ra->ra_vaddr ?
296 		ra->ra_vaddr : mapiodev(ra->ra_reg, 0, sizeof (*amd)));
297 
298 	sc->sc_map.mr_mmr1 = AMD_MMR1_GX | AMD_MMR1_GER |
299 			     AMD_MMR1_GR | AMD_MMR1_STG;
300 	sc->sc_au.au_amd = amd;
301 	/* set boot defaults */
302 	sc->sc_rlevel = 128;
303 	sc->sc_plevel = 128;
304 	sc->sc_mlevel = 0;
305 	sc->sc_out_port = SUNAUDIO_SPEAKER;
306 
307 	init_amd(amd);
308 
309 #ifndef AUDIO_C_HANDLER
310 	auiop = &sc->sc_au;
311 	intr_fasttrap(pri, amd7930_trap);
312 #else
313 	sc->sc_hwih.ih_fun = amd7930hwintr;
314 	sc->sc_hwih.ih_arg = &sc->sc_au;
315 	intr_establish(pri, &sc->sc_hwih);
316 #endif
317 	sc->sc_swih.ih_fun = amd7930swintr;
318 	sc->sc_swih.ih_arg = sc;
319 	intr_establish(PIL_AUSOFT, &sc->sc_swih);
320 
321 	evcnt_attach(&sc->sc_dev, "intr", &sc->sc_intrcnt);
322 
323 	if (audio_hardware_attach(&sa_hw_if, sc) != 0)
324 		printf("audio: could not attach to audio pseudo-device driver\n");
325 }
326 
327 static void
328 init_amd(amd)
329 	register volatile struct amd7930 *amd;
330 {
331 	/* disable interrupts */
332 	amd->cr = AMDR_INIT;
333 	amd->dr = AMD_INIT_PMS_ACTIVE | AMD_INIT_INT_DISABLE;
334 
335 	/*
336 	 * Initialize the mux unit.  We use MCR3 to route audio (MAP)
337 	 * through channel Bb.  MCR1 and MCR2 are unused.
338 	 * Setting the INT enable bit in MCR4 will generate an interrupt
339 	 * on each converted audio sample.
340 	 */
341 	amd->cr = AMDR_MUX_1_4;
342  	amd->dr = 0;
343 	amd->dr = 0;
344 	amd->dr = (AMD_MCRCHAN_BB << 4) | AMD_MCRCHAN_BA;
345 	amd->dr = AMD_MCR4_INT_ENABLE;
346 }
347 
348 int
349 amd7930_open(dev, flags)
350 	dev_t dev;
351 	int flags;
352 {
353 	register struct amd7930_softc *sc;
354 	int unit = AUDIOUNIT(dev);
355 
356 	DPRINTF(("sa_open: unit %d\n",unit));
357 
358 	if (unit >= audio_cd.cd_ndevs)
359 		return (ENODEV);
360 	if ((sc = audio_cd.cd_devs[unit]) == NULL)
361 		return (ENXIO);
362 	if (sc->sc_open)
363 		return (EBUSY);
364 	sc->sc_open = 1;
365 	sc->sc_locked = 0;
366 	sc->sc_rintr = 0;
367 	sc->sc_rarg = 0;
368 	sc->sc_pintr = 0;
369 	sc->sc_parg = 0;
370 
371 	sc->sc_au.au_rdata = 0;
372 	sc->sc_au.au_pdata = 0;
373 
374 	DPRINTF(("saopen: ok -> sc=0x%x\n",sc));
375 
376 	return (0);
377 }
378 
379 void
380 amd7930_close(addr)
381 	void *addr;
382 {
383 	register struct amd7930_softc *sc = addr;
384 
385 	DPRINTF(("sa_close: sc=0x%x\n", sc));
386 	/*
387 	 * halt i/o, clear open flag, and done.
388 	 */
389 	amd7930_halt_input(sc);
390 	amd7930_halt_output(sc);
391 	sc->sc_open = 0;
392 
393 	DPRINTF(("sa_close: closed.\n"));
394 }
395 
396 int
397 amd7930_set_params(addr, mode, p, q)
398 	void *addr;
399 	int mode;
400 	struct audio_params *p, *q;
401 {
402 	if (p->sample_rate < 7500 || p->sample_rate > 8500 ||
403 	    p->encoding != AUDIO_ENCODING_ULAW ||
404 	    p->precision != 8 ||
405 	    p->channels != 1)
406 		return EINVAL;
407 	p->sample_rate = 8000;	/* no other sampling rates supported by amd chip */
408 
409 	/* Update setting for the other mode. */
410 	q->sample_rate = p->sample_rate;
411 	q->encoding = p->encoding;
412 	q->channels = p->channels;
413 	q->precision = p->precision;
414 
415 	return 0;
416 }
417 
418 int
419 amd7930_query_encoding(addr, fp)
420 	void *addr;
421 	struct audio_encoding *fp;
422 {
423 	switch (fp->index) {	/* ??? */
424 	    case 0:
425 		    strcpy(fp->name, AudioEmulaw);
426 		    fp->encoding = AUDIO_ENCODING_ULAW;
427 		    fp->precision = 8;
428 		    fp->flags = 0;
429 		    break;
430 	    default:
431 		    return(EINVAL);
432 		    /*NOTREACHED*/
433 	}
434 	return(0);
435 }
436 
437 int
438 amd7930_round_blocksize(addr, blk)
439 	void *addr;
440 	int blk;
441 {
442 	return(blk);
443 }
444 
445 int
446 amd7930_set_out_port(addr, port)
447 	void *addr;
448 	int port;
449 {
450 	register struct amd7930_softc *sc = addr;
451 
452 	switch(port) {
453 	    case SUNAUDIO_SPEAKER:
454 	    case SUNAUDIO_HEADPHONES:
455 		sc->sc_out_port = port;	/* set on commit */
456 		break;
457 	    default:
458 		return(EINVAL);
459 	}
460 	return(0);
461 }
462 
463 int
464 amd7930_get_out_port(addr)
465 	void *addr;
466 {
467 	register struct amd7930_softc *sc = addr;
468 
469 	return(sc->sc_out_port);
470 }
471 
472 int
473 amd7930_set_in_port(addr, port)
474 	void *addr;
475 	int port;
476 {
477 	if (port != SUNAUDIO_MIC_PORT)
478 		return(EINVAL);
479 
480 	return(0);	/* only microphone input supported by amd chip */
481 }
482 
483 int
484 amd7930_get_in_port(addr)
485 	void *addr;
486 {
487 	return(SUNAUDIO_MIC_PORT);
488 }
489 
490 int
491 amd7930_commit_settings(addr)
492 	void *addr;
493 {
494 	register struct amd7930_softc *sc = addr;
495 	register struct mapreg *map;
496 	register volatile struct amd7930 *amd;
497 	register int s, level;
498 
499 	DPRINTF(("sa_commit.\n"));
500 
501 	map = &sc->sc_map;
502 	amd = sc->sc_au.au_amd;
503 
504 	map->mr_gx = gx_coeff[sc->sc_rlevel];
505 	map->mr_stgr = gx_coeff[sc->sc_mlevel];
506 
507 	level = (sc->sc_plevel * (256 + NGER)) >> 8;
508 	if (level >= 256) {
509 		map->mr_ger = ger_coeff[level - 256];
510 		map->mr_gr = gx_coeff[255];
511 	} else {
512 		map->mr_ger = ger_coeff[0];
513 		map->mr_gr = gx_coeff[level];
514 	}
515 
516 	if (sc->sc_out_port == SUNAUDIO_SPEAKER)
517 		map->mr_mmr2 |= AMD_MMR2_LS;
518 	else
519 		map->mr_mmr2 &= ~AMD_MMR2_LS;
520 
521 	s = splaudio();
522 
523 	amd->cr = AMDR_MAP_MMR1;
524 	amd->dr = map->mr_mmr1;
525 	amd->cr = AMDR_MAP_GX;
526 	WAMD16(amd, map->mr_gx);
527 	amd->cr = AMDR_MAP_STG;
528 	WAMD16(amd, map->mr_stgr);
529 	amd->cr = AMDR_MAP_GR;
530 	WAMD16(amd, map->mr_gr);
531 	amd->cr = AMDR_MAP_GER;
532 	WAMD16(amd, map->mr_ger);
533 	amd->cr = AMDR_MAP_MMR2;
534 	amd->dr = map->mr_mmr2;
535 
536 	splx(s);
537 	return(0);
538 }
539 
540 int
541 amd7930_start_output(addr, p, cc, intr, arg)
542 	void *addr;
543 	void *p;
544 	int cc;
545 	void (*intr) __P((void *));
546 	void *arg;
547 {
548 	register struct amd7930_softc *sc = addr;
549 
550 #ifdef AUDIO_DEBUG
551 	if (amd7930debug > 1)
552 		Dprintf("sa_start_output: cc=%d 0x%x (0x%x)\n", cc, intr, arg);
553 #endif
554 
555 	if (!sc->sc_locked) {
556 		register volatile struct amd7930 *amd;
557 
558 		amd = sc->sc_au.au_amd;
559 		amd->cr = AMDR_INIT;
560 		amd->dr = AMD_INIT_PMS_ACTIVE;
561 		sc->sc_locked = 1;
562 		DPRINTF(("sa_start_output: started intrs.\n"));
563 	}
564 	sc->sc_pintr = intr;
565 	sc->sc_parg = arg;
566 	sc->sc_au.au_pdata = p;
567 	sc->sc_au.au_pend = p + cc - 1;
568 	return(0);
569 }
570 
571 /* ARGSUSED */
572 int
573 amd7930_start_input(addr, p, cc, intr, arg)
574 	void *addr;
575 	void *p;
576 	int cc;
577 	void (*intr) __P((void *));
578 	void *arg;
579 {
580 	register struct amd7930_softc *sc = addr;
581 
582 #ifdef AUDIO_DEBUG
583 	if (amd7930debug > 1)
584 		Dprintf("sa_start_input: cc=%d 0x%x (0x%x)\n", cc, intr, arg);
585 #endif
586 
587 	if (!sc->sc_locked) {
588 		register volatile struct amd7930 *amd;
589 
590 		amd = sc->sc_au.au_amd;
591 		amd->cr = AMDR_INIT;
592 		amd->dr = AMD_INIT_PMS_ACTIVE;
593 		sc->sc_locked = 1;
594 		DPRINTF(("sa_start_input: started intrs.\n"));
595 	}
596 	sc->sc_rintr = intr;
597 	sc->sc_rarg = arg;
598 	sc->sc_au.au_rdata = p;
599 	sc->sc_au.au_rend = p + cc -1;
600 	return(0);
601 }
602 
603 int
604 amd7930_halt_output(addr)
605 	void *addr;
606 {
607 	register struct amd7930_softc *sc = addr;
608 	register volatile struct amd7930 *amd;
609 
610 	/* XXX only halt, if input is also halted ?? */
611 	amd = sc->sc_au.au_amd;
612 	amd->cr = AMDR_INIT;
613 	amd->dr = AMD_INIT_PMS_ACTIVE | AMD_INIT_INT_DISABLE;
614 	sc->sc_locked = 0;
615 
616 	return(0);
617 }
618 
619 int
620 amd7930_halt_input(addr)
621 	void *addr;
622 {
623 	register struct amd7930_softc *sc = addr;
624 	register volatile struct amd7930 *amd;
625 
626 	/* XXX only halt, if output is also halted ?? */
627 	amd = sc->sc_au.au_amd;
628 	amd->cr = AMDR_INIT;
629 	amd->dr = AMD_INIT_PMS_ACTIVE | AMD_INIT_INT_DISABLE;
630 	sc->sc_locked = 0;
631 
632 	return(0);
633 }
634 
635 int
636 amd7930_cont_output(addr)
637 	void *addr;
638 {
639 	DPRINTF(("amd7930_cont_output: never called, what should it do?!\n"));
640 	return(0);
641 }
642 
643 int
644 amd7930_cont_input(addr)
645 	void *addr;
646 {
647 	DPRINTF(("amd7930_cont_input: never called, what should it do?!\n"));
648 	return(0);
649 }
650 
651 int
652 amd7930_getdev(addr, retp)
653         void *addr;
654         struct audio_device *retp;
655 {
656         *retp = amd7930_device;
657         return 0;
658 }
659 
660 int
661 amd7930_setfd(addr, flag)
662         void *addr;
663         int flag;
664 {
665         /* Always full-duplex */
666         return(0);
667 }
668 
669 int
670 amd7930_set_port(addr, cp)
671 	void *addr;
672 	mixer_ctrl_t *cp;
673 {
674 	register struct amd7930_softc *sc = addr;
675 
676 	DPRINTF(("amd7930_set_port: port=%d", cp->dev));
677 
678 	if (cp->type != AUDIO_MIXER_VALUE || cp->un.value.num_channels != 1)
679 		return(EINVAL);
680 
681 	switch(cp->dev) {
682 	    case SUNAUDIO_MIC_PORT:
683 		    sc->sc_rlevel = cp->un.value.level[AUDIO_MIXER_LEVEL_MONO];
684 		    break;
685 	    case SUNAUDIO_SPEAKER:
686 	    case SUNAUDIO_HEADPHONES:
687 		    sc->sc_plevel = cp->un.value.level[AUDIO_MIXER_LEVEL_MONO];
688 		    break;
689 	    case SUNAUDIO_MONITOR:
690 		    sc->sc_mlevel = cp->un.value.level[AUDIO_MIXER_LEVEL_MONO];
691 		    break;
692 	    default:
693 		    return(EINVAL);
694 		    /* NOTREACHED */
695 	}
696 	return(0);
697 }
698 
699 int
700 amd7930_get_port(addr, cp)
701 	void *addr;
702 	mixer_ctrl_t *cp;
703 {
704 	register struct amd7930_softc *sc = addr;
705 
706 	DPRINTF(("amd7930_get_port: port=%d", cp->dev));
707 
708 	if (cp->type != AUDIO_MIXER_VALUE || cp->un.value.num_channels != 1)
709 		return(EINVAL);
710 
711 	switch(cp->dev) {
712 	    case SUNAUDIO_MIC_PORT:
713 		    cp->un.value.level[AUDIO_MIXER_LEVEL_MONO] = sc->sc_rlevel;
714 		    break;
715 	    case SUNAUDIO_SPEAKER:
716 	    case SUNAUDIO_HEADPHONES:
717 		    cp->un.value.level[AUDIO_MIXER_LEVEL_MONO] = sc->sc_plevel;
718 		    break;
719 	    case SUNAUDIO_MONITOR:
720 		    cp->un.value.level[AUDIO_MIXER_LEVEL_MONO] = sc->sc_mlevel;
721 		    break;
722 	    default:
723 		    return(EINVAL);
724 		    /* NOTREACHED */
725 	}
726 	return(0);
727 }
728 
729 int
730 amd7930_query_devinfo(addr, dip)
731 	void *addr;
732 	register mixer_devinfo_t *dip;
733 {
734 	switch(dip->index) {
735 	    case SUNAUDIO_MIC_PORT:
736 		    dip->type = AUDIO_MIXER_VALUE;
737 		    dip->mixer_class = SUNAUDIO_INPUT_CLASS;
738 		    dip->prev = dip->next = AUDIO_MIXER_LAST;
739 		    strcpy(dip->label.name, AudioNmicrophone);
740 		    dip->un.v.num_channels = 1;
741 		    strcpy(dip->un.v.units.name, AudioNvolume);
742 		    break;
743 	    case SUNAUDIO_SPEAKER:
744 		    dip->type = AUDIO_MIXER_VALUE;
745 		    dip->mixer_class = SUNAUDIO_OUTPUT_CLASS;
746 		    dip->prev = dip->next = AUDIO_MIXER_LAST;
747 		    strcpy(dip->label.name, AudioNspeaker);
748 		    dip->un.v.num_channels = 1;
749 		    strcpy(dip->un.v.units.name, AudioNvolume);
750 		    break;
751 	    case SUNAUDIO_HEADPHONES:
752 		    dip->type = AUDIO_MIXER_VALUE;
753 		    dip->mixer_class = SUNAUDIO_OUTPUT_CLASS;
754 		    dip->prev = dip->next = AUDIO_MIXER_LAST;
755 		    strcpy(dip->label.name, AudioNheadphone);
756 		    dip->un.v.num_channels = 1;
757 		    strcpy(dip->un.v.units.name, AudioNvolume);
758 		    break;
759 	    case SUNAUDIO_MONITOR:
760 		    dip->type = AUDIO_MIXER_VALUE;
761 		    dip->mixer_class = SUNAUDIO_OUTPUT_CLASS;
762 		    dip->next = dip->prev = AUDIO_MIXER_LAST;
763 		    strcpy(dip->label.name, AudioNmonitor);
764 		    dip->un.v.num_channels = 1;
765 		    strcpy(dip->un.v.units.name, AudioNvolume);
766 		    break;
767 	    case SUNAUDIO_INPUT_CLASS:
768 		    dip->type = AUDIO_MIXER_CLASS;
769 		    dip->mixer_class = SUNAUDIO_INPUT_CLASS;
770 		    dip->next = dip->prev = AUDIO_MIXER_LAST;
771 		    strcpy(dip->label.name, AudioCInputs);
772 		    break;
773 	    case SUNAUDIO_OUTPUT_CLASS:
774 		    dip->type = AUDIO_MIXER_CLASS;
775 		    dip->mixer_class = SUNAUDIO_OUTPUT_CLASS;
776 		    dip->next = dip->prev = AUDIO_MIXER_LAST;
777 		    strcpy(dip->label.name, AudioCOutputs);
778 		    break;
779 	    default:
780 		    return ENXIO;
781 		    /*NOTREACHED*/
782 	}
783 
784 	DPRINTF(("AUDIO_MIXER_DEVINFO: name=%s\n", dip->label.name));
785 
786 	return(0);
787 }
788 
789 #ifdef AUDIO_C_HANDLER
790 int
791 amd7930hwintr(au0)
792 	void *au0;
793 {
794 	register struct auio *au = au0;
795 	register volatile struct amd7930 *amd = au->au_amd;
796 	register u_char *d, *e;
797 	register int k;
798 
799 	k = amd->ir;		/* clear interrupt */
800 
801 	/* receive incoming data */
802 	d = au->au_rdata;
803 	e = au->au_rend;
804 	if (d && d <= e) {
805 		*d = amd->bbrb;
806 		au->au_rdata++;
807 		if (d == e) {
808 #ifdef AUDIO_DEBUG
809 		        if (amd7930debug > 1)
810                 		Dprintf("amd7930hwintr: swintr(r) requested");
811 #endif
812 			AUDIO_SET_SWINTR;
813 		}
814 	}
815 
816 	/* send outgoing data */
817 	d = au->au_pdata;
818 	e = au->au_pend;
819 	if (d && d <= e) {
820 		amd->bbtb = *d;
821 		au->au_pdata++;
822 		if (d == e) {
823 #ifdef AUDIO_DEBUG
824 		        if (amd7930debug > 1)
825                 		Dprintf("amd7930hwintr: swintr(p) requested");
826 #endif
827 			AUDIO_SET_SWINTR;
828 		}
829 	}
830 
831 	*(au->au_intrcnt)++;
832 	return (1);
833 }
834 #endif /* AUDIO_C_HANDLER */
835 
836 int
837 amd7930swintr(sc0)
838 	void *sc0;
839 {
840 	register struct amd7930_softc *sc = sc0;
841 	register struct auio *au;
842 	register int s, ret = 0;
843 
844 #ifdef AUDIO_DEBUG
845 	if (amd7930debug > 1)
846 		Dprintf("audiointr: sc=0x%x\n",sc);
847 #endif
848 
849 	au = &sc->sc_au;
850 	s = splaudio();
851 	if (au->au_rdata > au->au_rend && sc->sc_rintr != NULL) {
852 		splx(s);
853 		ret = 1;
854 		(*sc->sc_rintr)(sc->sc_rarg);
855 		s = splaudio();
856 	}
857 	if (au->au_pdata > au->au_pend && sc->sc_pintr != NULL) {
858 		splx(s);
859 		ret = 1;
860 		(*sc->sc_pintr)(sc->sc_parg);
861 	} else
862 		splx(s);
863 	return (ret);
864 }
865 #endif /* NAUDIO > 0 */
866