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