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