awacs.c revision 1.22 1 /* $NetBSD: awacs.c,v 1.22 2005/01/10 22:01:36 kent Exp $ */
2
3 /*-
4 * Copyright (c) 2000 Tsubai Masanari. All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 #include <sys/cdefs.h>
30 __KERNEL_RCSID(0, "$NetBSD: awacs.c,v 1.22 2005/01/10 22:01:36 kent Exp $");
31
32 #include <sys/param.h>
33 #include <sys/audioio.h>
34 #include <sys/device.h>
35 #include <sys/malloc.h>
36 #include <sys/systm.h>
37
38 #include <dev/auconv.h>
39 #include <dev/audio_if.h>
40 #include <dev/mulaw.h>
41
42 #include <uvm/uvm_extern.h>
43
44 #include <machine/autoconf.h>
45 #include <machine/pio.h>
46
47 #include <dev/ofw/openfirm.h>
48 #include <macppc/dev/dbdma.h>
49
50 #ifdef AWACS_DEBUG
51 # define DPRINTF printf
52 #else
53 # define DPRINTF while (0) printf
54 #endif
55
56 /* sc_flags values */
57 #define AWACS_CAP_BSWAP 0x0001
58
59 struct awacs_softc {
60 struct device sc_dev;
61 int sc_flags;
62
63 void (*sc_ointr)(void *); /* DMA completion intr handler */
64 void *sc_oarg; /* arg for sc_ointr() */
65 int sc_opages; /* # of output pages */
66
67 void (*sc_iintr)(void *); /* DMA completion intr handler */
68 void *sc_iarg; /* arg for sc_iintr() */
69
70 u_int sc_record_source; /* recording source mask */
71 u_int sc_output_mask; /* output source mask */
72
73 char *sc_reg;
74 u_int sc_codecctl0;
75 u_int sc_codecctl1;
76 u_int sc_codecctl2;
77 u_int sc_codecctl4;
78 u_int sc_soundctl;
79
80 struct dbdma_regmap *sc_odma;
81 struct dbdma_regmap *sc_idma;
82 struct dbdma_command *sc_odmacmd;
83 struct dbdma_command *sc_idmacmd;
84
85 #define AWACS_NFORMATS 2
86 struct audio_format sc_formats[AWACS_NFORMATS];
87 };
88
89 int awacs_match(struct device *, struct cfdata *, void *);
90 void awacs_attach(struct device *, struct device *, void *);
91 int awacs_intr(void *);
92
93 void awacs_close(void *);
94 int awacs_query_encoding(void *, struct audio_encoding *);
95 int awacs_set_params(void *, int, int, audio_params_t *, audio_params_t *,
96 stream_filter_list_t *, stream_filter_list_t *);
97 int awacs_round_blocksize(void *, int, int, const audio_params_t *);
98 int awacs_trigger_output(void *, void *, void *, int, void (*)(void *),
99 void *, const audio_params_t *);
100 int awacs_trigger_input(void *, void *, void *, int, void (*)(void *),
101 void *, const audio_params_t *);
102 int awacs_halt_output(void *);
103 int awacs_halt_input(void *);
104 int awacs_getdev(void *, struct audio_device *);
105 int awacs_set_port(void *, mixer_ctrl_t *);
106 int awacs_get_port(void *, mixer_ctrl_t *);
107 int awacs_query_devinfo(void *, mixer_devinfo_t *);
108 size_t awacs_round_buffersize(void *, int, size_t);
109 paddr_t awacs_mappage(void *, void *, off_t, int);
110 int awacs_get_props(void *);
111
112 static inline u_int awacs_read_reg(struct awacs_softc *, int);
113 static inline void awacs_write_reg(struct awacs_softc *, int, int);
114 void awacs_write_codec(struct awacs_softc *, int);
115 void awacs_set_speaker_volume(struct awacs_softc *, int, int);
116 void awacs_set_ext_volume(struct awacs_softc *, int, int);
117 int awacs_set_rate(struct awacs_softc *, struct audio_params *);
118
119 CFATTACH_DECL(awacs, sizeof(struct awacs_softc),
120 awacs_match, awacs_attach, NULL, NULL);
121
122 const struct audio_hw_if awacs_hw_if = {
123 NULL, /* open */
124 awacs_close,
125 NULL,
126 awacs_query_encoding,
127 awacs_set_params,
128 awacs_round_blocksize,
129 NULL,
130 NULL,
131 NULL,
132 NULL,
133 NULL,
134 awacs_halt_output,
135 awacs_halt_input,
136 NULL,
137 awacs_getdev,
138 NULL,
139 awacs_set_port,
140 awacs_get_port,
141 awacs_query_devinfo,
142 NULL,
143 NULL,
144 awacs_round_buffersize,
145 awacs_mappage,
146 awacs_get_props,
147 awacs_trigger_output,
148 awacs_trigger_input,
149 NULL,
150 };
151
152 struct audio_device awacs_device = {
153 "AWACS",
154 "",
155 "awacs"
156 };
157
158 #define AWACS_NFORMATS 2
159 #define AWACS_FORMATS_LE 0
160 static const struct audio_format awacs_formats[AWACS_NFORMATS] = {
161 {NULL, AUMODE_PLAY | AUMODE_RECORD, AUDIO_ENCODING_SLINEAR_LE, 16, 16,
162 2, AUFMT_STEREO, 8, {7350, 8820, 11025, 14700, 17640, 22050, 29400, 44100}},
163 {NULL, AUMODE_PLAY | AUMODE_RECORD, AUDIO_ENCODING_SLINEAR_BE, 16, 16,
164 2, AUFMT_STEREO, 8, {7350, 8820, 11025, 14700, 17640, 22050, 29400, 44100}},
165 };
166
167 /* register offset */
168 #define AWACS_SOUND_CTRL 0x00
169 #define AWACS_CODEC_CTRL 0x10
170 #define AWACS_CODEC_STATUS 0x20
171 #define AWACS_CLIP_COUNT 0x30
172 #define AWACS_BYTE_SWAP 0x40
173
174 /* sound control */
175 #define AWACS_INPUT_SUBFRAME0 0x00000001
176 #define AWACS_INPUT_SUBFRAME1 0x00000002
177 #define AWACS_INPUT_SUBFRAME2 0x00000004
178 #define AWACS_INPUT_SUBFRAME3 0x00000008
179
180 #define AWACS_OUTPUT_SUBFRAME0 0x00000010
181 #define AWACS_OUTPUT_SUBFRAME1 0x00000020
182 #define AWACS_OUTPUT_SUBFRAME2 0x00000040
183 #define AWACS_OUTPUT_SUBFRAME3 0x00000080
184
185 #define AWACS_RATE_44100 0x00000000
186 #define AWACS_RATE_29400 0x00000100
187 #define AWACS_RATE_22050 0x00000200
188 #define AWACS_RATE_17640 0x00000300
189 #define AWACS_RATE_14700 0x00000400
190 #define AWACS_RATE_11025 0x00000500
191 #define AWACS_RATE_8820 0x00000600
192 #define AWACS_RATE_7350 0x00000700
193 #define AWACS_RATE_MASK 0x00000700
194
195 /* codec control */
196 #define AWACS_CODEC_ADDR0 0x00000000
197 #define AWACS_CODEC_ADDR1 0x00001000
198 #define AWACS_CODEC_ADDR2 0x00002000
199 #define AWACS_CODEC_ADDR4 0x00004000
200 #define AWACS_CODEC_EMSEL0 0x00000000
201 #define AWACS_CODEC_EMSEL1 0x00400000
202 #define AWACS_CODEC_EMSEL2 0x00800000
203 #define AWACS_CODEC_EMSEL4 0x00c00000
204 #define AWACS_CODEC_BUSY 0x01000000
205
206 /* cc0 */
207 #define AWACS_DEFAULT_CD_GAIN 0x000000bb
208 #define AWACS_INPUT_CD 0x00000200
209 #define AWACS_INPUT_LINE 0x00000400
210 #define AWACS_INPUT_MICROPHONE 0x00000800
211 #define AWACS_INPUT_MASK 0x00000e00
212
213 /* cc1 */
214 #define AWACS_MUTE_SPEAKER 0x00000080
215 #define AWACS_MUTE_HEADPHONE 0x00000200
216
217 int
218 awacs_match(parent, match, aux)
219 struct device *parent;
220 struct cfdata *match;
221 void *aux;
222 {
223 struct confargs *ca = aux;
224
225 if (strcmp(ca->ca_name, "i2s") == 0)
226 return 1;
227
228 if (strcmp(ca->ca_name, "awacs") != 0 &&
229 strcmp(ca->ca_name, "davbus") != 0)
230 return 0;
231
232 if (ca->ca_nreg < 24 || ca->ca_nintr < 12)
233 return 0;
234
235 return 1;
236 }
237
238 void
239 awacs_attach(parent, self, aux)
240 struct device *parent;
241 struct device *self;
242 void *aux;
243 {
244 struct awacs_softc *sc = (struct awacs_softc *)self;
245 struct confargs *ca = aux;
246 int cirq, oirq, iirq, cirq_type, oirq_type, iirq_type;
247
248 ca->ca_reg[0] += ca->ca_baseaddr;
249 ca->ca_reg[2] += ca->ca_baseaddr;
250 ca->ca_reg[4] += ca->ca_baseaddr;
251
252 sc->sc_reg = mapiodev(ca->ca_reg[0], ca->ca_reg[1]);
253
254 sc->sc_odma = mapiodev(ca->ca_reg[2], ca->ca_reg[3]); /* out */
255 sc->sc_idma = mapiodev(ca->ca_reg[4], ca->ca_reg[5]); /* in */
256 sc->sc_odmacmd = dbdma_alloc(20 * sizeof(struct dbdma_command));
257 sc->sc_idmacmd = dbdma_alloc(20 * sizeof(struct dbdma_command));
258
259 if (strcmp(ca->ca_name, "i2s") == 0) {
260 int node, intr[6];
261
262 node = OF_child(ca->ca_node);
263 if (node == 0) {
264 printf("no i2s-a child\n");
265 return;
266 }
267 if (OF_getprop(node, "interrupts", intr, sizeof(intr)) == -1) {
268 printf("no interrupt property\n");
269 return;
270 }
271
272 cirq = intr[0];
273 oirq = intr[2];
274 iirq = intr[4];
275 cirq_type = intr[1] ? IST_LEVEL : IST_EDGE;
276 oirq_type = intr[3] ? IST_LEVEL : IST_EDGE;
277 iirq_type = intr[5] ? IST_LEVEL : IST_EDGE;
278 } else if (ca->ca_nintr == 24) {
279 cirq = ca->ca_intr[0];
280 oirq = ca->ca_intr[2];
281 iirq = ca->ca_intr[4];
282 cirq_type = ca->ca_intr[1] ? IST_LEVEL : IST_EDGE;
283 oirq_type = ca->ca_intr[3] ? IST_LEVEL : IST_EDGE;
284 iirq_type = ca->ca_intr[5] ? IST_LEVEL : IST_EDGE;
285 } else {
286 cirq = ca->ca_intr[0];
287 oirq = ca->ca_intr[1];
288 iirq = ca->ca_intr[2];
289 cirq_type = oirq_type = iirq_type = IST_LEVEL;
290 }
291
292 intr_establish(cirq, cirq_type, IPL_AUDIO, awacs_intr, sc);
293 intr_establish(oirq, oirq_type, IPL_AUDIO, awacs_intr, sc);
294 /* intr_establish(iirq, iirq_type, IPL_AUDIO, awacs_intr, sc); */
295
296 printf(": irq %d,%d,%d\n", cirq, oirq, iirq);
297
298 memcpy(&sc->sc_formats, awacs_formats, sizeof(awacs_formats));
299 /* XXX Uni-North based models don't have byteswap capability. */
300 if (OF_finddevice("/uni-n") == -1) {
301 sc->sc_flags |= AWACS_CAP_BSWAP;
302 } else {
303 AUFMT_INVALIDATE(&sc->sc_formats[AWACS_FORMATS_LE]);
304 }
305
306 sc->sc_soundctl = AWACS_INPUT_SUBFRAME0 | AWACS_OUTPUT_SUBFRAME0 |
307 AWACS_RATE_44100;
308 awacs_write_reg(sc, AWACS_SOUND_CTRL, sc->sc_soundctl);
309
310 sc->sc_codecctl0 = AWACS_CODEC_ADDR0 | AWACS_CODEC_EMSEL0;
311 sc->sc_codecctl1 = AWACS_CODEC_ADDR1 | AWACS_CODEC_EMSEL0;
312 sc->sc_codecctl2 = AWACS_CODEC_ADDR2 | AWACS_CODEC_EMSEL0;
313 sc->sc_codecctl4 = AWACS_CODEC_ADDR4 | AWACS_CODEC_EMSEL0;
314
315 sc->sc_codecctl0 |= AWACS_INPUT_CD | AWACS_DEFAULT_CD_GAIN;
316 awacs_write_codec(sc, sc->sc_codecctl0);
317
318 /* Set initial volume[s] */
319 awacs_set_speaker_volume(sc, 80, 80);
320
321 /* Set loopback (for CD?) */
322 /* sc->sc_codecctl1 |= 0x440; */
323 sc->sc_codecctl1 |= 0x40;
324 awacs_write_codec(sc, sc->sc_codecctl1);
325
326 /* default output to speakers */
327 sc->sc_output_mask = 1 << 0;
328 sc->sc_codecctl1 &= ~AWACS_MUTE_SPEAKER;
329 sc->sc_codecctl1 |= AWACS_MUTE_HEADPHONE;
330 awacs_write_codec(sc, sc->sc_codecctl1);
331
332 /* default input from CD */
333 sc->sc_record_source = 1 << 0;
334 sc->sc_codecctl0 &= ~AWACS_INPUT_MASK;
335 sc->sc_codecctl0 |= AWACS_INPUT_CD;
336 awacs_write_codec(sc, sc->sc_codecctl0);
337
338 /* Enable interrupts and looping mode. */
339 /* XXX ... */
340
341 audio_attach_mi(&awacs_hw_if, sc, &sc->sc_dev);
342 }
343
344 static inline u_int
345 awacs_read_reg(sc, reg)
346 struct awacs_softc *sc;
347 int reg;
348 {
349 char *addr = sc->sc_reg;
350
351 return in32rb(addr + reg);
352 }
353
354 static inline void
355 awacs_write_reg(sc, reg, val)
356 struct awacs_softc *sc;
357 int reg, val;
358 {
359 char *addr = sc->sc_reg;
360
361 out32rb(addr + reg, val);
362 }
363
364 void
365 awacs_write_codec(sc, value)
366 struct awacs_softc *sc;
367 int value;
368 {
369 awacs_write_reg(sc, AWACS_CODEC_CTRL, value);
370 while (awacs_read_reg(sc, AWACS_CODEC_CTRL) & AWACS_CODEC_BUSY);
371 }
372
373 int
374 awacs_intr(v)
375 void *v;
376 {
377 struct awacs_softc *sc = v;
378 struct dbdma_command *cmd = sc->sc_odmacmd;
379 int count = sc->sc_opages;
380 int status;
381
382 /* Fill used buffer(s). */
383 while (count-- > 0) {
384 /* if DBDMA_INT_ALWAYS */
385 if (in16rb(&cmd->d_command) & 0x30) { /* XXX */
386 status = in16rb(&cmd->d_status);
387 cmd->d_status = 0;
388 if (status) /* status == 0x8400 */
389 if (sc->sc_ointr)
390 (*sc->sc_ointr)(sc->sc_oarg);
391 }
392 cmd++;
393 }
394
395 return 1;
396 }
397
398 /*
399 * Close function is called at splaudio().
400 */
401 void
402 awacs_close(h)
403 void *h;
404 {
405 struct awacs_softc *sc = h;
406
407 awacs_halt_output(sc);
408 awacs_halt_input(sc);
409
410 sc->sc_ointr = 0;
411 sc->sc_iintr = 0;
412 }
413
414 int
415 awacs_query_encoding(h, ae)
416 void *h;
417 struct audio_encoding *ae;
418 {
419 struct awacs_softc *sc = h;
420
421 ae->flags = AUDIO_ENCODINGFLAG_EMULATED;
422
423 switch (ae->index) {
424 case 0:
425 strcpy(ae->name, AudioEslinear);
426 ae->encoding = AUDIO_ENCODING_SLINEAR;
427 ae->precision = 16;
428 ae->flags = 0;
429 return 0;
430 case 1:
431 strcpy(ae->name, AudioEslinear_be);
432 ae->encoding = AUDIO_ENCODING_SLINEAR_BE;
433 ae->precision = 16;
434 ae->flags = 0;
435 return 0;
436 case 2:
437 strcpy(ae->name, AudioEslinear_le);
438 ae->encoding = AUDIO_ENCODING_SLINEAR_LE;
439 ae->precision = 16;
440 if (sc->sc_flags & AWACS_CAP_BSWAP)
441 ae->flags = 0;
442 return 0;
443 case 3:
444 strcpy(ae->name, AudioEulinear_be);
445 ae->encoding = AUDIO_ENCODING_ULINEAR_BE;
446 ae->precision = 16;
447 return 0;
448 case 4:
449 strcpy(ae->name, AudioEulinear_le);
450 ae->encoding = AUDIO_ENCODING_ULINEAR_LE;
451 ae->precision = 16;
452 return 0;
453 case 5:
454 strcpy(ae->name, AudioEmulaw);
455 ae->encoding = AUDIO_ENCODING_ULAW;
456 ae->precision = 8;
457 return 0;
458 case 6:
459 strcpy(ae->name, AudioEalaw);
460 ae->encoding = AUDIO_ENCODING_ALAW;
461 ae->precision = 8;
462 return 0;
463 default:
464 return EINVAL;
465 }
466 }
467
468 int
469 awacs_set_params(h, setmode, usemode, play, rec, pfil, rfil)
470 void *h;
471 int setmode, usemode;
472 struct audio_params *play, *rec;
473 stream_filter_list_t *pfil, *rfil;
474 {
475 struct awacs_softc *sc = h;
476 struct audio_params *p = NULL;
477 stream_filter_list_t *fil;
478 int mode, i;
479
480 /*
481 * This device only has one clock, so make the sample rates match.
482 */
483 if (play->sample_rate != rec->sample_rate &&
484 usemode == (AUMODE_PLAY | AUMODE_RECORD)) {
485 if (setmode == AUMODE_PLAY) {
486 rec->sample_rate = play->sample_rate;
487 setmode |= AUMODE_RECORD;
488 } else if (setmode == AUMODE_RECORD) {
489 play->sample_rate = rec->sample_rate;
490 setmode |= AUMODE_PLAY;
491 } else
492 return EINVAL;
493 }
494
495 for (mode = AUMODE_RECORD; mode != -1;
496 mode = mode == AUMODE_RECORD ? AUMODE_PLAY : -1) {
497 if ((setmode & mode) == 0)
498 continue;
499
500 p = mode == AUMODE_PLAY ? play : rec;
501 fil = mode == AUMODE_PLAY ? pfil : rfil;
502 switch (p->sample_rate) {
503 case 48000: /* aurateconv */
504 case 44100:
505 case 29400:
506 case 22050:
507 case 17640:
508 case 14700:
509 case 11025:
510 case 8820:
511 case 8000: /* aurateconv */
512 case 7350:
513 break;
514 default:
515 return EINVAL;
516 }
517 awacs_write_reg(sc, AWACS_BYTE_SWAP, 0);
518 i = auconv_set_converter(sc->sc_formats, AWACS_NFORMATS,
519 mode, p, TRUE, fil);
520 if (i < 0)
521 return EINVAL;
522 if (i == AWACS_FORMATS_LE)
523 awacs_write_reg(sc, AWACS_BYTE_SWAP, 1);
524 if (fil->req_size > 0)
525 p = &fil->filters[0].param;
526 if (awacs_set_rate(sc, p))
527 return EINVAL;
528 }
529 return 0;
530 }
531
532 int
533 awacs_round_blocksize(h, size, mode, param)
534 void *h;
535 int size;
536 int mode;
537 const audio_params_t *param;
538 {
539 if (size < PAGE_SIZE)
540 size = PAGE_SIZE;
541 return size & ~PGOFSET;
542 }
543
544 int
545 awacs_halt_output(h)
546 void *h;
547 {
548 struct awacs_softc *sc = h;
549
550 dbdma_stop(sc->sc_odma);
551 dbdma_reset(sc->sc_odma);
552 return 0;
553 }
554
555 int
556 awacs_halt_input(h)
557 void *h;
558 {
559 struct awacs_softc *sc = h;
560
561 dbdma_stop(sc->sc_idma);
562 dbdma_reset(sc->sc_idma);
563 return 0;
564 }
565
566 int
567 awacs_getdev(h, retp)
568 void *h;
569 struct audio_device *retp;
570 {
571 *retp = awacs_device;
572 return 0;
573 }
574
575 enum {
576 AWACS_MONITOR_CLASS,
577 AWACS_OUTPUT_CLASS,
578 AWACS_RECORD_CLASS,
579 AWACS_OUTPUT_SELECT,
580 AWACS_VOL_SPEAKER,
581 AWACS_VOL_HEADPHONE,
582 AWACS_INPUT_SELECT,
583 AWACS_VOL_INPUT,
584 AWACS_ENUM_LAST
585 };
586
587 int
588 awacs_set_port(h, mc)
589 void *h;
590 mixer_ctrl_t *mc;
591 {
592 struct awacs_softc *sc = h;
593 int l, r;
594
595 DPRINTF("awacs_set_port dev = %d, type = %d\n", mc->dev, mc->type);
596
597 l = mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT];
598 r = mc->un.value.level[AUDIO_MIXER_LEVEL_RIGHT];
599
600 switch (mc->dev) {
601 case AWACS_OUTPUT_SELECT:
602 /* No change necessary? */
603 if (mc->un.mask == sc->sc_output_mask)
604 return 0;
605
606 sc->sc_codecctl1 |= AWACS_MUTE_SPEAKER | AWACS_MUTE_HEADPHONE;
607 if (mc->un.mask & 1 << 0)
608 sc->sc_codecctl1 &= ~AWACS_MUTE_SPEAKER;
609 if (mc->un.mask & 1 << 1)
610 sc->sc_codecctl1 &= ~AWACS_MUTE_HEADPHONE;
611
612 awacs_write_codec(sc, sc->sc_codecctl1);
613 sc->sc_output_mask = mc->un.mask;
614 return 0;
615
616 case AWACS_VOL_SPEAKER:
617 awacs_set_speaker_volume(sc, l, r);
618 return 0;
619
620 case AWACS_VOL_HEADPHONE:
621 awacs_set_ext_volume(sc, l, r);
622 return 0;
623
624 case AWACS_INPUT_SELECT:
625 /* no change necessary? */
626 if (mc->un.mask == sc->sc_record_source)
627 return 0;
628 switch (mc->un.mask) {
629 case 1 << 0: /* CD */
630 sc->sc_codecctl0 &= ~AWACS_INPUT_MASK;
631 sc->sc_codecctl0 |= AWACS_INPUT_CD;
632 awacs_write_codec(sc, sc->sc_codecctl0);
633 break;
634 case 1 << 1: /* microphone */
635 sc->sc_codecctl0 &= ~AWACS_INPUT_MASK;
636 sc->sc_codecctl0 |= AWACS_INPUT_MICROPHONE;
637 awacs_write_codec(sc, sc->sc_codecctl0);
638 break;
639 case 1 << 2: /* line in */
640 sc->sc_codecctl0 &= ~AWACS_INPUT_MASK;
641 sc->sc_codecctl0 |= AWACS_INPUT_LINE;
642 awacs_write_codec(sc, sc->sc_codecctl0);
643 break;
644 default: /* invalid argument */
645 return EINVAL;
646 }
647 sc->sc_record_source = mc->un.mask;
648 return 0;
649
650 case AWACS_VOL_INPUT:
651 sc->sc_codecctl0 &= ~0xff;
652 sc->sc_codecctl0 |= (l & 0xf0) | (r >> 4);
653 awacs_write_codec(sc, sc->sc_codecctl0);
654 return 0;
655 }
656
657 return ENXIO;
658 }
659
660 int
661 awacs_get_port(h, mc)
662 void *h;
663 mixer_ctrl_t *mc;
664 {
665 struct awacs_softc *sc = h;
666 int vol, l, r;
667
668 DPRINTF("awacs_get_port dev = %d, type = %d\n", mc->dev, mc->type);
669
670 switch (mc->dev) {
671 case AWACS_OUTPUT_SELECT:
672 mc->un.mask = sc->sc_output_mask;
673 return 0;
674
675 case AWACS_VOL_SPEAKER:
676 vol = sc->sc_codecctl4;
677 l = (15 - ((vol & 0x3c0) >> 6)) * 16;
678 r = (15 - (vol & 0x0f)) * 16;
679 mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT] = l;
680 mc->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = r;
681 return 0;
682
683 case AWACS_VOL_HEADPHONE:
684 vol = sc->sc_codecctl2;
685 l = (15 - ((vol & 0x3c0) >> 6)) * 16;
686 r = (15 - (vol & 0x0f)) * 16;
687 mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT] = l;
688 mc->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = r;
689 return 0;
690
691 case AWACS_INPUT_SELECT:
692 mc->un.mask = sc->sc_record_source;
693 return 0;
694
695 case AWACS_VOL_INPUT:
696 vol = sc->sc_codecctl0 & 0xff;
697 l = (vol & 0xf0);
698 r = (vol & 0x0f) << 4;
699 mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT] = l;
700 mc->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = r;
701 return 0;
702
703 default:
704 return ENXIO;
705 }
706
707 return 0;
708 }
709
710 int
711 awacs_query_devinfo(h, dip)
712 void *h;
713 mixer_devinfo_t *dip;
714 {
715
716 DPRINTF("query_devinfo %d\n", dip->index);
717
718 switch (dip->index) {
719
720 case AWACS_OUTPUT_SELECT:
721 dip->mixer_class = AWACS_MONITOR_CLASS;
722 strcpy(dip->label.name, AudioNoutput);
723 dip->type = AUDIO_MIXER_SET;
724 dip->prev = dip->next = AUDIO_MIXER_LAST;
725 dip->un.s.num_mem = 2;
726 strcpy(dip->un.s.member[0].label.name, AudioNspeaker);
727 dip->un.s.member[0].mask = 1 << 0;
728 strcpy(dip->un.s.member[1].label.name, AudioNheadphone);
729 dip->un.s.member[1].mask = 1 << 1;
730 return 0;
731
732 case AWACS_VOL_SPEAKER:
733 dip->mixer_class = AWACS_OUTPUT_CLASS;
734 strcpy(dip->label.name, AudioNspeaker);
735 dip->type = AUDIO_MIXER_VALUE;
736 dip->prev = dip->next = AUDIO_MIXER_LAST;
737 dip->un.v.num_channels = 2;
738 strcpy(dip->un.v.units.name, AudioNvolume);
739 return 0;
740
741 case AWACS_VOL_HEADPHONE:
742 dip->mixer_class = AWACS_OUTPUT_CLASS;
743 strcpy(dip->label.name, AudioNheadphone);
744 dip->type = AUDIO_MIXER_VALUE;
745 dip->prev = dip->next = AUDIO_MIXER_LAST;
746 dip->un.v.num_channels = 2;
747 strcpy(dip->un.v.units.name, AudioNvolume);
748 return 0;
749
750 case AWACS_INPUT_SELECT:
751 dip->mixer_class = AWACS_RECORD_CLASS;
752 strcpy(dip->label.name, AudioNsource);
753 dip->type = AUDIO_MIXER_SET;
754 dip->prev = dip->next = AUDIO_MIXER_LAST;
755 dip->un.s.num_mem = 3;
756 strcpy(dip->un.s.member[0].label.name, AudioNcd);
757 dip->un.s.member[0].mask = 1 << 0;
758 strcpy(dip->un.s.member[1].label.name, AudioNmicrophone);
759 dip->un.s.member[1].mask = 1 << 1;
760 strcpy(dip->un.s.member[2].label.name, AudioNline);
761 dip->un.s.member[2].mask = 1 << 2;
762 return 0;
763
764 case AWACS_VOL_INPUT:
765 dip->mixer_class = AWACS_RECORD_CLASS;
766 strcpy(dip->label.name, AudioNrecord);
767 dip->type = AUDIO_MIXER_VALUE;
768 dip->prev = dip->next = AUDIO_MIXER_LAST;
769 dip->un.v.num_channels = 2;
770 strcpy(dip->un.v.units.name, AudioNvolume);
771 return 0;
772
773 case AWACS_MONITOR_CLASS:
774 dip->mixer_class = AWACS_MONITOR_CLASS;
775 strcpy(dip->label.name, AudioCmonitor);
776 dip->type = AUDIO_MIXER_CLASS;
777 dip->next = dip->prev = AUDIO_MIXER_LAST;
778 return 0;
779
780 case AWACS_OUTPUT_CLASS:
781 dip->mixer_class = AWACS_OUTPUT_CLASS;
782 strcpy(dip->label.name, AudioCoutputs);
783 dip->type = AUDIO_MIXER_CLASS;
784 dip->next = dip->prev = AUDIO_MIXER_LAST;
785 return 0;
786
787 case AWACS_RECORD_CLASS:
788 dip->mixer_class = AWACS_RECORD_CLASS;
789 strcpy(dip->label.name, AudioCrecord);
790 dip->type = AUDIO_MIXER_CLASS;
791 dip->next = dip->prev = AUDIO_MIXER_LAST;
792 return 0;
793 }
794
795 return ENXIO;
796 }
797
798 size_t
799 awacs_round_buffersize(h, dir, size)
800 void *h;
801 int dir;
802 size_t size;
803 {
804 if (size > 65536)
805 size = 65536;
806 return size;
807 }
808
809 paddr_t
810 awacs_mappage(h, mem, off, prot)
811 void *h;
812 void *mem;
813 off_t off;
814 int prot;
815 {
816 if (off < 0)
817 return -1;
818 return -1; /* XXX */
819 }
820
821 int
822 awacs_get_props(h)
823 void *h;
824 {
825 return AUDIO_PROP_FULLDUPLEX /* | AUDIO_PROP_MMAP */;
826 }
827
828 int
829 awacs_trigger_output(h, start, end, bsize, intr, arg, param)
830 void *h;
831 void *start, *end;
832 int bsize;
833 void (*intr)(void *);
834 void *arg;
835 const audio_params_t *param;
836 {
837 struct awacs_softc *sc = h;
838 struct dbdma_command *cmd = sc->sc_odmacmd;
839 vaddr_t va;
840 int i, len, intmode;
841
842 DPRINTF("trigger_output %p %p 0x%x\n", start, end, bsize);
843
844 sc->sc_ointr = intr;
845 sc->sc_oarg = arg;
846 sc->sc_opages = ((char *)end - (char *)start) / PAGE_SIZE;
847
848 #ifdef DIAGNOSTIC
849 if (sc->sc_opages > 16)
850 panic("awacs_trigger_output");
851 #endif
852
853 va = (vaddr_t)start;
854 len = 0;
855 for (i = sc->sc_opages; i > 0; i--) {
856 len += PAGE_SIZE;
857 if (len < bsize)
858 intmode = DBDMA_INT_NEVER;
859 else {
860 len = 0;
861 intmode = DBDMA_INT_ALWAYS;
862 }
863
864 DBDMA_BUILD(cmd, DBDMA_CMD_OUT_MORE, 0, PAGE_SIZE, vtophys(va),
865 intmode, DBDMA_WAIT_NEVER, DBDMA_BRANCH_NEVER);
866 va += PAGE_SIZE;
867 cmd++;
868 }
869
870 DBDMA_BUILD(cmd, DBDMA_CMD_NOP, 0, 0, 0,
871 DBDMA_INT_NEVER, DBDMA_WAIT_NEVER, DBDMA_BRANCH_ALWAYS);
872 dbdma_st32(&cmd->d_cmddep, vtophys((vaddr_t)sc->sc_odmacmd));
873
874 dbdma_start(sc->sc_odma, sc->sc_odmacmd);
875
876 return 0;
877 }
878
879 int
880 awacs_trigger_input(h, start, end, bsize, intr, arg, param)
881 void *h;
882 void *start, *end;
883 int bsize;
884 void (*intr)(void *);
885 void *arg;
886 const audio_params_t *param;
887 {
888 printf("awacs_trigger_input called\n");
889
890 return 1;
891 }
892
893 void
894 awacs_set_speaker_volume(sc, left, right)
895 struct awacs_softc *sc;
896 int left, right;
897 {
898 int lval = 15 - (left & 0xff) / 16;
899 int rval = 15 - (right & 0xff) / 16;
900
901 DPRINTF("speaker_volume %d %d\n", lval, rval);
902
903 sc->sc_codecctl4 &= ~0x3cf;
904 sc->sc_codecctl4 |= (lval << 6) | rval;
905 awacs_write_codec(sc, sc->sc_codecctl4);
906 }
907
908 void
909 awacs_set_ext_volume(sc, left, right)
910 struct awacs_softc *sc;
911 int left, right;
912 {
913 int lval = 15 - (left & 0xff) / 16;
914 int rval = 15 - (right & 0xff) / 16;
915
916 DPRINTF("ext_volume %d %d\n", lval, rval);
917
918 sc->sc_codecctl2 &= ~0x3cf;
919 sc->sc_codecctl2 |= (lval << 6) | rval;
920 awacs_write_codec(sc, sc->sc_codecctl2);
921 }
922
923 int
924 awacs_set_rate(sc, p)
925 struct awacs_softc *sc;
926 struct audio_params *p;
927 {
928 int c;
929
930 switch (p->sample_rate) {
931 case 44100:
932 c = AWACS_RATE_44100;
933 break;
934 case 29400:
935 c = AWACS_RATE_29400;
936 break;
937 case 22050:
938 c = AWACS_RATE_22050;
939 break;
940 case 17640:
941 c = AWACS_RATE_17640;
942 break;
943 case 14700:
944 c = AWACS_RATE_14700;
945 break;
946 case 11025:
947 c = AWACS_RATE_11025;
948 break;
949 case 8820:
950 c = AWACS_RATE_8820;
951 break;
952 case 7350:
953 c = AWACS_RATE_7350;
954 break;
955 default:
956 return -1;
957 }
958
959 sc->sc_soundctl &= ~AWACS_RATE_MASK;
960 sc->sc_soundctl |= c;
961 awacs_write_reg(sc, AWACS_SOUND_CTRL, sc->sc_soundctl);
962
963 return 0;
964 }
965