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