Annotation of sys/arch/zaurus/dev/zaurus_audio.c, Revision 1.1
1.1 ! nbrk 1: /* $OpenBSD: zaurus_audio.c,v 1.8 2005/08/18 13:23:02 robert Exp $ */
! 2:
! 3: /*
! 4: * Copyright (c) 2005 Christopher Pascoe <pascoe@openbsd.org>
! 5: *
! 6: * Permission to use, copy, modify, and distribute this software for any
! 7: * purpose with or without fee is hereby granted, provided that the above
! 8: * copyright notice and this permission notice appear in all copies.
! 9: *
! 10: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
! 11: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
! 12: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
! 13: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
! 14: * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
! 15: * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
! 16: * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
! 17: */
! 18:
! 19: /*
! 20: * TODO:
! 21: * - powerhooks (currently only works until first suspend)
! 22: * - record support
! 23: */
! 24:
! 25: #include <sys/param.h>
! 26: #include <sys/systm.h>
! 27: #include <sys/timeout.h>
! 28: #include <sys/device.h>
! 29: #include <sys/malloc.h>
! 30: #include <sys/kernel.h>
! 31: #include <sys/audioio.h>
! 32:
! 33: #include <machine/intr.h>
! 34: #include <machine/bus.h>
! 35:
! 36: #include <arm/xscale/pxa2x0reg.h>
! 37: #include <arm/xscale/pxa2x0var.h>
! 38: #include <arm/xscale/pxa2x0_i2c.h>
! 39: #include <arm/xscale/pxa2x0_i2s.h>
! 40: #include <arm/xscale/pxa2x0_dmac.h>
! 41: #include <arm/xscale/pxa2x0_gpio.h>
! 42:
! 43: #include <zaurus/dev/zaurus_scoopvar.h>
! 44: #include <dev/i2c/wm8750reg.h>
! 45:
! 46: #include <dev/audio_if.h>
! 47: #include <dev/mulaw.h>
! 48: #include <dev/auconv.h>
! 49:
! 50: #define WM8750_ADDRESS 0x1B
! 51: #define SPKR_VOLUME 112
! 52:
! 53: #define wm8750_write(sc, reg, val) pxa2x0_i2c_write_2(&sc->sc_i2c, \
! 54: WM8750_ADDRESS, (((reg) << 9) | ((val) & 0x1ff)))
! 55:
! 56: int zaudio_match(struct device *, void *, void *);
! 57: void zaudio_attach(struct device *, struct device *, void *);
! 58: int zaudio_detach(struct device *, int);
! 59: void zaudio_power(int, void *);
! 60:
! 61: #define ZAUDIO_OP_SPKR 0
! 62: #define ZAUDIO_OP_HP 1
! 63:
! 64: #define ZAUDIO_JACK_STATE_OUT 0
! 65: #define ZAUDIO_JACK_STATE_IN 1
! 66: #define ZAUDIO_JACK_STATE_INS 2
! 67: #define ZAUDIO_JACK_STATE_REM 3
! 68:
! 69: /* GPIO pins */
! 70: #define GPIO_HP_IN_C3000 116
! 71:
! 72: struct zaudio_volume {
! 73: u_int8_t left;
! 74: u_int8_t right;
! 75: };
! 76:
! 77: struct zaudio_softc {
! 78: struct device sc_dev;
! 79:
! 80: /* i2s device softc */
! 81: /* NB: pxa2x0_i2s requires this to be the second struct member */
! 82: struct pxa2x0_i2s_softc sc_i2s;
! 83:
! 84: /* i2c device softc */
! 85: struct pxa2x0_i2c_softc sc_i2c;
! 86:
! 87: void *sc_powerhook;
! 88: int sc_playing;
! 89:
! 90: struct zaudio_volume sc_volume[2];
! 91: char sc_unmute[2];
! 92:
! 93: int sc_state;
! 94: int sc_icount;
! 95: struct timeout sc_to;
! 96: };
! 97:
! 98: struct cfattach zaudio_ca = {
! 99: sizeof(struct zaudio_softc), zaudio_match, zaudio_attach,
! 100: zaudio_detach
! 101: };
! 102:
! 103: struct cfdriver zaudio_cd = {
! 104: NULL, "zaudio", DV_DULL
! 105: };
! 106:
! 107: struct audio_device wm8750_device = {
! 108: "WM8750",
! 109: "1.0",
! 110: "wm"
! 111: };
! 112:
! 113: void zaudio_init(struct zaudio_softc *);
! 114: int zaudio_jack_intr(void *);
! 115: void zaudio_jack(void *);
! 116: void zaudio_standby(struct zaudio_softc *);
! 117: void zaudio_update_volume(struct zaudio_softc *, int);
! 118: void zaudio_update_mutes(struct zaudio_softc *);
! 119: void zaudio_play_setup(struct zaudio_softc *);
! 120: int zaudio_open(void *, int);
! 121: void zaudio_close(void *);
! 122: int zaudio_query_encoding(void *, struct audio_encoding *);
! 123: int zaudio_set_params(void *, int, int, struct audio_params *,
! 124: struct audio_params *);
! 125: int zaudio_halt_output(void *);
! 126: int zaudio_halt_input(void *);
! 127: int zaudio_getdev(void *, struct audio_device *);
! 128: int zaudio_set_port(void *, struct mixer_ctrl *);
! 129: int zaudio_get_port(void *, struct mixer_ctrl *);
! 130: int zaudio_query_devinfo(void *, struct mixer_devinfo *);
! 131: int zaudio_get_props(void *);
! 132: int zaudio_start_output(void *, void *, int, void (*)(void *), void *);
! 133: int zaudio_start_input(void *, void *, int, void (*)(void *), void *);
! 134:
! 135: struct audio_hw_if wm8750_hw_if = {
! 136: zaudio_open,
! 137: zaudio_close,
! 138: NULL /* zaudio_drain */,
! 139: zaudio_query_encoding,
! 140: zaudio_set_params,
! 141: pxa2x0_i2s_round_blocksize,
! 142: NULL /* zaudio_commit_settings */,
! 143: NULL /* zaudio_init_output */,
! 144: NULL /* zaudio_init_input */,
! 145: zaudio_start_output,
! 146: zaudio_start_input,
! 147: zaudio_halt_output,
! 148: zaudio_halt_input,
! 149: NULL /* zaudio_speaker_ctl */,
! 150: zaudio_getdev,
! 151: NULL /* zaudio_setfd */,
! 152: zaudio_set_port,
! 153: zaudio_get_port,
! 154: zaudio_query_devinfo,
! 155: pxa2x0_i2s_allocm,
! 156: pxa2x0_i2s_freem,
! 157: pxa2x0_i2s_round_buffersize,
! 158: pxa2x0_i2s_mappage,
! 159: zaudio_get_props,
! 160: NULL /* zaudio_trigger_output */,
! 161: NULL /* zaudio_trigger_input */
! 162: };
! 163:
! 164: static const unsigned short playback_registers[][2] = {
! 165: /* Unmute DAC */
! 166: { ADCDACCTL_REG, 0x000 },
! 167:
! 168: /* 16 bit audio words */
! 169: { AUDINT_REG, AUDINT_SET_FORMAT(2) },
! 170:
! 171: /* Enable thermal protection, power */
! 172: { ADCTL1_REG, ADCTL1_TSDEN | ADCTL1_SET_VSEL(3) },
! 173:
! 174: /* Enable speaker driver, DAC oversampling */
! 175: { ADCTL2_REG, ADCTL2_ROUT2INV | ADCTL2_DACOSR },
! 176:
! 177: /* Set DAC voltage references */
! 178: { PWRMGMT1_REG, PWRMGMT1_SET_VMIDSEL(1) | PWRMGMT1_VREF },
! 179:
! 180: /* Direct DACs to output mixers */
! 181: { LOUTMIX1_REG, LOUTMIX1_LD2LO },
! 182: { ROUTMIX2_REG, ROUTMIX2_RD2RO },
! 183:
! 184: /* End of list */
! 185: { 0xffff, 0xffff }
! 186: };
! 187:
! 188: int
! 189: zaudio_match(struct device *parent, void *match, void *aux)
! 190: {
! 191: return (1);
! 192: }
! 193:
! 194: void
! 195: zaudio_attach(struct device *parent, struct device *self, void *aux)
! 196: {
! 197: struct zaudio_softc *sc = (struct zaudio_softc *)self;
! 198: struct pxaip_attach_args *pxa = aux;
! 199: int err;
! 200:
! 201: sc->sc_powerhook = powerhook_establish(zaudio_power, sc);
! 202: if (sc->sc_powerhook == NULL) {
! 203: printf(": unable to establish powerhook\n");
! 204: return;
! 205: }
! 206:
! 207: sc->sc_i2s.sc_iot = pxa->pxa_iot;
! 208: sc->sc_i2s.sc_dmat = pxa->pxa_dmat;
! 209: sc->sc_i2s.sc_size = PXA2X0_I2S_SIZE;
! 210: if (pxa2x0_i2s_attach_sub(&sc->sc_i2s)) {
! 211: printf(": unable to attach I2S\n");
! 212: goto fail_i2s;
! 213: }
! 214:
! 215: sc->sc_i2c.sc_iot = pxa->pxa_iot;
! 216: sc->sc_i2c.sc_size = PXA2X0_I2C_SIZE;
! 217: if (pxa2x0_i2c_attach_sub(&sc->sc_i2c)) {
! 218: printf(": unable to attach I2C\n");
! 219: goto fail_i2c;
! 220: }
! 221:
! 222: /* Check for an I2C response from the wm8750 */
! 223: pxa2x0_i2c_open(&sc->sc_i2c);
! 224: err = wm8750_write(sc, RESET_REG, 0);
! 225: pxa2x0_i2c_close(&sc->sc_i2c);
! 226:
! 227: if (err) {
! 228: printf(": codec failed to respond\n");
! 229: goto fail_probe;
! 230: }
! 231: delay(100);
! 232:
! 233: /* Speaker on, headphones off by default. */
! 234: sc->sc_volume[ZAUDIO_OP_SPKR].left = 240;
! 235: sc->sc_unmute[ZAUDIO_OP_SPKR] = 1;
! 236: sc->sc_volume[ZAUDIO_OP_HP].left = 180;
! 237: sc->sc_volume[ZAUDIO_OP_HP].right = 180;
! 238: sc->sc_unmute[ZAUDIO_OP_HP] = 0;
! 239:
! 240: /* Configure headphone jack state change handling. */
! 241: timeout_set(&sc->sc_to, zaudio_jack, sc);
! 242: pxa2x0_gpio_set_function(GPIO_HP_IN_C3000, GPIO_IN);
! 243: (void)pxa2x0_gpio_intr_establish(GPIO_HP_IN_C3000,
! 244: IST_EDGE_BOTH, IPL_BIO, zaudio_jack_intr, sc, "hpjk");
! 245:
! 246: zaudio_init(sc);
! 247:
! 248: printf(": I2C, I2S, WM8750 Audio\n");
! 249:
! 250: audio_attach_mi(&wm8750_hw_if, sc, &sc->sc_dev);
! 251:
! 252: return;
! 253:
! 254: fail_probe:
! 255: pxa2x0_i2c_detach_sub(&sc->sc_i2c);
! 256: fail_i2c:
! 257: pxa2x0_i2s_detach_sub(&sc->sc_i2s);
! 258: fail_i2s:
! 259: powerhook_disestablish(sc->sc_powerhook);
! 260: }
! 261:
! 262: int
! 263: zaudio_detach(struct device *self, int flags)
! 264: {
! 265: struct zaudio_softc *sc = (struct zaudio_softc *)self;
! 266:
! 267: if (sc->sc_powerhook != NULL) {
! 268: powerhook_disestablish(sc->sc_powerhook);
! 269: sc->sc_powerhook = NULL;
! 270: }
! 271:
! 272: pxa2x0_i2c_detach_sub(&sc->sc_i2c);
! 273: pxa2x0_i2s_detach_sub(&sc->sc_i2s);
! 274:
! 275: return (0);
! 276: }
! 277:
! 278: void
! 279: zaudio_power(int why, void *arg)
! 280: {
! 281: struct zaudio_softc *sc = arg;
! 282:
! 283: switch (why) {
! 284: case PWR_STANDBY:
! 285: case PWR_SUSPEND:
! 286: timeout_del(&sc->sc_to);
! 287: zaudio_standby(sc);
! 288: break;
! 289:
! 290: case PWR_RESUME:
! 291: pxa2x0_i2s_init(&sc->sc_i2s);
! 292: pxa2x0_i2c_init(&sc->sc_i2c);
! 293: zaudio_init(sc);
! 294: break;
! 295: }
! 296: }
! 297:
! 298: void
! 299: zaudio_init(struct zaudio_softc *sc)
! 300: {
! 301: pxa2x0_i2c_open(&sc->sc_i2c);
! 302:
! 303: /* Reset the codec */
! 304: wm8750_write(sc, RESET_REG, 0);
! 305: delay(100);
! 306:
! 307: /* Switch to standby power only */
! 308: wm8750_write(sc, PWRMGMT1_REG, PWRMGMT1_SET_VMIDSEL(2));
! 309: wm8750_write(sc, PWRMGMT2_REG, 0);
! 310:
! 311: /* Configure digital interface for I2S */
! 312: wm8750_write(sc, AUDINT_REG, AUDINT_SET_FORMAT(2));
! 313:
! 314: /* Initialise volume levels */
! 315: zaudio_update_volume(sc, ZAUDIO_OP_SPKR);
! 316: zaudio_update_volume(sc, ZAUDIO_OP_HP);
! 317: scoop_set_headphone(0);
! 318:
! 319: pxa2x0_i2c_close(&sc->sc_i2c);
! 320:
! 321: /* Assume that the jack state has changed. */
! 322: zaudio_jack(sc);
! 323:
! 324: }
! 325:
! 326: int
! 327: zaudio_jack_intr(void *v)
! 328: {
! 329: struct zaudio_softc *sc = v;
! 330:
! 331: if (!timeout_triggered(&sc->sc_to))
! 332: zaudio_jack(sc);
! 333:
! 334: return (1);
! 335: }
! 336:
! 337: void
! 338: zaudio_jack(void *v)
! 339: {
! 340: struct zaudio_softc *sc = v;
! 341:
! 342: switch (sc->sc_state) {
! 343: case ZAUDIO_JACK_STATE_OUT:
! 344: if (pxa2x0_gpio_get_bit(GPIO_HP_IN_C3000)) {
! 345: sc->sc_state = ZAUDIO_JACK_STATE_INS;
! 346: sc->sc_icount = 0;
! 347: }
! 348: break;
! 349: case ZAUDIO_JACK_STATE_INS:
! 350: if (sc->sc_icount++ > 2) {
! 351: if (pxa2x0_gpio_get_bit(GPIO_HP_IN_C3000)) {
! 352: sc->sc_state = ZAUDIO_JACK_STATE_IN;
! 353: sc->sc_unmute[ZAUDIO_OP_SPKR] = 0;
! 354: sc->sc_unmute[ZAUDIO_OP_HP] = 1;
! 355: goto update_mutes;
! 356: } else
! 357: sc->sc_state = ZAUDIO_JACK_STATE_OUT;
! 358: }
! 359: break;
! 360: case ZAUDIO_JACK_STATE_IN:
! 361: if (!pxa2x0_gpio_get_bit(GPIO_HP_IN_C3000)) {
! 362: sc->sc_state = ZAUDIO_JACK_STATE_REM;
! 363: sc->sc_icount = 0;
! 364: }
! 365: break;
! 366: case ZAUDIO_JACK_STATE_REM:
! 367: if (sc->sc_icount++ > 2) {
! 368: if (!pxa2x0_gpio_get_bit(GPIO_HP_IN_C3000)) {
! 369: sc->sc_state = ZAUDIO_JACK_STATE_OUT;
! 370: sc->sc_unmute[ZAUDIO_OP_SPKR] = 1;
! 371: sc->sc_unmute[ZAUDIO_OP_HP] = 0;
! 372: goto update_mutes;
! 373: } else
! 374: sc->sc_state = ZAUDIO_JACK_STATE_IN;
! 375: }
! 376: break;
! 377: }
! 378:
! 379: timeout_add(&sc->sc_to, hz/4);
! 380: return;
! 381:
! 382: update_mutes:
! 383: timeout_del(&sc->sc_to);
! 384:
! 385: if (sc->sc_playing) {
! 386: pxa2x0_i2c_open(&sc->sc_i2c);
! 387: zaudio_update_mutes(sc);
! 388: pxa2x0_i2c_close(&sc->sc_i2c);
! 389: }
! 390: }
! 391:
! 392: void
! 393: zaudio_standby(struct zaudio_softc *sc)
! 394: {
! 395: pxa2x0_i2c_open(&sc->sc_i2c);
! 396:
! 397: /* Switch codec to standby power only */
! 398: wm8750_write(sc, PWRMGMT1_REG, PWRMGMT1_SET_VMIDSEL(2));
! 399: wm8750_write(sc, PWRMGMT2_REG, 0);
! 400:
! 401: scoop_set_headphone(0);
! 402:
! 403: pxa2x0_i2c_close(&sc->sc_i2c);
! 404: }
! 405:
! 406: void
! 407: zaudio_update_volume(struct zaudio_softc *sc, int output)
! 408: {
! 409: switch(output) {
! 410: case ZAUDIO_OP_SPKR:
! 411: wm8750_write(sc, LOUT2VOL_REG, LOUT2VOL_LO2VU | LOUT2VOL_LO2ZC |
! 412: LOUT2VOL_SET_LOUT2VOL(sc->sc_volume[ZAUDIO_OP_SPKR
! 413: ].left >> 1));
! 414: wm8750_write(sc, ROUT2VOL_REG, ROUT2VOL_RO2VU | ROUT2VOL_RO2ZC |
! 415: ROUT2VOL_SET_ROUT2VOL(sc->sc_volume[ZAUDIO_OP_SPKR
! 416: ].left >> 1));
! 417: break;
! 418: case ZAUDIO_OP_HP:
! 419: wm8750_write(sc, LOUT1VOL_REG, LOUT1VOL_LO1VU | LOUT1VOL_LO1ZC |
! 420: LOUT1VOL_SET_LOUT1VOL(sc->sc_volume[ZAUDIO_OP_HP
! 421: ].left >> 1));
! 422: wm8750_write(sc, ROUT1VOL_REG, ROUT1VOL_RO1VU | ROUT1VOL_RO1ZC |
! 423: ROUT1VOL_SET_ROUT1VOL(sc->sc_volume[ZAUDIO_OP_HP
! 424: ].right >> 1));
! 425: break;
! 426: }
! 427: }
! 428:
! 429: void
! 430: zaudio_update_mutes(struct zaudio_softc *sc)
! 431: {
! 432: unsigned short val;
! 433:
! 434: val = PWRMGMT2_DACL | PWRMGMT2_DACR;
! 435:
! 436: if (sc->sc_unmute[ZAUDIO_OP_SPKR])
! 437: val |= PWRMGMT2_LOUT2 | PWRMGMT2_ROUT2;
! 438:
! 439: if (sc->sc_unmute[ZAUDIO_OP_HP])
! 440: val |= PWRMGMT2_LOUT1 | PWRMGMT2_ROUT1;
! 441:
! 442: wm8750_write(sc, PWRMGMT2_REG, val);
! 443:
! 444: scoop_set_headphone(sc->sc_unmute[ZAUDIO_OP_HP]);
! 445: }
! 446:
! 447: void
! 448: zaudio_play_setup(struct zaudio_softc *sc)
! 449: {
! 450: int i = 0;
! 451:
! 452: pxa2x0_i2c_open(&sc->sc_i2c);
! 453:
! 454: /* Program the codec with playback settings */
! 455: while (playback_registers[i][0] != 0xffff) {
! 456: wm8750_write(sc, playback_registers[i][0],
! 457: playback_registers[i][1]);
! 458: i++;
! 459: }
! 460: zaudio_update_mutes(sc);
! 461:
! 462: pxa2x0_i2c_close(&sc->sc_i2c);
! 463: }
! 464:
! 465: int
! 466: zaudio_open(void *hdl, int flags)
! 467: {
! 468: struct zaudio_softc *sc = hdl;
! 469:
! 470: /* Power on the I2S bus and codec */
! 471: pxa2x0_i2s_open(&sc->sc_i2s);
! 472:
! 473: return 0;
! 474: }
! 475:
! 476: void
! 477: zaudio_close(void *hdl)
! 478: {
! 479: struct zaudio_softc *sc = hdl;
! 480:
! 481: /* Power off the I2S bus and codec */
! 482: pxa2x0_i2s_close(&sc->sc_i2s);
! 483: }
! 484:
! 485: int
! 486: zaudio_query_encoding(void *hdl, struct audio_encoding *aep)
! 487: {
! 488: switch (aep->index) {
! 489: case 0:
! 490: strlcpy(aep->name, AudioEulinear, sizeof(aep->name));
! 491: aep->encoding = AUDIO_ENCODING_ULINEAR;
! 492: aep->precision = 8;
! 493: aep->flags = 0;
! 494: return (0);
! 495: case 1:
! 496: strlcpy(aep->name, AudioEmulaw, sizeof(aep->name));
! 497: aep->encoding = AUDIO_ENCODING_ULAW;
! 498: aep->precision = 8;
! 499: aep->flags = AUDIO_ENCODINGFLAG_EMULATED;
! 500: return (0);
! 501: case 2:
! 502: strlcpy(aep->name, AudioEalaw, sizeof(aep->name));
! 503: aep->encoding = AUDIO_ENCODING_ALAW;
! 504: aep->precision = 8;
! 505: aep->flags = AUDIO_ENCODINGFLAG_EMULATED;
! 506: return (0);
! 507: case 3:
! 508: strlcpy(aep->name, AudioEslinear, sizeof(aep->name));
! 509: aep->encoding = AUDIO_ENCODING_SLINEAR;
! 510: aep->precision = 8;
! 511: aep->flags = AUDIO_ENCODINGFLAG_EMULATED;
! 512: return (0);
! 513: case 4:
! 514: strlcpy(aep->name, AudioEslinear_le, sizeof(aep->name));
! 515: aep->encoding = AUDIO_ENCODING_SLINEAR_LE;
! 516: aep->precision = 16;
! 517: aep->flags = 0;
! 518: return (0);
! 519: case 5:
! 520: strlcpy(aep->name, AudioEulinear_le, sizeof(aep->name));
! 521: aep->encoding = AUDIO_ENCODING_ULINEAR_LE;
! 522: aep->precision = 16;
! 523: aep->flags = AUDIO_ENCODINGFLAG_EMULATED;
! 524: return (0);
! 525: case 6:
! 526: strlcpy(aep->name, AudioEslinear_be, sizeof(aep->name));
! 527: aep->encoding = AUDIO_ENCODING_SLINEAR_BE;
! 528: aep->precision = 16;
! 529: aep->flags = AUDIO_ENCODINGFLAG_EMULATED;
! 530: return (0);
! 531: case 7:
! 532: strlcpy(aep->name, AudioEulinear_be, sizeof(aep->name));
! 533: aep->encoding = AUDIO_ENCODING_ULINEAR_BE;
! 534: aep->precision = 16;
! 535: aep->flags = AUDIO_ENCODINGFLAG_EMULATED;
! 536: return (0);
! 537: default:
! 538: return (EINVAL);
! 539: }
! 540: }
! 541:
! 542: int
! 543: zaudio_set_params(void *hdl, int setmode, int usemode,
! 544: struct audio_params *play, struct audio_params *rec)
! 545: {
! 546: struct zaudio_softc *sc = hdl;
! 547:
! 548: if (setmode & AUMODE_PLAY) {
! 549: play->factor = 1;
! 550: play->sw_code = NULL;
! 551: switch(play->encoding) {
! 552: case AUDIO_ENCODING_ULAW:
! 553: switch (play->channels) {
! 554: case 1:
! 555: play->factor = 4;
! 556: play->sw_code = mulaw_to_slinear16_mts;
! 557: break;
! 558: case 2:
! 559: play->factor = 2;
! 560: play->sw_code = mulaw_to_slinear16;
! 561: break;
! 562: default:
! 563: return (EINVAL);
! 564: }
! 565: break;
! 566: case AUDIO_ENCODING_SLINEAR_LE:
! 567: switch (play->precision) {
! 568: case 8:
! 569: switch (play->channels) {
! 570: case 1:
! 571: play->factor = 4;
! 572: play->sw_code = linear8_to_linear16_mts;
! 573: break;
! 574: case 2:
! 575: play->factor = 2;
! 576: play->sw_code = linear8_to_linear16;
! 577: break;
! 578: default:
! 579: return (EINVAL);
! 580: }
! 581: break;
! 582: case 16:
! 583: switch (play->channels) {
! 584: case 1:
! 585: play->factor = 2;
! 586: play->sw_code = noswap_bytes_mts;
! 587: break;
! 588: case 2:
! 589: break;
! 590: default:
! 591: return (EINVAL);
! 592: }
! 593: break;
! 594: default:
! 595: return (EINVAL);
! 596: }
! 597: break;
! 598: case AUDIO_ENCODING_ULINEAR_LE:
! 599: switch (play->precision) {
! 600: case 8:
! 601: switch (play->channels) {
! 602: case 1:
! 603: play->factor = 4;
! 604: play->sw_code =
! 605: ulinear8_to_linear16_mts;
! 606: break;
! 607: case 2:
! 608: play->factor = 2;
! 609: play->sw_code = ulinear8_to_linear16;
! 610: break;
! 611: default:
! 612: return (EINVAL);
! 613: }
! 614: break;
! 615: case 16:
! 616: switch (play->channels) {
! 617: case 1:
! 618: play->factor = 2;
! 619: play->sw_code = change_sign16_mts;
! 620: break;
! 621: case 2:
! 622: play->sw_code = change_sign16;
! 623: break;
! 624: default:
! 625: return (EINVAL);
! 626: }
! 627: break;
! 628: default:
! 629: return (EINVAL);
! 630: }
! 631: break;
! 632: case AUDIO_ENCODING_ALAW:
! 633: switch (play->channels) {
! 634: case 1:
! 635: play->factor = 4;
! 636: play->sw_code = alaw_to_slinear16_mts;
! 637: case 2:
! 638: play->factor = 2;
! 639: play->sw_code = alaw_to_slinear16;
! 640: default:
! 641: return (EINVAL);
! 642: }
! 643: break;
! 644: case AUDIO_ENCODING_SLINEAR_BE:
! 645: switch (play->precision) {
! 646: case 8:
! 647: switch (play->channels) {
! 648: case 1:
! 649: play->factor = 4;
! 650: play->sw_code =
! 651: linear8_to_linear16_mts;
! 652: break;
! 653: case 2:
! 654: play->factor = 2;
! 655: play->sw_code = linear8_to_linear16;
! 656: break;
! 657: default:
! 658: return (EINVAL);
! 659: }
! 660: break;
! 661: case 16:
! 662: switch (play->channels) {
! 663: case 1:
! 664: play->factor = 2;
! 665: play->sw_code = swap_bytes_mts;
! 666: break;
! 667: case 2:
! 668: play->sw_code = swap_bytes;
! 669: break;
! 670: default:
! 671: return (EINVAL);
! 672: }
! 673: break;
! 674: default:
! 675: return (EINVAL);
! 676: }
! 677: break;
! 678: case AUDIO_ENCODING_ULINEAR_BE:
! 679: switch (play->precision) {
! 680: case 8:
! 681: switch (play->channels) {
! 682: case 1:
! 683: play->factor = 4;
! 684: play->sw_code =
! 685: ulinear8_to_linear16_mts;
! 686: break;
! 687: case 2:
! 688: play->factor = 2;
! 689: play->sw_code = ulinear8_to_linear16;
! 690: break;
! 691: default:
! 692: return (EINVAL);
! 693: }
! 694: break;
! 695: case 16:
! 696: switch (play->channels) {
! 697: case 1:
! 698: play->factor = 2;
! 699: play->sw_code =
! 700: change_sign16_swap_bytes_mts;
! 701: break;
! 702: case 2:
! 703: play->sw_code =
! 704: change_sign16_swap_bytes;
! 705: break;
! 706: default:
! 707: return (EINVAL);
! 708: }
! 709: break;
! 710: default:
! 711: return (EINVAL);
! 712: }
! 713: break;
! 714: default:
! 715: return (EINVAL);
! 716: }
! 717:
! 718: pxa2x0_i2s_setspeed(&sc->sc_i2s, &play->sample_rate);
! 719: }
! 720:
! 721: #if RECORD_XXX_NOT_YET
! 722: if (setmode & AUMODE_RECORD) {
! 723: rec->factor = 1;
! 724: rec->sw_code = NULL;
! 725: switch(rec->encoding) {
! 726: case AUDIO_ENCODING_ULAW:
! 727: rec->sw_code = ulinear8_to_mulaw;
! 728: break;
! 729: case AUDIO_ENCODING_SLINEAR_LE:
! 730: if (rec->precision == 8)
! 731: rec->sw_code = change_sign8;
! 732: break;
! 733: case AUDIO_ENCODING_ULINEAR_LE:
! 734: if (rec->precision == 16)
! 735: rec->sw_code = change_sign16;
! 736: break;
! 737: case AUDIO_ENCODING_ALAW:
! 738: rec->sw_code = ulinear8_to_alaw;
! 739: break;
! 740: case AUDIO_ENCODING_SLINEAR_BE:
! 741: if (rec->precision == 16)
! 742: rec->sw_code = swap_bytes;
! 743: else
! 744: rec->sw_code = change_sign8;
! 745: break;
! 746: case AUDIO_ENCODING_ULINEAR_BE:
! 747: if (rec->precision == 16)
! 748: rec->sw_code = swap_bytes_change_sign16;
! 749: break;
! 750: default:
! 751: return (EINVAL);
! 752: }
! 753:
! 754: pxa2x0_i2s_setspeed(sc, &rec->sample_rate);
! 755: }
! 756: #endif
! 757:
! 758: return (0);
! 759: }
! 760:
! 761: int
! 762: zaudio_halt_output(void *hdl)
! 763: {
! 764: struct zaudio_softc *sc = hdl;
! 765:
! 766: /* XXX forcibly stop output DMA? */
! 767:
! 768: zaudio_standby(sc);
! 769: sc->sc_playing = 0;
! 770:
! 771: return 0;
! 772: }
! 773:
! 774: int
! 775: zaudio_halt_input(void *hdl)
! 776: {
! 777: /* struct zaudio_softc *sc = hdl; */
! 778:
! 779: return 0;
! 780: }
! 781:
! 782: int
! 783: zaudio_getdev(void *hdl, struct audio_device *ret)
! 784: {
! 785: /* struct zaudio_softc *sc = hdl; */
! 786:
! 787: *ret = wm8750_device;
! 788: return 0;
! 789: }
! 790:
! 791: #define ZAUDIO_SPKR_LVL 0
! 792: #define ZAUDIO_SPKR_MUTE 1
! 793: #define ZAUDIO_HP_LVL 2
! 794: #define ZAUDIO_HP_MUTE 3
! 795: #define ZAUDIO_OUTPUT_CLASS 4
! 796:
! 797: int
! 798: zaudio_set_port(void *hdl, struct mixer_ctrl *mc)
! 799: {
! 800: struct zaudio_softc *sc = hdl;
! 801: int error = EINVAL, s;
! 802:
! 803: s = splbio();
! 804: pxa2x0_i2c_open(&sc->sc_i2c);
! 805:
! 806: switch (mc->dev) {
! 807: case ZAUDIO_SPKR_LVL:
! 808: if (mc->type != AUDIO_MIXER_VALUE)
! 809: break;
! 810: if (mc->un.value.num_channels == 1)
! 811: sc->sc_volume[ZAUDIO_OP_SPKR].left =
! 812: mc->un.value.level[AUDIO_MIXER_LEVEL_MONO];
! 813: else
! 814: break;
! 815: zaudio_update_volume(sc, ZAUDIO_OP_SPKR);
! 816: error = 0;
! 817: break;
! 818: case ZAUDIO_SPKR_MUTE:
! 819: if (mc->type != AUDIO_MIXER_ENUM)
! 820: break;
! 821: sc->sc_unmute[ZAUDIO_OP_SPKR] = mc->un.ord ? 1 : 0;
! 822: zaudio_update_mutes(sc);
! 823: error = 0;
! 824: break;
! 825: case ZAUDIO_HP_LVL:
! 826: if (mc->type != AUDIO_MIXER_VALUE)
! 827: break;
! 828: if (mc->un.value.num_channels == 1) {
! 829: sc->sc_volume[ZAUDIO_OP_HP].left =
! 830: mc->un.value.level[AUDIO_MIXER_LEVEL_MONO];
! 831: sc->sc_volume[ZAUDIO_OP_HP].right =
! 832: mc->un.value.level[AUDIO_MIXER_LEVEL_MONO];
! 833: } else if (mc->un.value.num_channels == 2) {
! 834: sc->sc_volume[ZAUDIO_OP_HP].left =
! 835: mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT];
! 836: sc->sc_volume[ZAUDIO_OP_HP].right =
! 837: mc->un.value.level[AUDIO_MIXER_LEVEL_RIGHT];
! 838: }
! 839: else
! 840: break;
! 841: zaudio_update_volume(sc, ZAUDIO_OP_HP);
! 842: error = 0;
! 843: break;
! 844: case ZAUDIO_HP_MUTE:
! 845: if (mc->type != AUDIO_MIXER_ENUM)
! 846: break;
! 847: sc->sc_unmute[ZAUDIO_OP_HP] = mc->un.ord ? 1 : 0;
! 848: zaudio_update_mutes(sc);
! 849: error = 0;
! 850: break;
! 851: }
! 852:
! 853: pxa2x0_i2c_close(&sc->sc_i2c);
! 854: splx(s);
! 855:
! 856: return error;
! 857: }
! 858:
! 859: int
! 860: zaudio_get_port(void *hdl, struct mixer_ctrl *mc)
! 861: {
! 862: struct zaudio_softc *sc = hdl;
! 863: int error = EINVAL;
! 864:
! 865: switch (mc->dev) {
! 866: case ZAUDIO_SPKR_LVL:
! 867: if (mc->type != AUDIO_MIXER_VALUE)
! 868: break;
! 869: if (mc->un.value.num_channels == 1)
! 870: mc->un.value.level[AUDIO_MIXER_LEVEL_MONO] =
! 871: sc->sc_volume[ZAUDIO_OP_SPKR].left;
! 872: else
! 873: break;
! 874: error = 0;
! 875: break;
! 876: case ZAUDIO_SPKR_MUTE:
! 877: if (mc->type != AUDIO_MIXER_ENUM)
! 878: break;
! 879: mc->un.ord = sc->sc_unmute[ZAUDIO_OP_SPKR] ? 1 : 0;
! 880: error = 0;
! 881: break;
! 882: case ZAUDIO_HP_LVL:
! 883: if (mc->type != AUDIO_MIXER_VALUE)
! 884: break;
! 885: if (mc->un.value.num_channels == 1)
! 886: mc->un.value.level[AUDIO_MIXER_LEVEL_MONO] =
! 887: sc->sc_volume[ZAUDIO_OP_HP].left;
! 888: else if (mc->un.value.num_channels == 2) {
! 889: mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT] =
! 890: sc->sc_volume[ZAUDIO_OP_HP].left;
! 891: mc->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] =
! 892: sc->sc_volume[ZAUDIO_OP_HP].right;
! 893: }
! 894: else
! 895: break;
! 896: error = 0;
! 897: break;
! 898: case ZAUDIO_HP_MUTE:
! 899: if (mc->type != AUDIO_MIXER_ENUM)
! 900: break;
! 901: mc->un.ord = sc->sc_unmute[ZAUDIO_OP_HP] ? 1 : 0;
! 902: error = 0;
! 903: break;
! 904: }
! 905:
! 906: return error;
! 907: }
! 908:
! 909: int
! 910: zaudio_query_devinfo(void *hdl, struct mixer_devinfo *di)
! 911: {
! 912: /* struct zaudio_softc *sc = hdl; */
! 913:
! 914: switch (di->index) {
! 915: case ZAUDIO_SPKR_LVL:
! 916: di->type = AUDIO_MIXER_VALUE;
! 917: di->mixer_class = ZAUDIO_OUTPUT_CLASS;
! 918: di->prev = AUDIO_MIXER_LAST;
! 919: di->next = ZAUDIO_SPKR_MUTE;
! 920: strlcpy(di->label.name, AudioNspeaker,
! 921: sizeof(di->label.name));
! 922: strlcpy(di->un.v.units.name, AudioNvolume,
! 923: sizeof(di->un.v.units.name));
! 924: di->un.v.num_channels = 1;
! 925: break;
! 926: case ZAUDIO_SPKR_MUTE:
! 927: di->type = AUDIO_MIXER_ENUM;
! 928: di->mixer_class = ZAUDIO_OUTPUT_CLASS;
! 929: di->prev = ZAUDIO_SPKR_LVL;
! 930: di->next = AUDIO_MIXER_LAST;
! 931: goto mute;
! 932: case ZAUDIO_HP_LVL:
! 933: di->type = AUDIO_MIXER_VALUE;
! 934: di->mixer_class = ZAUDIO_OUTPUT_CLASS;
! 935: di->prev = AUDIO_MIXER_LAST;
! 936: di->next = ZAUDIO_HP_MUTE;
! 937: strlcpy(di->label.name, AudioNheadphone,
! 938: sizeof(di->label.name));
! 939: di->un.v.num_channels = 1;
! 940: strlcpy(di->un.v.units.name, AudioNvolume,
! 941: sizeof(di->un.v.units.name));
! 942: break;
! 943: case ZAUDIO_HP_MUTE:
! 944: di->type = AUDIO_MIXER_ENUM;
! 945: di->mixer_class = ZAUDIO_OUTPUT_CLASS;
! 946: di->prev = ZAUDIO_HP_LVL;
! 947: di->next = AUDIO_MIXER_LAST;
! 948: mute:
! 949: strlcpy(di->label.name, AudioNmute, sizeof(di->label.name));
! 950: di->un.e.num_mem = 2;
! 951: strlcpy(di->un.e.member[0].label.name, AudioNon,
! 952: sizeof(di->un.e.member[0].label.name));
! 953: di->un.e.member[0].ord = 0;
! 954: strlcpy(di->un.e.member[1].label.name, AudioNoff,
! 955: sizeof(di->un.e.member[1].label.name));
! 956: di->un.e.member[1].ord = 1;
! 957: break;
! 958: case ZAUDIO_OUTPUT_CLASS:
! 959: di->type = AUDIO_MIXER_CLASS;
! 960: di->mixer_class = ZAUDIO_OUTPUT_CLASS;
! 961: di->prev = AUDIO_MIXER_LAST;
! 962: di->next = AUDIO_MIXER_LAST;
! 963: strlcpy(di->label.name, AudioCoutputs,
! 964: sizeof(di->label.name));
! 965: break;
! 966: default:
! 967: return ENXIO;
! 968: }
! 969:
! 970: return 0;
! 971: }
! 972:
! 973: int
! 974: zaudio_get_props(void *hdl)
! 975: {
! 976: return AUDIO_PROP_MMAP | AUDIO_PROP_INDEPENDENT | AUDIO_PROP_FULLDUPLEX;
! 977: }
! 978:
! 979: int
! 980: zaudio_start_output(void *hdl, void *block, int bsize, void (*intr)(void *),
! 981: void *intrarg)
! 982: {
! 983: struct zaudio_softc *sc = hdl;
! 984: int err;
! 985:
! 986: /* Power up codec if we are not already playing. */
! 987: if (!sc->sc_playing) {
! 988: sc->sc_playing = 1;
! 989: zaudio_play_setup(sc);
! 990: }
! 991:
! 992: /* Start DMA via I2S */
! 993: err = pxa2x0_i2s_start_output(&sc->sc_i2s, block, bsize, intr, intrarg);
! 994: if (err) {
! 995: zaudio_standby(sc);
! 996: sc->sc_playing = 0;
! 997: }
! 998: return err;
! 999: }
! 1000:
! 1001: int
! 1002: zaudio_start_input(void *hdl, void *block, int bsize, void (*intr)(void *),
! 1003: void *intrarg)
! 1004: {
! 1005: return ENXIO;
! 1006: }
CVSweb