Annotation of sys/arch/hppa/gsc/harmony.c, Revision 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