audioamd.c revision 1.27.50.1 1 /* $NetBSD: audioamd.c,v 1.27.50.1 2019/06/10 22:06:46 christos 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.27.50.1 2019/06/10 22:06:46 christos 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 #include <sys/bus.h>
45 #include <sys/intr.h>
46 #include <sys/mutex.h>
47
48 #include <machine/autoconf.h>
49
50 #include <sys/audioio.h>
51 #include <dev/audio/audio_if.h>
52
53 #include <dev/ic/am7930reg.h>
54 #include <dev/ic/am7930var.h>
55 #include <sparc/dev/audioamdvar.h>
56
57 #define AUDIO_ROM_NAME "audio"
58
59 #ifdef AUDIO_DEBUG
60 #define DPRINTF(x) if (am7930debug) printf x
61 #define DPRINTFN(n,x) if (am7930debug>(n)) printf x
62 #else
63 #define DPRINTF(x)
64 #define DPRINTFN(n,x)
65 #endif /* AUDIO_DEBUG */
66
67
68 /* interrupt interfaces */
69 int am7930hwintr(void *);
70 struct auio *auiop;
71 void am7930swintr(void *);
72
73 /* from amd7930intr.s: */
74 void amd7930_trap(void);
75
76 /*
77 * interrupt-handler status
78 */
79 struct am7930_intrhand {
80 int (*ih_fun)(void *);
81 void *ih_arg;
82 };
83
84 struct audioamd_softc {
85 struct am7930_softc sc_am7930; /* glue to MI code */
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 kmutex_t sc_lock;
101 };
102
103 int audioamd_mainbus_match(device_t, cfdata_t, void *);
104 void audioamd_mainbus_attach(device_t, device_t, void *);
105 int audioamd_obio_match(device_t, cfdata_t, void *);
106 void audioamd_obio_attach(device_t, device_t, void *);
107 int audioamd_sbus_match(device_t, cfdata_t, void *);
108 void audioamd_sbus_attach(device_t, device_t, void *);
109 void audioamd_attach(struct audioamd_softc *, int);
110
111 CFATTACH_DECL_NEW(audioamd_mainbus, sizeof(struct audioamd_softc),
112 audioamd_mainbus_match, audioamd_mainbus_attach, NULL, NULL);
113
114 CFATTACH_DECL_NEW(audioamd_obio, sizeof(struct audioamd_softc),
115 audioamd_obio_match, audioamd_obio_attach, NULL, NULL);
116
117 CFATTACH_DECL_NEW(audioamd_sbus, sizeof(struct audioamd_softc),
118 audioamd_sbus_match, audioamd_sbus_attach, NULL, NULL);
119
120 /*
121 * Define our interface into the am7930 MI driver.
122 */
123
124 uint8_t audioamd_codec_iread(struct am7930_softc *, int);
125 uint16_t audioamd_codec_iread16(struct am7930_softc *, int);
126 uint8_t audioamd_codec_dread(struct audioamd_softc *, int);
127 void audioamd_codec_iwrite(struct am7930_softc *, int, uint8_t);
128 void audioamd_codec_iwrite16(struct am7930_softc *, int, uint16_t);
129 void audioamd_codec_dwrite(struct audioamd_softc *, int, uint8_t);
130 void audioamd_onopen(struct am7930_softc *);
131 void audioamd_onclose(struct am7930_softc *);
132
133 struct am7930_glue audioamd_glue = {
134 audioamd_codec_iread,
135 audioamd_codec_iwrite,
136 audioamd_codec_iread16,
137 audioamd_codec_iwrite16,
138 audioamd_onopen,
139 audioamd_onclose,
140 };
141
142 /*
143 * Define our interface to the higher level audio driver.
144 */
145 int audioamd_start_output(void *, void *, int, void (*)(void *), void *);
146 int audioamd_start_input(void *, void *, int, void (*)(void *), void *);
147 int audioamd_getdev(void *, struct audio_device *);
148 void audioamd_get_locks(void *opaque, kmutex_t **intr, kmutex_t **thread);
149
150 const struct audio_hw_if sa_hw_if = {
151 .open = am7930_open,
152 .close = am7930_close,
153 .query_format = am7930_query_format,
154 .set_format = am7930_set_format,
155 .commit_settings = am7930_commit_settings,
156 .start_output = audioamd_start_output, /* md */
157 .start_input = audioamd_start_input, /* md */
158 .halt_output = am7930_halt_output,
159 .halt_input = am7930_halt_input,
160 .getdev = audioamd_getdev,
161 .set_port = am7930_set_port,
162 .get_port = am7930_get_port,
163 .query_devinfo = am7930_query_devinfo,
164 .get_props = am7930_get_props,
165 .get_locks = audioamd_get_locks,
166 };
167
168 struct audio_device audioamd_device = {
169 "am7930",
170 "x",
171 "audioamd"
172 };
173
174
175 int
176 audioamd_mainbus_match(device_t parent, cfdata_t cf, void *aux)
177 {
178 struct mainbus_attach_args *ma;
179
180 ma = aux;
181 if (CPU_ISSUN4)
182 return 0;
183 return strcmp(AUDIO_ROM_NAME, ma->ma_name) == 0;
184 }
185
186 int
187 audioamd_obio_match(device_t parent, cfdata_t cf, void *aux)
188 {
189 union obio_attach_args *uoba;
190
191 uoba = aux;
192 if (uoba->uoba_isobio4 != 0)
193 return 0;
194
195 return strcmp("audio", uoba->uoba_sbus.sa_name) == 0;
196 }
197
198 int
199 audioamd_sbus_match(device_t parent, cfdata_t cf, void *aux)
200 {
201 struct sbus_attach_args *sa;
202
203 sa = aux;
204 return strcmp(AUDIO_ROM_NAME, sa->sa_name) == 0;
205 }
206
207 void
208 audioamd_mainbus_attach(device_t parent, device_t self, void *aux)
209 {
210 struct mainbus_attach_args *ma;
211 struct audioamd_softc *sc;
212 bus_space_handle_t bh;
213
214 ma = aux;
215 sc = device_private(self);
216 sc->sc_am7930.sc_dev = self;
217 sc->sc_bt = ma->ma_bustag;
218
219 if (bus_space_map(
220 ma->ma_bustag,
221 ma->ma_paddr,
222 AM7930_DREG_SIZE,
223 BUS_SPACE_MAP_LINEAR,
224 &bh) != 0) {
225 printf("%s: cannot map registers\n", device_xname(self));
226 return;
227 }
228 sc->sc_bh = bh;
229 audioamd_attach(sc, ma->ma_pri);
230 }
231
232 void
233 audioamd_obio_attach(device_t parent, device_t self, void *aux)
234 {
235 union obio_attach_args *uoba;
236 struct sbus_attach_args *sa;
237 struct audioamd_softc *sc;
238 bus_space_handle_t bh;
239
240 uoba = aux;
241 sa = &uoba->uoba_sbus;
242 sc = device_private(self);
243 sc->sc_am7930.sc_dev = self;
244 sc->sc_bt = sa->sa_bustag;
245
246 if (sbus_bus_map(sa->sa_bustag,
247 sa->sa_slot, sa->sa_offset,
248 AM7930_DREG_SIZE,
249 0, &bh) != 0) {
250 printf("%s: cannot map registers\n", device_xname(self));
251 return;
252 }
253 sc->sc_bh = bh;
254 audioamd_attach(sc, sa->sa_pri);
255 }
256
257 void
258 audioamd_sbus_attach(device_t parent, device_t self, void *aux)
259 {
260 struct sbus_attach_args *sa;
261 struct audioamd_softc *sc;
262 bus_space_handle_t bh;
263
264 sa = aux;
265 sc = device_private(self);
266 sc->sc_am7930.sc_dev = self;
267 sc->sc_bt = sa->sa_bustag;
268
269 if (sbus_bus_map(sa->sa_bustag,
270 sa->sa_slot, sa->sa_offset,
271 AM7930_DREG_SIZE,
272 0, &bh) != 0) {
273 printf("%s: cannot map registers\n", device_xname(self));
274 return;
275 }
276 sc->sc_bh = bh;
277 audioamd_attach(sc, sa->sa_pri);
278 }
279
280 void
281 audioamd_attach(struct audioamd_softc *sc, int pri)
282 {
283 device_t self;
284
285 /*
286 * Set up glue for MI code early; we use some of it here.
287 */
288 self = sc->sc_am7930.sc_dev;
289 sc->sc_am7930.sc_glue = &audioamd_glue;
290 mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_HIGH);
291
292 am7930_init(&sc->sc_am7930, AUDIOAMD_POLL_MODE);
293
294 auiop = &sc->sc_au;
295
296 /* Copy bus tag & handle for use by am7930_trap */
297 sc->sc_au.au_bt = sc->sc_bt;
298 sc->sc_au.au_bh = sc->sc_bh;
299 (void)bus_intr_establish2(sc->sc_bt, pri, IPL_HIGH,
300 am7930hwintr, sc,
301 #ifdef notyet /* XXX amd7930intr.s needs to be fixed for MI softint(9) */
302 amd7930_trap
303 #else
304 NULL
305 #endif
306 );
307
308 sc->sc_sicookie = softint_establish(SOFTINT_SERIAL, am7930swintr, sc);
309 if (sc->sc_sicookie == NULL) {
310 printf("\n%s: cannot establish software interrupt\n",
311 device_xname(self));
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 device_xname(self), "intr");
320
321 audio_attach_mi(&sa_hw_if, sc, self);
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
332 /* reset pdma state */
333 mutex_spin_enter(&mdsc->sc_lock);
334 mdsc->sc_rintr = 0;
335 mdsc->sc_rarg = 0;
336 mdsc->sc_pintr = 0;
337 mdsc->sc_parg = 0;
338 mdsc->sc_au.au_rdata = 0;
339 mdsc->sc_au.au_pdata = 0;
340 mutex_spin_exit(&mdsc->sc_lock);
341 }
342
343
344 void
345 audioamd_onclose(struct am7930_softc *sc)
346 {
347 /* On sparc, just do the chipset-level halt. */
348 am7930_halt_input(sc);
349 am7930_halt_output(sc);
350 }
351
352 int
353 audioamd_start_output(void *addr, void *p, int cc,
354 void (*intr)(void *), void *arg)
355 {
356 struct audioamd_softc *sc;
357
358 DPRINTFN(1, ("sa_start_output: cc=%d %p (%p)\n", cc, intr, arg));
359 sc = addr;
360
361 mutex_spin_enter(&sc->sc_lock);
362 audioamd_codec_iwrite(&sc->sc_am7930,
363 AM7930_IREG_INIT, AM7930_INIT_PMS_ACTIVE);
364 sc->sc_pintr = intr;
365 sc->sc_parg = arg;
366 sc->sc_au.au_pdata = p;
367 sc->sc_au.au_pend = (char *)p + cc - 1;
368 mutex_spin_exit(&sc->sc_lock);
369
370 DPRINTF(("sa_start_output: started intrs.\n"));
371 return(0);
372 }
373
374 int
375 audioamd_start_input(void *addr, void *p, int cc,
376 void (*intr)(void *), void *arg)
377 {
378 struct audioamd_softc *sc;
379
380 DPRINTFN(1, ("sa_start_input: cc=%d %p (%p)\n", cc, intr, arg));
381 sc = addr;
382
383 mutex_spin_enter(&sc->sc_lock);
384 audioamd_codec_iwrite(&sc->sc_am7930,
385 AM7930_IREG_INIT, AM7930_INIT_PMS_ACTIVE);
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 mutex_spin_exit(&sc->sc_lock);
391
392 DPRINTF(("sa_start_input: started intrs.\n"));
393
394 return(0);
395 }
396
397
398 /*
399 * Pseudo-DMA support: either C or locore assember.
400 */
401
402 int
403 am7930hwintr(void *v)
404 {
405 struct audioamd_softc *sc;
406 struct auio *au;
407 uint8_t *d, *e;
408 int k;
409
410 sc = v;
411 au = &sc->sc_au;
412 mutex_spin_enter(&sc->sc_lock);
413
414 /* clear interrupt */
415 k = audioamd_codec_dread(sc, AM7930_DREG_IR);
416 if ((k & (AM7930_IR_DTTHRSH|AM7930_IR_DRTHRSH|AM7930_IR_DSRI|
417 AM7930_IR_DERI|AM7930_IR_BBUFF)) == 0) {
418 mutex_spin_exit(&sc->sc_lock);
419 return 0;
420 }
421
422 /* receive incoming data */
423 d = au->au_rdata;
424 e = au->au_rend;
425 if (d && d <= e) {
426 *d = audioamd_codec_dread(sc, AM7930_DREG_BBRB);
427 au->au_rdata++;
428 if (d == e) {
429 DPRINTFN(1, ("am7930hwintr: swintr(r) requested"));
430 softint_schedule(sc->sc_sicookie);
431 }
432 }
433
434 /* send outgoing data */
435 d = au->au_pdata;
436 e = au->au_pend;
437 if (d && d <= e) {
438 audioamd_codec_dwrite(sc, AM7930_DREG_BBTB, *d);
439 au->au_pdata++;
440 if (d == e) {
441 DPRINTFN(1, ("am7930hwintr: swintr(p) requested"));
442 softint_schedule(sc->sc_sicookie);
443 }
444 }
445
446 au->au_intrcnt.ev_count++;
447 mutex_spin_exit(&sc->sc_lock);
448
449 return 1;
450 }
451
452 void
453 am7930swintr(void *sc0)
454 {
455 struct audioamd_softc *sc;
456 struct auio *au;
457 bool pint;
458
459 sc = sc0;
460 DPRINTFN(1, ("audiointr: sc=%p\n", sc););
461
462 au = &sc->sc_au;
463
464 mutex_spin_enter(&sc->sc_am7930.sc_lock);
465 if (au->au_rdata > au->au_rend && sc->sc_rintr != NULL) {
466 (*sc->sc_rintr)(sc->sc_rarg);
467 }
468 pint = (au->au_pdata > au->au_pend && sc->sc_pintr != NULL);
469 if (pint)
470 (*sc->sc_pintr)(sc->sc_parg);
471
472 mutex_spin_exit(&sc->sc_am7930.sc_lock);
473 }
474
475
476 /* indirect write */
477 void
478 audioamd_codec_iwrite(struct am7930_softc *sc, int reg, uint8_t val)
479 {
480 struct audioamd_softc *mdsc;
481
482 mdsc = (struct audioamd_softc *)sc;
483 audioamd_codec_dwrite(mdsc, AM7930_DREG_CR, reg);
484 audioamd_codec_dwrite(mdsc, AM7930_DREG_DR, val);
485 }
486
487 void
488 audioamd_codec_iwrite16(struct am7930_softc *sc, int reg, uint16_t val)
489 {
490 struct audioamd_softc *mdsc;
491
492 mdsc = (struct audioamd_softc *)sc;
493 audioamd_codec_dwrite(mdsc, AM7930_DREG_CR, reg);
494 audioamd_codec_dwrite(mdsc, AM7930_DREG_DR, val);
495 audioamd_codec_dwrite(mdsc, AM7930_DREG_DR, val>>8);
496 }
497
498
499 /* indirect read */
500 uint8_t
501 audioamd_codec_iread(struct am7930_softc *sc, int reg)
502 {
503 struct audioamd_softc *mdsc;
504
505 mdsc = (struct audioamd_softc *)sc;
506 audioamd_codec_dwrite(mdsc, AM7930_DREG_CR, reg);
507 return (audioamd_codec_dread(mdsc, AM7930_DREG_DR));
508 }
509
510 uint16_t
511 audioamd_codec_iread16(struct am7930_softc *sc, int reg)
512 {
513 struct audioamd_softc *mdsc;
514 uint8_t lo, hi;
515
516 mdsc = (struct audioamd_softc *)sc;
517 audioamd_codec_dwrite(mdsc, AM7930_DREG_CR, reg);
518 lo = audioamd_codec_dread(mdsc, AM7930_DREG_DR);
519 hi = audioamd_codec_dread(mdsc, AM7930_DREG_DR);
520 return (hi << 8) | lo;
521 }
522
523 /* direct read */
524 uint8_t
525 audioamd_codec_dread(struct audioamd_softc *sc, int reg)
526 {
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(struct audioamd_softc *sc, int reg, uint8_t val)
534 {
535
536 bus_space_write_1(sc->sc_bt, sc->sc_bh, reg, val);
537 }
538
539 int
540 audioamd_getdev(void *addr, struct audio_device *retp)
541 {
542
543 *retp = audioamd_device;
544 return 0;
545 }
546
547 void
548 audioamd_get_locks(void *opaque, kmutex_t **intr, kmutex_t **thread)
549 {
550 struct audioamd_softc *asc = opaque;
551 struct am7930_softc *sc = &asc->sc_am7930;
552
553 *intr = &sc->sc_intr_lock;
554 *thread = &sc->sc_lock;
555 }
556
557 #endif /* NAUDIO > 0 */
558