xref: /netbsd/sys/arch/sparc/dev/audioamd.c (revision c4a72b64)
1 /*	$NetBSD: audioamd.c,v 1.13 2002/10/15 13:49:52 jdc Exp $	*/
2 /*	NetBSD: am7930_sparc.c,v 1.44 1999/03/14 22:29:00 jonathan Exp 	*/
3 
4 /*
5  * Copyright (c) 1995 Rolf Grossmann
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. All advertising materials mentioning features or use of this software
17  *    must display the following acknowledgement:
18  *      This product includes software developed by Rolf Grossmann.
19  * 4. The name of the author may not be used to endorse or promote products
20  *    derived from this software without specific prior written permission
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
23  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
25  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
27  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
31  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32  */
33 
34 #include "audio.h"
35 #if NAUDIO > 0
36 
37 #include <sys/param.h>
38 #include <sys/systm.h>
39 #include <sys/errno.h>
40 #include <sys/device.h>
41 
42 #include <machine/bus.h>
43 #include <machine/intr.h>
44 #include <machine/autoconf.h>
45 
46 #include <sys/audioio.h>
47 #include <dev/audio_if.h>
48 
49 #include <dev/ic/am7930reg.h>
50 #include <dev/ic/am7930var.h>
51 #include <sparc/dev/audioamdvar.h>
52 
53 #define AUDIO_ROM_NAME "audio"
54 
55 #ifdef AUDIO_DEBUG
56 #define DPRINTF(x)      if (am7930debug) printf x
57 #define DPRINTFN(n,x)   if (am7930debug>(n)) printf x
58 #else
59 #define DPRINTF(x)
60 #define DPRINTFN(n,x)
61 #endif	/* AUDIO_DEBUG */
62 
63 
64 /* interrupt interfaces */
65 #ifdef AUDIO_C_HANDLER
66 int	am7930hwintr __P((void *));
67 #if defined(SUN4M)
68 #define AUDIO_SET_SWINTR do {		\
69 	if (CPU_ISSUN4M)		\
70 		raise(0, 4);		\
71 	else				\
72 		ienab_bis(IE_L4);	\
73 } while(0);
74 #else
75 #define AUDIO_SET_SWINTR ienab_bis(IE_L4)
76 #endif /* defined(SUN4M) */
77 #else
78 struct auio *auiop;
79 #endif /* AUDIO_C_HANDLER */
80 int	am7930swintr __P((void *));
81 
82 /*
83  * interrupt-handler status
84  */
85 struct am7930_intrhand {
86 	int	(*ih_fun) __P((void *));
87 	void	*ih_arg;
88 };
89 
90 struct audioamd_softc {
91 	struct am7930_softc sc_am7930;	/* glue to MI code */
92 
93 	bus_space_tag_t sc_bt;		/* bus cookie */
94 	bus_space_handle_t sc_bh;	/* device registers */
95 
96 	struct am7930_intrhand	sc_ih;	/* interrupt vector (hw or sw)  */
97 	void	(*sc_rintr)(void*);	/* input completion intr handler */
98 	void	*sc_rarg;		/* arg for sc_rintr() */
99 	void	(*sc_pintr)(void*);	/* output completion intr handler */
100 	void	*sc_parg;		/* arg for sc_pintr() */
101 
102 	/* sc_au is special in that the hardware interrupt handler uses it */
103 	struct  auio sc_au;		/* recv and xmit buffers, etc */
104 #define sc_intrcnt	sc_au.au_intrcnt	/* statistics */
105 };
106 
107 void	audioamd_mainbus_attach __P((struct device *,
108 		struct device *, void *));
109 int	audioamd_mainbus_match __P((struct device *, struct cfdata *, void *));
110 void	audioamd_obio_attach __P((struct device *, struct device *, void *));
111 int	audioamd_obio_match __P((struct device *, struct cfdata *, void *));
112 void	audioamd_sbus_attach __P((struct device *, struct device *, void *));
113 int	audioamd_sbus_match __P((struct device *, struct cfdata *, void *));
114 void	audioamd_attach(struct audioamd_softc *sc, int);
115 
116 CFATTACH_DECL(audioamd_mainbus, sizeof(struct audioamd_softc),
117     audioamd_mainbus_match, audioamd_mainbus_attach, NULL, NULL);
118 
119 CFATTACH_DECL(audioamd_obio, sizeof(struct audioamd_softc),
120     audioamd_obio_match, audioamd_obio_attach, NULL, NULL);
121 
122 CFATTACH_DECL(audioamd_sbus, sizeof(struct audioamd_softc),
123     audioamd_sbus_match, audioamd_sbus_attach, NULL, NULL);
124 
125 /*
126  * Define our interface into the am7930 MI driver.
127  */
128 
129 u_int8_t	audioamd_codec_iread __P((struct am7930_softc *, int));
130 u_int16_t	audioamd_codec_iread16 __P((struct am7930_softc *, int));
131 u_int8_t	audioamd_codec_dread __P((struct audioamd_softc *, int));
132 void	audioamd_codec_iwrite __P((struct am7930_softc *, int, u_int8_t));
133 void	audioamd_codec_iwrite16 __P((struct am7930_softc *, int, u_int16_t));
134 void	audioamd_codec_dwrite __P((struct audioamd_softc *, int, u_int8_t));
135 void	audioamd_onopen __P((struct am7930_softc *sc));
136 void	audioamd_onclose __P((struct am7930_softc *sc));
137 
138 struct am7930_glue audioamd_glue = {
139 	audioamd_codec_iread,
140 	audioamd_codec_iwrite,
141 	audioamd_codec_iread16,
142 	audioamd_codec_iwrite16,
143 	audioamd_onopen,
144 	audioamd_onclose,
145 	0,
146 	0,
147 	0,
148 };
149 
150 /*
151  * Define our interface to the higher level audio driver.
152  */
153 int	audioamd_start_output __P((void *, void *, int, void (*)(void *),
154 				  void *));
155 int	audioamd_start_input __P((void *, void *, int, void (*)(void *),
156 				 void *));
157 int	audioamd_getdev __P((void *, struct audio_device *));
158 
159 struct audio_hw_if sa_hw_if = {
160 	am7930_open,
161 	am7930_close,
162 	0,
163 	am7930_query_encoding,
164 	am7930_set_params,
165 	am7930_round_blocksize,
166 	am7930_commit_settings,
167 	0,
168 	0,
169 	audioamd_start_output,		/* md */
170 	audioamd_start_input,		/* md */
171 	am7930_halt_output,
172 	am7930_halt_input,
173 	0,
174 	audioamd_getdev,
175 	0,
176 	am7930_set_port,
177 	am7930_get_port,
178 	am7930_query_devinfo,
179 	0,
180 	0,
181 	0,
182         0,
183 	am7930_get_props,
184 	0,
185 	0,
186         0,
187 };
188 
189 struct audio_device audioamd_device = {
190 	"am7930",
191 	"x",
192 	"audioamd"
193 };
194 
195 
196 int
197 audioamd_mainbus_match(parent, cf, aux)
198 	struct device *parent;
199 	struct cfdata *cf;
200 	void *aux;
201 {
202 	struct mainbus_attach_args *ma = aux;
203 
204 	if (CPU_ISSUN4)
205 		return (0);
206 	return (strcmp(AUDIO_ROM_NAME, ma->ma_name) == 0);
207 }
208 
209 int
210 audioamd_obio_match(parent, cf, aux)
211 	struct device *parent;
212 	struct cfdata *cf;
213 	void *aux;
214 {
215 	union obio_attach_args *uoba = aux;
216 
217 	if (uoba->uoba_isobio4 != 0)
218 		return (0);
219 
220 	return (strcmp("audio", uoba->uoba_sbus.sa_name) == 0);
221 }
222 
223 int
224 audioamd_sbus_match(parent, cf, aux)
225 	struct device *parent;
226 	struct cfdata *cf;
227 	void *aux;
228 {
229 	struct sbus_attach_args *sa = aux;
230 
231 	return (strcmp(AUDIO_ROM_NAME, sa->sa_name) == 0);
232 }
233 
234 void
235 audioamd_mainbus_attach(parent, self, aux)
236 	struct device *parent, *self;
237 	void *aux;
238 {
239 	struct mainbus_attach_args *ma = aux;
240 	struct audioamd_softc *sc = (struct audioamd_softc *)self;
241 	bus_space_handle_t bh;
242 
243 	sc->sc_bt = ma->ma_bustag;
244 
245 	if (bus_space_map(
246 			ma->ma_bustag,
247 			ma->ma_paddr,
248 			AM7930_DREG_SIZE,
249 			BUS_SPACE_MAP_LINEAR,
250 			&bh) != 0) {
251 		printf("%s: cannot map registers\n", self->dv_xname);
252 		return;
253 	}
254 	sc->sc_bh = bh;
255 	audioamd_attach(sc, ma->ma_pri);
256 }
257 
258 void
259 audioamd_obio_attach(parent, self, aux)
260 	struct device *parent, *self;
261 	void *aux;
262 {
263 	union obio_attach_args *uoba = aux;
264 	struct sbus_attach_args *sa = &uoba->uoba_sbus;
265 	struct audioamd_softc *sc = (struct audioamd_softc *)self;
266 	bus_space_handle_t bh;
267 
268 	sc->sc_bt = sa->sa_bustag;
269 
270 	if (sbus_bus_map(sa->sa_bustag,
271 			 sa->sa_slot, sa->sa_offset,
272 			 AM7930_DREG_SIZE,
273 			 0, &bh) != 0) {
274 		printf("%s: cannot map registers\n", self->dv_xname);
275 		return;
276 	}
277 	sc->sc_bh = bh;
278 	audioamd_attach(sc, sa->sa_pri);
279 }
280 
281 void
282 audioamd_sbus_attach(parent, self, aux)
283 	struct device *parent, *self;
284 	void *aux;
285 {
286 	struct sbus_attach_args *sa = aux;
287 	struct audioamd_softc *sc = (struct audioamd_softc *)self;
288 	bus_space_handle_t bh;
289 
290 	sc->sc_bt = sa->sa_bustag;
291 
292 	if (sbus_bus_map(sa->sa_bustag,
293 			 sa->sa_slot, sa->sa_offset,
294 			 AM7930_DREG_SIZE,
295 			 0, &bh) != 0) {
296 		printf("%s: cannot map registers\n", self->dv_xname);
297 		return;
298 	}
299 	sc->sc_bh = bh;
300 	audioamd_attach(sc, sa->sa_pri);
301 }
302 
303 void
304 audioamd_attach(sc, pri)
305 	struct audioamd_softc *sc;
306 	int pri;
307 {
308 
309 	printf(" softpri %d\n", PIL_AUSOFT);
310 
311 	/*
312 	 * Set up glue for MI code early; we use some of it here.
313 	 */
314 	sc->sc_am7930.sc_glue = &audioamd_glue;
315 
316 	am7930_init(&sc->sc_am7930, AUDIOAMD_POLL_MODE);
317 
318 #ifndef AUDIO_C_HANDLER
319 	auiop = &sc->sc_au;
320 	(void)bus_intr_establish(sc->sc_bt, pri, IPL_AUDIO,
321 				 BUS_INTR_ESTABLISH_FASTTRAP,
322 				 (int (*) __P((void *)))amd7930_trap, NULL);
323 #else
324 	(void)bus_intr_establish(sc->sc_bt, pri, IPL_AUDIO, 0,
325 				 am7930hwintr, sc);
326 #endif
327 	(void)bus_intr_establish(sc->sc_bt, PIL_AUSOFT, IPL_AUDIO,
328 				 BUS_INTR_ESTABLISH_SOFTINTR,
329 				 am7930swintr, sc);
330 
331 	evcnt_attach_dynamic(&sc->sc_intrcnt, EVCNT_TYPE_INTR, NULL,
332 	    sc->sc_am7930.sc_dev.dv_xname, "intr");
333 
334 	audio_attach_mi(&sa_hw_if, sc, &sc->sc_am7930.sc_dev);
335 }
336 
337 
338 void
339 audioamd_onopen(sc)
340 	struct am7930_softc *sc;
341 {
342 	struct audioamd_softc *mdsc = (struct audioamd_softc *)sc;
343 
344 	/* reset pdma state */
345 	mdsc->sc_rintr = 0;
346 	mdsc->sc_rarg = 0;
347 	mdsc->sc_pintr = 0;
348 	mdsc->sc_parg = 0;
349 
350 	mdsc->sc_au.au_rdata = 0;
351 	mdsc->sc_au.au_pdata = 0;
352 }
353 
354 
355 void
356 audioamd_onclose(sc)
357 	struct am7930_softc *sc;
358 {
359 	/* On sparc, just do the chipset-level halt. */
360 	am7930_halt_input(sc);
361 	am7930_halt_output(sc);
362 }
363 
364 int
365 audioamd_start_output(addr, p, cc, intr, arg)
366 	void *addr;
367 	void *p;
368 	int cc;
369 	void (*intr) __P((void *));
370 	void *arg;
371 {
372 	struct audioamd_softc *sc = addr;
373 
374 	DPRINTFN(1, ("sa_start_output: cc=%d %p (%p)\n", cc, intr, arg));
375 
376 	if (!sc->sc_am7930.sc_locked) {
377 		audioamd_codec_iwrite(&sc->sc_am7930,
378 			AM7930_IREG_INIT, AM7930_INIT_PMS_ACTIVE);
379 		sc->sc_am7930.sc_locked = 1;
380 		DPRINTF(("sa_start_output: started intrs.\n"));
381 	}
382 	sc->sc_pintr = intr;
383 	sc->sc_parg = arg;
384 #ifndef AUDIO_C_HANDLER
385 	sc->sc_au.au_bt = sc->sc_bt;
386 	sc->sc_au.au_bh = sc->sc_bh;
387 #endif
388 	sc->sc_au.au_pdata = p;
389 	sc->sc_au.au_pend = (char *)p + cc - 1;
390 	return(0);
391 }
392 
393 int
394 audioamd_start_input(addr, p, cc, intr, arg)
395 	void *addr;
396 	void *p;
397 	int cc;
398 	void (*intr) __P((void *));
399 	void *arg;
400 {
401 	struct audioamd_softc *sc = addr;
402 
403 	DPRINTFN(1, ("sa_start_input: cc=%d %p (%p)\n", cc, intr, arg));
404 
405 	if (!sc->sc_am7930.sc_locked) {
406 		audioamd_codec_iwrite(&sc->sc_am7930,
407 			AM7930_IREG_INIT, AM7930_INIT_PMS_ACTIVE);
408 		sc->sc_am7930.sc_locked = 1;
409 		DPRINTF(("sa_start_input: started intrs.\n"));
410 	}
411 	sc->sc_rintr = intr;
412 	sc->sc_rarg = arg;
413 #ifndef AUDIO_C_HANDLER
414 	sc->sc_au.au_bt = sc->sc_bt;
415 	sc->sc_au.au_bh = sc->sc_bh;
416 #endif
417 	sc->sc_au.au_rdata = p;
418 	sc->sc_au.au_rend = (char *)p + cc -1;
419 	return(0);
420 }
421 
422 
423 /*
424  * Pseudo-DMA support: either C or locore assember.
425  */
426 
427 #ifdef AUDIO_C_HANDLER
428 int
429 am7930hwintr(v)
430 	void *v;
431 {
432 	struct audioamd_softc *sc = v;
433 	struct auio *au = &sc->sc_au;
434 	u_int8_t *d, *e;
435 	int k;
436 
437 	/* clear interrupt */
438 	k = audioamd_codec_dread(sc, AM7930_DREG_IR);
439 
440 	/* receive incoming data */
441 	d = au->au_rdata;
442 	e = au->au_rend;
443 	if (d && d <= e) {
444 		*d = audioamd_codec_dread(sc, AM7930_DREG_BBRB);
445 		au->au_rdata++;
446 		if (d == e) {
447 			DPRINTFN(1, ("am7930hwintr: swintr(r) requested"));
448 			AUDIO_SET_SWINTR;
449 		}
450 	}
451 
452 	/* send outgoing data */
453 	d = au->au_pdata;
454 	e = au->au_pend;
455 	if (d && d <= e) {
456 		audioamd_codec_dwrite(sc, AM7930_DREG_BBTB, *d);
457 		au->au_pdata++;
458 		if (d == e) {
459 			DPRINTFN(1, ("am7930hwintr: swintr(p) requested"));
460 			AUDIO_SET_SWINTR;
461 		}
462 	}
463 
464 	au->au_intrcnt.ev_count++;
465 	return (1);
466 }
467 #endif /* AUDIO_C_HANDLER */
468 
469 int
470 am7930swintr(sc0)
471 	void *sc0;
472 {
473 	struct audioamd_softc *sc = sc0;
474 	struct auio *au;
475 	int s, ret = 0;
476 
477 	DPRINTFN(1, ("audiointr: sc=%p\n", sc););
478 
479 	au = &sc->sc_au;
480 	s = splaudio();
481 	if (au->au_rdata > au->au_rend && sc->sc_rintr != NULL) {
482 		splx(s);
483 		ret = 1;
484 		(*sc->sc_rintr)(sc->sc_rarg);
485 		s = splaudio();
486 	}
487 	if (au->au_pdata > au->au_pend && sc->sc_pintr != NULL) {
488 		splx(s);
489 		ret = 1;
490 		(*sc->sc_pintr)(sc->sc_parg);
491 	} else
492 		splx(s);
493 	return (ret);
494 }
495 
496 
497 /* indirect write */
498 void
499 audioamd_codec_iwrite(sc, reg, val)
500 	struct am7930_softc *sc;
501 	int reg;
502 	u_int8_t val;
503 {
504 	struct audioamd_softc *mdsc = (struct audioamd_softc *)sc;
505 
506 	audioamd_codec_dwrite(mdsc, AM7930_DREG_CR, reg);
507 	audioamd_codec_dwrite(mdsc, AM7930_DREG_DR, val);
508 }
509 
510 void
511 audioamd_codec_iwrite16(sc, reg, val)
512 	struct am7930_softc *sc;
513 	int reg;
514 	u_int16_t val;
515 {
516 	struct audioamd_softc *mdsc = (struct audioamd_softc *)sc;
517 
518 	audioamd_codec_dwrite(mdsc, AM7930_DREG_CR, reg);
519 	audioamd_codec_dwrite(mdsc, AM7930_DREG_DR, val);
520 	audioamd_codec_dwrite(mdsc, AM7930_DREG_DR, val>>8);
521 }
522 
523 
524 /* indirect read */
525 u_int8_t
526 audioamd_codec_iread(sc, reg)
527 	struct am7930_softc *sc;
528 	int reg;
529 {
530 	struct audioamd_softc *mdsc = (struct audioamd_softc *)sc;
531 
532 	audioamd_codec_dwrite(mdsc, AM7930_DREG_CR, reg);
533 	return (audioamd_codec_dread(mdsc, AM7930_DREG_DR));
534 }
535 
536 u_int16_t
537 audioamd_codec_iread16(sc, reg)
538 	struct am7930_softc *sc;
539 	int reg;
540 {
541 	struct audioamd_softc *mdsc = (struct audioamd_softc *)sc;
542 	u_int8_t lo, hi;
543 
544 	audioamd_codec_dwrite(mdsc, AM7930_DREG_CR, reg);
545 	lo = audioamd_codec_dread(mdsc, AM7930_DREG_DR);
546 	hi = audioamd_codec_dread(mdsc, AM7930_DREG_DR);
547 	return ((hi << 8) | lo);
548 }
549 
550 /* direct read */
551 u_int8_t
552 audioamd_codec_dread(sc, reg)
553 	struct audioamd_softc *sc;
554 	int reg;
555 {
556 	return (bus_space_read_1(sc->sc_bt, sc->sc_bh, reg));
557 }
558 
559 /* direct write */
560 void
561 audioamd_codec_dwrite(sc, reg, val)
562 	struct audioamd_softc *sc;
563 	int reg;
564 	u_int8_t val;
565 {
566 	bus_space_write_1(sc->sc_bt, sc->sc_bh, reg, val);
567 }
568 
569 int
570 audioamd_getdev(addr, retp)
571 	void *addr;
572 	struct audio_device *retp;
573 {
574 
575 	*retp = audioamd_device;
576 	return (0);
577 }
578 
579 #endif /* NAUDIO > 0 */
580