xref: /openbsd-src/sys/dev/sbus/cs4231.c (revision 2b0358df1d88d06ef4139321dd05bd5e05d91eaf)
1 /*	$OpenBSD: cs4231.c,v 1.29 2008/04/21 00:32:43 jakemsr Exp $	*/
2 
3 /*
4  * Copyright (c) 1999 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  * Effort sponsored in part by the Defense Advanced Research Projects
29  * Agency (DARPA) and Air Force Research Laboratory, Air Force
30  * Materiel Command, USAF, under agreement number F30602-01-2-0537.
31  *
32  */
33 
34 /*
35  * Driver for CS4231 based audio found in some sun4m systems (cs4231)
36  * based on ideas from the S/Linux project and the NetBSD project.
37  */
38 
39 #include <sys/param.h>
40 #include <sys/systm.h>
41 #include <sys/errno.h>
42 #include <sys/ioctl.h>
43 #include <sys/device.h>
44 #include <sys/proc.h>
45 #include <sys/malloc.h>
46 
47 #include <machine/bus.h>
48 #include <machine/intr.h>
49 #include <machine/autoconf.h>
50 
51 #include <sys/audioio.h>
52 #include <dev/audio_if.h>
53 #include <dev/auconv.h>
54 
55 #include <dev/ic/ad1848reg.h>
56 #include <dev/ic/cs4231reg.h>
57 #include <dev/ic/apcdmareg.h>
58 #include <dev/sbus/sbusvar.h>
59 #include <dev/sbus/cs4231var.h>
60 
61 #define	CSAUDIO_DAC_LVL		0
62 #define	CSAUDIO_LINE_IN_LVL	1
63 #define	CSAUDIO_MIC_LVL		2
64 #define	CSAUDIO_CD_LVL		3
65 #define	CSAUDIO_MONITOR_LVL	4
66 #define	CSAUDIO_OUTPUT_LVL	5
67 #define	CSAUDIO_LINE_IN_MUTE	6
68 #define	CSAUDIO_DAC_MUTE	7
69 #define	CSAUDIO_CD_MUTE		8
70 #define	CSAUDIO_MIC_MUTE	9
71 #define	CSAUDIO_MONITOR_MUTE	10
72 #define	CSAUDIO_OUTPUT_MUTE	11
73 #define	CSAUDIO_REC_LVL		12
74 #define	CSAUDIO_RECORD_SOURCE	13
75 #define	CSAUDIO_OUTPUT		14
76 #define	CSAUDIO_INPUT_CLASS	15
77 #define	CSAUDIO_OUTPUT_CLASS	16
78 #define	CSAUDIO_RECORD_CLASS	17
79 #define	CSAUDIO_MONITOR_CLASS	18
80 
81 #define	CSPORT_AUX2		0
82 #define	CSPORT_AUX1		1
83 #define	CSPORT_DAC		2
84 #define	CSPORT_LINEIN		3
85 #define	CSPORT_MONO		4
86 #define	CSPORT_MONITOR		5
87 #define	CSPORT_SPEAKER		6
88 #define	CSPORT_LINEOUT		7
89 #define	CSPORT_HEADPHONE	8
90 #define	CSPORT_MICROPHONE	9
91 
92 #define MIC_IN_PORT	0
93 #define LINE_IN_PORT	1
94 #define AUX1_IN_PORT	2
95 #define DAC_IN_PORT	3
96 
97 #ifdef AUDIO_DEBUG
98 #define	DPRINTF(x)	printf x
99 #else
100 #define	DPRINTF(x)
101 #endif
102 
103 #define	CS_TIMEOUT	90000
104 
105 #define	CS_PC_LINEMUTE	XCTL0_ENABLE
106 #define	CS_PC_HDPHMUTE	XCTL1_ENABLE
107 #define	CS_AFS_TI	0x40		/* timer interrupt */
108 #define	CS_AFS_CI	0x20		/* capture interrupt */
109 #define	CS_AFS_PI	0x10		/* playback interrupt */
110 #define	CS_AFS_CU	0x08		/* capture underrun */
111 #define	CS_AFS_CO	0x04		/* capture overrun */
112 #define	CS_AFS_PO	0x02		/* playback overrun */
113 #define	CS_AFS_PU	0x01		/* playback underrun */
114 
115 #define CS_WRITE(sc,r,v)	\
116     bus_space_write_1((sc)->sc_bustag, (sc)->sc_regs, (r) << 2, (v))
117 #define	CS_READ(sc,r)		\
118     bus_space_read_1((sc)->sc_bustag, (sc)->sc_regs, (r) << 2)
119 
120 #define	APC_WRITE(sc,r,v)	\
121     bus_space_write_4(sc->sc_bustag, sc->sc_regs, r, v)
122 #define	APC_READ(sc,r)		\
123     bus_space_read_4(sc->sc_bustag, sc->sc_regs, r)
124 
125 int	cs4231_match(struct device *, void *, void *);
126 void	cs4231_attach(struct device *, struct device *, void *);
127 int	cs4231_intr(void *);
128 
129 int	cs4231_set_speed(struct cs4231_softc *, u_long *);
130 void	cs4231_setup_output(struct cs4231_softc *sc);
131 
132 void		cs4231_write(struct cs4231_softc *, u_int8_t, u_int8_t);
133 u_int8_t	cs4231_read(struct cs4231_softc *, u_int8_t);
134 
135 /* Audio interface */
136 int	cs4231_open(void *, int);
137 void	cs4231_close(void *);
138 int	cs4231_query_encoding(void *, struct audio_encoding *);
139 int	cs4231_set_params(void *, int, int, struct audio_params *,
140     struct audio_params *);
141 int	cs4231_round_blocksize(void *, int);
142 int	cs4231_commit_settings(void *);
143 int	cs4231_halt_output(void *);
144 int	cs4231_halt_input(void *);
145 int	cs4231_getdev(void *, struct audio_device *);
146 int	cs4231_set_port(void *, mixer_ctrl_t *);
147 int	cs4231_get_port(void *, mixer_ctrl_t *);
148 int	cs4231_query_devinfo(void *, mixer_devinfo_t *);
149 void *	cs4231_alloc(void *, int, size_t, int, int);
150 void	cs4231_free(void *, void *, int);
151 int	cs4231_get_props(void *);
152 int	cs4231_trigger_output(void *, void *, void *, int,
153     void (*)(void *), void *, struct audio_params *);
154 int	cs4231_trigger_input(void *, void *, void *, int,
155     void (*)(void *), void *, struct audio_params *);
156 
157 struct audio_hw_if cs4231_sa_hw_if = {
158 	cs4231_open,
159 	cs4231_close,
160 	0,
161 	cs4231_query_encoding,
162 	cs4231_set_params,
163 	cs4231_round_blocksize,
164 	cs4231_commit_settings,
165 	0,
166 	0,
167 	0,
168 	0,
169 	cs4231_halt_output,
170 	cs4231_halt_input,
171 	0,
172 	cs4231_getdev,
173 	0,
174 	cs4231_set_port,
175 	cs4231_get_port,
176 	cs4231_query_devinfo,
177 	cs4231_alloc,
178 	cs4231_free,
179 	0,
180 	0,
181 	cs4231_get_props,
182 	cs4231_trigger_output,
183 	cs4231_trigger_input,
184 	0
185 };
186 
187 struct cfattach audiocs_ca = {
188 	sizeof (struct cs4231_softc), cs4231_match, cs4231_attach
189 };
190 
191 struct cfdriver audiocs_cd = {
192 	NULL, "audiocs", DV_DULL
193 };
194 
195 struct audio_device cs4231_device = {
196 	"SUNW,CS4231",
197 	"b",
198 	"onboard1",
199 };
200 
201 int
202 cs4231_match(struct device *parent, void *vcf, void *aux)
203 {
204 	struct sbus_attach_args *sa = aux;
205 
206 	return (strcmp("SUNW,CS4231", sa->sa_name) == 0);
207 }
208 
209 void
210 cs4231_attach(struct device *parent, struct device *self, void *aux)
211 {
212 	struct sbus_attach_args *sa = aux;
213 	struct cs4231_softc *sc = (struct cs4231_softc *)self;
214 	int node;
215 	u_int32_t sbusburst, burst;
216 
217 	node = sa->sa_node;
218 
219 	/* Pass on the bus tags */
220 	sc->sc_bustag = sa->sa_bustag;
221 	sc->sc_dmatag = sa->sa_dmatag;
222 
223 	/* Make sure things are sane. */
224 	if (sa->sa_nintr != 1) {
225 		printf(": expected 1 interrupt, got %d\n", sa->sa_nintr);
226 		return;
227 	}
228 	if (sa->sa_nreg != 1) {
229 		printf(": expected 1 register set, got %d\n",
230 		    sa->sa_nreg);
231 		return;
232 	}
233 
234 	if (bus_intr_establish(sa->sa_bustag, sa->sa_pri, IPL_AUDIO, 0,
235 	    cs4231_intr, sc, self->dv_xname) == NULL) {
236 		printf(": couldn't establish interrupt, pri %d\n",
237 		    INTLEV(sa->sa_pri));
238 		return;
239 	}
240 
241 	if (sbus_bus_map(sa->sa_bustag,
242 	    sa->sa_reg[0].sbr_slot,
243 	    (bus_addr_t)sa->sa_reg[0].sbr_offset,
244 	    (bus_size_t)sa->sa_reg[0].sbr_size,
245 	    BUS_SPACE_MAP_LINEAR, 0, &sc->sc_regs) != 0) {
246 		printf(": couldn't map registers\n");
247 		return;
248 	}
249 
250 	sbusburst = ((struct sbus_softc *)parent)->sc_burst;
251 	if (sbusburst == 0)
252 		sbusburst = SBUS_BURST_32 - 1;	/* 1->16 */
253 	burst = getpropint(node, "burst-sizes", -1);
254 	if (burst == -1)
255 		burst = sbusburst;
256 	sc->sc_burst = burst & sbusburst;
257 
258 	printf("\n");
259 
260 	audio_attach_mi(&cs4231_sa_hw_if, sc, &sc->sc_dev);
261 
262 	/* Default to speaker, unmuted, reasonable volume */
263 	sc->sc_out_port = CSPORT_SPEAKER;
264 	sc->sc_in_port = CSPORT_MICROPHONE;
265 	sc->sc_mute[CSPORT_SPEAKER] = 1;
266 	sc->sc_mute[CSPORT_MONITOR] = 1;
267 	sc->sc_volume[CSPORT_SPEAKER].left = 192;
268 	sc->sc_volume[CSPORT_SPEAKER].right = 192;
269 }
270 
271 /*
272  * Write to one of the indexed registers of cs4231.
273  */
274 void
275 cs4231_write(struct cs4231_softc *sc, u_int8_t r, u_int8_t v)
276 {
277 	CS_WRITE(sc, AD1848_IADDR, r);
278 	CS_WRITE(sc, AD1848_IDATA, v);
279 }
280 
281 /*
282  * Read from one of the indexed registers of cs4231.
283  */
284 u_int8_t
285 cs4231_read(struct cs4231_softc *sc, u_int8_t r)
286 {
287 	CS_WRITE(sc, AD1848_IADDR, r);
288 	return (CS_READ(sc, AD1848_IDATA));
289 }
290 
291 int
292 cs4231_set_speed(struct cs4231_softc *sc, u_long *argp)
293 {
294 	/*
295 	 * The available speeds are in the following table. Keep the speeds in
296 	 * the increasing order.
297 	 */
298 	typedef struct {
299 		int speed;
300 		u_char bits;
301 	} speed_struct;
302 	u_long arg = *argp;
303 
304 	const static speed_struct speed_table[] = {
305 		{5510,	(0 << 1) | CLOCK_XTAL2},
306 		{5510,	(0 << 1) | CLOCK_XTAL2},
307 		{6620,	(7 << 1) | CLOCK_XTAL2},
308 		{8000,	(0 << 1) | CLOCK_XTAL1},
309 		{9600,	(7 << 1) | CLOCK_XTAL1},
310 		{11025,	(1 << 1) | CLOCK_XTAL2},
311 		{16000,	(1 << 1) | CLOCK_XTAL1},
312 		{18900,	(2 << 1) | CLOCK_XTAL2},
313 		{22050,	(3 << 1) | CLOCK_XTAL2},
314 		{27420,	(2 << 1) | CLOCK_XTAL1},
315 		{32000,	(3 << 1) | CLOCK_XTAL1},
316 		{33075,	(6 << 1) | CLOCK_XTAL2},
317 		{33075,	(4 << 1) | CLOCK_XTAL2},
318 		{44100,	(5 << 1) | CLOCK_XTAL2},
319 		{48000,	(6 << 1) | CLOCK_XTAL1},
320 	};
321 
322 	int i, n, selected = -1;
323 
324 	n = sizeof(speed_table) / sizeof(speed_struct);
325 
326 	if (arg < speed_table[0].speed)
327 		selected = 0;
328 	if (arg > speed_table[n - 1].speed)
329 		selected = n - 1;
330 
331 	for (i = 1; selected == -1 && i < n; i++) {
332 		if (speed_table[i].speed == arg)
333 			selected = i;
334 		else if (speed_table[i].speed > arg) {
335 			int diff1, diff2;
336 
337 			diff1 = arg - speed_table[i - 1].speed;
338 			diff2 = speed_table[i].speed - arg;
339 			if (diff1 < diff2)
340 				selected = i - 1;
341 			else
342 				selected = i;
343 		}
344 	}
345 
346 	if (selected == -1)
347 		selected = 3;
348 
349 	sc->sc_speed_bits = speed_table[selected].bits;
350 	sc->sc_need_commit = 1;
351 	*argp = speed_table[selected].speed;
352 
353 	return (0);
354 }
355 
356 /*
357  * Audio interface functions
358  */
359 int
360 cs4231_open(void *vsc, int flags)
361 {
362 	struct cs4231_softc *sc = vsc;
363 	int tries;
364 
365 	if (sc->sc_open)
366 		return (EBUSY);
367 	sc->sc_open = 1;
368 
369 	sc->sc_capture.cs_intr = NULL;
370 	sc->sc_capture.cs_arg = NULL;
371 	sc->sc_capture.cs_locked = 0;
372 
373 	sc->sc_playback.cs_intr = NULL;
374 	sc->sc_playback.cs_arg = NULL;
375 	sc->sc_playback.cs_locked = 0;
376 
377 	APC_WRITE(sc, APC_CSR, APC_CSR_RESET);
378 	DELAY(10);
379 	APC_WRITE(sc, APC_CSR, 0);
380 	DELAY(10);
381 	APC_WRITE(sc, APC_CSR, APC_READ(sc, APC_CSR) | APC_CSR_CODEC_RESET);
382 
383 	DELAY(20);
384 
385 	APC_WRITE(sc, APC_CSR, APC_READ(sc, APC_CSR) & (~APC_CSR_CODEC_RESET));
386 
387 	for (tries = CS_TIMEOUT;
388 	     tries && CS_READ(sc, AD1848_IADDR) == SP_IN_INIT; tries--)
389 		DELAY(10);
390 	if (tries == 0)
391 		printf("%s: timeout waiting for reset\n", sc->sc_dev.dv_xname);
392 
393 	/* Turn on cs4231 mode */
394 	cs4231_write(sc, SP_MISC_INFO,
395 	    cs4231_read(sc, SP_MISC_INFO) | MODE2);
396 
397 	cs4231_setup_output(sc);
398 
399 	cs4231_write(sc, SP_PIN_CONTROL,
400 	    cs4231_read(sc, SP_PIN_CONTROL) | INTERRUPT_ENABLE);
401 
402 	return (0);
403 }
404 
405 void
406 cs4231_setup_output(struct cs4231_softc *sc)
407 {
408 	u_int8_t pc, mi, rm, lm;
409 
410 	pc = cs4231_read(sc, SP_PIN_CONTROL) | CS_PC_HDPHMUTE | CS_PC_LINEMUTE;
411 
412 	mi = cs4231_read(sc, CS_MONO_IO_CONTROL) | MONO_OUTPUT_MUTE;
413 
414 	lm = cs4231_read(sc, SP_LEFT_OUTPUT_CONTROL);
415 	lm &= ~OUTPUT_ATTEN_BITS;
416 	lm |= ((~(sc->sc_volume[CSPORT_SPEAKER].left >> 2)) &
417 	    OUTPUT_ATTEN_BITS) | OUTPUT_MUTE;
418 
419 	rm = cs4231_read(sc, SP_RIGHT_OUTPUT_CONTROL);
420 	rm &= ~OUTPUT_ATTEN_BITS;
421 	rm |= ((~(sc->sc_volume[CSPORT_SPEAKER].right >> 2)) &
422 	    OUTPUT_ATTEN_BITS) | OUTPUT_MUTE;
423 
424 	if (sc->sc_mute[CSPORT_MONITOR]) {
425 		lm &= ~OUTPUT_MUTE;
426 		rm &= ~OUTPUT_MUTE;
427 	}
428 
429 	switch (sc->sc_out_port) {
430 	case CSPORT_HEADPHONE:
431 		if (sc->sc_mute[CSPORT_SPEAKER])
432 			pc &= ~CS_PC_HDPHMUTE;
433 		break;
434 	case CSPORT_SPEAKER:
435 		if (sc->sc_mute[CSPORT_SPEAKER])
436 			mi &= ~MONO_OUTPUT_MUTE;
437 		break;
438 	case CSPORT_LINEOUT:
439 		if (sc->sc_mute[CSPORT_SPEAKER])
440 			pc &= ~CS_PC_LINEMUTE;
441 		break;
442 	}
443 
444 	cs4231_write(sc, SP_LEFT_OUTPUT_CONTROL, lm);
445 	cs4231_write(sc, SP_RIGHT_OUTPUT_CONTROL, rm);
446 	cs4231_write(sc, SP_PIN_CONTROL, pc);
447 	cs4231_write(sc, CS_MONO_IO_CONTROL, mi);
448 
449 	/* XXX doesn't really belong here... */
450 	switch (sc->sc_in_port) {
451 	case CSPORT_LINEIN:
452 		pc = LINE_INPUT;
453 		break;
454 	case CSPORT_AUX1:
455 		pc = AUX_INPUT;
456 		break;
457 	case CSPORT_DAC:
458 		pc = MIXED_DAC_INPUT;
459 		break;
460 	case CSPORT_MICROPHONE:
461 	default:
462 		pc = MIC_INPUT;
463 		break;
464 	}
465 	lm = cs4231_read(sc, SP_LEFT_INPUT_CONTROL);
466 	rm = cs4231_read(sc, SP_RIGHT_INPUT_CONTROL);
467 	lm &= ~(MIXED_DAC_INPUT | ATTEN_22_5);
468 	rm &= ~(MIXED_DAC_INPUT | ATTEN_22_5);
469 	lm |= pc | (sc->sc_adc.left >> 4);
470 	rm |= pc | (sc->sc_adc.right >> 4);
471 	cs4231_write(sc, SP_LEFT_INPUT_CONTROL, lm);
472 	cs4231_write(sc, SP_RIGHT_INPUT_CONTROL, rm);
473 }
474 
475 void
476 cs4231_close(void *vsc)
477 {
478 	struct cs4231_softc *sc = vsc;
479 
480 	cs4231_halt_input(sc);
481 	cs4231_halt_output(sc);
482 	cs4231_write(sc, SP_PIN_CONTROL,
483 	    cs4231_read(sc, SP_PIN_CONTROL) & (~INTERRUPT_ENABLE));
484 	sc->sc_open = 0;
485 }
486 
487 int
488 cs4231_query_encoding(void *vsc, struct audio_encoding *fp)
489 {
490 	int err = 0;
491 
492 	switch (fp->index) {
493 	case 0:
494 		strlcpy(fp->name, AudioEmulaw, sizeof fp->name);
495 		fp->encoding = AUDIO_ENCODING_ULAW;
496 		fp->precision = 8;
497 		fp->flags = 0;
498 		break;
499 	case 1:
500 		strlcpy(fp->name, AudioEalaw, sizeof fp->name);
501 		fp->encoding = AUDIO_ENCODING_ALAW;
502 		fp->precision = 8;
503 		fp->flags = 0;
504 		break;
505 	case 2:
506 		strlcpy(fp->name, AudioEslinear_le, sizeof fp->name);
507 		fp->encoding = AUDIO_ENCODING_SLINEAR_LE;
508 		fp->precision = 16;
509 		fp->flags = 0;
510 		break;
511 	case 3:
512 		strlcpy(fp->name, AudioEulinear, sizeof fp->name);
513 		fp->encoding = AUDIO_ENCODING_ULINEAR;
514 		fp->precision = 8;
515 		fp->flags = 0;
516 		break;
517 	case 4:
518 		strlcpy(fp->name, AudioEslinear_be, sizeof fp->name);
519 		fp->encoding = AUDIO_ENCODING_SLINEAR_BE;
520 		fp->precision = 16;
521 		fp->flags = 0;
522 		break;
523 	case 5:
524 		strlcpy(fp->name, AudioEslinear, sizeof fp->name);
525 		fp->encoding = AUDIO_ENCODING_SLINEAR;
526 		fp->precision = 8;
527 		fp->flags = AUDIO_ENCODINGFLAG_EMULATED;
528 		break;
529 	case 6:
530 		strlcpy(fp->name, AudioEulinear_le, sizeof fp->name);
531 		fp->encoding = AUDIO_ENCODING_ULINEAR_LE;
532 		fp->precision = 16;
533 		fp->flags = AUDIO_ENCODINGFLAG_EMULATED;
534 		break;
535 	case 7:
536 		strlcpy(fp->name, AudioEulinear_be, sizeof fp->name);
537 		fp->encoding = AUDIO_ENCODING_ULINEAR_BE;
538 		fp->precision = 16;
539 		fp->flags = AUDIO_ENCODINGFLAG_EMULATED;
540 		break;
541 	case 8:
542 		strlcpy(fp->name, AudioEadpcm, sizeof fp->name);
543 		fp->encoding = AUDIO_ENCODING_ADPCM;
544 		fp->precision = 8;
545 		fp->flags = 0;
546 		break;
547 	default:
548 		err = EINVAL;
549 	}
550 	return (err);
551 }
552 
553 int
554 cs4231_set_params(void *vsc, int setmode, int usemode,
555     struct audio_params *p, struct audio_params *r)
556 {
557 	struct cs4231_softc *sc = (struct cs4231_softc *)vsc;
558 	int err, bits, enc = p->encoding;
559 	void (*pswcode)(void *, u_char *, int cnt) = NULL;
560 	void (*rswcode)(void *, u_char *, int cnt) = NULL;
561 
562 	switch (enc) {
563 	case AUDIO_ENCODING_ULAW:
564 		if (p->precision != 8)
565 			return (EINVAL);
566 		bits = FMT_ULAW >> 5;
567 		break;
568 	case AUDIO_ENCODING_ALAW:
569 		if (p->precision != 8)
570 			return (EINVAL);
571 		bits = FMT_ALAW >> 5;
572 		break;
573 	case AUDIO_ENCODING_SLINEAR_LE:
574 		if (p->precision == 8) {
575 			bits = FMT_PCM8 >> 5;
576 			pswcode = rswcode = change_sign8;
577 		} else if (p->precision == 16)
578 			bits = FMT_TWOS_COMP >> 5;
579 		else
580 			return (EINVAL);
581 		break;
582 	case AUDIO_ENCODING_ULINEAR:
583 		if (p->precision != 8)
584 			return (EINVAL);
585 		bits = FMT_PCM8 >> 5;
586 		break;
587 	case AUDIO_ENCODING_SLINEAR_BE:
588 		if (p->precision == 8) {
589 			bits = FMT_PCM8 >> 5;
590 			pswcode = rswcode = change_sign8;
591 		} else if (p->precision == 16)
592 			bits = FMT_TWOS_COMP_BE >> 5;
593 		else
594 			return (EINVAL);
595 		break;
596 	case AUDIO_ENCODING_SLINEAR:
597 		if (p->precision != 8)
598 			return (EINVAL);
599 		bits = FMT_PCM8 >> 5;
600 		pswcode = rswcode = change_sign8;
601 		break;
602 	case AUDIO_ENCODING_ULINEAR_LE:
603 		if (p->precision == 8)
604 			bits = FMT_PCM8 >> 5;
605 		else if (p->precision == 16) {
606 			bits = FMT_TWOS_COMP >> 5;
607 			pswcode = rswcode = change_sign16_le;
608 		} else
609 			return (EINVAL);
610 		break;
611 	case AUDIO_ENCODING_ULINEAR_BE:
612 		if (p->precision == 8)
613 			bits = FMT_PCM8 >> 5;
614 		else if (p->precision == 16) {
615 			bits = FMT_TWOS_COMP_BE >> 5;
616 			pswcode = rswcode = change_sign16_be;
617 		} else
618 			return (EINVAL);
619 		break;
620 	case AUDIO_ENCODING_ADPCM:
621 		if (p->precision != 8)
622 			return (EINVAL);
623 		bits = FMT_ADPCM >> 5;
624 		break;
625 	default:
626 		return (EINVAL);
627 	}
628 
629 	if (p->channels != 1 && p->channels != 2)
630 		return (EINVAL);
631 
632 	err = cs4231_set_speed(sc, &p->sample_rate);
633 	if (err)
634 		return (err);
635 
636 	p->sw_code = pswcode;
637 	r->sw_code = rswcode;
638 
639 	sc->sc_format_bits = bits;
640 	sc->sc_channels = p->channels;
641 	sc->sc_precision = p->precision;
642 	sc->sc_need_commit = 1;
643 	return (0);
644 }
645 
646 int
647 cs4231_round_blocksize(void *vsc, int blk)
648 {
649 	return ((blk + 3) & (-4));
650 }
651 
652 int
653 cs4231_commit_settings(void *vsc)
654 {
655 	struct cs4231_softc *sc = (struct cs4231_softc *)vsc;
656 	int s, tries;
657 	u_int8_t r, fs;
658 
659 	if (sc->sc_need_commit == 0)
660 		return (0);
661 
662 	fs = sc->sc_speed_bits | (sc->sc_format_bits << 5);
663 	if (sc->sc_channels == 2)
664 		fs |= FMT_STEREO;
665 
666 	s = splaudio();
667 
668 	r = cs4231_read(sc, SP_INTERFACE_CONFIG) | AUTO_CAL_ENABLE;
669 	CS_WRITE(sc, AD1848_IADDR, MODE_CHANGE_ENABLE);
670 	CS_WRITE(sc, AD1848_IADDR, MODE_CHANGE_ENABLE | SP_INTERFACE_CONFIG);
671 	CS_WRITE(sc, AD1848_IDATA, r);
672 
673 	CS_WRITE(sc, AD1848_IADDR, MODE_CHANGE_ENABLE | SP_CLOCK_DATA_FORMAT);
674 	CS_WRITE(sc, AD1848_IDATA, fs);
675 	CS_READ(sc, AD1848_IDATA);
676 	CS_READ(sc, AD1848_IDATA);
677 	tries = CS_TIMEOUT;
678 	for (tries = CS_TIMEOUT;
679 	     tries && CS_READ(sc, AD1848_IADDR) == SP_IN_INIT; tries--)
680 		DELAY(10);
681 	if (tries == 0)
682 		printf("%s: timeout committing fspb\n", sc->sc_dev.dv_xname);
683 
684 	CS_WRITE(sc, AD1848_IADDR, MODE_CHANGE_ENABLE | CS_REC_FORMAT);
685 	CS_WRITE(sc, AD1848_IDATA, fs);
686 	CS_READ(sc, AD1848_IDATA);
687 	CS_READ(sc, AD1848_IDATA);
688 	for (tries = CS_TIMEOUT;
689 	     tries && CS_READ(sc, AD1848_IADDR) == SP_IN_INIT; tries--)
690 		DELAY(10);
691 	if (tries == 0)
692 		printf("%s: timeout committing cdf\n", sc->sc_dev.dv_xname);
693 
694 	CS_WRITE(sc, AD1848_IADDR, 0);
695 	for (tries = CS_TIMEOUT;
696 	     tries && CS_READ(sc, AD1848_IADDR) == SP_IN_INIT; tries--)
697 		DELAY(10);
698 	if (tries == 0)
699 		printf("%s: timeout waiting for !mce\n", sc->sc_dev.dv_xname);
700 
701 	CS_WRITE(sc, AD1848_IADDR, SP_TEST_AND_INIT);
702 	for (tries = CS_TIMEOUT;
703 	     tries && CS_READ(sc, AD1848_IDATA) & AUTO_CAL_IN_PROG; tries--)
704 		DELAY(10);
705 	if (tries == 0)
706 		printf("%s: timeout waiting for autocalibration\n",
707 		    sc->sc_dev.dv_xname);
708 
709 	splx(s);
710 
711 	sc->sc_need_commit = 0;
712 	return (0);
713 }
714 
715 int
716 cs4231_halt_output(void *vsc)
717 {
718 	struct cs4231_softc *sc = (struct cs4231_softc *)vsc;
719 
720 	/* XXX Kills some capture bits */
721 	APC_WRITE(sc, APC_CSR, APC_READ(sc, APC_CSR) &
722 	    ~(APC_CSR_EI | APC_CSR_GIE | APC_CSR_PIE |
723 	      APC_CSR_EIE | APC_CSR_PDMA_GO | APC_CSR_PMIE));
724 	cs4231_write(sc, SP_INTERFACE_CONFIG,
725 	    cs4231_read(sc, SP_INTERFACE_CONFIG) & (~PLAYBACK_ENABLE));
726 	sc->sc_playback.cs_locked = 0;
727 	return (0);
728 }
729 
730 int
731 cs4231_halt_input(void *vsc)
732 {
733 	struct cs4231_softc *sc = (struct cs4231_softc *)vsc;
734 
735 	/* XXX Kills some playback bits */
736 	APC_WRITE(sc, APC_CSR, APC_CSR_CAPTURE_PAUSE);
737 	cs4231_write(sc, SP_INTERFACE_CONFIG,
738 	    cs4231_read(sc, SP_INTERFACE_CONFIG) & (~CAPTURE_ENABLE));
739 	sc->sc_capture.cs_locked = 0;
740 	return (0);
741 }
742 
743 int
744 cs4231_getdev(void *vsc, struct audio_device *retp)
745 {
746 	*retp = cs4231_device;
747 	return (0);
748 }
749 
750 int
751 cs4231_set_port(void *vsc, mixer_ctrl_t *cp)
752 {
753 	struct cs4231_softc *sc = (struct cs4231_softc *)vsc;
754 	int error = EINVAL;
755 
756 	DPRINTF(("cs4231_set_port: port=%d type=%d\n", cp->dev, cp->type));
757 
758 	switch (cp->dev) {
759 	case CSAUDIO_DAC_LVL:
760 		if (cp->type != AUDIO_MIXER_VALUE)
761 			break;
762 		if (cp->un.value.num_channels == 1)
763 			cs4231_write(sc, SP_LEFT_AUX1_CONTROL,
764 			    cp->un.value.level[AUDIO_MIXER_LEVEL_MONO] &
765 			    LINE_INPUT_ATTEN_BITS);
766 		else if (cp->un.value.num_channels == 2) {
767 			cs4231_write(sc, SP_LEFT_AUX1_CONTROL,
768 			    cp->un.value.level[AUDIO_MIXER_LEVEL_LEFT] &
769 			    LINE_INPUT_ATTEN_BITS);
770 			cs4231_write(sc, SP_RIGHT_AUX1_CONTROL,
771 			    cp->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] &
772 			    LINE_INPUT_ATTEN_BITS);
773 		} else
774 			break;
775 		error = 0;
776 		break;
777 	case CSAUDIO_LINE_IN_LVL:
778 		if (cp->type != AUDIO_MIXER_VALUE)
779 			break;
780 		if (cp->un.value.num_channels == 1)
781 			cs4231_write(sc, CS_LEFT_LINE_CONTROL,
782 			    cp->un.value.level[AUDIO_MIXER_LEVEL_MONO] &
783 			    AUX_INPUT_ATTEN_BITS);
784 		else if (cp->un.value.num_channels == 2) {
785 			cs4231_write(sc, CS_LEFT_LINE_CONTROL,
786 			    cp->un.value.level[AUDIO_MIXER_LEVEL_LEFT] &
787 			    AUX_INPUT_ATTEN_BITS);
788 			cs4231_write(sc, CS_RIGHT_LINE_CONTROL,
789 			    cp->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] &
790 			    AUX_INPUT_ATTEN_BITS);
791 		} else
792 			break;
793 		error = 0;
794 		break;
795 	case CSAUDIO_MIC_LVL:
796 		if (cp->type != AUDIO_MIXER_VALUE)
797 			break;
798 		if (cp->un.value.num_channels == 1) {
799 #if 0
800 			cs4231_write(sc, CS_MONO_IO_CONTROL,
801 			    cp->un.value.level[AUDIO_MIXER_LEVEL_MONO] &
802 			    MONO_INPUT_ATTEN_BITS);
803 #endif
804 		} else
805 			break;
806 		error = 0;
807 		break;
808 	case CSAUDIO_CD_LVL:
809 		if (cp->type != AUDIO_MIXER_VALUE)
810 			break;
811 		if (cp->un.value.num_channels == 1) {
812 			cs4231_write(sc, SP_LEFT_AUX2_CONTROL,
813 			    cp->un.value.level[AUDIO_MIXER_LEVEL_MONO] &
814 			    LINE_INPUT_ATTEN_BITS);
815 		} else if (cp->un.value.num_channels == 2) {
816 			cs4231_write(sc, SP_LEFT_AUX2_CONTROL,
817 			    cp->un.value.level[AUDIO_MIXER_LEVEL_LEFT] &
818 			    LINE_INPUT_ATTEN_BITS);
819 			cs4231_write(sc, SP_RIGHT_AUX2_CONTROL,
820 			    cp->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] &
821 			    LINE_INPUT_ATTEN_BITS);
822 		} else
823 			break;
824 		error = 0;
825 		break;
826 	case CSAUDIO_MONITOR_LVL:
827 		if (cp->type != AUDIO_MIXER_VALUE)
828 			break;
829 		if (cp->un.value.num_channels == 1)
830 			cs4231_write(sc, SP_DIGITAL_MIX,
831 			    cp->un.value.level[AUDIO_MIXER_LEVEL_MONO] << 2);
832 		else
833 			break;
834 		error = 0;
835 		break;
836 	case CSAUDIO_OUTPUT_LVL:
837 		if (cp->type != AUDIO_MIXER_VALUE)
838 			break;
839 		if (cp->un.value.num_channels == 1) {
840 			sc->sc_volume[CSPORT_SPEAKER].left =
841 			    cp->un.value.level[AUDIO_MIXER_LEVEL_MONO];
842 			sc->sc_volume[CSPORT_SPEAKER].right =
843 			    cp->un.value.level[AUDIO_MIXER_LEVEL_MONO];
844 		}
845 		else if (cp->un.value.num_channels == 2) {
846 			sc->sc_volume[CSPORT_SPEAKER].left =
847 			    cp->un.value.level[AUDIO_MIXER_LEVEL_LEFT];
848 			sc->sc_volume[CSPORT_SPEAKER].right =
849 			    cp->un.value.level[AUDIO_MIXER_LEVEL_RIGHT];
850 		}
851 		else
852 			break;
853 
854 		cs4231_setup_output(sc);
855 		error = 0;
856 		break;
857 	case CSAUDIO_OUTPUT:
858 		if (cp->type != AUDIO_MIXER_ENUM)
859 			break;
860 		if (cp->un.ord != CSPORT_LINEOUT &&
861 		    cp->un.ord != CSPORT_SPEAKER &&
862 		    cp->un.ord != CSPORT_HEADPHONE)
863 			return (EINVAL);
864 		sc->sc_out_port = cp->un.ord;
865 		cs4231_setup_output(sc);
866 		error = 0;
867 		break;
868 	case CSAUDIO_LINE_IN_MUTE:
869 		if (cp->type != AUDIO_MIXER_ENUM)
870 			break;
871 		sc->sc_mute[CSPORT_LINEIN] = cp->un.ord ? 1 : 0;
872 		error = 0;
873 		break;
874 	case CSAUDIO_DAC_MUTE:
875 		if (cp->type != AUDIO_MIXER_ENUM)
876 			break;
877 		sc->sc_mute[CSPORT_AUX1] = cp->un.ord ? 1 : 0;
878 		error = 0;
879 		break;
880 	case CSAUDIO_CD_MUTE:
881 		if (cp->type != AUDIO_MIXER_ENUM)
882 			break;
883 		sc->sc_mute[CSPORT_AUX2] = cp->un.ord ? 1 : 0;
884 		error = 0;
885 		break;
886 	case CSAUDIO_MIC_MUTE:
887 		if (cp->type != AUDIO_MIXER_ENUM)
888 			break;
889 		sc->sc_mute[CSPORT_MONO] = cp->un.ord ? 1 : 0;
890 		error = 0;
891 		break;
892 	case CSAUDIO_MONITOR_MUTE:
893 		if (cp->type != AUDIO_MIXER_ENUM)
894 			break;
895 		sc->sc_mute[CSPORT_MONITOR] = cp->un.ord ? 1 : 0;
896 		error = 0;
897 		break;
898 	case CSAUDIO_OUTPUT_MUTE:
899 		if (cp->type != AUDIO_MIXER_ENUM)
900 			break;
901 		sc->sc_mute[CSPORT_SPEAKER] = cp->un.ord ? 1 : 0;
902 		cs4231_setup_output(sc);
903 		error = 0;
904 		break;
905 	case CSAUDIO_REC_LVL:
906 		if (cp->type != AUDIO_MIXER_VALUE)
907 			break;
908 		if (cp->un.value.num_channels == 1) {
909 			sc->sc_adc.left =
910 			    cp->un.value.level[AUDIO_MIXER_LEVEL_MONO];
911 			sc->sc_adc.right =
912 			    cp->un.value.level[AUDIO_MIXER_LEVEL_MONO];
913 		} else if (cp->un.value.num_channels == 2) {
914 			sc->sc_adc.left =
915 			    cp->un.value.level[AUDIO_MIXER_LEVEL_LEFT];
916 			sc->sc_adc.right =
917 			    cp->un.value.level[AUDIO_MIXER_LEVEL_RIGHT];
918 		} else
919 			break;
920 		cs4231_setup_output(sc);
921 		error = 0;
922 		break;
923 	case CSAUDIO_RECORD_SOURCE:
924 		if (cp->type != AUDIO_MIXER_ENUM)
925 			break;
926 		if (cp->un.ord == CSPORT_MICROPHONE ||
927 		    cp->un.ord == CSPORT_LINEIN ||
928 		    cp->un.ord == CSPORT_AUX1 ||
929 		    cp->un.ord == CSPORT_DAC) {
930 			sc->sc_in_port  = cp->un.ord;
931 			error = 0;
932 			cs4231_setup_output(sc);
933 		}
934 		break;
935 	}
936 
937 	return (error);
938 }
939 
940 int
941 cs4231_get_port(void *vsc, mixer_ctrl_t *cp)
942 {
943 	struct cs4231_softc *sc = (struct cs4231_softc *)vsc;
944 	int error = EINVAL;
945 
946 	DPRINTF(("cs4231_get_port: port=%d type=%d\n", cp->dev, cp->type));
947 
948 	switch (cp->dev) {
949 	case CSAUDIO_DAC_LVL:
950 		if (cp->type != AUDIO_MIXER_VALUE)
951 			break;
952 		if (cp->un.value.num_channels == 1)
953 			cp->un.value.level[AUDIO_MIXER_LEVEL_MONO]=
954 			    cs4231_read(sc, SP_LEFT_AUX1_CONTROL) &
955 			    LINE_INPUT_ATTEN_BITS;
956 		else if (cp->un.value.num_channels == 2) {
957 			cp->un.value.level[AUDIO_MIXER_LEVEL_LEFT] =
958 			    cs4231_read(sc, SP_LEFT_AUX1_CONTROL) &
959 			    LINE_INPUT_ATTEN_BITS;
960 			cp->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] =
961 			    cs4231_read(sc, SP_RIGHT_AUX1_CONTROL) &
962 			    LINE_INPUT_ATTEN_BITS;
963 		} else
964 			break;
965 		error = 0;
966 		break;
967 	case CSAUDIO_LINE_IN_LVL:
968 		if (cp->type != AUDIO_MIXER_VALUE)
969 			break;
970 		if (cp->un.value.num_channels == 1)
971 			cp->un.value.level[AUDIO_MIXER_LEVEL_MONO] =
972 			    cs4231_read(sc, CS_LEFT_LINE_CONTROL) & AUX_INPUT_ATTEN_BITS;
973 		else if (cp->un.value.num_channels == 2) {
974 			cp->un.value.level[AUDIO_MIXER_LEVEL_LEFT] =
975 			    cs4231_read(sc, CS_LEFT_LINE_CONTROL) & AUX_INPUT_ATTEN_BITS;
976 			cp->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] =
977 			    cs4231_read(sc, CS_RIGHT_LINE_CONTROL) & AUX_INPUT_ATTEN_BITS;
978 		} else
979 			break;
980 		error = 0;
981 		break;
982 	case CSAUDIO_MIC_LVL:
983 		if (cp->type != AUDIO_MIXER_VALUE)
984 			break;
985 		if (cp->un.value.num_channels == 1) {
986 #if 0
987 			cp->un.value.level[AUDIO_MIXER_LEVEL_MONO] =
988 			    cs4231_read(sc, CS_MONO_IO_CONTROL) &
989 			    MONO_INPUT_ATTEN_BITS;
990 #endif
991 		} else
992 			break;
993 		error = 0;
994 		break;
995 	case CSAUDIO_CD_LVL:
996 		if (cp->type != AUDIO_MIXER_VALUE)
997 			break;
998 		if (cp->un.value.num_channels == 1)
999 			cp->un.value.level[AUDIO_MIXER_LEVEL_MONO] =
1000 			    cs4231_read(sc, SP_LEFT_AUX2_CONTROL) &
1001 			    LINE_INPUT_ATTEN_BITS;
1002 		else if (cp->un.value.num_channels == 2) {
1003 			cp->un.value.level[AUDIO_MIXER_LEVEL_LEFT] =
1004 			    cs4231_read(sc, SP_LEFT_AUX2_CONTROL) &
1005 			    LINE_INPUT_ATTEN_BITS;
1006 			cp->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] =
1007 			    cs4231_read(sc, SP_RIGHT_AUX2_CONTROL) &
1008 			    LINE_INPUT_ATTEN_BITS;
1009 		}
1010 		else
1011 			break;
1012 		error = 0;
1013 		break;
1014 	case CSAUDIO_MONITOR_LVL:
1015 		if (cp->type != AUDIO_MIXER_VALUE)
1016 			break;
1017 		if (cp->un.value.num_channels != 1)
1018 			break;
1019 		cp->un.value.level[AUDIO_MIXER_LEVEL_MONO] =
1020 		    cs4231_read(sc, SP_DIGITAL_MIX) >> 2;
1021 		error = 0;
1022 		break;
1023 	case CSAUDIO_OUTPUT_LVL:
1024 		if (cp->type != AUDIO_MIXER_VALUE)
1025 			break;
1026 		if (cp->un.value.num_channels == 1)
1027 			cp->un.value.level[AUDIO_MIXER_LEVEL_MONO] =
1028 			    sc->sc_volume[CSPORT_SPEAKER].left;
1029 		else if (cp->un.value.num_channels == 2) {
1030 			cp->un.value.level[AUDIO_MIXER_LEVEL_LEFT] =
1031 			    sc->sc_volume[CSPORT_SPEAKER].left;
1032 			cp->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] =
1033 			    sc->sc_volume[CSPORT_SPEAKER].right;
1034 		}
1035 		else
1036 			break;
1037 		error = 0;
1038 		break;
1039 	case CSAUDIO_LINE_IN_MUTE:
1040 		if (cp->type != AUDIO_MIXER_ENUM)
1041 			break;
1042 		cp->un.ord = sc->sc_mute[CSPORT_LINEIN] ? 1 : 0;
1043 		error = 0;
1044 		break;
1045 	case CSAUDIO_DAC_MUTE:
1046 		if (cp->type != AUDIO_MIXER_ENUM)
1047 			break;
1048 		cp->un.ord = sc->sc_mute[CSPORT_AUX1] ? 1 : 0;
1049 		error = 0;
1050 		break;
1051 	case CSAUDIO_CD_MUTE:
1052 		if (cp->type != AUDIO_MIXER_ENUM)
1053 			break;
1054 		cp->un.ord = sc->sc_mute[CSPORT_AUX2] ? 1 : 0;
1055 		error = 0;
1056 		break;
1057 	case CSAUDIO_MIC_MUTE:
1058 		if (cp->type != AUDIO_MIXER_ENUM)
1059 			break;
1060 		cp->un.ord = sc->sc_mute[CSPORT_MONO] ? 1 : 0;
1061 		error = 0;
1062 		break;
1063 	case CSAUDIO_MONITOR_MUTE:
1064 		if (cp->type != AUDIO_MIXER_ENUM)
1065 			break;
1066 		cp->un.ord = sc->sc_mute[CSPORT_MONITOR] ? 1 : 0;
1067 		error = 0;
1068 		break;
1069 	case CSAUDIO_OUTPUT_MUTE:
1070 		if (cp->type != AUDIO_MIXER_ENUM)
1071 			break;
1072 		cp->un.ord = sc->sc_mute[CSPORT_SPEAKER] ? 1 : 0;
1073 		error = 0;
1074 		break;
1075 	case CSAUDIO_REC_LVL:
1076 		if (cp->type != AUDIO_MIXER_VALUE)
1077 			break;
1078 		if (cp->un.value.num_channels == 1) {
1079 			cp->un.value.level[AUDIO_MIXER_LEVEL_MONO] =
1080 			    sc->sc_adc.left;
1081 		} else if (cp->un.value.num_channels == 2) {
1082 			cp->un.value.level[AUDIO_MIXER_LEVEL_LEFT] =
1083 			    sc->sc_adc.left;
1084 			cp->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] =
1085 			    sc->sc_adc.right;
1086 		} else
1087 			break;
1088 		error = 0;
1089 		break;
1090 	case CSAUDIO_RECORD_SOURCE:
1091 		if (cp->type != AUDIO_MIXER_ENUM)
1092 			break;
1093 		cp->un.ord = sc->sc_in_port;
1094 		error = 0;
1095 		break;
1096 	case CSAUDIO_OUTPUT:
1097 		if (cp->type != AUDIO_MIXER_ENUM)
1098 			break;
1099 		cp->un.ord = sc->sc_out_port;
1100 		error = 0;
1101 		break;
1102 	}
1103 	return (error);
1104 }
1105 
1106 int
1107 cs4231_query_devinfo(void *vsc, mixer_devinfo_t *dip)
1108 {
1109 	int err = 0;
1110 
1111 	switch (dip->index) {
1112 	case CSAUDIO_MIC_LVL:		/* mono/microphone mixer */
1113 		dip->type = AUDIO_MIXER_VALUE;
1114 		dip->mixer_class = CSAUDIO_INPUT_CLASS;
1115 		dip->prev = AUDIO_MIXER_LAST;
1116 		dip->next = CSAUDIO_MIC_MUTE;
1117 		strlcpy(dip->label.name, AudioNmicrophone,
1118 		    sizeof dip->label.name);
1119 		dip->un.v.num_channels = 1;
1120 		strlcpy(dip->un.v.units.name, AudioNvolume,
1121 		    sizeof dip->un.v.units.name);
1122 		break;
1123 	case CSAUDIO_DAC_LVL:		/* dacout */
1124 		dip->type = AUDIO_MIXER_VALUE;
1125 		dip->mixer_class = CSAUDIO_INPUT_CLASS;
1126 		dip->prev = AUDIO_MIXER_LAST;
1127 		dip->next = CSAUDIO_DAC_MUTE;
1128 		strlcpy(dip->label.name, AudioNdac,
1129 		    sizeof dip->label.name);
1130 		dip->un.v.num_channels = 2;
1131 		strlcpy(dip->un.v.units.name, AudioNvolume,
1132 		    sizeof dip->un.v.units.name);
1133 		break;
1134 	case CSAUDIO_LINE_IN_LVL:	/* line */
1135 		dip->type = AUDIO_MIXER_VALUE;
1136 		dip->mixer_class = CSAUDIO_INPUT_CLASS;
1137 		dip->prev = AUDIO_MIXER_LAST;
1138 		dip->next = CSAUDIO_LINE_IN_MUTE;
1139 		strlcpy(dip->label.name, AudioNline, sizeof dip->label.name);
1140 		dip->un.v.num_channels = 2;
1141 		strlcpy(dip->un.v.units.name, AudioNvolume,
1142 		    sizeof dip->un.v.units.name);
1143 		break;
1144 	case CSAUDIO_CD_LVL:		/* cd */
1145 		dip->type = AUDIO_MIXER_VALUE;
1146 		dip->mixer_class = CSAUDIO_INPUT_CLASS;
1147 		dip->prev = AUDIO_MIXER_LAST;
1148 		dip->next = CSAUDIO_CD_MUTE;
1149 		strlcpy(dip->label.name, AudioNcd, sizeof dip->label.name);
1150 		dip->un.v.num_channels = 2;
1151 		strlcpy(dip->un.v.units.name, AudioNvolume,
1152 		    sizeof dip->un.v.units.name);
1153 		break;
1154 	case CSAUDIO_MONITOR_LVL:	/* monitor level */
1155 		dip->type = AUDIO_MIXER_VALUE;
1156 		dip->mixer_class = CSAUDIO_MONITOR_CLASS;
1157 		dip->prev = AUDIO_MIXER_LAST;
1158 		dip->next = CSAUDIO_MONITOR_MUTE;
1159 		strlcpy(dip->label.name, AudioNmonitor,
1160 		    sizeof dip->label.name);
1161 		dip->un.v.num_channels = 1;
1162 		strlcpy(dip->un.v.units.name, AudioNvolume,
1163 		    sizeof dip->un.v.units.name);
1164 		break;
1165 	case CSAUDIO_OUTPUT_LVL:
1166 		dip->type = AUDIO_MIXER_VALUE;
1167 		dip->mixer_class = CSAUDIO_OUTPUT_CLASS;
1168 		dip->prev = AUDIO_MIXER_LAST;
1169 		dip->next = CSAUDIO_OUTPUT_MUTE;
1170 		strlcpy(dip->label.name, AudioNoutput, sizeof dip->label.name);
1171 		dip->un.v.num_channels = 2;
1172 		strlcpy(dip->un.v.units.name, AudioNvolume,
1173 		    sizeof dip->un.v.units.name);
1174 		break;
1175 	case CSAUDIO_LINE_IN_MUTE:
1176 		dip->type = AUDIO_MIXER_ENUM;
1177 		dip->mixer_class = CSAUDIO_INPUT_CLASS;
1178 		dip->prev = CSAUDIO_LINE_IN_LVL;
1179 		dip->next = AUDIO_MIXER_LAST;
1180 		goto mute;
1181 	case CSAUDIO_DAC_MUTE:
1182 		dip->type = AUDIO_MIXER_ENUM;
1183 		dip->mixer_class = CSAUDIO_INPUT_CLASS;
1184 		dip->prev = CSAUDIO_DAC_LVL;
1185 		dip->next = AUDIO_MIXER_LAST;
1186 		goto mute;
1187 	case CSAUDIO_CD_MUTE:
1188 		dip->type = AUDIO_MIXER_ENUM;
1189 		dip->mixer_class = CSAUDIO_INPUT_CLASS;
1190 		dip->prev = CSAUDIO_CD_LVL;
1191 		dip->next = AUDIO_MIXER_LAST;
1192 		goto mute;
1193 	case CSAUDIO_MIC_MUTE:
1194 		dip->type = AUDIO_MIXER_ENUM;
1195 		dip->mixer_class = CSAUDIO_INPUT_CLASS;
1196 		dip->prev = CSAUDIO_MIC_LVL;
1197 		dip->next = AUDIO_MIXER_LAST;
1198 		goto mute;
1199 	case CSAUDIO_MONITOR_MUTE:
1200 		dip->type = AUDIO_MIXER_ENUM;
1201 		dip->mixer_class = CSAUDIO_OUTPUT_CLASS;
1202 		dip->prev = CSAUDIO_MONITOR_LVL;
1203 		dip->next = AUDIO_MIXER_LAST;
1204 		goto mute;
1205 	case CSAUDIO_OUTPUT_MUTE:
1206 		dip->type = AUDIO_MIXER_ENUM;
1207 		dip->mixer_class = CSAUDIO_OUTPUT_CLASS;
1208 		dip->prev = CSAUDIO_OUTPUT_LVL;
1209 		dip->next = AUDIO_MIXER_LAST;
1210 		goto mute;
1211 
1212 	mute:
1213 		strlcpy(dip->label.name, AudioNmute, sizeof dip->label.name);
1214 		dip->un.e.num_mem = 2;
1215 		strlcpy(dip->un.e.member[0].label.name, AudioNon,
1216 		    sizeof dip->un.e.member[0].label.name);
1217 		dip->un.e.member[0].ord = 0;
1218 		strlcpy(dip->un.e.member[1].label.name, AudioNoff,
1219 		    sizeof dip->un.e.member[1].label.name);
1220 		dip->un.e.member[1].ord = 1;
1221 		break;
1222 	case CSAUDIO_REC_LVL:		/* record level */
1223 		dip->type = AUDIO_MIXER_VALUE;
1224 		dip->mixer_class = CSAUDIO_RECORD_CLASS;
1225 		dip->prev = AUDIO_MIXER_LAST;
1226 		dip->next = CSAUDIO_RECORD_SOURCE;
1227 		strlcpy(dip->label.name, AudioNrecord, sizeof dip->label.name);
1228 		dip->un.v.num_channels = 2;
1229 		strlcpy(dip->un.v.units.name, AudioNvolume,
1230 		    sizeof dip->un.v.units.name);
1231 		break;
1232 	case CSAUDIO_RECORD_SOURCE:
1233 		dip->type = AUDIO_MIXER_ENUM;
1234 		dip->mixer_class = CSAUDIO_RECORD_CLASS;
1235 		dip->prev = CSAUDIO_REC_LVL;
1236 		dip->next = AUDIO_MIXER_LAST;
1237 		strlcpy(dip->label.name, AudioNsource, sizeof dip->label.name);
1238 		dip->un.e.num_mem = 4;
1239 		strlcpy(dip->un.e.member[0].label.name, AudioNmicrophone,
1240 		    sizeof dip->un.e.member[0].label.name);
1241 		dip->un.e.member[0].ord = CSPORT_MICROPHONE;
1242 		strlcpy(dip->un.e.member[1].label.name, AudioNline,
1243 		    sizeof dip->un.e.member[1].label.name);
1244 		dip->un.e.member[1].ord = CSPORT_LINEIN;
1245 		strlcpy(dip->un.e.member[2].label.name, AudioNcd,
1246 		    sizeof dip->un.e.member[2].label.name);
1247 		dip->un.e.member[2].ord = CSPORT_AUX1;
1248 		strlcpy(dip->un.e.member[3].label.name, AudioNdac,
1249 		    sizeof dip->un.e.member[3].label.name);
1250 		dip->un.e.member[3].ord = CSPORT_DAC;
1251 		break;
1252 	case CSAUDIO_OUTPUT:
1253 		dip->type = AUDIO_MIXER_ENUM;
1254 		dip->mixer_class = CSAUDIO_MONITOR_CLASS;
1255 		dip->prev = dip->next = AUDIO_MIXER_LAST;
1256 		strlcpy(dip->label.name, AudioNoutput, sizeof dip->label.name);
1257 		dip->un.e.num_mem = 3;
1258 		strlcpy(dip->un.e.member[0].label.name, AudioNspeaker,
1259 		    sizeof dip->un.e.member[0].label.name);
1260 		dip->un.e.member[0].ord = CSPORT_SPEAKER;
1261 		strlcpy(dip->un.e.member[1].label.name, AudioNline,
1262 		    sizeof dip->un.e.member[1].label.name);
1263 		dip->un.e.member[1].ord = CSPORT_LINEOUT;
1264 		strlcpy(dip->un.e.member[2].label.name, AudioNheadphone,
1265 		    sizeof dip->un.e.member[2].label.name);
1266 		dip->un.e.member[2].ord = CSPORT_HEADPHONE;
1267 		break;
1268 	case CSAUDIO_INPUT_CLASS:	/* input class descriptor */
1269 		dip->type = AUDIO_MIXER_CLASS;
1270 		dip->mixer_class = CSAUDIO_INPUT_CLASS;
1271 		dip->prev = AUDIO_MIXER_LAST;
1272 		dip->next = AUDIO_MIXER_LAST;
1273 		strlcpy(dip->label.name, AudioCinputs, sizeof dip->label.name);
1274 		break;
1275 	case CSAUDIO_OUTPUT_CLASS:	/* output class descriptor */
1276 		dip->type = AUDIO_MIXER_CLASS;
1277 		dip->mixer_class = CSAUDIO_OUTPUT_CLASS;
1278 		dip->prev = AUDIO_MIXER_LAST;
1279 		dip->next = AUDIO_MIXER_LAST;
1280 		strlcpy(dip->label.name, AudioCoutputs,
1281 		    sizeof dip->label.name);
1282 		break;
1283 	case CSAUDIO_MONITOR_CLASS:	/* monitor class descriptor */
1284 		dip->type = AUDIO_MIXER_CLASS;
1285 		dip->mixer_class = CSAUDIO_MONITOR_CLASS;
1286 		dip->prev = AUDIO_MIXER_LAST;
1287 		dip->next = AUDIO_MIXER_LAST;
1288 		strlcpy(dip->label.name, AudioCmonitor,
1289 		    sizeof dip->label.name);
1290 		break;
1291 	case CSAUDIO_RECORD_CLASS:	/* record class descriptor */
1292 		dip->type = AUDIO_MIXER_CLASS;
1293 		dip->mixer_class = CSAUDIO_RECORD_CLASS;
1294 		dip->prev = AUDIO_MIXER_LAST;
1295 		dip->next = AUDIO_MIXER_LAST;
1296 		strlcpy(dip->label.name, AudioCrecord, sizeof dip->label.name);
1297 		break;
1298 	default:
1299 		err = ENXIO;
1300 	}
1301 
1302 	return (err);
1303 }
1304 
1305 int
1306 cs4231_get_props(void *vsc)
1307 {
1308 	return (AUDIO_PROP_FULLDUPLEX);
1309 }
1310 
1311 /*
1312  * Hardware interrupt handler
1313  */
1314 int
1315 cs4231_intr(void *vsc)
1316 {
1317 	struct cs4231_softc *sc = (struct cs4231_softc *)vsc;
1318 	u_int32_t csr;
1319 	u_int8_t reg, status;
1320 	struct cs_dma *p;
1321 	int r = 0;
1322 
1323 	csr = APC_READ(sc, APC_CSR);
1324 	APC_WRITE(sc, APC_CSR, csr);
1325 
1326 	if ((csr & APC_CSR_EIE) && (csr & APC_CSR_EI)) {
1327 		printf("%s: error interrupt\n", sc->sc_dev.dv_xname);
1328 		r = 1;
1329 	}
1330 
1331 	if ((csr & APC_CSR_PIE) && (csr & APC_CSR_PI)) {
1332 		/* playback interrupt */
1333 		r = 1;
1334 	}
1335 
1336 	if ((csr & APC_CSR_GIE) && (csr & APC_CSR_GI)) {
1337 		/* general interrupt */
1338 		status = CS_READ(sc, AD1848_STATUS);
1339 		if (status & (INTERRUPT_STATUS | SAMPLE_ERROR)) {
1340 			reg = cs4231_read(sc, CS_IRQ_STATUS);
1341 			if (reg & CS_AFS_PI) {
1342 				cs4231_write(sc, SP_LOWER_BASE_COUNT, 0xff);
1343 				cs4231_write(sc, SP_UPPER_BASE_COUNT, 0xff);
1344 			}
1345 			if (reg & CS_AFS_CI) {
1346 				cs4231_write(sc, CS_LOWER_REC_CNT, 0xff);
1347 				cs4231_write(sc, CS_UPPER_REC_CNT, 0xff);
1348 			}
1349 			CS_WRITE(sc, AD1848_STATUS, 0);
1350 		}
1351 		r = 1;
1352 	}
1353 
1354 
1355 	if (csr & (APC_CSR_PI|APC_CSR_PMI|APC_CSR_PIE|APC_CSR_PD))
1356 		r = 1;
1357 
1358 	if ((csr & APC_CSR_PMIE) && (csr & APC_CSR_PMI)) {
1359 		struct cs_channel *chan = &sc->sc_playback;
1360 		u_long nextaddr, togo;
1361 
1362 		p = chan->cs_curdma;
1363 		togo = chan->cs_segsz - chan->cs_cnt;
1364 		if (togo == 0) {
1365 			nextaddr = (u_int32_t)p->dmamap->dm_segs[0].ds_addr;
1366 			chan->cs_cnt = togo = chan->cs_blksz;
1367 		} else {
1368 			nextaddr = APC_READ(sc, APC_PNVA) + chan->cs_blksz;
1369 			if (togo > chan->cs_blksz)
1370 				togo = chan->cs_blksz;
1371 			chan->cs_cnt += togo;
1372 		}
1373 
1374 		APC_WRITE(sc, APC_PNVA, nextaddr);
1375 		APC_WRITE(sc, APC_PNC, togo);
1376 
1377 		if (chan->cs_intr != NULL)
1378 			(*chan->cs_intr)(chan->cs_arg);
1379 		r = 1;
1380 	}
1381 
1382 	if ((csr & APC_CSR_CIE) && (csr & APC_CSR_CI)) {
1383 		if (csr & APC_CSR_CD) {
1384 			struct cs_channel *chan = &sc->sc_capture;
1385 			u_long nextaddr, togo;
1386 
1387 			p = chan->cs_curdma;
1388 			togo = chan->cs_segsz - chan->cs_cnt;
1389 			if (togo == 0) {
1390 				nextaddr =
1391 				    (u_int32_t)p->dmamap->dm_segs[0].ds_addr;
1392 				chan->cs_cnt = togo = chan->cs_blksz;
1393 			} else {
1394 				nextaddr = APC_READ(sc, APC_CNVA) +
1395 				    chan->cs_blksz;
1396 				if (togo > chan->cs_blksz)
1397 					togo = chan->cs_blksz;
1398 				chan->cs_cnt += togo;
1399 			}
1400 
1401 			APC_WRITE(sc, APC_CNVA, nextaddr);
1402 			APC_WRITE(sc, APC_CNC, togo);
1403 
1404 			if (chan->cs_intr != NULL)
1405 				(*chan->cs_intr)(chan->cs_arg);
1406 		}
1407 		r = 1;
1408 	}
1409 
1410 	if ((csr & APC_CSR_CMIE) && (csr & APC_CSR_CMI)) {
1411 		/* capture empty */
1412 		r = 1;
1413 	}
1414 
1415 	return (r);
1416 }
1417 
1418 void *
1419 cs4231_alloc(void *vsc, int direction, size_t size, int pool, int flags)
1420 {
1421 	struct cs4231_softc *sc = (struct cs4231_softc *)vsc;
1422 	bus_dma_tag_t dmat = sc->sc_dmatag;
1423 	struct cs_dma *p;
1424 
1425 	p = (struct cs_dma *)malloc(sizeof(struct cs_dma), pool, flags);
1426 	if (p == NULL)
1427 		return (NULL);
1428 
1429 	if (bus_dmamap_create(dmat, size, 1, size, 0,
1430 	    BUS_DMA_NOWAIT, &p->dmamap) != 0)
1431 		goto fail;
1432 
1433 	p->size = size;
1434 
1435 	if (bus_dmamem_alloc(dmat, size, 64*1024, 0, p->segs,
1436 	    sizeof(p->segs)/sizeof(p->segs[0]), &p->nsegs,
1437 	    BUS_DMA_NOWAIT) != 0)
1438 		goto fail1;
1439 
1440 	if (bus_dmamem_map(dmat, p->segs, p->nsegs, p->size,
1441 	    &p->addr, BUS_DMA_NOWAIT | BUS_DMA_COHERENT) != 0)
1442 		goto fail2;
1443 
1444 	if (bus_dmamap_load(dmat, p->dmamap, p->addr, size, NULL,
1445 	    BUS_DMA_NOWAIT) != 0)
1446 		goto fail3;
1447 
1448 	p->next = sc->sc_dmas;
1449 	sc->sc_dmas = p;
1450 	return (p->addr);
1451 
1452 fail3:
1453 	bus_dmamem_unmap(dmat, p->addr, p->size);
1454 fail2:
1455 	bus_dmamem_free(dmat, p->segs, p->nsegs);
1456 fail1:
1457 	bus_dmamap_destroy(dmat, p->dmamap);
1458 fail:
1459 	free(p, pool);
1460 	return (NULL);
1461 }
1462 
1463 void
1464 cs4231_free(void *vsc, void *ptr, int pool)
1465 {
1466 	struct cs4231_softc *sc = vsc;
1467 	bus_dma_tag_t dmat = sc->sc_dmatag;
1468 	struct cs_dma *p, **pp;
1469 
1470 	for (pp = &sc->sc_dmas; (p = *pp) != NULL; pp = &(*pp)->next) {
1471 		if (p->addr != ptr)
1472 			continue;
1473 		bus_dmamap_unload(dmat, p->dmamap);
1474 		bus_dmamem_unmap(dmat, p->addr, p->size);
1475 		bus_dmamem_free(dmat, p->segs, p->nsegs);
1476 		bus_dmamap_destroy(dmat, p->dmamap);
1477 		*pp = p->next;
1478 		free(p, pool);
1479 		return;
1480 	}
1481 	printf("%s: attempt to free rogue pointer\n", sc->sc_dev.dv_xname);
1482 }
1483 
1484 int
1485 cs4231_trigger_output(void *vsc, void *start, void *end, int blksize,
1486     void (*intr)(void *), void *arg, struct audio_params *param)
1487 {
1488 	struct cs4231_softc *sc = vsc;
1489 	struct cs_channel *chan = &sc->sc_playback;
1490 	struct cs_dma *p;
1491 	u_int32_t csr;
1492 	u_long n;
1493 
1494 	if (chan->cs_locked != 0) {
1495 		printf("%s: trigger_output: already running\n",
1496 		    sc->sc_dev.dv_xname);
1497 		return (EINVAL);
1498 	}
1499 
1500 	chan->cs_locked = 1;
1501 	chan->cs_intr = intr;
1502 	chan->cs_arg = arg;
1503 
1504 	for (p = sc->sc_dmas; p->addr != start; p = p->next)
1505 		/*EMPTY*/;
1506 	if (p == NULL) {
1507 		printf("%s: trigger_output: bad addr: %p\n",
1508 		    sc->sc_dev.dv_xname, start);
1509 		return (EINVAL);
1510 	}
1511 
1512 	n = (char *)end - (char *)start;
1513 
1514 	/*
1515 	 * Do only `blksize' at a time, so audio_pint() is kept
1516 	 * synchronous with us...
1517 	 */
1518 	chan->cs_blksz = blksize;
1519 	chan->cs_curdma = p;
1520 	chan->cs_segsz = n;
1521 
1522 	if (n > chan->cs_blksz)
1523 		n = chan->cs_blksz;
1524 
1525 	chan->cs_cnt = n;
1526 
1527 	csr = APC_READ(sc, APC_CSR);
1528 
1529 	APC_WRITE(sc, APC_PNVA, (u_long)p->dmamap->dm_segs[0].ds_addr);
1530 	APC_WRITE(sc, APC_PNC, (u_long)n);
1531 
1532 	if ((csr & APC_CSR_PDMA_GO) == 0 || (csr & APC_CSR_PPAUSE) != 0) {
1533 		APC_WRITE(sc, APC_CSR,
1534 		    APC_READ(sc, APC_CSR) & ~(APC_CSR_PIE | APC_CSR_PPAUSE));
1535 		APC_WRITE(sc, APC_CSR, APC_READ(sc, APC_CSR) |
1536 		    APC_CSR_EI | APC_CSR_GIE | APC_CSR_PIE | APC_CSR_EIE |
1537 		    APC_CSR_PMIE | APC_CSR_PDMA_GO);
1538 		cs4231_write(sc, SP_LOWER_BASE_COUNT, 0xff);
1539 		cs4231_write(sc, SP_UPPER_BASE_COUNT, 0xff);
1540 		cs4231_write(sc, SP_INTERFACE_CONFIG,
1541 		    cs4231_read(sc, SP_INTERFACE_CONFIG) | PLAYBACK_ENABLE);
1542 	}
1543 	return (0);
1544 }
1545 
1546 int
1547 cs4231_trigger_input(void *vsc, void *start, void *end, int blksize,
1548     void (*intr)(void *), void *arg, struct audio_params *param)
1549 {
1550 	struct cs4231_softc *sc = vsc;
1551 	struct cs_channel *chan = &sc->sc_capture;
1552 	struct cs_dma *p;
1553 	u_int32_t csr;
1554 	u_long n;
1555 
1556 	if (chan->cs_locked != 0) {
1557 		printf("%s: trigger_input: already running\n",
1558 		    sc->sc_dev.dv_xname);
1559 		return (EINVAL);
1560 	}
1561 	chan->cs_locked = 1;
1562 	chan->cs_intr = intr;
1563 	chan->cs_arg = arg;
1564 
1565 	for (p = sc->sc_dmas; p->addr != start; p = p->next)
1566 		/*EMPTY*/;
1567 	if (p == NULL) {
1568 		printf("%s: trigger_input: bad addr: %p\n",
1569 		    sc->sc_dev.dv_xname, start);
1570 		return (EINVAL);
1571 	}
1572 
1573 	n = (char *)end - (char *)start;
1574 
1575 	/*
1576 	 * Do only `blksize' at a time, so audio_cint() is kept
1577 	 * synchronous with us...
1578 	 */
1579 	chan->cs_blksz = blksize;
1580 	chan->cs_curdma = p;
1581 	chan->cs_segsz = n;
1582 
1583 	if (n > chan->cs_blksz)
1584 		n = chan->cs_blksz;
1585 	chan->cs_cnt = n;
1586 
1587 	APC_WRITE(sc, APC_CNVA, p->dmamap->dm_segs[0].ds_addr);
1588 	APC_WRITE(sc, APC_CNC, (u_long)n);
1589 
1590 	csr = APC_READ(sc, APC_CSR);
1591 	if ((csr & APC_CSR_CDMA_GO) == 0 || (csr & APC_CSR_CPAUSE) != 0) {
1592 		csr &= APC_CSR_CPAUSE;
1593 		csr |= APC_CSR_GIE | APC_CSR_CMIE | APC_CSR_CIE | APC_CSR_EI |
1594 		    APC_CSR_CDMA_GO;
1595 		APC_WRITE(sc, APC_CSR, csr);
1596 		cs4231_write(sc, CS_LOWER_REC_CNT, 0xff);
1597 		cs4231_write(sc, CS_UPPER_REC_CNT, 0xff);
1598 		cs4231_write(sc, SP_INTERFACE_CONFIG,
1599 		    cs4231_read(sc, SP_INTERFACE_CONFIG) | CAPTURE_ENABLE);
1600 	}
1601 
1602 	if (APC_READ(sc, APC_CSR) & APC_CSR_CD) {
1603 		u_long nextaddr, togo;
1604 
1605 		p = chan->cs_curdma;
1606 		togo = chan->cs_segsz - chan->cs_cnt;
1607 		if (togo == 0) {
1608 			nextaddr = (u_int32_t)p->dmamap->dm_segs[0].ds_addr;
1609 			chan->cs_cnt = togo = chan->cs_blksz;
1610 		} else {
1611 			nextaddr = APC_READ(sc, APC_CNVA) + chan->cs_blksz;
1612 			if (togo > chan->cs_blksz)
1613 				togo = chan->cs_blksz;
1614 			chan->cs_cnt += togo;
1615 		}
1616 
1617 		APC_WRITE(sc, APC_CNVA, nextaddr);
1618 		APC_WRITE(sc, APC_CNC, togo);
1619 	}
1620 
1621 	return (0);
1622 }
1623