audioamd.c revision 1.7.4.4 1 /* $NetBSD: audioamd.c,v 1.7.4.4 2002/10/18 02:39:51 nathanw 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