1/*	$NetBSD$	*/
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$");
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_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 */
69int	am7930hwintr(void *);
70struct auio *auiop;
71void	am7930swintr(void *);
72
73/* from amd7930intr.s: */
74void	amd7930_trap(void);
75
76/*
77 * interrupt-handler status
78 */
79struct am7930_intrhand {
80	int	(*ih_fun)(void *);
81	void	*ih_arg;
82};
83
84struct 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
103int	audioamd_mainbus_match(device_t, cfdata_t, void *);
104void	audioamd_mainbus_attach(device_t, device_t, void *);
105int	audioamd_obio_match(device_t, cfdata_t, void *);
106void	audioamd_obio_attach(device_t, device_t, void *);
107int	audioamd_sbus_match(device_t, cfdata_t, void *);
108void	audioamd_sbus_attach(device_t, device_t, void *);
109void	audioamd_attach(struct audioamd_softc *, int);
110
111CFATTACH_DECL_NEW(audioamd_mainbus, sizeof(struct audioamd_softc),
112    audioamd_mainbus_match, audioamd_mainbus_attach, NULL, NULL);
113
114CFATTACH_DECL_NEW(audioamd_obio, sizeof(struct audioamd_softc),
115    audioamd_obio_match, audioamd_obio_attach, NULL, NULL);
116
117CFATTACH_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
124uint8_t	audioamd_codec_iread(struct am7930_softc *, int);
125uint16_t	audioamd_codec_iread16(struct am7930_softc *, int);
126uint8_t	audioamd_codec_dread(struct audioamd_softc *, int);
127void	audioamd_codec_iwrite(struct am7930_softc *, int, uint8_t);
128void	audioamd_codec_iwrite16(struct am7930_softc *, int, uint16_t);
129void	audioamd_codec_dwrite(struct audioamd_softc *, int, uint8_t);
130void	audioamd_onopen(struct am7930_softc *);
131void	audioamd_onclose(struct am7930_softc *);
132
133struct 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	0,
141	0,
142	0,
143};
144
145/*
146 * Define our interface to the higher level audio driver.
147 */
148int	audioamd_start_output(void *, void *, int, void (*)(void *), void *);
149int	audioamd_start_input(void *, void *, int, void (*)(void *), void *);
150int	audioamd_getdev(void *, struct audio_device *);
151void	audioamd_get_locks(void *opaque, kmutex_t **intr, kmutex_t **thread);
152
153const 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	audioamd_get_locks,
182};
183
184struct audio_device audioamd_device = {
185	"am7930",
186	"x",
187	"audioamd"
188};
189
190
191int
192audioamd_mainbus_match(device_t parent, cfdata_t 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
202int
203audioamd_obio_match(device_t parent, cfdata_t 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
214int
215audioamd_sbus_match(device_t parent, cfdata_t 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
223void
224audioamd_mainbus_attach(device_t parent, device_t 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 = device_private(self);
232	sc->sc_am7930.sc_dev = self;
233	sc->sc_bt = ma->ma_bustag;
234
235	if (bus_space_map(
236			ma->ma_bustag,
237			ma->ma_paddr,
238			AM7930_DREG_SIZE,
239			BUS_SPACE_MAP_LINEAR,
240			&bh) != 0) {
241		printf("%s: cannot map registers\n", device_xname(self));
242		return;
243	}
244	sc->sc_bh = bh;
245	audioamd_attach(sc, ma->ma_pri);
246}
247
248void
249audioamd_obio_attach(device_t parent, device_t self, void *aux)
250{
251	union obio_attach_args *uoba;
252	struct sbus_attach_args *sa;
253	struct audioamd_softc *sc;
254	bus_space_handle_t bh;
255
256	uoba = aux;
257	sa = &uoba->uoba_sbus;
258	sc = device_private(self);
259	sc->sc_am7930.sc_dev = self;
260	sc->sc_bt = sa->sa_bustag;
261
262	if (sbus_bus_map(sa->sa_bustag,
263			 sa->sa_slot, sa->sa_offset,
264			 AM7930_DREG_SIZE,
265			 0, &bh) != 0) {
266		printf("%s: cannot map registers\n", device_xname(self));
267		return;
268	}
269	sc->sc_bh = bh;
270	audioamd_attach(sc, sa->sa_pri);
271}
272
273void
274audioamd_sbus_attach(device_t parent, device_t self, void *aux)
275{
276	struct sbus_attach_args *sa;
277	struct audioamd_softc *sc;
278	bus_space_handle_t bh;
279
280	sa = aux;
281	sc = device_private(self);
282	sc->sc_am7930.sc_dev = self;
283	sc->sc_bt = sa->sa_bustag;
284
285	if (sbus_bus_map(sa->sa_bustag,
286			 sa->sa_slot, sa->sa_offset,
287			 AM7930_DREG_SIZE,
288			 0, &bh) != 0) {
289		printf("%s: cannot map registers\n", device_xname(self));
290		return;
291	}
292	sc->sc_bh = bh;
293	audioamd_attach(sc, sa->sa_pri);
294}
295
296void
297audioamd_attach(struct audioamd_softc *sc, int pri)
298{
299	device_t self;
300
301	/*
302	 * Set up glue for MI code early; we use some of it here.
303	 */
304	self = sc->sc_am7930.sc_dev;
305	sc->sc_am7930.sc_glue = &audioamd_glue;
306	mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_HIGH);
307
308	am7930_init(&sc->sc_am7930, AUDIOAMD_POLL_MODE);
309
310	auiop = &sc->sc_au;
311
312	/* Copy bus tag & handle for use by am7930_trap */
313	sc->sc_au.au_bt = sc->sc_bt;
314	sc->sc_au.au_bh = sc->sc_bh;
315	(void)bus_intr_establish2(sc->sc_bt, pri, IPL_HIGH,
316				  am7930hwintr, sc,
317#ifdef notyet /* XXX amd7930intr.s needs to be fixed for MI softint(9) */
318				  amd7930_trap
319#else
320				  NULL
321#endif
322				  );
323
324	sc->sc_sicookie = softint_establish(SOFTINT_SERIAL, am7930swintr, sc);
325	if (sc->sc_sicookie == NULL) {
326		printf("\n%s: cannot establish software interrupt\n",
327			device_xname(self));
328		return;
329	}
330
331	printf(" softpri %d\n", IPL_SOFTAUDIO);
332
333
334	evcnt_attach_dynamic(&sc->sc_intrcnt, EVCNT_TYPE_INTR, NULL,
335	    device_xname(self), "intr");
336
337	audio_attach_mi(&sa_hw_if, sc, self);
338}
339
340
341void
342audioamd_onopen(struct am7930_softc *sc)
343{
344	struct audioamd_softc *mdsc;
345
346	mdsc = (struct audioamd_softc *)sc;
347
348	/* reset pdma state */
349	mutex_spin_enter(&mdsc->sc_lock);
350	mdsc->sc_rintr = 0;
351	mdsc->sc_rarg = 0;
352	mdsc->sc_pintr = 0;
353	mdsc->sc_parg = 0;
354	mdsc->sc_au.au_rdata = 0;
355	mdsc->sc_au.au_pdata = 0;
356	mutex_spin_exit(&mdsc->sc_lock);
357}
358
359
360void
361audioamd_onclose(struct am7930_softc *sc)
362{
363	/* On sparc, just do the chipset-level halt. */
364	am7930_halt_input(sc);
365	am7930_halt_output(sc);
366}
367
368int
369audioamd_start_output(void *addr, void *p, int cc,
370		      void (*intr)(void *), void *arg)
371{
372	struct audioamd_softc *sc;
373
374	DPRINTFN(1, ("sa_start_output: cc=%d %p (%p)\n", cc, intr, arg));
375	sc = addr;
376
377	mutex_spin_enter(&sc->sc_lock);
378	audioamd_codec_iwrite(&sc->sc_am7930,
379	    AM7930_IREG_INIT, AM7930_INIT_PMS_ACTIVE);
380	sc->sc_pintr = intr;
381	sc->sc_parg = arg;
382	sc->sc_au.au_pdata = p;
383	sc->sc_au.au_pend = (char *)p + cc - 1;
384	mutex_spin_exit(&sc->sc_lock);
385
386	DPRINTF(("sa_start_output: started intrs.\n"));
387	return(0);
388}
389
390int
391audioamd_start_input(void *addr, void *p, int cc,
392		     void (*intr)(void *), void *arg)
393{
394	struct audioamd_softc *sc;
395
396	DPRINTFN(1, ("sa_start_input: cc=%d %p (%p)\n", cc, intr, arg));
397	sc = addr;
398
399	mutex_spin_enter(&sc->sc_lock);
400	audioamd_codec_iwrite(&sc->sc_am7930,
401	    AM7930_IREG_INIT, AM7930_INIT_PMS_ACTIVE);
402	sc->sc_rintr = intr;
403	sc->sc_rarg = arg;
404	sc->sc_au.au_rdata = p;
405	sc->sc_au.au_rend = (char *)p + cc -1;
406	mutex_spin_exit(&sc->sc_lock);
407
408	DPRINTF(("sa_start_input: started intrs.\n"));
409
410	return(0);
411}
412
413
414/*
415 * Pseudo-DMA support: either C or locore assember.
416 */
417
418int
419am7930hwintr(void *v)
420{
421	struct audioamd_softc *sc;
422	struct auio *au;
423	uint8_t *d, *e;
424	int k;
425
426	sc = v;
427	au = &sc->sc_au;
428	mutex_spin_enter(&sc->sc_lock);
429
430	/* clear interrupt */
431	k = audioamd_codec_dread(sc, AM7930_DREG_IR);
432	if ((k & (AM7930_IR_DTTHRSH|AM7930_IR_DRTHRSH|AM7930_IR_DSRI|
433		  AM7930_IR_DERI|AM7930_IR_BBUFF)) == 0) {
434		mutex_spin_exit(&sc->sc_lock);
435		return 0;
436	}
437
438	/* receive incoming data */
439	d = au->au_rdata;
440	e = au->au_rend;
441	if (d && d <= e) {
442		*d = audioamd_codec_dread(sc, AM7930_DREG_BBRB);
443		au->au_rdata++;
444		if (d == e) {
445			DPRINTFN(1, ("am7930hwintr: swintr(r) requested"));
446			softint_schedule(sc->sc_sicookie);
447		}
448	}
449
450	/* send outgoing data */
451	d = au->au_pdata;
452	e = au->au_pend;
453	if (d && d <= e) {
454		audioamd_codec_dwrite(sc, AM7930_DREG_BBTB, *d);
455		au->au_pdata++;
456		if (d == e) {
457			DPRINTFN(1, ("am7930hwintr: swintr(p) requested"));
458			softint_schedule(sc->sc_sicookie);
459		}
460	}
461
462	au->au_intrcnt.ev_count++;
463	mutex_spin_exit(&sc->sc_lock);
464
465	return 1;
466}
467
468void
469am7930swintr(void *sc0)
470{
471	struct audioamd_softc *sc;
472	struct auio *au;
473	bool pint;
474
475	sc = sc0;
476	DPRINTFN(1, ("audiointr: sc=%p\n", sc););
477
478	au = &sc->sc_au;
479
480	mutex_spin_enter(&sc->sc_am7930.sc_lock);
481	if (au->au_rdata > au->au_rend && sc->sc_rintr != NULL) {
482		(*sc->sc_rintr)(sc->sc_rarg);
483	}
484	pint = (au->au_pdata > au->au_pend && sc->sc_pintr != NULL);
485	if (pint)
486		(*sc->sc_pintr)(sc->sc_parg);
487
488	mutex_spin_exit(&sc->sc_am7930.sc_lock);
489}
490
491
492/* indirect write */
493void
494audioamd_codec_iwrite(struct am7930_softc *sc, int reg, uint8_t val)
495{
496	struct audioamd_softc *mdsc;
497
498	mdsc = (struct audioamd_softc *)sc;
499	audioamd_codec_dwrite(mdsc, AM7930_DREG_CR, reg);
500	audioamd_codec_dwrite(mdsc, AM7930_DREG_DR, val);
501}
502
503void
504audioamd_codec_iwrite16(struct am7930_softc *sc, int reg, uint16_t val)
505{
506	struct audioamd_softc *mdsc;
507
508	mdsc = (struct audioamd_softc *)sc;
509	audioamd_codec_dwrite(mdsc, AM7930_DREG_CR, reg);
510	audioamd_codec_dwrite(mdsc, AM7930_DREG_DR, val);
511	audioamd_codec_dwrite(mdsc, AM7930_DREG_DR, val>>8);
512}
513
514
515/* indirect read */
516uint8_t
517audioamd_codec_iread(struct am7930_softc *sc, int reg)
518{
519	struct audioamd_softc *mdsc;
520
521	mdsc = (struct audioamd_softc *)sc;
522	audioamd_codec_dwrite(mdsc, AM7930_DREG_CR, reg);
523	return (audioamd_codec_dread(mdsc, AM7930_DREG_DR));
524}
525
526uint16_t
527audioamd_codec_iread16(struct am7930_softc *sc, int reg)
528{
529	struct audioamd_softc *mdsc;
530	uint8_t lo, hi;
531
532	mdsc = (struct audioamd_softc *)sc;
533	audioamd_codec_dwrite(mdsc, AM7930_DREG_CR, reg);
534	lo = audioamd_codec_dread(mdsc, AM7930_DREG_DR);
535	hi = audioamd_codec_dread(mdsc, AM7930_DREG_DR);
536	return (hi << 8) | lo;
537}
538
539/* direct read */
540uint8_t
541audioamd_codec_dread(struct audioamd_softc *sc, int reg)
542{
543
544	return bus_space_read_1(sc->sc_bt, sc->sc_bh, reg);
545}
546
547/* direct write */
548void
549audioamd_codec_dwrite(struct audioamd_softc *sc, int reg, uint8_t val)
550{
551
552	bus_space_write_1(sc->sc_bt, sc->sc_bh, reg, val);
553}
554
555int
556audioamd_getdev(void *addr, struct audio_device *retp)
557{
558
559	*retp = audioamd_device;
560	return 0;
561}
562
563void
564audioamd_get_locks(void *opaque, kmutex_t **intr, kmutex_t **thread)
565{
566	struct audioamd_softc *asc = opaque;
567	struct am7930_softc *sc = &asc->sc_am7930;
568
569	*intr = &sc->sc_intr_lock;
570	*thread = &sc->sc_lock;
571}
572
573#endif /* NAUDIO > 0 */
574