xref: /netbsd/sys/arch/sparc/dev/audioamd.c (revision bf9ec67e)
1 /*	$NetBSD: audioamd.c,v 1.8 2002/03/11 16:27:01 pk 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(sc)
395 	struct audioamd_softc *au0;
396 {
397 	struct auio *au = &sc->sc_au;
398 	u_int8_t *d, *e;
399 	int k;
400 
401 	/* clear interrupt */
402 	k = audioamd_codec_dread(sc, AM7930_DREG_IR);
403 
404 	/* receive incoming data */
405 	d = au->au_rdata;
406 	e = au->au_rend;
407 	if (d && d <= e) {
408 		*d = audioamd_codec_dread(sc, AM7930_DREG_BBRB);
409 		au->au_rdata++;
410 		if (d == e) {
411 			DPRINTFN(1, ("am7930hwintr: swintr(r) requested"));
412 			AUDIO_SET_SWINTR;
413 		}
414 	}
415 
416 	/* send outgoing data */
417 	d = au->au_pdata;
418 	e = au->au_pend;
419 	if (d && d <= e) {
420 		audioamd_codec_dwrite(sc, AM7930_DREG_BBTB, *d);
421 		au->au_pdata++;
422 		if (d == e) {
423 			DPRINTFN(1, ("am7930hwintr: swintr(p) requested"));
424 			AUDIO_SET_SWINTR;
425 		}
426 	}
427 
428 	*(au->au_intrcnt)++;
429 	return (1);
430 }
431 #endif /* AUDIO_C_HANDLER */
432 
433 int
434 am7930swintr(sc0)
435 	void *sc0;
436 {
437 	struct audioamd_softc *sc = sc0;
438 	struct auio *au;
439 	int s, ret = 0;
440 
441 	DPRINTFN(1, ("audiointr: sc=%p\n", sc););
442 
443 	au = &sc->sc_au;
444 	s = splaudio();
445 	if (au->au_rdata > au->au_rend && sc->sc_rintr != NULL) {
446 		splx(s);
447 		ret = 1;
448 		(*sc->sc_rintr)(sc->sc_rarg);
449 		s = splaudio();
450 	}
451 	if (au->au_pdata > au->au_pend && sc->sc_pintr != NULL) {
452 		splx(s);
453 		ret = 1;
454 		(*sc->sc_pintr)(sc->sc_parg);
455 	} else
456 		splx(s);
457 	return (ret);
458 }
459 
460 
461 /* indirect write */
462 void
463 audioamd_codec_iwrite(sc, reg, val)
464 	struct am7930_softc *sc;
465 	int reg;
466 	u_int8_t val;
467 {
468 	struct audioamd_softc *mdsc = (struct audioamd_softc *)sc;
469 
470 	audioamd_codec_dwrite(mdsc, AM7930_DREG_CR, reg);
471 	audioamd_codec_dwrite(mdsc, AM7930_DREG_DR, val);
472 }
473 
474 void
475 audioamd_codec_iwrite16(sc, reg, val)
476 	struct am7930_softc *sc;
477 	int reg;
478 	u_int16_t val;
479 {
480 	struct audioamd_softc *mdsc = (struct audioamd_softc *)sc;
481 
482 	audioamd_codec_dwrite(mdsc, AM7930_DREG_CR, reg);
483 	audioamd_codec_dwrite(mdsc, AM7930_DREG_DR, val);
484 	audioamd_codec_dwrite(mdsc, AM7930_DREG_DR, val>>8);
485 }
486 
487 
488 /* indirect read */
489 u_int8_t
490 audioamd_codec_iread(sc, reg)
491 	struct am7930_softc *sc;
492 	int reg;
493 {
494 	struct audioamd_softc *mdsc = (struct audioamd_softc *)sc;
495 
496 	audioamd_codec_dwrite(mdsc, AM7930_DREG_CR, reg);
497 	return (audioamd_codec_dread(mdsc, AM7930_DREG_DR));
498 }
499 
500 u_int16_t
501 audioamd_codec_iread16(sc, reg)
502 	struct am7930_softc *sc;
503 	int reg;
504 {
505 	struct audioamd_softc *mdsc = (struct audioamd_softc *)sc;
506 	u_int8_t lo, hi;
507 
508 	audioamd_codec_dwrite(mdsc, AM7930_DREG_CR, reg);
509 	lo = audioamd_codec_dread(mdsc, AM7930_DREG_DR);
510 	hi = audioamd_codec_dread(mdsc, AM7930_DREG_DR);
511 	return ((hi << 8) | lo);
512 }
513 
514 /* direct read */
515 u_int8_t
516 audioamd_codec_dread(sc, reg)
517 	struct audioamd_softc *sc;
518 	int reg;
519 {
520 	return (bus_space_read_1(sc->sc_bt, sc->sc_bh, reg));
521 }
522 
523 /* direct write */
524 void
525 audioamd_codec_dwrite(sc, reg, val)
526 	struct audioamd_softc *sc;
527 	int reg;
528 	u_int8_t val;
529 {
530 	bus_space_write_1(sc->sc_bt, sc->sc_bh, reg, val);
531 }
532 
533 int
534 audioamd_getdev(addr, retp)
535 	void *addr;
536 	struct audio_device *retp;
537 {
538 
539 	*retp = audioamd_device;
540 	return (0);
541 }
542 
543 #endif /* NAUDIO > 0 */
544