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