audioamd.c revision 1.20 1 /* $NetBSD: audioamd.c,v 1.20 2005/01/15 15:19:51 kent 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 <sys/cdefs.h>
35 __KERNEL_RCSID(0, "$NetBSD: audioamd.c,v 1.20 2005/01/15 15:19:51 kent Exp $");
36
37 #include "audio.h"
38 #if NAUDIO > 0
39
40 #include <sys/param.h>
41 #include <sys/systm.h>
42 #include <sys/errno.h>
43 #include <sys/device.h>
44
45 #include <machine/bus.h>
46 #include <machine/intr.h>
47 #include <machine/autoconf.h>
48
49 #include <sys/audioio.h>
50 #include <dev/audio_if.h>
51
52 #include <dev/ic/am7930reg.h>
53 #include <dev/ic/am7930var.h>
54 #include <sparc/dev/audioamdvar.h>
55
56 #define AUDIO_ROM_NAME "audio"
57
58 #ifdef AUDIO_DEBUG
59 #define DPRINTF(x) if (am7930debug) printf x
60 #define DPRINTFN(n,x) if (am7930debug>(n)) printf x
61 #else
62 #define DPRINTF(x)
63 #define DPRINTFN(n,x)
64 #endif /* AUDIO_DEBUG */
65
66
67 /* interrupt interfaces */
68 int am7930hwintr(void *);
69 struct auio *auiop;
70 void am7930swintr(void *);
71
72 /* from amd7930intr.s: */
73 void amd7930_trap(void);
74
75 /*
76 * interrupt-handler status
77 */
78 struct am7930_intrhand {
79 int (*ih_fun)(void *);
80 void *ih_arg;
81 };
82
83 struct audioamd_softc {
84 struct am7930_softc sc_am7930; /* glue to MI code */
85
86 bus_space_tag_t sc_bt; /* bus cookie */
87 bus_space_handle_t sc_bh; /* device registers */
88
89 struct am7930_intrhand sc_ih; /* interrupt vector (hw or sw) */
90 void (*sc_rintr)(void*); /* input completion intr handler */
91 void *sc_rarg; /* arg for sc_rintr() */
92 void (*sc_pintr)(void*); /* output completion intr handler */
93 void *sc_parg; /* arg for sc_pintr() */
94
95 /* sc_au is special in that the hardware interrupt handler uses it */
96 struct auio sc_au; /* recv and xmit buffers, etc */
97 #define sc_intrcnt sc_au.au_intrcnt /* statistics */
98 void *sc_sicookie; /* softintr(9) cookie */
99 };
100
101 void audioamd_mainbus_attach(struct device *, struct device *, void *);
102 int audioamd_mainbus_match(struct device *, struct cfdata *, void *);
103 void audioamd_obio_attach(struct device *, struct device *, void *);
104 int audioamd_obio_match(struct device *, struct cfdata *, void *);
105 void audioamd_sbus_attach(struct device *, struct device *, void *);
106 int audioamd_sbus_match(struct device *, struct cfdata *, void *);
107 void audioamd_attach(struct audioamd_softc *, int);
108
109 CFATTACH_DECL(audioamd_mainbus, sizeof(struct audioamd_softc),
110 audioamd_mainbus_match, audioamd_mainbus_attach, NULL, NULL);
111
112 CFATTACH_DECL(audioamd_obio, sizeof(struct audioamd_softc),
113 audioamd_obio_match, audioamd_obio_attach, NULL, NULL);
114
115 CFATTACH_DECL(audioamd_sbus, sizeof(struct audioamd_softc),
116 audioamd_sbus_match, audioamd_sbus_attach, NULL, NULL);
117
118 /*
119 * Define our interface into the am7930 MI driver.
120 */
121
122 uint8_t audioamd_codec_iread(struct am7930_softc *, int);
123 uint16_t audioamd_codec_iread16(struct am7930_softc *, int);
124 uint8_t audioamd_codec_dread(struct audioamd_softc *, int);
125 void audioamd_codec_iwrite(struct am7930_softc *, int, uint8_t);
126 void audioamd_codec_iwrite16(struct am7930_softc *, int, uint16_t);
127 void audioamd_codec_dwrite(struct audioamd_softc *, int, uint8_t);
128 void audioamd_onopen(struct am7930_softc *);
129 void audioamd_onclose(struct am7930_softc *);
130
131 struct am7930_glue audioamd_glue = {
132 audioamd_codec_iread,
133 audioamd_codec_iwrite,
134 audioamd_codec_iread16,
135 audioamd_codec_iwrite16,
136 audioamd_onopen,
137 audioamd_onclose,
138 0,
139 0,
140 0,
141 };
142
143 /*
144 * Define our interface to the higher level audio driver.
145 */
146 int audioamd_start_output(void *, void *, int, void (*)(void *), void *);
147 int audioamd_start_input(void *, void *, int, void (*)(void *), void *);
148 int audioamd_getdev(void *, struct audio_device *);
149
150 const struct audio_hw_if sa_hw_if = {
151 am7930_open,
152 am7930_close,
153 0,
154 am7930_query_encoding,
155 am7930_set_params,
156 am7930_round_blocksize,
157 am7930_commit_settings,
158 0,
159 0,
160 audioamd_start_output, /* md */
161 audioamd_start_input, /* md */
162 am7930_halt_output,
163 am7930_halt_input,
164 0,
165 audioamd_getdev,
166 0,
167 am7930_set_port,
168 am7930_get_port,
169 am7930_query_devinfo,
170 0,
171 0,
172 0,
173 0,
174 am7930_get_props,
175 0,
176 0,
177 0,
178 };
179
180 struct audio_device audioamd_device = {
181 "am7930",
182 "x",
183 "audioamd"
184 };
185
186
187 int
188 audioamd_mainbus_match(struct device *parent, struct cfdata *cf, void *aux)
189 {
190 struct mainbus_attach_args *ma;
191
192 ma = aux;
193 if (CPU_ISSUN4)
194 return 0;
195 return strcmp(AUDIO_ROM_NAME, ma->ma_name) == 0;
196 }
197
198 int
199 audioamd_obio_match(struct device *parent, struct cfdata *cf, void *aux)
200 {
201 union obio_attach_args *uoba;
202
203 uoba = aux;
204 if (uoba->uoba_isobio4 != 0)
205 return 0;
206
207 return strcmp("audio", uoba->uoba_sbus.sa_name) == 0;
208 }
209
210 int
211 audioamd_sbus_match(struct device *parent, struct cfdata *cf, void *aux)
212 {
213 struct sbus_attach_args *sa;
214
215 sa = aux;
216 return strcmp(AUDIO_ROM_NAME, sa->sa_name) == 0;
217 }
218
219 void
220 audioamd_mainbus_attach(struct device *parent, struct device *self, void *aux)
221 {
222 struct mainbus_attach_args *ma;
223 struct audioamd_softc *sc;
224 bus_space_handle_t bh;
225
226 ma = aux;
227 sc = (struct audioamd_softc *)self;
228 sc->sc_bt = ma->ma_bustag;
229
230 if (bus_space_map(
231 ma->ma_bustag,
232 ma->ma_paddr,
233 AM7930_DREG_SIZE,
234 BUS_SPACE_MAP_LINEAR,
235 &bh) != 0) {
236 printf("%s: cannot map registers\n", self->dv_xname);
237 return;
238 }
239 sc->sc_bh = bh;
240 audioamd_attach(sc, ma->ma_pri);
241 }
242
243 void
244 audioamd_obio_attach(struct device *parent, struct device *self, void *aux)
245 {
246 union obio_attach_args *uoba;
247 struct sbus_attach_args *sa;
248 struct audioamd_softc *sc;
249 bus_space_handle_t bh;
250
251 uoba = aux;
252 sa = &uoba->uoba_sbus;
253 sc = (struct audioamd_softc *)self;
254 sc->sc_bt = sa->sa_bustag;
255
256 if (sbus_bus_map(sa->sa_bustag,
257 sa->sa_slot, sa->sa_offset,
258 AM7930_DREG_SIZE,
259 0, &bh) != 0) {
260 printf("%s: cannot map registers\n", self->dv_xname);
261 return;
262 }
263 sc->sc_bh = bh;
264 audioamd_attach(sc, sa->sa_pri);
265 }
266
267 void
268 audioamd_sbus_attach(struct device *parent, struct device *self, void *aux)
269 {
270 struct sbus_attach_args *sa;
271 struct audioamd_softc *sc;
272 bus_space_handle_t bh;
273
274 sa = aux;
275 sc = (struct audioamd_softc *)self;
276 sc->sc_bt = sa->sa_bustag;
277
278 if (sbus_bus_map(sa->sa_bustag,
279 sa->sa_slot, sa->sa_offset,
280 AM7930_DREG_SIZE,
281 0, &bh) != 0) {
282 printf("%s: cannot map registers\n", self->dv_xname);
283 return;
284 }
285 sc->sc_bh = bh;
286 audioamd_attach(sc, sa->sa_pri);
287 }
288
289 void
290 audioamd_attach(struct audioamd_softc *sc, int pri)
291 {
292
293 /*
294 * Set up glue for MI code early; we use some of it here.
295 */
296 sc->sc_am7930.sc_glue = &audioamd_glue;
297
298 am7930_init(&sc->sc_am7930, AUDIOAMD_POLL_MODE);
299
300 auiop = &sc->sc_au;
301
302 /* Copy bus tag & handle for use by am7930_trap */
303 sc->sc_au.au_bt = sc->sc_bt;
304 sc->sc_au.au_bh = sc->sc_bh;
305 (void)bus_intr_establish2(sc->sc_bt, pri, IPL_AUDIO,
306 am7930hwintr, sc, amd7930_trap);
307
308 sc->sc_sicookie = softintr_establish(IPL_SOFTAUDIO, am7930swintr, sc);
309 if (sc->sc_sicookie == NULL) {
310 printf("\n%s: cannot establish software interrupt\n",
311 sc->sc_am7930.sc_dev.dv_xname);
312 return;
313 }
314
315 printf(" softpri %d\n", IPL_SOFTAUDIO);
316
317
318 evcnt_attach_dynamic(&sc->sc_intrcnt, EVCNT_TYPE_INTR, NULL,
319 sc->sc_am7930.sc_dev.dv_xname, "intr");
320
321 audio_attach_mi(&sa_hw_if, sc, &sc->sc_am7930.sc_dev);
322 }
323
324
325 void
326 audioamd_onopen(struct am7930_softc *sc)
327 {
328 struct audioamd_softc *mdsc;
329
330 mdsc = (struct audioamd_softc *)sc;
331 /* reset pdma state */
332 mdsc->sc_rintr = 0;
333 mdsc->sc_rarg = 0;
334 mdsc->sc_pintr = 0;
335 mdsc->sc_parg = 0;
336
337 mdsc->sc_au.au_rdata = 0;
338 mdsc->sc_au.au_pdata = 0;
339 }
340
341
342 void
343 audioamd_onclose(struct am7930_softc *sc)
344 {
345 /* On sparc, just do the chipset-level halt. */
346 am7930_halt_input(sc);
347 am7930_halt_output(sc);
348 }
349
350 int
351 audioamd_start_output(void *addr, void *p, int cc,
352 void (*intr)(void *), void *arg)
353 {
354 struct audioamd_softc *sc;
355
356 DPRINTFN(1, ("sa_start_output: cc=%d %p (%p)\n", cc, intr, arg));
357 sc = addr;
358 audioamd_codec_iwrite(&sc->sc_am7930,
359 AM7930_IREG_INIT, AM7930_INIT_PMS_ACTIVE);
360 DPRINTF(("sa_start_output: started intrs.\n"));
361 sc->sc_pintr = intr;
362 sc->sc_parg = arg;
363 sc->sc_au.au_pdata = p;
364 sc->sc_au.au_pend = (char *)p + cc - 1;
365 return(0);
366 }
367
368 int
369 audioamd_start_input(void *addr, void *p, int cc,
370 void (*intr)(void *), void *arg)
371 {
372 struct audioamd_softc *sc;
373
374 DPRINTFN(1, ("sa_start_input: cc=%d %p (%p)\n", cc, intr, arg));
375 sc = addr;
376 audioamd_codec_iwrite(&sc->sc_am7930,
377 AM7930_IREG_INIT, AM7930_INIT_PMS_ACTIVE);
378 DPRINTF(("sa_start_input: started intrs.\n"));
379 sc->sc_rintr = intr;
380 sc->sc_rarg = arg;
381 sc->sc_au.au_rdata = p;
382 sc->sc_au.au_rend = (char *)p + cc -1;
383 return(0);
384 }
385
386
387 /*
388 * Pseudo-DMA support: either C or locore assember.
389 */
390
391 int
392 am7930hwintr(void *v)
393 {
394 struct audioamd_softc *sc;
395 struct auio *au;
396 uint8_t *d, *e;
397 int k;
398
399 sc = v;
400 au = &sc->sc_au;
401 /* clear interrupt */
402 k = audioamd_codec_dread(sc, AM7930_DREG_IR);
403 if ((k & (AM7930_IR_DTTHRSH|AM7930_IR_DRTHRSH|AM7930_IR_DSRI|
404 AM7930_IR_DERI|AM7930_IR_BBUFF)) == 0)
405 return 0;
406
407 /* receive incoming data */
408 d = au->au_rdata;
409 e = au->au_rend;
410 if (d && d <= e) {
411 *d = audioamd_codec_dread(sc, AM7930_DREG_BBRB);
412 au->au_rdata++;
413 if (d == e) {
414 DPRINTFN(1, ("am7930hwintr: swintr(r) requested"));
415 softintr_schedule(sc->sc_sicookie);
416 }
417 }
418
419 /* send outgoing data */
420 d = au->au_pdata;
421 e = au->au_pend;
422 if (d && d <= e) {
423 audioamd_codec_dwrite(sc, AM7930_DREG_BBTB, *d);
424 au->au_pdata++;
425 if (d == e) {
426 DPRINTFN(1, ("am7930hwintr: swintr(p) requested"));
427 softintr_schedule(sc->sc_sicookie);
428 }
429 }
430
431 au->au_intrcnt.ev_count++;
432 return 1;
433 }
434
435 void
436 am7930swintr(void *sc0)
437 {
438 struct audioamd_softc *sc;
439 struct auio *au;
440 int s;
441
442 sc = sc0;
443 DPRINTFN(1, ("audiointr: sc=%p\n", sc););
444
445 au = &sc->sc_au;
446 s = splaudio();
447 if (au->au_rdata > au->au_rend && sc->sc_rintr != NULL) {
448 splx(s);
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 (*sc->sc_pintr)(sc->sc_parg);
455 } else
456 splx(s);
457 }
458
459
460 /* indirect write */
461 void
462 audioamd_codec_iwrite(struct am7930_softc *sc, int reg, uint8_t val)
463 {
464 struct audioamd_softc *mdsc;
465
466 mdsc = (struct audioamd_softc *)sc;
467 audioamd_codec_dwrite(mdsc, AM7930_DREG_CR, reg);
468 audioamd_codec_dwrite(mdsc, AM7930_DREG_DR, val);
469 }
470
471 void
472 audioamd_codec_iwrite16(struct am7930_softc *sc, int reg, uint16_t val)
473 {
474 struct audioamd_softc *mdsc;
475
476 mdsc = (struct audioamd_softc *)sc;
477 audioamd_codec_dwrite(mdsc, AM7930_DREG_CR, reg);
478 audioamd_codec_dwrite(mdsc, AM7930_DREG_DR, val);
479 audioamd_codec_dwrite(mdsc, AM7930_DREG_DR, val>>8);
480 }
481
482
483 /* indirect read */
484 uint8_t
485 audioamd_codec_iread(struct am7930_softc *sc, int reg)
486 {
487 struct audioamd_softc *mdsc;
488
489 mdsc = (struct audioamd_softc *)sc;
490 audioamd_codec_dwrite(mdsc, AM7930_DREG_CR, reg);
491 return (audioamd_codec_dread(mdsc, AM7930_DREG_DR));
492 }
493
494 uint16_t
495 audioamd_codec_iread16(struct am7930_softc *sc, int reg)
496 {
497 struct audioamd_softc *mdsc;
498 uint8_t lo, hi;
499
500 mdsc = (struct audioamd_softc *)sc;
501 audioamd_codec_dwrite(mdsc, AM7930_DREG_CR, reg);
502 lo = audioamd_codec_dread(mdsc, AM7930_DREG_DR);
503 hi = audioamd_codec_dread(mdsc, AM7930_DREG_DR);
504 return (hi << 8) | lo;
505 }
506
507 /* direct read */
508 uint8_t
509 audioamd_codec_dread(struct audioamd_softc *sc, int reg)
510 {
511
512 return bus_space_read_1(sc->sc_bt, sc->sc_bh, reg);
513 }
514
515 /* direct write */
516 void
517 audioamd_codec_dwrite(struct audioamd_softc *sc, int reg, uint8_t val)
518 {
519
520 bus_space_write_1(sc->sc_bt, sc->sc_bh, reg, val);
521 }
522
523 int
524 audioamd_getdev(void *addr, struct audio_device *retp)
525 {
526
527 *retp = audioamd_device;
528 return 0;
529 }
530
531 #endif /* NAUDIO > 0 */
532