harmony.c revision 1.36
1/*	$OpenBSD: harmony.c,v 1.36 2022/02/16 06:21:18 anton Exp $	*/
2
3/*
4 * Copyright (c) 2003 Jason L. Wright (jason@thought.net)
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
20 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
24 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
25 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/*
30 * Harmony (CS4215/AD1849 LASI) audio interface.
31 */
32
33#include <sys/param.h>
34#include <sys/kernel.h>
35#include <sys/systm.h>
36#include <sys/errno.h>
37#include <sys/ioctl.h>
38#include <sys/device.h>
39#include <sys/proc.h>
40#include <sys/malloc.h>
41
42#include <sys/audioio.h>
43#include <dev/audio_if.h>
44
45#include <machine/cpu.h>
46#include <machine/intr.h>
47#include <machine/iomod.h>
48#include <machine/autoconf.h>
49#include <machine/bus.h>
50
51#include <hppa/dev/cpudevs.h>
52#include <hppa/gsc/gscbusvar.h>
53#include <hppa/gsc/harmonyreg.h>
54#include <hppa/gsc/harmonyvar.h>
55
56int     harmony_open(void *, int);
57void    harmony_close(void *);
58int     harmony_set_params(void *, int, int, struct audio_params *,
59    struct audio_params *);
60int     harmony_round_blocksize(void *, int);
61int     harmony_commit_settings(void *);
62int     harmony_halt_output(void *);
63int     harmony_halt_input(void *);
64int     harmony_set_port(void *, mixer_ctrl_t *);
65int     harmony_get_port(void *, mixer_ctrl_t *);
66int     harmony_query_devinfo(void *addr, mixer_devinfo_t *);
67void *  harmony_allocm(void *, int, size_t, int, int);
68void    harmony_freem(void *, void *, int);
69size_t  harmony_round_buffersize(void *, int, size_t);
70int     harmony_get_props(void *);
71int     harmony_trigger_output(void *, void *, void *, int,
72    void (*intr)(void *), void *, struct audio_params *);
73int     harmony_trigger_input(void *, void *, void *, int,
74    void (*intr)(void *), void *, struct audio_params *);
75
76struct audio_hw_if harmony_sa_hw_if = {
77	harmony_open,
78	harmony_close,
79	harmony_set_params,
80	harmony_round_blocksize,
81	harmony_commit_settings,
82	NULL,
83	NULL,
84	NULL,
85	NULL,
86	harmony_halt_output,
87	harmony_halt_input,
88	NULL,
89	NULL,
90	harmony_set_port,
91	harmony_get_port,
92	harmony_query_devinfo,
93	harmony_allocm,
94	harmony_freem,
95	harmony_round_buffersize,
96	harmony_get_props,
97	harmony_trigger_output,
98	harmony_trigger_input
99};
100
101int harmony_match(struct device *, void *, void *);
102void harmony_attach(struct device *, struct device *, void *);
103int harmony_intr(void *);
104void harmony_intr_enable(struct harmony_softc *);
105void harmony_intr_disable(struct harmony_softc *);
106u_int32_t harmony_speed_bits(struct harmony_softc *, u_long *);
107int harmony_set_gainctl(struct harmony_softc *);
108void harmony_reset_codec(struct harmony_softc *);
109void harmony_start_cp(struct harmony_softc *);
110void harmony_tick_pb(void *);
111void harmony_tick_cp(void *);
112void harmony_try_more(struct harmony_softc *);
113
114void harmony_acc_tmo(void *);
115#define	ADD_CLKALLICA(sc) do {						\
116	(sc)->sc_acc <<= 1;						\
117	(sc)->sc_acc |= READ_REG((sc), HARMONY_DIAG) & DIAG_CO;		\
118	if ((sc)->sc_acc_cnt++ && !((sc)->sc_acc_cnt % 32))		\
119		enqueue_randomness((sc)->sc_acc_num ^= (sc)->sc_acc);	\
120} while(0)
121
122int
123harmony_match(parent, match, aux)
124	struct device *parent;
125	void *match, *aux;
126{
127	struct gsc_attach_args *ga = aux;
128	bus_space_handle_t bh;
129	u_int32_t cntl;
130
131	if (ga->ga_type.iodc_type == HPPA_TYPE_FIO) {
132		if (ga->ga_type.iodc_sv_model == HPPA_FIO_A1 ||
133		    ga->ga_type.iodc_sv_model == HPPA_FIO_A2NB ||
134		    ga->ga_type.iodc_sv_model == HPPA_FIO_A1NB ||
135		    ga->ga_type.iodc_sv_model == HPPA_FIO_A2) {
136			if (bus_space_map(ga->ga_iot, ga->ga_hpa,
137			    HARMONY_NREGS, 0, &bh) != 0)
138				return (0);
139			cntl = bus_space_read_4(ga->ga_iot, bh, HARMONY_ID) &
140			    ID_REV_MASK;
141			bus_space_unmap(ga->ga_iot, bh, HARMONY_NREGS);
142			if (cntl == ID_REV_TS || cntl == ID_REV_NOTS)
143				return (1);
144		}
145	}
146	return (0);
147}
148
149void
150harmony_attach(parent, self, aux)
151	struct device *parent, *self;
152	void *aux;
153{
154	struct harmony_softc *sc = (struct harmony_softc *)self;
155	struct gsc_attach_args *ga = aux;
156	u_int8_t rev;
157	u_int32_t cntl;
158	int i;
159
160	sc->sc_bt = ga->ga_iot;
161	sc->sc_dmat = ga->ga_dmatag;
162
163	if (bus_space_map(sc->sc_bt, ga->ga_hpa, HARMONY_NREGS, 0,
164	    &sc->sc_bh) != 0) {
165		printf(": couldn't map registers\n");
166		return;
167	}
168
169	cntl = READ_REG(sc, HARMONY_ID);
170	sc->sc_teleshare = (cntl & ID_REV_MASK) == ID_REV_TS;
171
172	if (bus_dmamem_alloc(sc->sc_dmat, sizeof(struct harmony_empty),
173	    PAGE_SIZE, 0, &sc->sc_empty_seg, 1, &sc->sc_empty_rseg,
174	    BUS_DMA_NOWAIT) != 0) {
175		printf(": couldn't alloc DMA memory\n");
176		bus_space_unmap(sc->sc_bt, sc->sc_bh, HARMONY_NREGS);
177		return;
178	}
179	if (bus_dmamem_map(sc->sc_dmat, &sc->sc_empty_seg, 1,
180	    sizeof(struct harmony_empty), (caddr_t *)&sc->sc_empty_kva,
181	    BUS_DMA_NOWAIT) != 0) {
182		printf(": couldn't map DMA memory\n");
183		bus_dmamem_free(sc->sc_dmat, &sc->sc_empty_seg,
184		    sc->sc_empty_rseg);
185		bus_space_unmap(sc->sc_bt, sc->sc_bh, HARMONY_NREGS);
186		return;
187	}
188	if (bus_dmamap_create(sc->sc_dmat, sizeof(struct harmony_empty), 1,
189	    sizeof(struct harmony_empty), 0, BUS_DMA_NOWAIT,
190	    &sc->sc_empty_map) != 0) {
191		printf(": can't create DMA map\n");
192		bus_dmamem_unmap(sc->sc_dmat, (caddr_t)sc->sc_empty_kva,
193		    sizeof(struct harmony_empty));
194		bus_dmamem_free(sc->sc_dmat, &sc->sc_empty_seg,
195		    sc->sc_empty_rseg);
196		bus_space_unmap(sc->sc_bt, sc->sc_bh, HARMONY_NREGS);
197		return;
198	}
199	if (bus_dmamap_load(sc->sc_dmat, sc->sc_empty_map, sc->sc_empty_kva,
200	    sizeof(struct harmony_empty), NULL, BUS_DMA_NOWAIT) != 0) {
201		printf(": can't load DMA map\n");
202		bus_dmamap_destroy(sc->sc_dmat, sc->sc_empty_map);
203		bus_dmamem_unmap(sc->sc_dmat, (caddr_t)sc->sc_empty_kva,
204		    sizeof(struct harmony_empty));
205		bus_dmamem_free(sc->sc_dmat, &sc->sc_empty_seg,
206		    sc->sc_empty_rseg);
207		bus_space_unmap(sc->sc_bt, sc->sc_bh, HARMONY_NREGS);
208		return;
209	}
210
211	sc->sc_playback_empty = 0;
212	for (i = 0; i < PLAYBACK_EMPTYS; i++)
213		sc->sc_playback_paddrs[i] =
214		    sc->sc_empty_map->dm_segs[0].ds_addr +
215		    offsetof(struct harmony_empty, playback[i][0]);
216
217	sc->sc_capture_empty = 0;
218	for (i = 0; i < CAPTURE_EMPTYS; i++)
219		sc->sc_capture_paddrs[i] =
220		    sc->sc_empty_map->dm_segs[0].ds_addr +
221		    offsetof(struct harmony_empty, playback[i][0]);
222
223	bus_dmamap_sync(sc->sc_dmat, sc->sc_empty_map,
224	    offsetof(struct harmony_empty, playback[0][0]),
225	    PLAYBACK_EMPTYS * HARMONY_BUFSIZE, BUS_DMASYNC_PREWRITE);
226
227	(void)gsc_intr_establish((struct gsc_softc *)parent, ga->ga_irq,
228	    IPL_AUDIO, harmony_intr, sc, sc->sc_dv.dv_xname);
229
230	/* set defaults */
231	sc->sc_in_port = HARMONY_IN_LINE;
232	sc->sc_out_port = HARMONY_OUT_SPEAKER;
233	sc->sc_input_lvl.left = sc->sc_input_lvl.right = 240;
234	sc->sc_output_lvl.left = sc->sc_output_lvl.right = 244;
235	sc->sc_monitor_lvl.left = sc->sc_monitor_lvl.right = 208;
236	sc->sc_outputgain = 0;
237
238	/* reset chip, and push default gain controls */
239	harmony_reset_codec(sc);
240
241	cntl = READ_REG(sc, HARMONY_CNTL);
242	rev = (cntl & CNTL_CODEC_REV_MASK) >> CNTL_CODEC_REV_SHIFT;
243	printf(": rev %u", rev);
244
245	if (sc->sc_teleshare)
246		printf(", teleshare");
247	printf("\n");
248
249	if ((rev & CS4215_REV_VER) >= CS4215_REV_VER_E)
250		sc->sc_hasulinear8 = 1;
251
252	audio_attach_mi(&harmony_sa_hw_if, sc, NULL, &sc->sc_dv);
253
254	timeout_set(&sc->sc_acc_tmo, harmony_acc_tmo, sc);
255	sc->sc_acc_num = 0xa5a5a5a5;
256}
257
258void
259harmony_reset_codec(struct harmony_softc *sc)
260{
261	/* silence */
262	WRITE_REG(sc, HARMONY_GAINCTL, GAINCTL_OUTPUT_LEFT_M |
263	    GAINCTL_OUTPUT_RIGHT_M | GAINCTL_MONITOR_M);
264
265	/* start reset */
266	WRITE_REG(sc, HARMONY_RESET, RESET_RST);
267
268	DELAY(100000);		/* wait at least 0.05 sec */
269
270	harmony_set_gainctl(sc);
271	WRITE_REG(sc, HARMONY_RESET, 0);
272}
273
274void
275harmony_acc_tmo(void *v)
276{
277	struct harmony_softc *sc = v;
278
279	ADD_CLKALLICA(sc);
280	timeout_add(&sc->sc_acc_tmo, 1);
281}
282
283/*
284 * interrupt handler
285 */
286int
287harmony_intr(vsc)
288	void *vsc;
289{
290	struct harmony_softc *sc = vsc;
291	struct harmony_channel *c;
292	u_int32_t dstatus;
293	int r = 0;
294
295	mtx_enter(&audio_lock);
296	ADD_CLKALLICA(sc);
297
298	harmony_intr_disable(sc);
299
300	dstatus = READ_REG(sc, HARMONY_DSTATUS);
301
302	if (dstatus & DSTATUS_PN) {
303		struct harmony_dma *d;
304		bus_addr_t nextaddr;
305		bus_size_t togo;
306
307		r = 1;
308		c = &sc->sc_playback;
309		d = c->c_current;
310		togo = c->c_segsz - c->c_cnt;
311		if (togo == 0) {
312			nextaddr = d->d_map->dm_segs[0].ds_addr;
313			c->c_cnt = togo = c->c_blksz;
314		} else {
315			nextaddr = c->c_lastaddr;
316			if (togo > c->c_blksz)
317				togo = c->c_blksz;
318			c->c_cnt += togo;
319		}
320
321		bus_dmamap_sync(sc->sc_dmat, d->d_map,
322		    nextaddr - d->d_map->dm_segs[0].ds_addr,
323		    c->c_blksz, BUS_DMASYNC_PREWRITE);
324
325		WRITE_REG(sc, HARMONY_PNXTADD, nextaddr);
326		SYNC_REG(sc, HARMONY_PNXTADD, BUS_SPACE_BARRIER_WRITE);
327		c->c_lastaddr = nextaddr + togo;
328		harmony_try_more(sc);
329	}
330
331	dstatus = READ_REG(sc, HARMONY_DSTATUS);
332
333	if (dstatus & DSTATUS_RN) {
334		c = &sc->sc_capture;
335		r = 1;
336		harmony_start_cp(sc);
337		if (sc->sc_capturing && c->c_intr != NULL)
338			(*c->c_intr)(c->c_intrarg);
339	}
340
341	if (READ_REG(sc, HARMONY_OV) & OV_OV) {
342		sc->sc_ov = 1;
343		WRITE_REG(sc, HARMONY_OV, 0);
344	} else
345		sc->sc_ov = 0;
346
347	harmony_intr_enable(sc);
348	mtx_leave(&audio_lock);
349	return (r);
350}
351
352void
353harmony_intr_enable(struct harmony_softc *sc)
354{
355	WRITE_REG(sc, HARMONY_DSTATUS, DSTATUS_IE);
356	SYNC_REG(sc, HARMONY_DSTATUS, BUS_SPACE_BARRIER_WRITE);
357}
358
359void
360harmony_intr_disable(struct harmony_softc *sc)
361{
362	WRITE_REG(sc, HARMONY_DSTATUS, 0);
363	SYNC_REG(sc, HARMONY_DSTATUS, BUS_SPACE_BARRIER_WRITE);
364}
365
366int
367harmony_open(void *vsc, int flags)
368{
369	struct harmony_softc *sc = vsc;
370
371	if (sc->sc_open)
372		return (EBUSY);
373	sc->sc_open = 1;
374	return (0);
375}
376
377void
378harmony_close(void *vsc)
379{
380	struct harmony_softc *sc = vsc;
381
382	/* XXX: not useful, halt_*() already called */
383	harmony_halt_input(sc);
384	harmony_halt_output(sc);
385	harmony_intr_disable(sc);
386	sc->sc_open = 0;
387}
388
389int
390harmony_set_params(void *vsc, int setmode, int usemode,
391    struct audio_params *p, struct audio_params *r)
392{
393	struct harmony_softc *sc = vsc;
394	u_int32_t bits;
395
396	switch (p->encoding) {
397	case AUDIO_ENCODING_ULAW:
398		bits = CNTL_FORMAT_ULAW;
399		p->precision = 8;
400		break;
401	case AUDIO_ENCODING_ALAW:
402		bits = CNTL_FORMAT_ALAW;
403		p->precision = 8;
404		break;
405	case AUDIO_ENCODING_SLINEAR_BE:
406		if (p->precision == 16) {
407			bits = CNTL_FORMAT_SLINEAR16BE;
408			break;
409		}
410		return (EINVAL);
411	case AUDIO_ENCODING_ULINEAR_LE:
412	case AUDIO_ENCODING_ULINEAR_BE:
413		if (p->precision == 8) {
414			bits = CNTL_FORMAT_ULINEAR8;
415			break;
416		}
417		return (EINVAL);
418	default:
419		return (EINVAL);
420	}
421
422	if (sc->sc_outputgain)
423		bits |= CNTL_OLB;
424
425	if (p->channels == 1)
426		bits |= CNTL_CHANS_MONO;
427	else if (p->channels == 2)
428		bits |= CNTL_CHANS_STEREO;
429	else
430		return (EINVAL);
431
432	r->sample_rate = p->sample_rate;
433	r->encoding = p->encoding;
434	r->precision = p->precision;
435	p->bps = AUDIO_BPS(p->precision);
436	r->bps = AUDIO_BPS(r->precision);
437	p->msb = r->msb = 1;
438
439	bits |= harmony_speed_bits(sc, &p->sample_rate);
440	sc->sc_cntlbits = bits;
441	sc->sc_need_commit = 1;
442
443	return (0);
444}
445
446int
447harmony_round_blocksize(void *vsc, int blk)
448{
449	return (HARMONY_BUFSIZE);
450}
451
452int
453harmony_commit_settings(void *vsc)
454{
455	struct harmony_softc *sc = vsc;
456	u_int32_t reg;
457	u_int8_t quietchar;
458	int i;
459
460	if (sc->sc_need_commit == 0)
461		return (0);
462
463	harmony_intr_disable(sc);
464
465	for (;;) {
466		reg = READ_REG(sc, HARMONY_DSTATUS);
467		if ((reg & (DSTATUS_PC | DSTATUS_RC)) == 0)
468			break;
469	}
470
471	/* Setting some bits in gainctl requires a reset */
472	harmony_reset_codec(sc);
473
474	/* set the silence character based on the encoding type */
475	bus_dmamap_sync(sc->sc_dmat, sc->sc_empty_map,
476	    offsetof(struct harmony_empty, playback[0][0]),
477	    PLAYBACK_EMPTYS * HARMONY_BUFSIZE, BUS_DMASYNC_POSTWRITE);
478	switch (sc->sc_cntlbits & CNTL_FORMAT_MASK) {
479	case CNTL_FORMAT_ULAW:
480		quietchar = 0x7f;
481		break;
482	case CNTL_FORMAT_ALAW:
483		quietchar = 0x55;
484		break;
485	case CNTL_FORMAT_SLINEAR16BE:
486	case CNTL_FORMAT_ULINEAR8:
487	default:
488		quietchar = 0;
489		break;
490	}
491	for (i = 0; i < PLAYBACK_EMPTYS; i++)
492		memset(&sc->sc_empty_kva->playback[i][0],
493		    quietchar, HARMONY_BUFSIZE);
494	bus_dmamap_sync(sc->sc_dmat, sc->sc_empty_map,
495	    offsetof(struct harmony_empty, playback[0][0]),
496	    PLAYBACK_EMPTYS * HARMONY_BUFSIZE, BUS_DMASYNC_PREWRITE);
497
498	for (;;) {
499		/* Wait for it to come out of control mode */
500		reg = READ_REG(sc, HARMONY_CNTL);
501		if ((reg & CNTL_C) == 0)
502			break;
503	}
504
505	bus_space_write_4(sc->sc_bt, sc->sc_bh, HARMONY_CNTL,
506	    sc->sc_cntlbits | CNTL_C);
507
508	for (;;) {
509		/* Wait for it to come out of control mode */
510		reg = READ_REG(sc, HARMONY_CNTL);
511		if ((reg & CNTL_C) == 0)
512			break;
513	}
514
515	sc->sc_need_commit = 0;
516
517	if (sc->sc_playing || sc->sc_capturing)
518		harmony_intr_enable(sc);
519
520	return (0);
521}
522
523int
524harmony_halt_output(void *vsc)
525{
526	struct harmony_softc *sc = vsc;
527
528	/* XXX: disable interrupts */
529	sc->sc_playing = 0;
530	return (0);
531}
532
533int
534harmony_halt_input(void *vsc)
535{
536	struct harmony_softc *sc = vsc;
537
538	/* XXX: disable interrupts */
539	sc->sc_capturing = 0;
540	return (0);
541}
542
543int
544harmony_set_port(void *vsc, mixer_ctrl_t *cp)
545{
546	struct harmony_softc *sc = vsc;
547	int err = EINVAL;
548
549	switch (cp->dev) {
550	case HARMONY_PORT_INPUT_LVL:
551		if (cp->type != AUDIO_MIXER_VALUE)
552			break;
553		if (cp->un.value.num_channels == 1)
554			sc->sc_input_lvl.left = sc->sc_input_lvl.right =
555			    cp->un.value.level[AUDIO_MIXER_LEVEL_MONO];
556		else if (cp->un.value.num_channels == 2) {
557			sc->sc_input_lvl.left =
558			    cp->un.value.level[AUDIO_MIXER_LEVEL_LEFT];
559			sc->sc_input_lvl.right =
560			    cp->un.value.level[AUDIO_MIXER_LEVEL_RIGHT];
561		} else
562			break;
563		sc->sc_need_commit = 1;
564		err = 0;
565		break;
566	case HARMONY_PORT_OUTPUT_LVL:
567		if (cp->type != AUDIO_MIXER_VALUE)
568			break;
569		if (cp->un.value.num_channels == 1)
570			sc->sc_output_lvl.left = sc->sc_output_lvl.right =
571			    cp->un.value.level[AUDIO_MIXER_LEVEL_MONO];
572		else if (cp->un.value.num_channels == 2) {
573			sc->sc_output_lvl.left =
574			    cp->un.value.level[AUDIO_MIXER_LEVEL_LEFT];
575			sc->sc_output_lvl.right =
576			    cp->un.value.level[AUDIO_MIXER_LEVEL_RIGHT];
577		} else
578			break;
579		sc->sc_need_commit = 1;
580		err = 0;
581		break;
582	case HARMONY_PORT_OUTPUT_GAIN:
583		if (cp->type != AUDIO_MIXER_ENUM)
584			break;
585		sc->sc_outputgain = cp->un.ord ? 1 : 0;
586		err = 0;
587		break;
588	case HARMONY_PORT_MONITOR_LVL:
589		if (cp->type != AUDIO_MIXER_VALUE)
590			break;
591		if (cp->un.value.num_channels != 1)
592			break;
593		sc->sc_monitor_lvl.left = sc->sc_input_lvl.right =
594		    cp->un.value.level[AUDIO_MIXER_LEVEL_MONO];
595		sc->sc_need_commit = 1;
596		err = 0;
597		break;
598	case HARMONY_PORT_RECORD_SOURCE:
599		if (cp->type != AUDIO_MIXER_ENUM)
600			break;
601		if (cp->un.ord != HARMONY_IN_LINE &&
602		    cp->un.ord != HARMONY_IN_MIC)
603			break;
604		sc->sc_in_port = cp->un.ord;
605		err = 0;
606		sc->sc_need_commit = 1;
607		break;
608	case HARMONY_PORT_OUTPUT_SOURCE:
609		if (cp->type != AUDIO_MIXER_ENUM)
610			break;
611		if (cp->un.ord != HARMONY_OUT_LINE &&
612		    cp->un.ord != HARMONY_OUT_SPEAKER &&
613		    cp->un.ord != HARMONY_OUT_HEADPHONE)
614			break;
615		sc->sc_out_port = cp->un.ord;
616		err = 0;
617		sc->sc_need_commit = 1;
618		break;
619	}
620
621	return (err);
622}
623
624int
625harmony_get_port(void *vsc, mixer_ctrl_t *cp)
626{
627	struct harmony_softc *sc = vsc;
628	int err = EINVAL;
629
630	switch (cp->dev) {
631	case HARMONY_PORT_INPUT_LVL:
632		if (cp->type != AUDIO_MIXER_VALUE)
633			break;
634		if (cp->un.value.num_channels == 1) {
635			cp->un.value.level[AUDIO_MIXER_LEVEL_MONO] =
636			    sc->sc_input_lvl.left;
637		} else if (cp->un.value.num_channels == 2) {
638			cp->un.value.level[AUDIO_MIXER_LEVEL_LEFT] =
639			    sc->sc_input_lvl.left;
640			cp->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] =
641			    sc->sc_input_lvl.right;
642		} else
643			break;
644		err = 0;
645		break;
646	case HARMONY_PORT_INPUT_OV:
647		if (cp->type != AUDIO_MIXER_ENUM)
648			break;
649		cp->un.ord = sc->sc_ov ? 1 : 0;
650		err = 0;
651		break;
652	case HARMONY_PORT_OUTPUT_LVL:
653		if (cp->type != AUDIO_MIXER_VALUE)
654			break;
655		if (cp->un.value.num_channels == 1) {
656			cp->un.value.level[AUDIO_MIXER_LEVEL_MONO] =
657			    sc->sc_output_lvl.left;
658		} else if (cp->un.value.num_channels == 2) {
659			cp->un.value.level[AUDIO_MIXER_LEVEL_LEFT] =
660			    sc->sc_output_lvl.left;
661			cp->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] =
662			    sc->sc_output_lvl.right;
663		} else
664			break;
665		err = 0;
666		break;
667	case HARMONY_PORT_OUTPUT_GAIN:
668		if (cp->type != AUDIO_MIXER_ENUM)
669			break;
670		cp->un.ord = sc->sc_outputgain ? 1 : 0;
671		err = 0;
672		break;
673	case HARMONY_PORT_MONITOR_LVL:
674		if (cp->type != AUDIO_MIXER_VALUE)
675			break;
676		if (cp->un.value.num_channels != 1)
677			break;
678		cp->un.value.level[AUDIO_MIXER_LEVEL_MONO] =
679		    sc->sc_monitor_lvl.left;
680		err = 0;
681		break;
682	case HARMONY_PORT_RECORD_SOURCE:
683		if (cp->type != AUDIO_MIXER_ENUM)
684			break;
685		cp->un.ord = sc->sc_in_port;
686		err = 0;
687		break;
688	case HARMONY_PORT_OUTPUT_SOURCE:
689		if (cp->type != AUDIO_MIXER_ENUM)
690			break;
691		cp->un.ord = sc->sc_out_port;
692		err = 0;
693		break;
694	}
695	return (0);
696}
697
698int
699harmony_query_devinfo(void *vsc, mixer_devinfo_t *dip)
700{
701	int err = 0;
702
703	switch (dip->index) {
704	case HARMONY_PORT_INPUT_LVL:
705		dip->type = AUDIO_MIXER_VALUE;
706		dip->mixer_class = HARMONY_PORT_INPUT_CLASS;
707		dip->prev = dip->next = AUDIO_MIXER_LAST;
708		strlcpy(dip->label.name, AudioNinput, sizeof dip->label.name);
709		dip->un.v.num_channels = 2;
710		strlcpy(dip->un.v.units.name, AudioNvolume,
711		    sizeof dip->un.v.units.name);
712		break;
713	case HARMONY_PORT_INPUT_OV:
714		dip->type = AUDIO_MIXER_ENUM;
715		dip->mixer_class = HARMONY_PORT_INPUT_CLASS;
716		dip->prev = dip->next = AUDIO_MIXER_LAST;
717		strlcpy(dip->label.name, "overrange", sizeof dip->label.name);
718		dip->un.e.num_mem = 2;
719		strlcpy(dip->un.e.member[0].label.name, AudioNoff,
720		    sizeof dip->un.e.member[0].label.name);
721		dip->un.e.member[0].ord = 0;
722		strlcpy(dip->un.e.member[1].label.name, AudioNon,
723		    sizeof dip->un.e.member[1].label.name);
724		dip->un.e.member[1].ord = 1;
725		break;
726	case HARMONY_PORT_OUTPUT_LVL:
727		dip->type = AUDIO_MIXER_VALUE;
728		dip->mixer_class = HARMONY_PORT_OUTPUT_CLASS;
729		dip->prev = dip->next = AUDIO_MIXER_LAST;
730		strlcpy(dip->label.name, AudioNoutput, sizeof dip->label.name);
731		dip->un.v.num_channels = 2;
732		strlcpy(dip->un.v.units.name, AudioNvolume,
733		    sizeof dip->un.v.units.name);
734		break;
735	case HARMONY_PORT_OUTPUT_GAIN:
736		dip->type = AUDIO_MIXER_ENUM;
737		dip->mixer_class = HARMONY_PORT_OUTPUT_CLASS;
738		dip->prev = dip->next = AUDIO_MIXER_LAST;
739		strlcpy(dip->label.name, "gain", sizeof dip->label.name);
740		dip->un.e.num_mem = 2;
741		strlcpy(dip->un.e.member[0].label.name, AudioNoff,
742		    sizeof dip->un.e.member[0].label.name);
743		dip->un.e.member[0].ord = 0;
744		strlcpy(dip->un.e.member[1].label.name, AudioNon,
745		    sizeof dip->un.e.member[1].label.name);
746		dip->un.e.member[1].ord = 1;
747		break;
748	case HARMONY_PORT_MONITOR_LVL:
749		dip->type = AUDIO_MIXER_VALUE;
750		dip->mixer_class = HARMONY_PORT_MONITOR_CLASS;
751		dip->prev = dip->next = AUDIO_MIXER_LAST;
752		strlcpy(dip->label.name, AudioNmonitor, sizeof dip->label.name);
753		dip->un.v.num_channels = 1;
754		strlcpy(dip->un.v.units.name, AudioNvolume,
755		    sizeof dip->un.v.units.name);
756		break;
757	case HARMONY_PORT_RECORD_SOURCE:
758		dip->type = AUDIO_MIXER_ENUM;
759		dip->mixer_class = HARMONY_PORT_RECORD_CLASS;
760		dip->prev = dip->next = AUDIO_MIXER_LAST;
761		strlcpy(dip->label.name, AudioNsource, sizeof dip->label.name);
762		dip->un.e.num_mem = 2;
763		strlcpy(dip->un.e.member[0].label.name, AudioNmicrophone,
764		    sizeof dip->un.e.member[0].label.name);
765		dip->un.e.member[0].ord = HARMONY_IN_MIC;
766		strlcpy(dip->un.e.member[1].label.name, AudioNline,
767		    sizeof dip->un.e.member[1].label.name);
768		dip->un.e.member[1].ord = HARMONY_IN_LINE;
769		break;
770	case HARMONY_PORT_OUTPUT_SOURCE:
771		dip->type = AUDIO_MIXER_ENUM;
772		dip->mixer_class = HARMONY_PORT_MONITOR_CLASS;
773		dip->prev = dip->next = AUDIO_MIXER_LAST;
774		strlcpy(dip->label.name, AudioNoutput, sizeof dip->label.name);
775		dip->un.e.num_mem = 3;
776		strlcpy(dip->un.e.member[0].label.name, AudioNline,
777		    sizeof dip->un.e.member[0].label.name);
778		dip->un.e.member[0].ord = HARMONY_OUT_LINE;
779		strlcpy(dip->un.e.member[1].label.name, AudioNspeaker,
780		    sizeof dip->un.e.member[1].label.name);
781		dip->un.e.member[1].ord = HARMONY_OUT_SPEAKER;
782		strlcpy(dip->un.e.member[2].label.name, AudioNheadphone,
783		    sizeof dip->un.e.member[2].label.name);
784		dip->un.e.member[2].ord = HARMONY_OUT_HEADPHONE;
785		break;
786	case HARMONY_PORT_INPUT_CLASS:
787		dip->type = AUDIO_MIXER_CLASS;
788		dip->mixer_class = HARMONY_PORT_INPUT_CLASS;
789		dip->prev = dip->next = AUDIO_MIXER_LAST;
790		strlcpy(dip->label.name, AudioCinputs, sizeof dip->label.name);
791		break;
792	case HARMONY_PORT_OUTPUT_CLASS:
793		dip->type = AUDIO_MIXER_CLASS;
794		dip->mixer_class = HARMONY_PORT_INPUT_CLASS;
795		dip->prev = dip->next = AUDIO_MIXER_LAST;
796		strlcpy(dip->label.name, AudioCoutputs, sizeof dip->label.name);
797		break;
798	case HARMONY_PORT_MONITOR_CLASS:
799		dip->type = AUDIO_MIXER_CLASS;
800		dip->mixer_class = HARMONY_PORT_INPUT_CLASS;
801		dip->prev = dip->next = AUDIO_MIXER_LAST;
802		strlcpy(dip->label.name, AudioCmonitor, sizeof dip->label.name);
803		break;
804	case HARMONY_PORT_RECORD_CLASS:
805		dip->type = AUDIO_MIXER_CLASS;
806		dip->mixer_class = HARMONY_PORT_RECORD_CLASS;
807		dip->prev = dip->next = AUDIO_MIXER_LAST;
808		strlcpy(dip->label.name, AudioCrecord, sizeof dip->label.name);
809		break;
810	default:
811		err = ENXIO;
812		break;
813	}
814
815	return (err);
816}
817
818void *
819harmony_allocm(void *vsc, int dir, size_t size, int pool, int flags)
820{
821	struct harmony_softc *sc = vsc;
822	struct harmony_dma *d;
823	int rseg;
824
825	d = (struct harmony_dma *)malloc(sizeof(struct harmony_dma), pool, flags);
826	if (d == NULL)
827		goto fail;
828
829	if (bus_dmamap_create(sc->sc_dmat, size, 1, size, 0, BUS_DMA_NOWAIT,
830	    &d->d_map) != 0)
831		goto fail1;
832
833	if (bus_dmamem_alloc(sc->sc_dmat, size, PAGE_SIZE, 0, &d->d_seg, 1,
834	    &rseg, BUS_DMA_NOWAIT) != 0)
835		goto fail2;
836
837	if (bus_dmamem_map(sc->sc_dmat, &d->d_seg, 1, size, &d->d_kva,
838	    BUS_DMA_NOWAIT) != 0)
839		goto fail3;
840
841	if (bus_dmamap_load(sc->sc_dmat, d->d_map, d->d_kva, size, NULL,
842	    BUS_DMA_NOWAIT) != 0)
843		goto fail4;
844
845	d->d_next = sc->sc_dmas;
846	sc->sc_dmas = d;
847	d->d_size = size;
848	return (d->d_kva);
849
850fail4:
851	bus_dmamem_unmap(sc->sc_dmat, d->d_kva, size);
852fail3:
853	bus_dmamem_free(sc->sc_dmat, &d->d_seg, 1);
854fail2:
855	bus_dmamap_destroy(sc->sc_dmat, d->d_map);
856fail1:
857	free(d, pool, sizeof *d);
858fail:
859	return (NULL);
860}
861
862void
863harmony_freem(void *vsc, void *ptr, int pool)
864{
865	struct harmony_softc *sc = vsc;
866	struct harmony_dma *d, **dd;
867
868	for (dd = &sc->sc_dmas; (d = *dd) != NULL; dd = &(*dd)->d_next) {
869		if (d->d_kva != ptr)
870			continue;
871		bus_dmamap_unload(sc->sc_dmat, d->d_map);
872		bus_dmamem_unmap(sc->sc_dmat, d->d_kva, d->d_size);
873		bus_dmamem_free(sc->sc_dmat, &d->d_seg, 1);
874		bus_dmamap_destroy(sc->sc_dmat, d->d_map);
875		free(d, pool, sizeof *d);
876		return;
877	}
878	printf("%s: free rogue pointer\n", sc->sc_dv.dv_xname);
879}
880
881size_t
882harmony_round_buffersize(void *vsc, int direction, size_t size)
883{
884	return ((size + HARMONY_BUFSIZE - 1) & (size_t)(-HARMONY_BUFSIZE));
885}
886
887int
888harmony_get_props(void *vsc)
889{
890	return (AUDIO_PROP_FULLDUPLEX);
891}
892
893int
894harmony_trigger_output(void *vsc, void *start, void *end, int blksize,
895    void (*intr)(void *), void *intrarg, struct audio_params *param)
896{
897	struct harmony_softc *sc = vsc;
898	struct harmony_channel *c = &sc->sc_playback;
899	struct harmony_dma *d;
900	bus_addr_t nextaddr;
901	bus_size_t togo;
902
903	for (d = sc->sc_dmas; d->d_kva != start; d = d->d_next)
904		/*EMPTY*/;
905	if (d == NULL) {
906		printf("%s: trigger_output: bad addr: %p\n",
907		    sc->sc_dv.dv_xname, start);
908		return (EINVAL);
909	}
910
911	c->c_intr = intr;
912	c->c_intrarg = intrarg;
913	c->c_blksz = blksize;
914	c->c_current = d;
915	c->c_segsz = (caddr_t)end - (caddr_t)start;
916	c->c_cnt = 0;
917	c->c_lastaddr = d->d_map->dm_segs[0].ds_addr;
918
919	sc->sc_playing = 1;
920
921	togo = c->c_segsz - c->c_cnt;
922	if (togo == 0) {
923		nextaddr = d->d_map->dm_segs[0].ds_addr;
924		c->c_cnt = togo = c->c_blksz;
925	} else {
926		nextaddr = c->c_lastaddr;
927		if (togo > c->c_blksz)
928			togo = c->c_blksz;
929		c->c_cnt += togo;
930	}
931
932	bus_dmamap_sync(sc->sc_dmat, d->d_map,
933	    nextaddr - d->d_map->dm_segs[0].ds_addr,
934	    c->c_blksz, BUS_DMASYNC_PREWRITE);
935
936	mtx_enter(&audio_lock);
937	WRITE_REG(sc, HARMONY_PNXTADD, nextaddr);
938	c->c_theaddr = nextaddr;
939	SYNC_REG(sc, HARMONY_PNXTADD, BUS_SPACE_BARRIER_WRITE);
940	c->c_lastaddr = nextaddr + togo;
941
942	harmony_start_cp(sc);
943	harmony_intr_enable(sc);
944	mtx_leave(&audio_lock);
945	return (0);
946}
947
948void
949harmony_start_cp(struct harmony_softc *sc)
950{
951	struct harmony_channel *c = &sc->sc_capture;
952	struct harmony_dma *d;
953	bus_addr_t nextaddr;
954	bus_size_t togo;
955
956	if (sc->sc_capturing == 0) {
957		WRITE_REG(sc, HARMONY_RNXTADD,
958		    sc->sc_capture_paddrs[sc->sc_capture_empty]);
959		if (++sc->sc_capture_empty == CAPTURE_EMPTYS)
960			sc->sc_capture_empty = 0;
961	} else {
962		d = c->c_current;
963		togo = c->c_segsz - c->c_cnt;
964		if (togo == 0) {
965			nextaddr = d->d_map->dm_segs[0].ds_addr;
966			c->c_cnt = togo = c->c_blksz;
967		} else {
968			nextaddr = c->c_lastaddr;
969			if (togo > c->c_blksz)
970				togo = c->c_blksz;
971			c->c_cnt += togo;
972		}
973
974		bus_dmamap_sync(sc->sc_dmat, d->d_map,
975		    nextaddr - d->d_map->dm_segs[0].ds_addr,
976		    c->c_blksz, BUS_DMASYNC_PREWRITE);
977
978		WRITE_REG(sc, HARMONY_RNXTADD, nextaddr);
979		SYNC_REG(sc, HARMONY_RNXTADD, BUS_SPACE_BARRIER_WRITE);
980		c->c_lastaddr = nextaddr + togo;
981	}
982
983	timeout_add(&sc->sc_acc_tmo, 1);
984}
985
986int
987harmony_trigger_input(void *vsc, void *start, void *end, int blksize,
988    void (*intr)(void *), void *intrarg, struct audio_params *param)
989{
990	struct harmony_softc *sc = vsc;
991	struct harmony_channel *c = &sc->sc_capture;
992	struct harmony_dma *d;
993
994	for (d = sc->sc_dmas; d->d_kva != start; d = d->d_next)
995		/*EMPTY*/;
996	if (d == NULL) {
997		printf("%s: trigger_input: bad addr: %p\n",
998		    sc->sc_dv.dv_xname, start);
999		return (EINVAL);
1000	}
1001
1002	c->c_intr = intr;
1003	c->c_intrarg = intrarg;
1004	c->c_blksz = blksize;
1005	c->c_current = d;
1006	c->c_segsz = (caddr_t)end - (caddr_t)start;
1007	c->c_cnt = 0;
1008	c->c_lastaddr = d->d_map->dm_segs[0].ds_addr;
1009	mtx_enter(&audio_lock);
1010	sc->sc_capturing = 1;
1011	harmony_start_cp(sc);
1012	harmony_intr_enable(sc);
1013	mtx_leave(&audio_lock);
1014	return (0);
1015}
1016
1017static const struct speed_struct {
1018	u_int32_t speed;
1019	u_int32_t bits;
1020} harmony_speeds[] = {
1021	{ 5125, CNTL_RATE_5125 },
1022	{ 6615, CNTL_RATE_6615 },
1023	{ 8000, CNTL_RATE_8000 },
1024	{ 9600, CNTL_RATE_9600 },
1025	{ 11025, CNTL_RATE_11025 },
1026	{ 16000, CNTL_RATE_16000 },
1027	{ 18900, CNTL_RATE_18900 },
1028	{ 22050, CNTL_RATE_22050 },
1029	{ 27428, CNTL_RATE_27428 },
1030	{ 32000, CNTL_RATE_32000 },
1031	{ 33075, CNTL_RATE_33075 },
1032	{ 37800, CNTL_RATE_37800 },
1033	{ 44100, CNTL_RATE_44100 },
1034	{ 48000, CNTL_RATE_48000 },
1035};
1036
1037u_int32_t
1038harmony_speed_bits(struct harmony_softc *sc, u_long *speedp)
1039{
1040	int i, n, selected = -1;
1041
1042	n = sizeof(harmony_speeds) / sizeof(harmony_speeds[0]);
1043
1044	if ((*speedp) <= harmony_speeds[0].speed)
1045		selected = 0;
1046	else if ((*speedp) >= harmony_speeds[n - 1].speed)
1047		selected = n - 1;
1048	else {
1049		for (i = 1; selected == -1 && i < n; i++) {
1050			if ((*speedp) == harmony_speeds[i].speed)
1051				selected = i;
1052			else if ((*speedp) < harmony_speeds[i].speed) {
1053				int diff1, diff2;
1054
1055				diff1 = (*speedp) - harmony_speeds[i - 1].speed;
1056				diff2 = harmony_speeds[i].speed - (*speedp);
1057				if (diff1 < diff2)
1058					selected = i - 1;
1059				else
1060					selected = i;
1061			}
1062		}
1063	}
1064
1065	if (selected == -1)
1066		selected = 2;
1067
1068	*speedp = harmony_speeds[selected].speed;
1069	return (harmony_speeds[selected].bits);
1070}
1071
1072int
1073harmony_set_gainctl(struct harmony_softc *sc)
1074{
1075	u_int32_t bits, mask, val, old;
1076
1077	/* XXX leave these bits alone or the chip will not come out of CNTL */
1078	bits = GAINCTL_LE | GAINCTL_HE | GAINCTL_SE | GAINCTL_IS_MASK;
1079
1080	/* input level */
1081	bits |= ((sc->sc_input_lvl.left >> (8 - GAINCTL_INPUT_BITS)) <<
1082	    GAINCTL_INPUT_LEFT_S) & GAINCTL_INPUT_LEFT_M;
1083	bits |= ((sc->sc_input_lvl.right >> (8 - GAINCTL_INPUT_BITS)) <<
1084	    GAINCTL_INPUT_RIGHT_S) & GAINCTL_INPUT_RIGHT_M;
1085
1086	/* output level (inverted) */
1087	mask = (1 << GAINCTL_OUTPUT_BITS) - 1;
1088	val = mask - (sc->sc_output_lvl.left >> (8 - GAINCTL_OUTPUT_BITS));
1089	bits |= (val << GAINCTL_OUTPUT_LEFT_S) & GAINCTL_OUTPUT_LEFT_M;
1090	val = mask - (sc->sc_output_lvl.right >> (8 - GAINCTL_OUTPUT_BITS));
1091	bits |= (val << GAINCTL_OUTPUT_RIGHT_S) & GAINCTL_OUTPUT_RIGHT_M;
1092
1093	/* monitor level (inverted) */
1094	mask = (1 << GAINCTL_MONITOR_BITS) - 1;
1095	val = mask - (sc->sc_monitor_lvl.left >> (8 - GAINCTL_MONITOR_BITS));
1096	bits |= (val << GAINCTL_MONITOR_S) & GAINCTL_MONITOR_M;
1097
1098	/* XXX messing with these causes CNTL_C to get stuck... grr. */
1099	bits &= ~GAINCTL_IS_MASK;
1100	if (sc->sc_in_port == HARMONY_IN_MIC)
1101		bits |= GAINCTL_IS_LINE;
1102	else
1103		bits |= GAINCTL_IS_MICROPHONE;
1104
1105	/* XXX messing with these causes CNTL_C to get stuck... grr. */
1106	bits &= ~(GAINCTL_LE | GAINCTL_HE | GAINCTL_SE);
1107	if (sc->sc_out_port == HARMONY_OUT_LINE)
1108		bits |= GAINCTL_LE;
1109	else if (sc->sc_out_port == HARMONY_OUT_SPEAKER)
1110		bits |= GAINCTL_SE;
1111	else
1112		bits |= GAINCTL_HE;
1113
1114	mask = GAINCTL_LE | GAINCTL_HE | GAINCTL_SE | GAINCTL_IS_MASK;
1115	old = bus_space_read_4(sc->sc_bt, sc->sc_bh, HARMONY_GAINCTL);
1116	bus_space_write_4(sc->sc_bt, sc->sc_bh, HARMONY_GAINCTL, bits);
1117	if ((old & mask) != (bits & mask))
1118		return (1);
1119	return (0);
1120}
1121
1122void
1123harmony_try_more(struct harmony_softc *sc)
1124{
1125	struct harmony_channel *c = &sc->sc_playback;
1126	struct harmony_dma *d = c->c_current;
1127	u_int32_t cur;
1128	int i, nsegs;
1129
1130	cur = bus_space_read_4(sc->sc_bt, sc->sc_bh, HARMONY_PCURADD);
1131	cur &= PCURADD_BUFMASK;
1132	nsegs = 0;
1133
1134#ifdef DIAGNOSTIC
1135	if (cur < d->d_map->dm_segs[0].ds_addr ||
1136	    cur >= (d->d_map->dm_segs[0].ds_addr + c->c_segsz))
1137		panic("%s: bad current %x < %lx || %x > %lx",
1138		    sc->sc_dv.dv_xname, cur, d->d_map->dm_segs[0].ds_addr, cur,
1139		    d->d_map->dm_segs[0].ds_addr + c->c_segsz);
1140#endif /* DIAGNOSTIC */
1141
1142	if (cur > c->c_theaddr) {
1143		nsegs = (cur - c->c_theaddr) / HARMONY_BUFSIZE;
1144	} else if (cur < c->c_theaddr) {
1145		nsegs = (d->d_map->dm_segs[0].ds_addr + c->c_segsz -
1146		    c->c_theaddr) / HARMONY_BUFSIZE;
1147		nsegs += (cur - d->d_map->dm_segs[0].ds_addr) /
1148		    HARMONY_BUFSIZE;
1149	}
1150
1151	if (nsegs != 0 && c->c_intr != NULL) {
1152		for (i = 0; i < nsegs; i++)
1153			(*c->c_intr)(c->c_intrarg);
1154		c->c_theaddr = cur;
1155	}
1156}
1157
1158struct cfdriver harmony_cd = {
1159	NULL, "harmony", DV_DULL
1160};
1161
1162struct cfattach harmony_ca = {
1163	sizeof(struct harmony_softc), harmony_match, harmony_attach
1164};
1165