awacs.c revision 1.25.2.1 1 /* $NetBSD: awacs.c,v 1.25.2.1 2007/03/12 05:49:05 rmind 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.25.2.1 2007/03/12 05:49:05 rmind 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 #include <dev/i2c/sgsmixvar.h>
51 #include "sgsmix.h"
52
53 #ifdef AWACS_DEBUG
54 # define DPRINTF printf
55 #else
56 # define DPRINTF while (0) printf
57 #endif
58
59 /* sc_flags values */
60 #define AWACS_CAP_BSWAP 0x0001
61
62 struct awacs_softc {
63 struct device sc_dev;
64 int sc_flags;
65
66 void (*sc_ointr)(void *); /* DMA completion intr handler */
67 void *sc_oarg; /* arg for sc_ointr() */
68 int sc_opages; /* # of output pages */
69
70 void (*sc_iintr)(void *); /* DMA completion intr handler */
71 void *sc_iarg; /* arg for sc_iintr() */
72
73 u_int sc_record_source; /* recording source mask */
74 u_int sc_output_mask; /* output source mask */
75
76 int sc_screamer;
77 int sc_have_perch;
78 int spk_l, spk_r, hph_l, hph_r;
79 int sc_bass, sc_treble;
80 #if NSGSMIX > 0
81 struct device *sc_sgsmix;
82 #endif
83
84 char *sc_reg;
85 u_int sc_codecctl0;
86 u_int sc_codecctl1;
87 u_int sc_codecctl2;
88 u_int sc_codecctl4;
89 u_int sc_codecctl5;
90 u_int sc_codecctl6;
91 u_int sc_codecctl7;
92 u_int sc_soundctl;
93
94 struct dbdma_regmap *sc_odma;
95 struct dbdma_regmap *sc_idma;
96 struct dbdma_command *sc_odmacmd;
97 struct dbdma_command *sc_idmacmd;
98
99 #define AWACS_NFORMATS 2
100 struct audio_format sc_formats[AWACS_NFORMATS];
101 };
102
103 int awacs_match(struct device *, struct cfdata *, void *);
104 void awacs_attach(struct device *, struct device *, void *);
105 int awacs_intr(void *);
106
107 void awacs_close(void *);
108 int awacs_query_encoding(void *, struct audio_encoding *);
109 int awacs_set_params(void *, int, int, audio_params_t *, audio_params_t *,
110 stream_filter_list_t *, stream_filter_list_t *);
111 int awacs_round_blocksize(void *, int, int, const audio_params_t *);
112 int awacs_trigger_output(void *, void *, void *, int, void (*)(void *),
113 void *, const audio_params_t *);
114 int awacs_trigger_input(void *, void *, void *, int, void (*)(void *),
115 void *, const audio_params_t *);
116 int awacs_halt_output(void *);
117 int awacs_halt_input(void *);
118 int awacs_getdev(void *, struct audio_device *);
119 int awacs_set_port(void *, mixer_ctrl_t *);
120 int awacs_get_port(void *, mixer_ctrl_t *);
121 int awacs_query_devinfo(void *, mixer_devinfo_t *);
122 size_t awacs_round_buffersize(void *, int, size_t);
123 paddr_t awacs_mappage(void *, void *, off_t, int);
124 int awacs_get_props(void *);
125
126 static inline u_int awacs_read_reg(struct awacs_softc *, int);
127 static inline void awacs_write_reg(struct awacs_softc *, int, int);
128 void awacs_write_codec(struct awacs_softc *, int);
129 void awacs_set_speaker_volume(struct awacs_softc *, int, int);
130 void awacs_set_ext_volume(struct awacs_softc *, int, int);
131 void awacs_set_loopthrough_volume(struct awacs_softc *, int, int);
132 int awacs_set_rate(struct awacs_softc *, const audio_params_t *);
133 void awacs_select_output(struct awacs_softc *, int);
134 #if NSGSMIX > 0
135 static void awacs_set_bass(struct awacs_softc *, int);
136 static void awacs_set_treble(struct awacs_softc *, int);
137 #endif
138 static int awacs_setup_sgsmix(struct device *);
139
140 CFATTACH_DECL(awacs, sizeof(struct awacs_softc),
141 awacs_match, awacs_attach, NULL, NULL);
142
143 const struct audio_hw_if awacs_hw_if = {
144 NULL, /* open */
145 awacs_close,
146 NULL,
147 awacs_query_encoding,
148 awacs_set_params,
149 awacs_round_blocksize,
150 NULL,
151 NULL,
152 NULL,
153 NULL,
154 NULL,
155 awacs_halt_output,
156 awacs_halt_input,
157 NULL,
158 awacs_getdev,
159 NULL,
160 awacs_set_port,
161 awacs_get_port,
162 awacs_query_devinfo,
163 NULL,
164 NULL,
165 awacs_round_buffersize,
166 awacs_mappage,
167 awacs_get_props,
168 awacs_trigger_output,
169 awacs_trigger_input,
170 NULL,
171 };
172
173 struct audio_device awacs_device = {
174 "AWACS",
175 "",
176 "awacs"
177 };
178
179 #define AWACS_NFORMATS 2
180 #define AWACS_FORMATS_LE 0
181 static const struct audio_format awacs_formats[AWACS_NFORMATS] = {
182 {NULL, AUMODE_PLAY | AUMODE_RECORD, AUDIO_ENCODING_SLINEAR_LE, 16, 16,
183 2, AUFMT_STEREO, 8, {7350, 8820, 11025, 14700, 17640, 22050, 29400, 44100}},
184 {NULL, AUMODE_PLAY | AUMODE_RECORD, AUDIO_ENCODING_SLINEAR_BE, 16, 16,
185 2, AUFMT_STEREO, 8, {7350, 8820, 11025, 14700, 17640, 22050, 29400, 44100}},
186 };
187
188 /* register offset */
189 #define AWACS_SOUND_CTRL 0x00
190 #define AWACS_CODEC_CTRL 0x10
191 #define AWACS_CODEC_STATUS 0x20
192 #define AWACS_CLIP_COUNT 0x30
193 #define AWACS_BYTE_SWAP 0x40
194
195 /* sound control */
196 #define AWACS_INPUT_SUBFRAME0 0x00000001
197 #define AWACS_INPUT_SUBFRAME1 0x00000002
198 #define AWACS_INPUT_SUBFRAME2 0x00000004
199 #define AWACS_INPUT_SUBFRAME3 0x00000008
200
201 #define AWACS_OUTPUT_SUBFRAME0 0x00000010
202 #define AWACS_OUTPUT_SUBFRAME1 0x00000020
203 #define AWACS_OUTPUT_SUBFRAME2 0x00000040
204 #define AWACS_OUTPUT_SUBFRAME3 0x00000080
205
206 #define AWACS_RATE_44100 0x00000000
207 #define AWACS_RATE_29400 0x00000100
208 #define AWACS_RATE_22050 0x00000200
209 #define AWACS_RATE_17640 0x00000300
210 #define AWACS_RATE_14700 0x00000400
211 #define AWACS_RATE_11025 0x00000500
212 #define AWACS_RATE_8820 0x00000600
213 #define AWACS_RATE_7350 0x00000700
214 #define AWACS_RATE_MASK 0x00000700
215
216 /* codec control */
217 #define AWACS_CODEC_ADDR0 0x00000000
218 #define AWACS_CODEC_ADDR1 0x00001000
219 #define AWACS_CODEC_ADDR2 0x00002000
220 #define AWACS_CODEC_ADDR4 0x00004000
221 #define AWACS_CODEC_ADDR5 0x00005000
222 #define AWACS_CODEC_ADDR6 0x00006000
223 #define AWACS_CODEC_ADDR7 0x00007000
224 #define AWACS_CODEC_EMSEL0 0x00000000
225 #define AWACS_CODEC_EMSEL1 0x00400000
226 #define AWACS_CODEC_EMSEL2 0x00800000
227 #define AWACS_CODEC_EMSEL4 0x00c00000
228 #define AWACS_CODEC_BUSY 0x01000000
229
230 /* cc0 */
231 #define AWACS_DEFAULT_CD_GAIN 0x000000bb
232 #define AWACS_INPUT_CD 0x00000200
233 #define AWACS_INPUT_LINE 0x00000400
234 #define AWACS_INPUT_MICROPHONE 0x00000800
235 #define AWACS_INPUT_MASK 0x00000e00
236
237 /* cc1 */
238 #define AWACS_LOOP_THROUGH 0x00000040
239 #define AWACS_MUTE_SPEAKER 0x00000080
240 #define AWACS_MUTE_HEADPHONE 0x00000200
241 #define AWACS_PARALLEL_OUTPUT 0x00000c00
242
243 /* output */
244 #define OUTPUT_SPEAKER 1
245 #define OUTPUT_HEADPHONES 2
246 int
247 awacs_match(struct device *parent, struct cfdata *match, void *aux)
248 {
249 struct confargs *ca;
250
251 ca = aux;
252 if (strcmp(ca->ca_name, "i2s") == 0)
253 return 1;
254
255 if (strcmp(ca->ca_name, "awacs") != 0 &&
256 strcmp(ca->ca_name, "davbus") != 0)
257 return 0;
258
259 if (ca->ca_nreg < 24 || ca->ca_nintr < 12)
260 return 0;
261
262 return 1;
263 }
264
265 void
266 awacs_attach(struct device *parent, struct device *self, void *aux)
267 {
268 struct awacs_softc *sc;
269 struct confargs *ca;
270 int cirq, oirq, iirq, cirq_type, oirq_type, iirq_type;
271 int len = -1, perch;
272 char compat[255];
273
274 sc = (struct awacs_softc *)self;
275 ca = aux;
276
277 sc->sc_reg = mapiodev(ca->ca_baseaddr + ca->ca_reg[0], ca->ca_reg[1]);
278
279 /* out */
280 sc->sc_odma = mapiodev(ca->ca_baseaddr + ca->ca_reg[2], ca->ca_reg[3]);
281 sc->sc_odmacmd = dbdma_alloc(20 * sizeof(struct dbdma_command));
282 /* in */
283 sc->sc_idma = mapiodev(ca->ca_baseaddr + ca->ca_reg[4], ca->ca_reg[5]);
284 sc->sc_idmacmd = dbdma_alloc(20 * sizeof(struct dbdma_command));
285
286 aprint_debug("base: %08x reg: %08x %08x %08x\n",ca->ca_baseaddr,
287 ca->ca_reg[0], ca->ca_reg[2], ca->ca_reg[4]);
288
289 if (strcmp(ca->ca_name, "i2s") == 0) {
290 int node, intr[6];
291
292 node = OF_child(ca->ca_node);
293 if (node == 0) {
294 printf("no i2s-a child\n");
295 return;
296 }
297 if (OF_getprop(node, "interrupts", intr, sizeof(intr)) == -1) {
298 printf("no interrupt property\n");
299 return;
300 }
301
302 cirq = intr[0];
303 oirq = intr[2];
304 iirq = intr[4];
305 cirq_type = intr[1] ? IST_LEVEL : IST_EDGE;
306 oirq_type = intr[3] ? IST_LEVEL : IST_EDGE;
307 iirq_type = intr[5] ? IST_LEVEL : IST_EDGE;
308 } else if (ca->ca_nintr == 24) {
309 cirq = ca->ca_intr[0];
310 oirq = ca->ca_intr[2];
311 iirq = ca->ca_intr[4];
312 cirq_type = ca->ca_intr[1] ? IST_LEVEL : IST_EDGE;
313 oirq_type = ca->ca_intr[3] ? IST_LEVEL : IST_EDGE;
314 iirq_type = ca->ca_intr[5] ? IST_LEVEL : IST_EDGE;
315 } else {
316 cirq = ca->ca_intr[0];
317 oirq = ca->ca_intr[1];
318 iirq = ca->ca_intr[2];
319 cirq_type = oirq_type = iirq_type = IST_LEVEL;
320 }
321
322 intr_establish(cirq, cirq_type, IPL_AUDIO, awacs_intr, sc);
323 intr_establish(oirq, oirq_type, IPL_AUDIO, awacs_intr, sc);
324 intr_establish(iirq, iirq_type, IPL_AUDIO, awacs_intr, sc);
325
326 /* check if the chip is a screamer */
327 sc->sc_screamer = 0;
328 memset(compat, 0, 256);
329 if ((len = OF_getprop(ca->ca_node, "compatible", compat, 255)) <= 0) {
330 int child;
331
332 child = OF_child(ca->ca_node);
333 while ((child != -1) && (len <= 0)) {
334 len = OF_getprop(child, "compatible", compat, 255);
335 child = OF_peer(child);
336 }
337 }
338 if (len > 0) {
339 char *ptr = compat;
340
341 while (( ptr[0] != 0) && (sc->sc_screamer == 0)) {
342 sc->sc_screamer = (strcmp(ptr, "screamer") == 0);
343 ptr += strlen(ptr) + 1;
344 }
345 }
346 if (sc->sc_screamer)
347 printf(" Screamer");
348
349
350 printf(": irq %d,%d,%d\n", cirq, oirq, iirq);
351
352 sc->spk_l = 0;
353 sc->spk_r = 0;
354 sc->hph_l = 0;
355 sc->hph_r = 0;
356
357 memcpy(&sc->sc_formats, awacs_formats, sizeof(awacs_formats));
358 /* XXX Uni-North based models don't have byteswap capability. */
359 if (OF_finddevice("/uni-n") == -1) {
360 sc->sc_flags |= AWACS_CAP_BSWAP;
361 } else {
362 AUFMT_INVALIDATE(&sc->sc_formats[AWACS_FORMATS_LE]);
363 }
364
365 sc->sc_soundctl = AWACS_INPUT_SUBFRAME0 | AWACS_OUTPUT_SUBFRAME0 |
366 AWACS_RATE_44100;
367 awacs_write_reg(sc, AWACS_SOUND_CTRL, sc->sc_soundctl);
368
369 sc->sc_codecctl0 = AWACS_CODEC_ADDR0 | AWACS_CODEC_EMSEL0;
370 sc->sc_codecctl1 = AWACS_CODEC_ADDR1 | AWACS_CODEC_EMSEL0;
371 sc->sc_codecctl2 = AWACS_CODEC_ADDR2 | AWACS_CODEC_EMSEL0;
372 sc->sc_codecctl4 = AWACS_CODEC_ADDR4 | AWACS_CODEC_EMSEL0;
373 sc->sc_codecctl5 = AWACS_CODEC_ADDR5 | AWACS_CODEC_EMSEL0;
374 sc->sc_codecctl6 = AWACS_CODEC_ADDR6 | AWACS_CODEC_EMSEL0;
375 sc->sc_codecctl7 = AWACS_CODEC_ADDR7 | AWACS_CODEC_EMSEL0;
376
377 sc->sc_codecctl0 |= AWACS_INPUT_CD | AWACS_DEFAULT_CD_GAIN;
378 awacs_write_codec(sc, sc->sc_codecctl0);
379
380 /* Set loopback (for CD?) */
381 /* sc->sc_codecctl1 |= 0x440; */
382 sc->sc_codecctl1 |= (AWACS_LOOP_THROUGH | AWACS_PARALLEL_OUTPUT);
383
384 /* check for headphone present */
385 if (awacs_read_reg(sc, AWACS_CODEC_STATUS) & 0x8) {
386 /* default output to speakers */
387 printf(" headphones");
388 sc->sc_output_mask = OUTPUT_HEADPHONES;
389 sc->sc_codecctl1 &= ~AWACS_MUTE_HEADPHONE;
390 sc->sc_codecctl1 |= AWACS_MUTE_SPEAKER;
391 } else {
392 /* default output to speakers */
393 printf(" speaker");
394 sc->sc_output_mask = OUTPUT_SPEAKER;
395 sc->sc_codecctl1 &= ~AWACS_MUTE_SPEAKER;
396 sc->sc_codecctl1 |= AWACS_MUTE_HEADPHONE;
397 }
398 awacs_write_codec(sc, sc->sc_codecctl1);
399
400 delay(100);
401 if (sc->sc_screamer) {
402 awacs_write_codec(sc, sc->sc_codecctl6);
403 awacs_write_codec(sc, sc->sc_codecctl5);
404 delay(2);
405 awacs_write_codec(sc, sc->sc_codecctl1);
406 awacs_write_codec(sc, sc->sc_codecctl7);
407 }
408
409 /* default input from CD */
410 sc->sc_record_source = 1 << 0;
411 sc->sc_codecctl0 &= ~AWACS_INPUT_MASK;
412 sc->sc_codecctl0 |= AWACS_INPUT_CD;
413 awacs_write_codec(sc, sc->sc_codecctl0);
414
415 /* Enable interrupts and looping mode. */
416 /* XXX ... */
417
418 sc->sc_codecctl1 |= (AWACS_LOOP_THROUGH | AWACS_PARALLEL_OUTPUT);
419 awacs_write_codec(sc, sc->sc_codecctl1);
420
421 #if NSGSMIX > 0
422 sc->sc_sgsmix = NULL;
423 #endif
424 sc->sc_have_perch = 0;
425 if ((perch = OF_finddevice("/perch")) != -1) {
426
427 len = OF_getprop(perch, "compatible", compat, 255);
428 if (len > 0) {
429 printf("%s: found '%s' personality card\n",
430 sc->sc_dev.dv_xname, compat);
431 sc->sc_have_perch = 1;
432 config_finalize_register(&sc->sc_dev, awacs_setup_sgsmix);
433 }
434 }
435
436 /* Set initial volume[s] */
437 awacs_set_speaker_volume(sc, 80, 80);
438 awacs_set_ext_volume(sc, 120, 120);
439 //awacs_set_loopthrough_volume(sc, 240, 240);
440
441 audio_attach_mi(&awacs_hw_if, sc, &sc->sc_dev);
442 }
443
444 static int
445 awacs_setup_sgsmix(struct device *cookie)
446 {
447 struct awacs_softc *sc = (struct awacs_softc *)cookie;
448 #if NSGSMIX > 0
449 struct device *dv;
450 #endif
451
452 if (!sc->sc_have_perch)
453 return 0;
454 #if NSGSMIX > 0
455 /* look for sgsmix */
456 for (dv = alldevs.tqh_first; dv; dv=dv->dv_list.tqe_next) {
457 if (device_is_a(dv, "sgsmix")) {
458 sc->sc_sgsmix = dv;
459 break;
460 }
461 }
462 if (sc->sc_sgsmix == NULL)
463 return 0;
464
465 printf("%s: using %s\n", sc->sc_dev.dv_xname,
466 sc->sc_sgsmix->dv_xname);
467
468 awacs_select_output(sc, sc->sc_output_mask);
469 awacs_set_speaker_volume(sc, sc->spk_l, sc->spk_r);
470 awacs_set_ext_volume(sc, sc->hph_l, sc->hph_r);
471 awacs_set_bass(sc, 128);
472 awacs_set_treble(sc, 128);
473 #endif
474 return 0;
475 }
476
477
478 static inline u_int
479 awacs_read_reg(struct awacs_softc *sc, int reg)
480 {
481 char *addr;
482
483 addr = sc->sc_reg;
484 return in32rb(addr + reg);
485 }
486
487 static inline void
488 awacs_write_reg(struct awacs_softc *sc, int reg, int val)
489 {
490 char *addr;
491
492 addr = sc->sc_reg;
493 out32rb(addr + reg, val);
494 }
495
496 void
497 awacs_write_codec(struct awacs_softc *sc, int value)
498 {
499
500 do {
501 delay(100);
502 } while (awacs_read_reg(sc, AWACS_CODEC_CTRL) & AWACS_CODEC_BUSY);
503
504 awacs_write_reg(sc, AWACS_CODEC_CTRL, value);
505
506 do {
507 delay(100);
508 } while (awacs_read_reg(sc, AWACS_CODEC_CTRL) & AWACS_CODEC_BUSY);
509 }
510
511 int
512 awacs_intr(void *v)
513 {
514 struct awacs_softc *sc;
515 struct dbdma_command *cmd;
516 int count;
517 int status;
518
519 sc = v;
520 cmd = sc->sc_odmacmd;
521 count = sc->sc_opages;
522 /* Fill used buffer(s). */
523 while (count-- > 0) {
524 /* if DBDMA_INT_ALWAYS */
525 if (in16rb(&cmd->d_command) & 0x30) { /* XXX */
526 status = in16rb(&cmd->d_status);
527 cmd->d_status = 0;
528 if (status) /* status == 0x8400 */
529 if (sc->sc_ointr)
530 (*sc->sc_ointr)(sc->sc_oarg);
531 }
532 cmd++;
533 }
534
535 return 1;
536 }
537
538 /*
539 * Close function is called at splaudio().
540 */
541 void
542 awacs_close(void *h)
543 {
544 struct awacs_softc *sc;
545
546 sc = h;
547 awacs_halt_output(sc);
548 awacs_halt_input(sc);
549
550 sc->sc_ointr = 0;
551 sc->sc_iintr = 0;
552 }
553
554 int
555 awacs_query_encoding(void *h, struct audio_encoding *ae)
556 {
557 struct awacs_softc *sc;
558
559 sc = h;
560 ae->flags = AUDIO_ENCODINGFLAG_EMULATED;
561
562 switch (ae->index) {
563 case 0:
564 strcpy(ae->name, AudioEslinear);
565 ae->encoding = AUDIO_ENCODING_SLINEAR;
566 ae->precision = 16;
567 ae->flags = 0;
568 return 0;
569 case 1:
570 strcpy(ae->name, AudioEslinear_be);
571 ae->encoding = AUDIO_ENCODING_SLINEAR_BE;
572 ae->precision = 16;
573 ae->flags = 0;
574 return 0;
575 case 2:
576 strcpy(ae->name, AudioEslinear_le);
577 ae->encoding = AUDIO_ENCODING_SLINEAR_LE;
578 ae->precision = 16;
579 if (sc->sc_flags & AWACS_CAP_BSWAP)
580 ae->flags = 0;
581 return 0;
582 case 3:
583 strcpy(ae->name, AudioEulinear_be);
584 ae->encoding = AUDIO_ENCODING_ULINEAR_BE;
585 ae->precision = 16;
586 return 0;
587 case 4:
588 strcpy(ae->name, AudioEulinear_le);
589 ae->encoding = AUDIO_ENCODING_ULINEAR_LE;
590 ae->precision = 16;
591 return 0;
592 case 5:
593 strcpy(ae->name, AudioEmulaw);
594 ae->encoding = AUDIO_ENCODING_ULAW;
595 ae->precision = 8;
596 return 0;
597 case 6:
598 strcpy(ae->name, AudioEalaw);
599 ae->encoding = AUDIO_ENCODING_ALAW;
600 ae->precision = 8;
601 return 0;
602 default:
603 return EINVAL;
604 }
605 }
606
607 int
608 awacs_set_params(void *h, int setmode, int usemode,
609 audio_params_t *play, audio_params_t *rec,
610 stream_filter_list_t *pfil, stream_filter_list_t *rfil)
611 {
612 struct awacs_softc *sc;
613 audio_params_t *p;
614 stream_filter_list_t *fil;
615 int mode, i;
616
617 sc = h;
618 p = NULL;
619 /*
620 * This device only has one clock, so make the sample rates match.
621 */
622 if (play->sample_rate != rec->sample_rate &&
623 usemode == (AUMODE_PLAY | AUMODE_RECORD)) {
624 if (setmode == AUMODE_PLAY) {
625 rec->sample_rate = play->sample_rate;
626 setmode |= AUMODE_RECORD;
627 } else if (setmode == AUMODE_RECORD) {
628 play->sample_rate = rec->sample_rate;
629 setmode |= AUMODE_PLAY;
630 } else
631 return EINVAL;
632 }
633
634 for (mode = AUMODE_RECORD; mode != -1;
635 mode = mode == AUMODE_RECORD ? AUMODE_PLAY : -1) {
636 if ((setmode & mode) == 0)
637 continue;
638
639 p = mode == AUMODE_PLAY ? play : rec;
640 fil = mode == AUMODE_PLAY ? pfil : rfil;
641 switch (p->sample_rate) {
642 case 48000: /* aurateconv */
643 case 44100:
644 case 29400:
645 case 22050:
646 case 17640:
647 case 14700:
648 case 11025:
649 case 8820:
650 case 8000: /* aurateconv */
651 case 7350:
652 break;
653 default:
654 return EINVAL;
655 }
656 awacs_write_reg(sc, AWACS_BYTE_SWAP, 0);
657 i = auconv_set_converter(sc->sc_formats, AWACS_NFORMATS,
658 mode, p, true, fil);
659 if (i < 0)
660 return EINVAL;
661 if (i == AWACS_FORMATS_LE)
662 awacs_write_reg(sc, AWACS_BYTE_SWAP, 1);
663 if (fil->req_size > 0)
664 p = &fil->filters[0].param;
665 if (awacs_set_rate(sc, p))
666 return EINVAL;
667 }
668 return 0;
669 }
670
671 int
672 awacs_round_blocksize(void *h, int size, int mode, const audio_params_t *param)
673 {
674
675 if (size < PAGE_SIZE)
676 size = PAGE_SIZE;
677 return size & ~PGOFSET;
678 }
679
680 int
681 awacs_halt_output(void *h)
682 {
683 struct awacs_softc *sc;
684
685 sc = h;
686 dbdma_stop(sc->sc_odma);
687 dbdma_reset(sc->sc_odma);
688 return 0;
689 }
690
691 int
692 awacs_halt_input(void *h)
693 {
694 struct awacs_softc *sc;
695
696 sc = h;
697 dbdma_stop(sc->sc_idma);
698 dbdma_reset(sc->sc_idma);
699 return 0;
700 }
701
702 int
703 awacs_getdev(void *h, struct audio_device *retp)
704 {
705
706 *retp = awacs_device;
707 return 0;
708 }
709
710 enum {
711 AWACS_MONITOR_CLASS,
712 AWACS_OUTPUT_CLASS,
713 AWACS_RECORD_CLASS,
714 AWACS_OUTPUT_SELECT,
715 AWACS_VOL_SPEAKER,
716 AWACS_VOL_HEADPHONE,
717 AWACS_INPUT_SELECT,
718 AWACS_VOL_INPUT,
719 AWACS_BASS,
720 AWACS_TREBLE,
721 AWACS_ENUM_LAST
722 };
723
724 int
725 awacs_set_port(void *h, mixer_ctrl_t *mc)
726 {
727 struct awacs_softc *sc;
728 int l, r;
729
730 DPRINTF("awacs_set_port dev = %d, type = %d\n", mc->dev, mc->type);
731 sc = h;
732 l = mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT];
733 r = mc->un.value.level[AUDIO_MIXER_LEVEL_RIGHT];
734
735 switch (mc->dev) {
736 case AWACS_OUTPUT_SELECT:
737 /* No change necessary? */
738 if (mc->un.mask == sc->sc_output_mask)
739 return 0;
740 awacs_select_output(sc, mc->un.mask);
741 return 0;
742
743 case AWACS_VOL_SPEAKER:
744 awacs_set_speaker_volume(sc, l, r);
745 return 0;
746
747 case AWACS_VOL_HEADPHONE:
748 awacs_set_ext_volume(sc, l, r);
749 return 0;
750
751 case AWACS_INPUT_SELECT:
752 /* no change necessary? */
753 if (mc->un.mask == sc->sc_record_source)
754 return 0;
755 switch (mc->un.mask) {
756 case 1 << 0: /* CD */
757 sc->sc_codecctl0 &= ~AWACS_INPUT_MASK;
758 sc->sc_codecctl0 |= AWACS_INPUT_CD;
759 awacs_write_codec(sc, sc->sc_codecctl0);
760 break;
761 case 1 << 1: /* microphone */
762 sc->sc_codecctl0 &= ~AWACS_INPUT_MASK;
763 sc->sc_codecctl0 |= AWACS_INPUT_MICROPHONE;
764 awacs_write_codec(sc, sc->sc_codecctl0);
765 break;
766 case 1 << 2: /* line in */
767 sc->sc_codecctl0 &= ~AWACS_INPUT_MASK;
768 sc->sc_codecctl0 |= AWACS_INPUT_LINE;
769 awacs_write_codec(sc, sc->sc_codecctl0);
770 break;
771 default: /* invalid argument */
772 return EINVAL;
773 }
774 sc->sc_record_source = mc->un.mask;
775 return 0;
776
777 case AWACS_VOL_INPUT:
778 sc->sc_codecctl0 &= ~0xff;
779 sc->sc_codecctl0 |= (l & 0xf0) | (r >> 4);
780 awacs_write_codec(sc, sc->sc_codecctl0);
781 return 0;
782
783 #if NSGSMIX > 0
784 case AWACS_BASS:
785 awacs_set_bass(sc, l);
786 return 0;
787
788 case AWACS_TREBLE:
789 awacs_set_treble(sc, l);
790 return 0;
791 #endif
792 }
793
794 return ENXIO;
795 }
796
797 int
798 awacs_get_port(void *h, mixer_ctrl_t *mc)
799 {
800 struct awacs_softc *sc;
801 int l, r, vol;
802
803 DPRINTF("awacs_get_port dev = %d, type = %d\n", mc->dev, mc->type);
804 sc = h;
805 switch (mc->dev) {
806 case AWACS_OUTPUT_SELECT:
807 mc->un.mask = sc->sc_output_mask;
808 return 0;
809
810 case AWACS_VOL_SPEAKER:
811 mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT] = sc->spk_l;
812 mc->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = sc->spk_r;
813 return 0;
814
815 case AWACS_VOL_HEADPHONE:
816 mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT] = sc->hph_l;
817 mc->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = sc->hph_r;
818 return 0;
819
820 case AWACS_INPUT_SELECT:
821 mc->un.mask = sc->sc_record_source;
822 return 0;
823
824 case AWACS_VOL_INPUT:
825 vol = sc->sc_codecctl0 & 0xff;
826 l = (vol & 0xf0);
827 r = (vol & 0x0f) << 4;
828 mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT] = l;
829 mc->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = r;
830 return 0;
831 #if NSGSMIX > 0
832 case AWACS_BASS:
833 mc->un.value.level[AUDIO_MIXER_LEVEL_MONO] = sc->sc_bass;
834 return 0;
835
836 case AWACS_TREBLE:
837 mc->un.value.level[AUDIO_MIXER_LEVEL_MONO] = sc->sc_treble;
838 return 0;
839 #endif
840
841 default:
842 return ENXIO;
843 }
844
845 return 0;
846 }
847
848 int
849 awacs_query_devinfo(void *h, mixer_devinfo_t *dip)
850 {
851 #if NSGSMIX > 0
852 struct awacs_softc *sc = h;
853 #endif
854
855 DPRINTF("query_devinfo %d\n", dip->index);
856
857 switch (dip->index) {
858
859 case AWACS_OUTPUT_SELECT:
860 dip->mixer_class = AWACS_MONITOR_CLASS;
861 strcpy(dip->label.name, AudioNoutput);
862 dip->type = AUDIO_MIXER_SET;
863 dip->prev = dip->next = AUDIO_MIXER_LAST;
864 dip->un.s.num_mem = 2;
865 strcpy(dip->un.s.member[0].label.name, AudioNspeaker);
866 dip->un.s.member[0].mask = 1 << 0;
867 strcpy(dip->un.s.member[1].label.name, AudioNheadphone);
868 dip->un.s.member[1].mask = 1 << 1;
869 return 0;
870
871 case AWACS_VOL_SPEAKER:
872 dip->mixer_class = AWACS_OUTPUT_CLASS;
873 strcpy(dip->label.name, AudioNspeaker);
874 dip->type = AUDIO_MIXER_VALUE;
875 dip->prev = dip->next = AUDIO_MIXER_LAST;
876 dip->un.v.num_channels = 2;
877 strcpy(dip->un.v.units.name, AudioNvolume);
878 return 0;
879
880 case AWACS_VOL_HEADPHONE:
881 dip->mixer_class = AWACS_OUTPUT_CLASS;
882 strcpy(dip->label.name, AudioNheadphone);
883 dip->type = AUDIO_MIXER_VALUE;
884 dip->prev = dip->next = AUDIO_MIXER_LAST;
885 dip->un.v.num_channels = 2;
886 strcpy(dip->un.v.units.name, AudioNvolume);
887 return 0;
888
889 #if NSGSMIX > 0
890 case AWACS_BASS:
891 if (sc->sc_sgsmix == NULL)
892 return ENXIO;
893 dip->mixer_class = AWACS_MONITOR_CLASS;
894 strcpy(dip->label.name, AudioNbass);
895 dip->type = AUDIO_MIXER_VALUE;
896 dip->prev = dip->next = AUDIO_MIXER_LAST;
897 dip->un.v.num_channels = 1;
898 strcpy(dip->un.v.units.name, AudioNbass);
899 return 0;
900
901 case AWACS_TREBLE:
902 if (sc->sc_sgsmix == NULL)
903 return ENXIO;
904 dip->mixer_class = AWACS_MONITOR_CLASS;
905 strcpy(dip->label.name, AudioNtreble);
906 dip->type = AUDIO_MIXER_VALUE;
907 dip->prev = dip->next = AUDIO_MIXER_LAST;
908 dip->un.v.num_channels = 1;
909 strcpy(dip->un.v.units.name, AudioNtreble);
910 return 0;
911 #endif
912
913 case AWACS_INPUT_SELECT:
914 dip->mixer_class = AWACS_RECORD_CLASS;
915 strcpy(dip->label.name, AudioNsource);
916 dip->type = AUDIO_MIXER_SET;
917 dip->prev = dip->next = AUDIO_MIXER_LAST;
918 dip->un.s.num_mem = 3;
919 strcpy(dip->un.s.member[0].label.name, AudioNcd);
920 dip->un.s.member[0].mask = 1 << 0;
921 strcpy(dip->un.s.member[1].label.name, AudioNmicrophone);
922 dip->un.s.member[1].mask = 1 << 1;
923 strcpy(dip->un.s.member[2].label.name, AudioNline);
924 dip->un.s.member[2].mask = 1 << 2;
925 return 0;
926
927 case AWACS_VOL_INPUT:
928 dip->mixer_class = AWACS_RECORD_CLASS;
929 strcpy(dip->label.name, AudioNrecord);
930 dip->type = AUDIO_MIXER_VALUE;
931 dip->prev = dip->next = AUDIO_MIXER_LAST;
932 dip->un.v.num_channels = 2;
933 strcpy(dip->un.v.units.name, AudioNvolume);
934 return 0;
935
936 case AWACS_MONITOR_CLASS:
937 dip->mixer_class = AWACS_MONITOR_CLASS;
938 strcpy(dip->label.name, AudioCmonitor);
939 dip->type = AUDIO_MIXER_CLASS;
940 dip->next = dip->prev = AUDIO_MIXER_LAST;
941 return 0;
942
943 case AWACS_OUTPUT_CLASS:
944 dip->mixer_class = AWACS_OUTPUT_CLASS;
945 strcpy(dip->label.name, AudioCoutputs);
946 dip->type = AUDIO_MIXER_CLASS;
947 dip->next = dip->prev = AUDIO_MIXER_LAST;
948 return 0;
949
950 case AWACS_RECORD_CLASS:
951 dip->mixer_class = AWACS_RECORD_CLASS;
952 strcpy(dip->label.name, AudioCrecord);
953 dip->type = AUDIO_MIXER_CLASS;
954 dip->next = dip->prev = AUDIO_MIXER_LAST;
955 return 0;
956 }
957
958 return ENXIO;
959 }
960
961 size_t
962 awacs_round_buffersize(void *h, int dir, size_t size)
963 {
964
965 if (size > 65536)
966 size = 65536;
967 return size;
968 }
969
970 paddr_t
971 awacs_mappage(void *h, void *mem, off_t off, int prot)
972 {
973
974 if (off < 0)
975 return -1;
976 return -1; /* XXX */
977 }
978
979 int
980 awacs_get_props(void *h)
981 {
982 return AUDIO_PROP_FULLDUPLEX /* | AUDIO_PROP_MMAP */;
983 }
984
985 int
986 awacs_trigger_output(void *h, void *start, void *end, int bsize,
987 void (*intr)(void *), void *arg,
988 const audio_params_t *param)
989 {
990 struct awacs_softc *sc;
991 struct dbdma_command *cmd;
992 vaddr_t va;
993 int i, len, intmode;
994
995 DPRINTF("trigger_output %p %p 0x%x\n", start, end, bsize);
996 sc = h;
997 cmd = sc->sc_odmacmd;
998 sc->sc_ointr = intr;
999 sc->sc_oarg = arg;
1000 sc->sc_opages = ((char *)end - (char *)start) / PAGE_SIZE;
1001
1002 #ifdef DIAGNOSTIC
1003 if (sc->sc_opages > 16)
1004 panic("awacs_trigger_output");
1005 #endif
1006
1007 va = (vaddr_t)start;
1008 len = 0;
1009 for (i = sc->sc_opages; i > 0; i--) {
1010 len += PAGE_SIZE;
1011 if (len < bsize)
1012 intmode = DBDMA_INT_NEVER;
1013 else {
1014 len = 0;
1015 intmode = DBDMA_INT_ALWAYS;
1016 }
1017
1018 DBDMA_BUILD(cmd, DBDMA_CMD_OUT_MORE, 0, PAGE_SIZE, vtophys(va),
1019 intmode, DBDMA_WAIT_NEVER, DBDMA_BRANCH_NEVER);
1020 va += PAGE_SIZE;
1021 cmd++;
1022 }
1023
1024 DBDMA_BUILD(cmd, DBDMA_CMD_NOP, 0, 0, 0,
1025 DBDMA_INT_NEVER, DBDMA_WAIT_NEVER, DBDMA_BRANCH_ALWAYS);
1026 dbdma_st32(&cmd->d_cmddep, vtophys((vaddr_t)sc->sc_odmacmd));
1027
1028 dbdma_start(sc->sc_odma, sc->sc_odmacmd);
1029
1030 return 0;
1031 }
1032
1033 int
1034 awacs_trigger_input(void *h, void *start, void *end, int bsize,
1035 void (*intr)(void *), void *arg,
1036 const audio_params_t *param)
1037 {
1038
1039 printf("awacs_trigger_input called\n");
1040 return 1;
1041 }
1042
1043 void
1044 awacs_select_output(struct awacs_softc *sc, int mask)
1045 {
1046
1047 #if NSGSMIX > 0
1048 if (sc->sc_sgsmix) {
1049 if (mask & OUTPUT_HEADPHONES) {
1050 /* mute speakers */
1051 sgsmix_set_speaker_vol(sc->sc_sgsmix, 0, 0);
1052 sgsmix_set_headphone_vol(sc->sc_sgsmix,
1053 sc->hph_l, sc->hph_r);
1054 }
1055 if (mask & OUTPUT_SPEAKER) {
1056 /* mute headphones */
1057 sgsmix_set_speaker_vol(sc->sc_sgsmix,
1058 sc->hph_l, sc->hph_r);
1059 sgsmix_set_headphone_vol(sc->sc_sgsmix, 0, 0);
1060 }
1061 } else {
1062 #endif
1063 sc->sc_codecctl1 |= AWACS_MUTE_SPEAKER | AWACS_MUTE_HEADPHONE;
1064 if (mask & 1 << 0)
1065 sc->sc_codecctl1 &= ~AWACS_MUTE_SPEAKER;
1066 if (mask & 1 << 1)
1067 sc->sc_codecctl1 &= ~AWACS_MUTE_HEADPHONE;
1068
1069 awacs_write_codec(sc, sc->sc_codecctl1);
1070 #if NSGSMIX > 0
1071 }
1072 #endif
1073 sc->sc_output_mask = mask;
1074 }
1075
1076 void
1077 awacs_set_speaker_volume(struct awacs_softc *sc, int left, int right)
1078 {
1079
1080 #if NSGSMIX > 0
1081 if (sc->sc_sgsmix) {
1082 if (sc->sc_output_mask & OUTPUT_SPEAKER)
1083 sgsmix_set_speaker_vol(sc->sc_sgsmix, left, right);
1084 } else
1085 #endif
1086 {
1087 int lval;
1088 int rval;
1089
1090 lval = 15 - (left & 0xff) / 16;
1091 rval = 15 - (right & 0xff) / 16;
1092 DPRINTF("speaker_volume %d %d\n", lval, rval);
1093
1094 sc->sc_codecctl4 &= ~0x3cf;
1095 sc->sc_codecctl4 |= (lval << 6) | rval;
1096 awacs_write_codec(sc, sc->sc_codecctl4);
1097 }
1098 sc->spk_l = left;
1099 sc->spk_r = right;
1100 }
1101
1102 void
1103 awacs_set_ext_volume(struct awacs_softc *sc, int left, int right)
1104 {
1105
1106 #if NSGSMIX > 0
1107 if (sc->sc_sgsmix) {
1108 if (sc->sc_output_mask & OUTPUT_HEADPHONES)
1109 sgsmix_set_headphone_vol(sc->sc_sgsmix, left, right);
1110 } else
1111 #endif
1112 {
1113 int lval;
1114 int rval;
1115
1116 lval = 15 - (left & 0xff) / 16;
1117 rval = 15 - (right & 0xff) / 16;
1118 DPRINTF("ext_volume %d %d\n", lval, rval);
1119
1120 sc->sc_codecctl2 &= ~0x3cf;
1121 sc->sc_codecctl2 |= (lval << 6) | rval;
1122 awacs_write_codec(sc, sc->sc_codecctl2);
1123 }
1124 sc->hph_l = left;
1125 sc->hph_r = right;
1126 }
1127
1128 #if NSGSMIX > 0
1129 static void
1130 awacs_set_bass(struct awacs_softc *sc, int bass)
1131 {
1132
1133 if (sc->sc_bass == bass)
1134 return;
1135
1136 sc->sc_bass = bass;
1137 if (sc->sc_sgsmix)
1138 sgsmix_set_bass_treble(sc->sc_sgsmix, sc->sc_bass, sc->sc_treble);
1139 }
1140
1141 static void
1142 awacs_set_treble(struct awacs_softc *sc, int treble)
1143 {
1144
1145 if (sc->sc_treble == treble)
1146 return;
1147
1148 sc->sc_treble = treble;
1149 if (sc->sc_sgsmix)
1150 sgsmix_set_bass_treble(sc->sc_sgsmix, sc->sc_bass, sc->sc_treble);
1151 }
1152 #endif
1153
1154 void
1155 awacs_set_loopthrough_volume(struct awacs_softc *sc, int left, int right)
1156 {
1157 int lval;
1158 int rval;
1159
1160 lval = 15 - (left & 0xff) / 16;
1161 rval = 15 - (right & 0xff) / 16;
1162 DPRINTF("loopthrough_volume %d %d\n", lval, rval);
1163
1164 sc->sc_codecctl5 &= ~0x3cf;
1165 sc->sc_codecctl5 |= (lval << 6) | rval;
1166 awacs_write_codec(sc, sc->sc_codecctl5);
1167 }
1168
1169 int
1170 awacs_set_rate(struct awacs_softc *sc, const audio_params_t *p)
1171 {
1172 int c;
1173
1174 switch (p->sample_rate) {
1175 case 44100:
1176 c = AWACS_RATE_44100;
1177 break;
1178 case 29400:
1179 c = AWACS_RATE_29400;
1180 break;
1181 case 22050:
1182 c = AWACS_RATE_22050;
1183 break;
1184 case 17640:
1185 c = AWACS_RATE_17640;
1186 break;
1187 case 14700:
1188 c = AWACS_RATE_14700;
1189 break;
1190 case 11025:
1191 c = AWACS_RATE_11025;
1192 break;
1193 case 8820:
1194 c = AWACS_RATE_8820;
1195 break;
1196 case 7350:
1197 c = AWACS_RATE_7350;
1198 break;
1199 default:
1200 return -1;
1201 }
1202
1203 sc->sc_soundctl &= ~AWACS_RATE_MASK;
1204 sc->sc_soundctl |= c;
1205 awacs_write_reg(sc, AWACS_SOUND_CTRL, sc->sc_soundctl);
1206
1207 return 0;
1208 }
1209