xref: /netbsd-src/sys/arch/sparc/dev/audioamd.c (revision 08c81a9c2dc8c7300e893321eb65c0925d60871c)
1 /*	$NetBSD: audioamd.c,v 1.9 2002/06/13 21:02:45 gmcgarry 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_sbus_attach __P((struct device *, struct device *, void *));
111 int	audioamd_sbus_match __P((struct device *, struct cfdata *, void *));
112 void	audioamd_attach(struct audioamd_softc *sc, int);
113 
114 struct cfattach audioamd_mainbus_ca = {
115 	sizeof(struct audioamd_softc),
116 	audioamd_mainbus_match,
117 	audioamd_mainbus_attach
118 };
119 
120 struct cfattach audioamd_sbus_ca = {
121 	sizeof(struct audioamd_softc),
122 	audioamd_sbus_match,
123 	audioamd_sbus_attach
124 };
125 
126 /*
127  * Define our interface into the am7930 MI driver.
128  */
129 
130 u_int8_t	audioamd_codec_iread __P((struct am7930_softc *, int));
131 u_int16_t	audioamd_codec_iread16 __P((struct am7930_softc *, int));
132 u_int8_t	audioamd_codec_dread __P((struct audioamd_softc *, int));
133 void	audioamd_codec_iwrite __P((struct am7930_softc *, int, u_int8_t));
134 void	audioamd_codec_iwrite16 __P((struct am7930_softc *, int, u_int16_t));
135 void	audioamd_codec_dwrite __P((struct audioamd_softc *, int, u_int8_t));
136 void	audioamd_onopen __P((struct am7930_softc *sc));
137 void	audioamd_onclose __P((struct am7930_softc *sc));
138 
139 struct am7930_glue audioamd_glue = {
140 	audioamd_codec_iread,
141 	audioamd_codec_iwrite,
142 	audioamd_codec_iread16,
143 	audioamd_codec_iwrite16,
144 	audioamd_onopen,
145 	audioamd_onclose,
146 	0,
147 	0,
148 	0,
149 };
150 
151 /*
152  * Define our interface to the higher level audio driver.
153  */
154 int	audioamd_start_output __P((void *, void *, int, void (*)(void *),
155 				  void *));
156 int	audioamd_start_input __P((void *, void *, int, void (*)(void *),
157 				 void *));
158 int	audioamd_getdev __P((void *, struct audio_device *));
159 
160 struct audio_hw_if sa_hw_if = {
161 	am7930_open,
162 	am7930_close,
163 	0,
164 	am7930_query_encoding,
165 	am7930_set_params,
166 	am7930_round_blocksize,
167 	am7930_commit_settings,
168 	0,
169 	0,
170 	audioamd_start_output,		/* md */
171 	audioamd_start_input,		/* md */
172 	am7930_halt_output,
173 	am7930_halt_input,
174 	0,
175 	audioamd_getdev,
176 	0,
177 	am7930_set_port,
178 	am7930_get_port,
179 	am7930_query_devinfo,
180 	0,
181 	0,
182 	0,
183         0,
184 	am7930_get_props,
185 	0,
186 	0,
187         0,
188 };
189 
190 struct audio_device audioamd_device = {
191 	"am7930",
192 	"x",
193 	"audioamd"
194 };
195 
196 
197 int
198 audioamd_mainbus_match(parent, cf, aux)
199 	struct device *parent;
200 	struct cfdata *cf;
201 	void *aux;
202 {
203 	struct mainbus_attach_args *ma = aux;
204 
205 	if (CPU_ISSUN4)
206 		return (0);
207 	return (strcmp(AUDIO_ROM_NAME, ma->ma_name) == 0);
208 }
209 
210 int
211 audioamd_sbus_match(parent, cf, aux)
212 	struct device *parent;
213 	struct cfdata *cf;
214 	void *aux;
215 {
216 	struct sbus_attach_args *sa = aux;
217 
218 	return (strcmp(AUDIO_ROM_NAME, sa->sa_name) == 0);
219 }
220 
221 void
222 audioamd_mainbus_attach(parent, self, aux)
223 	struct device *parent, *self;
224 	void *aux;
225 {
226 	struct mainbus_attach_args *ma = aux;
227 	struct audioamd_softc *sc = (struct audioamd_softc *)self;
228 	bus_space_handle_t bh;
229 
230 	sc->sc_bt = ma->ma_bustag;
231 
232 	if (bus_space_map(
233 			ma->ma_bustag,
234 			ma->ma_paddr,
235 			AM7930_DREG_SIZE,
236 			BUS_SPACE_MAP_LINEAR,
237 			&bh) != 0) {
238 		printf("%s: cannot map registers\n", self->dv_xname);
239 		return;
240 	}
241 	sc->sc_bh = bh;
242 	audioamd_attach(sc, ma->ma_pri);
243 }
244 
245 
246 void
247 audioamd_sbus_attach(parent, self, aux)
248 	struct device *parent, *self;
249 	void *aux;
250 {
251 	struct sbus_attach_args *sa = aux;
252 	struct audioamd_softc *sc = (struct audioamd_softc *)self;
253 	bus_space_handle_t bh;
254 
255 	sc->sc_bt = sa->sa_bustag;
256 
257 	if (sbus_bus_map(sa->sa_bustag,
258 			 sa->sa_slot, sa->sa_offset,
259 			 AM7930_DREG_SIZE,
260 			 0, &bh) != 0) {
261 		printf("%s: cannot map registers\n", self->dv_xname);
262 		return;
263 	}
264 	sc->sc_bh = bh;
265 	audioamd_attach(sc, sa->sa_pri);
266 }
267 
268 void
269 audioamd_attach(sc, pri)
270 	struct audioamd_softc *sc;
271 	int pri;
272 {
273 
274 	printf(" softpri %d\n", PIL_AUSOFT);
275 
276 	/*
277 	 * Set up glue for MI code early; we use some of it here.
278 	 */
279 	sc->sc_am7930.sc_glue = &audioamd_glue;
280 
281 	am7930_init(&sc->sc_am7930, AUDIOAMD_POLL_MODE);
282 
283 #ifndef AUDIO_C_HANDLER
284 	auiop = &sc->sc_au;
285 	(void)bus_intr_establish(sc->sc_bt, pri, IPL_AUDIO,
286 				 BUS_INTR_ESTABLISH_FASTTRAP,
287 				 (int (*) __P((void *)))amd7930_trap, NULL);
288 #else
289 	(void)bus_intr_establish(sc->sc_bt, pri, IPL_AUDIO, 0,
290 				 am7930hwintr, sc);
291 #endif
292 	(void)bus_intr_establish(sc->sc_bt, PIL_AUSOFT, IPL_AUDIO,
293 				 BUS_INTR_ESTABLISH_SOFTINTR,
294 				 am7930swintr, sc);
295 
296 	evcnt_attach_dynamic(&sc->sc_intrcnt, EVCNT_TYPE_INTR, NULL,
297 	    sc->sc_am7930.sc_dev.dv_xname, "intr");
298 
299 	audio_attach_mi(&sa_hw_if, sc, &sc->sc_am7930.sc_dev);
300 }
301 
302 
303 void
304 audioamd_onopen(sc)
305 	struct am7930_softc *sc;
306 {
307 	struct audioamd_softc *mdsc = (struct audioamd_softc *)sc;
308 
309 	/* reset pdma state */
310 	mdsc->sc_rintr = 0;
311 	mdsc->sc_rarg = 0;
312 	mdsc->sc_pintr = 0;
313 	mdsc->sc_parg = 0;
314 
315 	mdsc->sc_au.au_rdata = 0;
316 	mdsc->sc_au.au_pdata = 0;
317 }
318 
319 
320 void
321 audioamd_onclose(sc)
322 	struct am7930_softc *sc;
323 {
324 	/* On sparc, just do the chipset-level halt. */
325 	am7930_halt_input(sc);
326 	am7930_halt_output(sc);
327 }
328 
329 int
330 audioamd_start_output(addr, p, cc, intr, arg)
331 	void *addr;
332 	void *p;
333 	int cc;
334 	void (*intr) __P((void *));
335 	void *arg;
336 {
337 	struct audioamd_softc *sc = addr;
338 
339 	DPRINTFN(1, ("sa_start_output: cc=%d %p (%p)\n", cc, intr, arg));
340 
341 	if (!sc->sc_am7930.sc_locked) {
342 		audioamd_codec_iwrite(&sc->sc_am7930,
343 			AM7930_IREG_INIT, AM7930_INIT_PMS_ACTIVE);
344 		sc->sc_am7930.sc_locked = 1;
345 		DPRINTF(("sa_start_output: started intrs.\n"));
346 	}
347 	sc->sc_pintr = intr;
348 	sc->sc_parg = arg;
349 #ifndef AUDIO_C_HANDLER
350 	sc->sc_au.au_bt = sc->sc_bt;
351 	sc->sc_au.au_bh = sc->sc_bh;
352 #endif
353 	sc->sc_au.au_pdata = p;
354 	sc->sc_au.au_pend = (char *)p + cc - 1;
355 	return(0);
356 }
357 
358 int
359 audioamd_start_input(addr, p, cc, intr, arg)
360 	void *addr;
361 	void *p;
362 	int cc;
363 	void (*intr) __P((void *));
364 	void *arg;
365 {
366 	struct audioamd_softc *sc = addr;
367 
368 	DPRINTFN(1, ("sa_start_input: cc=%d %p (%p)\n", cc, intr, arg));
369 
370 	if (!sc->sc_am7930.sc_locked) {
371 		audioamd_codec_iwrite(&sc->sc_am7930,
372 			AM7930_IREG_INIT, AM7930_INIT_PMS_ACTIVE);
373 		sc->sc_am7930.sc_locked = 1;
374 		DPRINTF(("sa_start_input: started intrs.\n"));
375 	}
376 	sc->sc_rintr = intr;
377 	sc->sc_rarg = arg;
378 #ifndef AUDIO_C_HANDLER
379 	sc->sc_au.au_bt = sc->sc_bt;
380 	sc->sc_au.au_bh = sc->sc_bh;
381 #endif
382 	sc->sc_au.au_rdata = p;
383 	sc->sc_au.au_rend = (char *)p + cc -1;
384 	return(0);
385 }
386 
387 
388 /*
389  * Pseudo-DMA support: either C or locore assember.
390  */
391 
392 #ifdef AUDIO_C_HANDLER
393 int
394 am7930hwintr(v)
395 	void *v;
396 {
397 	struct audioamd_softc *sc = v;
398 	struct auio *au = &sc->sc_au;
399 	u_int8_t *d, *e;
400 	int k;
401 
402 	/* clear interrupt */
403 	k = audioamd_codec_dread(sc, AM7930_DREG_IR);
404 
405 	/* receive incoming data */
406 	d = au->au_rdata;
407 	e = au->au_rend;
408 	if (d && d <= e) {
409 		*d = audioamd_codec_dread(sc, AM7930_DREG_BBRB);
410 		au->au_rdata++;
411 		if (d == e) {
412 			DPRINTFN(1, ("am7930hwintr: swintr(r) requested"));
413 			AUDIO_SET_SWINTR;
414 		}
415 	}
416 
417 	/* send outgoing data */
418 	d = au->au_pdata;
419 	e = au->au_pend;
420 	if (d && d <= e) {
421 		audioamd_codec_dwrite(sc, AM7930_DREG_BBTB, *d);
422 		au->au_pdata++;
423 		if (d == e) {
424 			DPRINTFN(1, ("am7930hwintr: swintr(p) requested"));
425 			AUDIO_SET_SWINTR;
426 		}
427 	}
428 
429 	au->au_intrcnt.ev_count++;
430 	return (1);
431 }
432 #endif /* AUDIO_C_HANDLER */
433 
434 int
435 am7930swintr(sc0)
436 	void *sc0;
437 {
438 	struct audioamd_softc *sc = sc0;
439 	struct auio *au;
440 	int s, ret = 0;
441 
442 	DPRINTFN(1, ("audiointr: sc=%p\n", sc););
443 
444 	au = &sc->sc_au;
445 	s = splaudio();
446 	if (au->au_rdata > au->au_rend && sc->sc_rintr != NULL) {
447 		splx(s);
448 		ret = 1;
449 		(*sc->sc_rintr)(sc->sc_rarg);
450 		s = splaudio();
451 	}
452 	if (au->au_pdata > au->au_pend && sc->sc_pintr != NULL) {
453 		splx(s);
454 		ret = 1;
455 		(*sc->sc_pintr)(sc->sc_parg);
456 	} else
457 		splx(s);
458 	return (ret);
459 }
460 
461 
462 /* indirect write */
463 void
464 audioamd_codec_iwrite(sc, reg, val)
465 	struct am7930_softc *sc;
466 	int reg;
467 	u_int8_t val;
468 {
469 	struct audioamd_softc *mdsc = (struct audioamd_softc *)sc;
470 
471 	audioamd_codec_dwrite(mdsc, AM7930_DREG_CR, reg);
472 	audioamd_codec_dwrite(mdsc, AM7930_DREG_DR, val);
473 }
474 
475 void
476 audioamd_codec_iwrite16(sc, reg, val)
477 	struct am7930_softc *sc;
478 	int reg;
479 	u_int16_t val;
480 {
481 	struct audioamd_softc *mdsc = (struct audioamd_softc *)sc;
482 
483 	audioamd_codec_dwrite(mdsc, AM7930_DREG_CR, reg);
484 	audioamd_codec_dwrite(mdsc, AM7930_DREG_DR, val);
485 	audioamd_codec_dwrite(mdsc, AM7930_DREG_DR, val>>8);
486 }
487 
488 
489 /* indirect read */
490 u_int8_t
491 audioamd_codec_iread(sc, reg)
492 	struct am7930_softc *sc;
493 	int reg;
494 {
495 	struct audioamd_softc *mdsc = (struct audioamd_softc *)sc;
496 
497 	audioamd_codec_dwrite(mdsc, AM7930_DREG_CR, reg);
498 	return (audioamd_codec_dread(mdsc, AM7930_DREG_DR));
499 }
500 
501 u_int16_t
502 audioamd_codec_iread16(sc, reg)
503 	struct am7930_softc *sc;
504 	int reg;
505 {
506 	struct audioamd_softc *mdsc = (struct audioamd_softc *)sc;
507 	u_int8_t lo, hi;
508 
509 	audioamd_codec_dwrite(mdsc, AM7930_DREG_CR, reg);
510 	lo = audioamd_codec_dread(mdsc, AM7930_DREG_DR);
511 	hi = audioamd_codec_dread(mdsc, AM7930_DREG_DR);
512 	return ((hi << 8) | lo);
513 }
514 
515 /* direct read */
516 u_int8_t
517 audioamd_codec_dread(sc, reg)
518 	struct audioamd_softc *sc;
519 	int reg;
520 {
521 	return (bus_space_read_1(sc->sc_bt, sc->sc_bh, reg));
522 }
523 
524 /* direct write */
525 void
526 audioamd_codec_dwrite(sc, reg, val)
527 	struct audioamd_softc *sc;
528 	int reg;
529 	u_int8_t val;
530 {
531 	bus_space_write_1(sc->sc_bt, sc->sc_bh, reg, val);
532 }
533 
534 int
535 audioamd_getdev(addr, retp)
536 	void *addr;
537 	struct audio_device *retp;
538 {
539 
540 	*retp = audioamd_device;
541 	return (0);
542 }
543 
544 #endif /* NAUDIO > 0 */
545