1 /* $NetBSD: esm.c,v 1.22 2003/02/03 01:11:54 kleink Exp $ */ 2 3 /*- 4 * Copyright (c) 2002, 2003 Matt Fredette 5 * All rights reserved. 6 * 7 * Copyright (c) 2000, 2001 Rene Hexel <rh@netbsd.org> 8 * All rights reserved. 9 * 10 * Copyright (c) 2000 Taku YAMAMOTO <taku@cent.saitama-u.ac.jp> 11 * All rights reserved. 12 * 13 * Redistribution and use in source and binary forms, with or without 14 * modification, are permitted provided that the following conditions 15 * are met: 16 * 1. Redistributions of source code must retain the above copyright 17 * notice, this list of conditions and the following disclaimer. 18 * 2. Redistributions in binary form must reproduce the above copyright 19 * notice, this list of conditions and the following disclaimer in the 20 * documentation and/or other materials provided with the distribution. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 * 34 * Taku Id: maestro.c,v 1.12 2000/09/06 03:32:34 taku Exp 35 * FreeBSD: /c/ncvs/src/sys/dev/sound/pci/maestro.c,v 1.4 2000/12/18 01:36:35 cg Exp 36 */ 37 38 /* 39 * TODO: 40 * - hardware volume support 41 * - fix 16-bit stereo recording, add 8-bit recording 42 * - MIDI support 43 * - joystick support 44 * 45 * 46 * Credits: 47 * 48 * This code is based on the FreeBSD driver written by Taku YAMAMOTO 49 * 50 * 51 * Original credits from the FreeBSD driver: 52 * 53 * Part of this code (especially in many magic numbers) was heavily inspired 54 * by the Linux driver originally written by 55 * Alan Cox <alan.cox@linux.org>, modified heavily by 56 * Zach Brown <zab@zabbo.net>. 57 * 58 * busdma()-ize and buffer size reduction were suggested by 59 * Cameron Grant <gandalf@vilnya.demon.co.uk>. 60 * Also he showed me the way to use busdma() suite. 61 * 62 * Internal speaker problems on NEC VersaPro's and Dell Inspiron 7500 63 * were looked at by 64 * Munehiro Matsuda <haro@tk.kubota.co.jp>, 65 * who brought patches based on the Linux driver with some simplification. 66 */ 67 68 #include <sys/cdefs.h> 69 __KERNEL_RCSID(0, "$NetBSD: esm.c,v 1.22 2003/02/03 01:11:54 kleink Exp $"); 70 71 #include <sys/param.h> 72 #include <sys/systm.h> 73 #include <sys/kernel.h> 74 #include <sys/malloc.h> 75 #include <sys/device.h> 76 77 #include <machine/bus.h> 78 79 #include <sys/audioio.h> 80 #include <dev/audio_if.h> 81 #include <dev/mulaw.h> 82 #include <dev/auconv.h> 83 #include <dev/ic/ac97var.h> 84 #include <dev/ic/ac97reg.h> 85 86 #include <dev/pci/pcidevs.h> 87 #include <dev/pci/pcivar.h> 88 89 #include <dev/pci/esmreg.h> 90 #include <dev/pci/esmvar.h> 91 92 #define PCI_CBIO 0x10 /* Configuration Base I/O Address */ 93 94 /* Debug */ 95 #ifdef AUDIO_DEBUG 96 #define DPRINTF(l,x) do { if (esm_debug & (l)) printf x; } while(0) 97 #define DUMPREG(x) do { if (esm_debug & ESM_DEBUG_REG) \ 98 esm_dump_regs(x); } while(0) 99 int esm_debug = 0xfffc; 100 #define ESM_DEBUG_CODECIO 0x0001 101 #define ESM_DEBUG_IRQ 0x0002 102 #define ESM_DEBUG_DMA 0x0004 103 #define ESM_DEBUG_TIMER 0x0008 104 #define ESM_DEBUG_REG 0x0010 105 #define ESM_DEBUG_PARAM 0x0020 106 #define ESM_DEBUG_APU 0x0040 107 #define ESM_DEBUG_CODEC 0x0080 108 #define ESM_DEBUG_PCI 0x0100 109 #define ESM_DEBUG_RESUME 0x0200 110 #else 111 #define DPRINTF(x,y) /* nothing */ 112 #define DUMPREG(x) /* nothing */ 113 #endif 114 115 #ifdef DIAGNOSTIC 116 #define RANGE(n, l, h) if ((n) < (l) || (n) >= (h)) \ 117 printf (#n "=%d out of range (%d, %d) in " \ 118 __FILE__ ", line %d\n", (n), (l), (h), __LINE__) 119 #else 120 #define RANGE(x,y,z) /* nothing */ 121 #endif 122 123 #define inline __inline 124 125 static inline void ringbus_setdest(struct esm_softc *, int, int); 126 127 static inline u_int16_t wp_rdreg(struct esm_softc *, u_int16_t); 128 static inline void wp_wrreg(struct esm_softc *, u_int16_t, u_int16_t); 129 static inline u_int16_t wp_rdapu(struct esm_softc *, int, u_int16_t); 130 static inline void wp_wrapu(struct esm_softc *, int, u_int16_t, 131 u_int16_t); 132 static inline void wp_settimer(struct esm_softc *, u_int); 133 static inline void wp_starttimer(struct esm_softc *); 134 static inline void wp_stoptimer(struct esm_softc *); 135 136 static inline u_int16_t wc_rdreg(struct esm_softc *, u_int16_t); 137 static inline void wc_wrreg(struct esm_softc *, u_int16_t, u_int16_t); 138 static inline u_int16_t wc_rdchctl(struct esm_softc *, int); 139 static inline void wc_wrchctl(struct esm_softc *, int, u_int16_t); 140 141 static inline u_int calc_timer_freq(struct esm_chinfo*); 142 static void set_timer(struct esm_softc *); 143 144 static void esmch_set_format(struct esm_chinfo *, 145 struct audio_params *p); 146 static void esmch_combine_input(struct esm_softc *, 147 struct esm_chinfo *ch); 148 149 /* Power Management */ 150 void esm_powerhook(int, void *); 151 152 CFATTACH_DECL(esm, sizeof(struct esm_softc), 153 esm_match, esm_attach, NULL, NULL); 154 155 struct audio_hw_if esm_hw_if = { 156 esm_open, 157 esm_close, 158 NULL, /* drain */ 159 esm_query_encoding, 160 esm_set_params, 161 esm_round_blocksize, 162 NULL, /* commit_settings */ 163 esm_init_output, 164 esm_init_input, 165 NULL, /* start_output */ 166 NULL, /* start_input */ 167 esm_halt_output, 168 esm_halt_input, 169 NULL, /* speaker_ctl */ 170 esm_getdev, 171 NULL, /* getfd */ 172 esm_set_port, 173 esm_get_port, 174 esm_query_devinfo, 175 esm_malloc, 176 esm_free, 177 esm_round_buffersize, 178 esm_mappage, 179 esm_get_props, 180 esm_trigger_output, 181 esm_trigger_input, 182 NULL, 183 }; 184 185 struct audio_device esm_device = { 186 "ESS Maestro", 187 "", 188 "esm" 189 }; 190 191 192 static audio_encoding_t esm_encoding[] = { 193 { 0, AudioEulinear, AUDIO_ENCODING_ULINEAR, 8, 0 }, 194 { 1, AudioEmulaw, AUDIO_ENCODING_ULAW, 8, 195 AUDIO_ENCODINGFLAG_EMULATED }, 196 { 2, AudioEalaw, AUDIO_ENCODING_ALAW, 8, AUDIO_ENCODINGFLAG_EMULATED }, 197 { 3, AudioEslinear, AUDIO_ENCODING_SLINEAR, 8, 0 }, 198 { 4, AudioEslinear_le, AUDIO_ENCODING_SLINEAR_LE, 16, 0 }, 199 { 5, AudioEulinear_le, AUDIO_ENCODING_ULINEAR_LE, 16, 200 AUDIO_ENCODINGFLAG_EMULATED }, 201 { 6, AudioEslinear_be, AUDIO_ENCODING_SLINEAR_BE, 16, 202 AUDIO_ENCODINGFLAG_EMULATED }, 203 { 7, AudioEulinear_be, AUDIO_ENCODING_ULINEAR_BE, 16, 204 AUDIO_ENCODINGFLAG_EMULATED }, 205 }; 206 207 #define MAESTRO_NENCODINGS 8 208 209 210 static const struct esm_quirks esm_quirks[] = { 211 /* COMPAL 38W2 OEM Notebook, e.g. Dell INSPIRON 5000e */ 212 { PCI_VENDOR_COMPAL, PCI_PRODUCT_COMPAL_38W2, ESM_QUIRKF_SWAPPEDCH }, 213 214 /* COMPAQ Armada M700 Notebook */ 215 { PCI_VENDOR_COMPAQ, PCI_PRODUCT_COMPAQ_M700, ESM_QUIRKF_SWAPPEDCH }, 216 217 /* NEC Versa Pro LX VA26D */ 218 { PCI_VENDOR_NEC, PCI_PRODUCT_NEC_VA26D, ESM_QUIRKF_GPIO }, 219 220 /* NEC Versa LX */ 221 { PCI_VENDOR_NEC, PCI_PRODUCT_NEC_VERSALX, ESM_QUIRKF_GPIO }, 222 223 /* Toshiba Portege */ 224 { PCI_VENDOR_TOSHIBA2, PCI_PRODUCT_TOSHIBA2_PORTEGE, ESM_QUIRKF_SWAPPEDCH } 225 }; 226 227 enum esm_quirk_flags 228 esm_get_quirks(pcireg_t subid) 229 { 230 int i; 231 232 for (i = 0; i < (sizeof esm_quirks / sizeof esm_quirks[0]); i++) { 233 if (PCI_VENDOR(subid) == esm_quirks[i].eq_vendor && 234 PCI_PRODUCT(subid) == esm_quirks[i].eq_product) { 235 return esm_quirks[i].eq_quirks; 236 } 237 } 238 239 return 0; 240 } 241 242 243 #ifdef AUDIO_DEBUG 244 struct esm_reg_info { 245 int offset; /* register offset */ 246 int width; /* 1/2/4 bytes */ 247 } dump_regs[] = { 248 { PORT_WAVCACHE_CTRL, 2 }, 249 { PORT_HOSTINT_CTRL, 2 }, 250 { PORT_HOSTINT_STAT, 2 }, 251 { PORT_HWVOL_VOICE_SHADOW, 1 }, 252 { PORT_HWVOL_VOICE, 1 }, 253 { PORT_HWVOL_MASTER_SHADOW, 1 }, 254 { PORT_HWVOL_MASTER, 1 }, 255 { PORT_RINGBUS_CTRL, 4 }, 256 { PORT_GPIO_DATA, 2 }, 257 { PORT_GPIO_MASK, 2 }, 258 { PORT_GPIO_DIR, 2 }, 259 { PORT_ASSP_CTRL_A, 1 }, 260 { PORT_ASSP_CTRL_B, 1 }, 261 { PORT_ASSP_CTRL_C, 1 }, 262 { PORT_ASSP_INT_STAT, 1 } 263 }; 264 265 static void 266 esm_dump_regs(struct esm_softc *ess) 267 { 268 int i; 269 270 printf("%s registers:", ess->sc_dev.dv_xname); 271 for (i = 0; i < (sizeof dump_regs / sizeof dump_regs[0]); i++) { 272 if (i % 5 == 0) 273 printf("\n"); 274 printf("0x%2.2x: ", dump_regs[i].offset); 275 switch(dump_regs[i].width) { 276 case 4: 277 printf("%8.8x, ", bus_space_read_4(ess->st, ess->sh, 278 dump_regs[i].offset)); 279 break; 280 case 2: 281 printf("%4.4x, ", bus_space_read_2(ess->st, ess->sh, 282 dump_regs[i].offset)); 283 break; 284 default: 285 printf("%2.2x, ", 286 bus_space_read_1(ess->st, ess->sh, 287 dump_regs[i].offset)); 288 } 289 } 290 printf("\n"); 291 } 292 #endif 293 294 295 /* ----------------------------- 296 * Subsystems. 297 */ 298 299 /* Codec/Ringbus */ 300 301 /* -------------------------------------------------------------------- */ 302 303 int 304 esm_read_codec(void *sc, u_int8_t regno, u_int16_t *result) 305 { 306 struct esm_softc *ess = sc; 307 unsigned t; 308 309 /* We have to wait for a SAFE time to write addr/data */ 310 for (t = 0; t < 20; t++) { 311 if ((bus_space_read_1(ess->st, ess->sh, PORT_CODEC_STAT) 312 & CODEC_STAT_MASK) != CODEC_STAT_PROGLESS) 313 break; 314 delay(2); /* 20.8us / 13 */ 315 } 316 if (t == 20) 317 printf("%s: esm_read_codec() PROGLESS timed out.\n", 318 ess->sc_dev.dv_xname); 319 320 bus_space_write_1(ess->st, ess->sh, PORT_CODEC_CMD, 321 CODEC_CMD_READ | regno); 322 delay(21); /* AC97 cycle = 20.8usec */ 323 324 /* Wait for data retrieve */ 325 for (t = 0; t < 20; t++) { 326 if ((bus_space_read_1(ess->st, ess->sh, PORT_CODEC_STAT) 327 & CODEC_STAT_MASK) == CODEC_STAT_RW_DONE) 328 break; 329 delay(2); /* 20.8us / 13 */ 330 } 331 if (t == 20) 332 /* Timed out, but perform dummy read. */ 333 printf("%s: esm_read_codec() RW_DONE timed out.\n", 334 ess->sc_dev.dv_xname); 335 336 *result = bus_space_read_2(ess->st, ess->sh, PORT_CODEC_REG); 337 338 return 0; 339 } 340 341 int 342 esm_write_codec(void *sc, u_int8_t regno, u_int16_t data) 343 { 344 struct esm_softc *ess = sc; 345 unsigned t; 346 347 /* We have to wait for a SAFE time to write addr/data */ 348 for (t = 0; t < 20; t++) { 349 if ((bus_space_read_1(ess->st, ess->sh, PORT_CODEC_STAT) 350 & CODEC_STAT_MASK) != CODEC_STAT_PROGLESS) 351 break; 352 delay(2); /* 20.8us / 13 */ 353 } 354 if (t == 20) { 355 /* Timed out. Abort writing. */ 356 printf("%s: esm_write_codec() PROGLESS timed out.\n", 357 ess->sc_dev.dv_xname); 358 return -1; 359 } 360 361 bus_space_write_2(ess->st, ess->sh, PORT_CODEC_REG, data); 362 bus_space_write_1(ess->st, ess->sh, PORT_CODEC_CMD, 363 CODEC_CMD_WRITE | regno); 364 365 return 0; 366 } 367 368 /* -------------------------------------------------------------------- */ 369 370 static inline void 371 ringbus_setdest(struct esm_softc *ess, int src, int dest) 372 { 373 u_int32_t data; 374 375 data = bus_space_read_4(ess->st, ess->sh, PORT_RINGBUS_CTRL); 376 data &= ~(0xfU << src); 377 data |= (0xfU & dest) << src; 378 bus_space_write_4(ess->st, ess->sh, PORT_RINGBUS_CTRL, data); 379 } 380 381 /* Wave Processor */ 382 383 static inline u_int16_t 384 wp_rdreg(struct esm_softc *ess, u_int16_t reg) 385 { 386 bus_space_write_2(ess->st, ess->sh, PORT_DSP_INDEX, reg); 387 return bus_space_read_2(ess->st, ess->sh, PORT_DSP_DATA); 388 } 389 390 static inline void 391 wp_wrreg(struct esm_softc *ess, u_int16_t reg, u_int16_t data) 392 { 393 bus_space_write_2(ess->st, ess->sh, PORT_DSP_INDEX, reg); 394 bus_space_write_2(ess->st, ess->sh, PORT_DSP_DATA, data); 395 } 396 397 static inline void 398 apu_setindex(struct esm_softc *ess, u_int16_t reg) 399 { 400 int t; 401 402 wp_wrreg(ess, WPREG_CRAM_PTR, reg); 403 /* Sometimes WP fails to set apu register index. */ 404 for (t = 0; t < 1000; t++) { 405 if (bus_space_read_2(ess->st, ess->sh, PORT_DSP_DATA) == reg) 406 break; 407 bus_space_write_2(ess->st, ess->sh, PORT_DSP_DATA, reg); 408 } 409 if (t == 1000) 410 printf("%s: apu_setindex() timed out.\n", ess->sc_dev.dv_xname); 411 } 412 413 static inline u_int16_t 414 wp_rdapu(struct esm_softc *ess, int ch, u_int16_t reg) 415 { 416 u_int16_t ret; 417 418 apu_setindex(ess, ((unsigned)ch << 4) + reg); 419 ret = wp_rdreg(ess, WPREG_DATA_PORT); 420 return ret; 421 } 422 423 static inline void 424 wp_wrapu(struct esm_softc *ess, int ch, u_int16_t reg, u_int16_t data) 425 { 426 int t; 427 428 DPRINTF(ESM_DEBUG_APU, 429 ("wp_wrapu(%p, ch=%d, reg=0x%x, data=0x%04x)\n", 430 ess, ch, reg, data)); 431 432 apu_setindex(ess, ((unsigned)ch << 4) + reg); 433 wp_wrreg(ess, WPREG_DATA_PORT, data); 434 for (t = 0; t < 1000; t++) { 435 if (bus_space_read_2(ess->st, ess->sh, PORT_DSP_DATA) == data) 436 break; 437 bus_space_write_2(ess->st, ess->sh, PORT_DSP_DATA, data); 438 } 439 if (t == 1000) 440 printf("%s: wp_wrapu() timed out.\n", ess->sc_dev.dv_xname); 441 } 442 443 static inline void 444 wp_settimer(struct esm_softc *ess, u_int freq) 445 { 446 u_int clock = 48000 << 2; 447 u_int prescale = 0, divide = (freq != 0) ? (clock / freq) : ~0; 448 449 RANGE(divide, WPTIMER_MINDIV, WPTIMER_MAXDIV); 450 451 for (; divide > 32 << 1; divide >>= 1) 452 prescale++; 453 divide = (divide + 1) >> 1; 454 455 for (; prescale < 7 && divide > 2 && !(divide & 1); divide >>= 1) 456 prescale++; 457 458 DPRINTF(ESM_DEBUG_TIMER, 459 ("wp_settimer(%p, %u): clock = %u, prescale = %u, divide = %u\n", 460 ess, freq, clock, prescale, divide)); 461 462 wp_wrreg(ess, WPREG_TIMER_ENABLE, 0); 463 wp_wrreg(ess, WPREG_TIMER_FREQ, 464 (prescale << WP_TIMER_FREQ_PRESCALE_SHIFT) | (divide - 1)); 465 wp_wrreg(ess, WPREG_TIMER_ENABLE, 1); 466 } 467 468 static inline void 469 wp_starttimer(struct esm_softc *ess) 470 { 471 wp_wrreg(ess, WPREG_TIMER_START, 1); 472 } 473 474 static inline void 475 wp_stoptimer(struct esm_softc *ess) 476 { 477 wp_wrreg(ess, WPREG_TIMER_START, 0); 478 bus_space_write_2(ess->st, ess->sh, PORT_INT_STAT, 1); 479 } 480 481 /* WaveCache */ 482 483 static inline u_int16_t 484 wc_rdreg(struct esm_softc *ess, u_int16_t reg) 485 { 486 bus_space_write_2(ess->st, ess->sh, PORT_WAVCACHE_INDEX, reg); 487 return bus_space_read_2(ess->st, ess->sh, PORT_WAVCACHE_DATA); 488 } 489 490 static inline void 491 wc_wrreg(struct esm_softc *ess, u_int16_t reg, u_int16_t data) 492 { 493 bus_space_write_2(ess->st, ess->sh, PORT_WAVCACHE_INDEX, reg); 494 bus_space_write_2(ess->st, ess->sh, PORT_WAVCACHE_DATA, data); 495 } 496 497 static inline u_int16_t 498 wc_rdchctl(struct esm_softc *ess, int ch) 499 { 500 return wc_rdreg(ess, ch << 3); 501 } 502 503 static inline void 504 wc_wrchctl(struct esm_softc *ess, int ch, u_int16_t data) 505 { 506 wc_wrreg(ess, ch << 3, data); 507 } 508 509 /* Power management */ 510 511 void 512 esm_power(struct esm_softc *ess, int status) 513 { 514 pcireg_t data; 515 int pmcapreg; 516 517 if (pci_get_capability(ess->pc, ess->tag, PCI_CAP_PWRMGMT, 518 &pmcapreg, 0)) { 519 data = pci_conf_read(ess->pc, ess->tag, pmcapreg + PCI_PMCSR); 520 if ((data && PCI_PMCSR_STATE_MASK) != status) 521 pci_conf_write(ess->pc, ess->tag, 522 pmcapreg + PCI_PMCSR, status); 523 } 524 } 525 526 527 /* ----------------------------- 528 * Controller. 529 */ 530 531 int 532 esm_attach_codec(void *sc, struct ac97_codec_if *codec_if) 533 { 534 struct esm_softc *ess = sc; 535 536 ess->codec_if = codec_if; 537 538 return 0; 539 } 540 541 void 542 esm_reset_codec(void *sc) 543 { 544 } 545 546 547 enum ac97_host_flags 548 esm_flags_codec(void *sc) 549 { 550 struct esm_softc *ess = sc; 551 552 return ess->codec_flags; 553 } 554 555 556 void 557 esm_initcodec(struct esm_softc *ess) 558 { 559 u_int16_t data; 560 561 DPRINTF(ESM_DEBUG_CODEC, ("esm_initcodec(%p)\n", ess)); 562 563 if (bus_space_read_4(ess->st, ess->sh, PORT_RINGBUS_CTRL) 564 & RINGBUS_CTRL_ACLINK_ENABLED) { 565 bus_space_write_4(ess->st, ess->sh, PORT_RINGBUS_CTRL, 0); 566 delay(104); /* 20.8us * (4 + 1) */ 567 } 568 /* XXX - 2nd codec should be looked at. */ 569 bus_space_write_4(ess->st, ess->sh, PORT_RINGBUS_CTRL, 570 RINGBUS_CTRL_AC97_SWRESET); 571 delay(2); 572 bus_space_write_4(ess->st, ess->sh, PORT_RINGBUS_CTRL, 573 RINGBUS_CTRL_ACLINK_ENABLED); 574 delay(21); 575 576 esm_read_codec(ess, 0, &data); 577 if (bus_space_read_1(ess->st, ess->sh, PORT_CODEC_STAT) 578 & CODEC_STAT_MASK) { 579 bus_space_write_4(ess->st, ess->sh, PORT_RINGBUS_CTRL, 0); 580 delay(21); 581 582 /* Try cold reset. */ 583 printf("%s: will perform cold reset.\n", ess->sc_dev.dv_xname); 584 data = bus_space_read_2(ess->st, ess->sh, PORT_GPIO_DIR); 585 if (pci_conf_read(ess->pc, ess->tag, 0x58) & 1) 586 data |= 0x10; 587 data |= 0x009 & 588 ~bus_space_read_2(ess->st, ess->sh, PORT_GPIO_DATA); 589 bus_space_write_2(ess->st, ess->sh, PORT_GPIO_MASK, 0xff6); 590 bus_space_write_2(ess->st, ess->sh, PORT_GPIO_DIR, 591 data | 0x009); 592 bus_space_write_2(ess->st, ess->sh, PORT_GPIO_DATA, 0x000); 593 delay(2); 594 bus_space_write_2(ess->st, ess->sh, PORT_GPIO_DATA, 0x001); 595 delay(1); 596 bus_space_write_2(ess->st, ess->sh, PORT_GPIO_DATA, 0x009); 597 delay(500000); 598 bus_space_write_2(ess->st, ess->sh, PORT_GPIO_DIR, data); 599 delay(84); /* 20.8us * 4 */ 600 bus_space_write_4(ess->st, ess->sh, PORT_RINGBUS_CTRL, 601 RINGBUS_CTRL_ACLINK_ENABLED); 602 delay(21); 603 } 604 } 605 606 void 607 esm_init(struct esm_softc *ess) 608 { 609 /* Reset direct sound. */ 610 bus_space_write_2(ess->st, ess->sh, PORT_HOSTINT_CTRL, 611 HOSTINT_CTRL_DSOUND_RESET); 612 delay(10000); 613 bus_space_write_2(ess->st, ess->sh, PORT_HOSTINT_CTRL, 0); 614 delay(10000); 615 616 /* Enable direct sound interruption. */ 617 bus_space_write_2(ess->st, ess->sh, PORT_HOSTINT_CTRL, 618 HOSTINT_CTRL_DSOUND_INT_ENABLED); 619 620 /* Setup Wave Processor. */ 621 622 /* Enable WaveCache */ 623 wp_wrreg(ess, WPREG_WAVE_ROMRAM, 624 WP_WAVE_VIRTUAL_ENABLED | WP_WAVE_DRAM_ENABLED); 625 bus_space_write_2(ess->st, ess->sh, PORT_WAVCACHE_CTRL, 626 WAVCACHE_ENABLED | WAVCACHE_WTSIZE_4MB); 627 628 /* Setup Codec/Ringbus. */ 629 esm_initcodec(ess); 630 bus_space_write_4(ess->st, ess->sh, PORT_RINGBUS_CTRL, 631 RINGBUS_CTRL_RINGBUS_ENABLED | RINGBUS_CTRL_ACLINK_ENABLED); 632 633 /* Undocumented registers from the Linux driver. */ 634 wp_wrreg(ess, 0x8, 0xB004); 635 wp_wrreg(ess, 0x9, 0x001B); 636 wp_wrreg(ess, 0xA, 0x8000); 637 wp_wrreg(ess, 0xB, 0x3F37); 638 wp_wrreg(ess, 0xD, 0x7632); 639 640 wp_wrreg(ess, WPREG_BASE, 0x8598); /* Parallel I/O */ 641 ringbus_setdest(ess, RINGBUS_SRC_ADC, 642 RINGBUS_DEST_STEREO | RINGBUS_DEST_DSOUND_IN); 643 ringbus_setdest(ess, RINGBUS_SRC_DSOUND, 644 RINGBUS_DEST_STEREO | RINGBUS_DEST_DAC); 645 646 /* Setup ASSP. Needed for Dell Inspiron 7500? */ 647 bus_space_write_1(ess->st, ess->sh, PORT_ASSP_CTRL_B, 0x00); 648 bus_space_write_1(ess->st, ess->sh, PORT_ASSP_CTRL_A, 0x03); 649 bus_space_write_1(ess->st, ess->sh, PORT_ASSP_CTRL_C, 0x00); 650 651 /* 652 * Setup GPIO. 653 * There seems to be speciality with NEC systems. 654 */ 655 if (esm_get_quirks(ess->subid) & ESM_QUIRKF_GPIO) { 656 bus_space_write_2(ess->st, ess->sh, PORT_GPIO_MASK, 657 0x9ff); 658 bus_space_write_2(ess->st, ess->sh, PORT_GPIO_DIR, 659 bus_space_read_2(ess->st, ess->sh, PORT_GPIO_DIR) | 660 0x600); 661 bus_space_write_2(ess->st, ess->sh, PORT_GPIO_DATA, 662 0x200); 663 } 664 665 DUMPREG(ess); 666 } 667 668 669 /* Channel controller. */ 670 671 int 672 esm_init_output (void *sc, void *start, int size) 673 { 674 struct esm_softc *ess = sc; 675 struct esm_dma *p; 676 677 p = &ess->sc_dma; 678 if ((caddr_t)start != p->addr + MAESTRO_PLAYBUF_OFF) { 679 printf("%s: esm_init_output: bad addr %p\n", 680 ess->sc_dev.dv_xname, start); 681 return EINVAL; 682 } 683 684 ess->pch.base = DMAADDR(p) + MAESTRO_PLAYBUF_OFF; 685 686 DPRINTF(ESM_DEBUG_DMA, ("%s: pch.base = 0x%x\n", 687 ess->sc_dev.dv_xname, ess->pch.base)); 688 689 return 0; 690 } 691 692 int 693 esm_init_input (void *sc, void *start, int size) 694 { 695 struct esm_softc *ess = sc; 696 struct esm_dma *p; 697 698 p = &ess->sc_dma; 699 if ((caddr_t)start != p->addr + MAESTRO_RECBUF_OFF) { 700 printf("%s: esm_init_input: bad addr %p\n", 701 ess->sc_dev.dv_xname, start); 702 return EINVAL; 703 } 704 705 switch (ess->rch.aputype) { 706 case APUTYPE_16BITSTEREO: 707 ess->rch.base = DMAADDR(p) + MAESTRO_RECBUF_L_OFF; 708 break; 709 default: 710 ess->rch.base = DMAADDR(p) + MAESTRO_RECBUF_OFF; 711 break; 712 } 713 714 DPRINTF(ESM_DEBUG_DMA, ("%s: rch.base = 0x%x\n", 715 ess->sc_dev.dv_xname, ess->rch.base)); 716 717 return 0; 718 } 719 720 int 721 esm_trigger_output(void *sc, void *start, void *end, int blksize, 722 void (*intr)(void *), void *arg, struct audio_params *param) 723 { 724 struct esm_softc *ess = sc; 725 struct esm_chinfo *ch = &ess->pch; 726 struct esm_dma *p; 727 int pan = 0, choffset; 728 int i, nch = 1; 729 unsigned speed = ch->sample_rate, offset, wpwa, dv; 730 size_t size; 731 u_int16_t apuch = ch->num << 1; 732 733 DPRINTF(ESM_DEBUG_DMA, 734 ("esm_trigger_output(%p, %p, %p, 0x%x, %p, %p, %p)\n", 735 sc, start, end, blksize, intr, arg, param)); 736 737 #ifdef DIAGNOSTIC 738 if (ess->pactive) { 739 printf("%s: esm_trigger_output: already running", 740 ess->sc_dev.dv_xname); 741 return EINVAL; 742 } 743 #endif 744 745 ess->sc_pintr = intr; 746 ess->sc_parg = arg; 747 p = &ess->sc_dma; 748 if ((caddr_t)start != p->addr + MAESTRO_PLAYBUF_OFF) { 749 printf("%s: esm_trigger_output: bad addr %p\n", 750 ess->sc_dev.dv_xname, start); 751 return EINVAL; 752 } 753 754 ess->pch.blocksize = blksize; 755 ess->pch.apublk = blksize >> 1; 756 ess->pactive = 1; 757 758 size = (size_t)(((caddr_t)end - (caddr_t)start) >> 1); 759 choffset = MAESTRO_PLAYBUF_OFF; 760 offset = choffset >> 1; 761 wpwa = APU_USE_SYSMEM | ((offset >> 8) & APU_64KPAGE_MASK); 762 763 DPRINTF(ESM_DEBUG_DMA, 764 ("choffs=0x%x, wpwa=0x%x, size=0x%x words\n", 765 choffset, wpwa, size)); 766 767 switch (ch->aputype) { 768 case APUTYPE_16BITSTEREO: 769 ess->pch.apublk >>= 1; 770 wpwa >>= 1; 771 size >>= 1; 772 offset >>= 1; 773 /* FALLTHROUGH */ 774 case APUTYPE_8BITSTEREO: 775 if (ess->codec_flags & AC97_HOST_SWAPPED_CHANNELS) 776 pan = 8; 777 else 778 pan = -8; 779 nch++; 780 break; 781 case APUTYPE_8BITLINEAR: 782 ess->pch.apublk <<= 1; 783 speed >>= 1; 784 break; 785 } 786 787 ess->pch.apubase = offset; 788 ess->pch.apubuf = size; 789 ess->pch.nextirq = ess->pch.apublk; 790 791 set_timer(ess); 792 wp_starttimer(ess); 793 794 dv = (((speed % 48000) << 16) + 24000) / 48000 795 + ((speed / 48000) << 16); 796 797 for (i = nch-1; i >= 0; i--) { 798 wp_wrapu(ess, apuch + i, APUREG_WAVESPACE, wpwa & 0xff00); 799 wp_wrapu(ess, apuch + i, APUREG_CURPTR, offset); 800 wp_wrapu(ess, apuch + i, APUREG_ENDPTR, offset + size); 801 wp_wrapu(ess, apuch + i, APUREG_LOOPLEN, size - 1); 802 wp_wrapu(ess, apuch + i, APUREG_AMPLITUDE, 0xe800); 803 wp_wrapu(ess, apuch + i, APUREG_POSITION, 0x8f00 804 | (RADIUS_CENTERCIRCLE << APU_RADIUS_SHIFT) 805 | ((PAN_FRONT + pan) << APU_PAN_SHIFT)); 806 wp_wrapu(ess, apuch + i, APUREG_FREQ_LOBYTE, APU_plus6dB 807 | ((dv & 0xff) << APU_FREQ_LOBYTE_SHIFT)); 808 wp_wrapu(ess, apuch + i, APUREG_FREQ_HIWORD, dv >> 8); 809 810 if (ch->aputype == APUTYPE_16BITSTEREO) 811 wpwa |= APU_STEREO >> 1; 812 pan = -pan; 813 } 814 815 wc_wrchctl(ess, apuch, ch->wcreg_tpl); 816 if (nch > 1) 817 wc_wrchctl(ess, apuch + 1, ch->wcreg_tpl); 818 819 wp_wrapu(ess, apuch, APUREG_APUTYPE, 820 (ch->aputype << APU_APUTYPE_SHIFT) | APU_DMA_ENABLED | 0xf); 821 if (ch->wcreg_tpl & WAVCACHE_CHCTL_STEREO) 822 wp_wrapu(ess, apuch + 1, APUREG_APUTYPE, 823 (ch->aputype << APU_APUTYPE_SHIFT) | APU_DMA_ENABLED | 0xf); 824 825 return 0; 826 } 827 828 829 int 830 esm_trigger_input(void *sc, void *start, void *end, int blksize, 831 void (*intr)(void *), void *arg, struct audio_params *param) 832 { 833 struct esm_softc *ess = sc; 834 struct esm_chinfo *ch = &ess->rch; 835 struct esm_dma *p; 836 u_int32_t chctl, choffset; 837 int i, nch = 1; 838 u_int32_t speed = ch->sample_rate, offset, wpwa, dv; 839 size_t size; 840 u_int16_t apuch = ch->num << 1; 841 u_int32_t mixoffset, mixdv; 842 size_t mixsize; 843 u_int16_t reg; 844 845 DPRINTF(ESM_DEBUG_DMA, 846 ("esm_trigger_input(%p, %p, %p, 0x%x, %p, %p, %p)\n", 847 sc, start, end, blksize, intr, arg, param)); 848 849 #ifdef DIAGNOSTIC 850 if (ess->ractive) { 851 printf("%s: esm_trigger_input: already running", 852 ess->sc_dev.dv_xname); 853 return EINVAL; 854 } 855 #endif 856 857 ess->sc_rintr = intr; 858 ess->sc_rarg = arg; 859 p = &ess->sc_dma; 860 if ((caddr_t)start != p->addr + MAESTRO_RECBUF_OFF) { 861 printf("%s: esm_trigger_input: bad addr %p\n", 862 ess->sc_dev.dv_xname, start); 863 return EINVAL; 864 } 865 866 ess->rch.buffer = (caddr_t)start; 867 ess->rch.offset = 0; 868 ess->rch.blocksize = blksize; 869 ess->rch.bufsize = ((caddr_t)end - (caddr_t)start); 870 ess->rch.apublk = blksize >> 1; 871 ess->ractive = 1; 872 873 size = (size_t)(((caddr_t)end - (caddr_t)start) >> 1); 874 choffset = MAESTRO_RECBUF_OFF; 875 switch (ch->aputype) { 876 case APUTYPE_16BITSTEREO: 877 size >>= 1; 878 choffset = MAESTRO_RECBUF_L_OFF; 879 ess->rch.apublk >>= 1; 880 nch++; 881 break; 882 case APUTYPE_16BITLINEAR: 883 break; 884 default: 885 ess->ractive = 0; 886 return EINVAL; 887 } 888 889 mixsize = (MAESTRO_MIXBUF_SZ >> 1) >> 1; 890 mixoffset = MAESTRO_MIXBUF_OFF; 891 892 ess->rch.apubase = (choffset >> 1); 893 ess->rch.apubuf = size; 894 ess->rch.nextirq = ess->rch.apublk; 895 896 set_timer(ess); 897 wp_starttimer(ess); 898 899 if (speed > 47999) speed = 47999; 900 if (speed < 4000) speed = 4000; 901 dv = (((speed % 48000) << 16) + 24000) / 48000 902 + ((speed / 48000) << 16); 903 mixdv = 65536; /* 48KHz */ 904 905 for (i = 0; i < nch; i++) { 906 907 /* Clear all rate conversion WP channel registers first. */ 908 for (reg = 0; reg < 15; reg++) 909 wp_wrapu(ess, apuch + i, reg, 0); 910 911 /* Program the WaveCache for the rate conversion WP channel. */ 912 chctl = (DMAADDR(p) + choffset - 0x10) & 913 WAVCACHE_CHCTL_ADDRTAG_MASK; 914 wc_wrchctl(ess, apuch + i, chctl); 915 916 /* Program the rate conversion WP channel. */ 917 wp_wrapu(ess, apuch + i, APUREG_FREQ_LOBYTE, APU_plus6dB 918 | ((dv & 0xff) << APU_FREQ_LOBYTE_SHIFT) | 0x08); 919 wp_wrapu(ess, apuch + i, APUREG_FREQ_HIWORD, dv >> 8); 920 offset = choffset >> 1; 921 wpwa = APU_USE_SYSMEM | ((offset >> 8) & APU_64KPAGE_MASK); 922 wp_wrapu(ess, apuch + i, APUREG_WAVESPACE, wpwa); 923 wp_wrapu(ess, apuch + i, APUREG_CURPTR, offset); 924 wp_wrapu(ess, apuch + i, APUREG_ENDPTR, offset + size); 925 wp_wrapu(ess, apuch + i, APUREG_LOOPLEN, size - 1); 926 wp_wrapu(ess, apuch + i, APUREG_EFFECTS_ENV, 0x00f0); 927 wp_wrapu(ess, apuch + i, APUREG_AMPLITUDE, 0xe800); 928 wp_wrapu(ess, apuch + i, APUREG_POSITION, 0x8f00 929 | (RADIUS_CENTERCIRCLE << APU_RADIUS_SHIFT) 930 | (PAN_FRONT << APU_PAN_SHIFT)); 931 wp_wrapu(ess, apuch + i, APUREG_ROUTE, apuch + 2 + i); 932 933 DPRINTF(ESM_DEBUG_DMA, 934 ("choffs=0x%x, wpwa=0x%x, offset=0x%x words, size=0x%x words\n", 935 choffset, wpwa, offset, size)); 936 937 /* Clear all mixer WP channel registers first. */ 938 for (reg = 0; reg < 15; reg++) 939 wp_wrapu(ess, apuch + 2 + i, reg, 0); 940 941 /* Program the WaveCache for the mixer WP channel. */ 942 chctl = (ess->rch.base + mixoffset - 0x10) & 943 WAVCACHE_CHCTL_ADDRTAG_MASK; 944 wc_wrchctl(ess, apuch + 2 + i, chctl); 945 946 /* Program the mixer WP channel. */ 947 wp_wrapu(ess, apuch + 2 + i, APUREG_FREQ_LOBYTE, APU_plus6dB 948 | ((mixdv & 0xff) << APU_FREQ_LOBYTE_SHIFT) | 0x08); 949 wp_wrapu(ess, apuch + 2 + i, APUREG_FREQ_HIWORD, mixdv >> 8); 950 offset = mixoffset >> 1; 951 wpwa = APU_USE_SYSMEM | ((offset >> 8) & APU_64KPAGE_MASK); 952 wp_wrapu(ess, apuch + 2 + i, APUREG_WAVESPACE, wpwa); 953 wp_wrapu(ess, apuch + 2 + i, APUREG_CURPTR, offset); 954 wp_wrapu(ess, apuch + 2 + i, APUREG_ENDPTR, 955 offset + mixsize); 956 wp_wrapu(ess, apuch + 2 + i, APUREG_LOOPLEN, mixsize); 957 wp_wrapu(ess, apuch + 2 + i, APUREG_EFFECTS_ENV, 0x00f0); 958 wp_wrapu(ess, apuch + 2 + i, APUREG_AMPLITUDE, 0xe800); 959 wp_wrapu(ess, apuch + 2 + i, APUREG_POSITION, 0x8f00 960 | (RADIUS_CENTERCIRCLE << APU_RADIUS_SHIFT) 961 | (PAN_FRONT << APU_PAN_SHIFT)); 962 wp_wrapu(ess, apuch + 2 + i, APUREG_ROUTE, 963 ROUTE_PARALLEL + i); 964 965 DPRINTF(ESM_DEBUG_DMA, 966 ("mixoffs=0x%x, wpwa=0x%x, offset=0x%x words, size=0x%x words\n", 967 mixoffset, wpwa, offset, mixsize)); 968 969 /* Assume we're going to loop to do the right channel. */ 970 choffset += MAESTRO_RECBUF_L_SZ; 971 mixoffset += MAESTRO_MIXBUF_SZ >> 1; 972 } 973 974 wp_wrapu(ess, apuch, APUREG_APUTYPE, 975 (APUTYPE_RATECONV << APU_APUTYPE_SHIFT) | 976 APU_DMA_ENABLED | 0xf); 977 if (nch > 1) 978 wp_wrapu(ess, apuch + 1, APUREG_APUTYPE, 979 (APUTYPE_RATECONV << APU_APUTYPE_SHIFT) | 980 APU_DMA_ENABLED | 0xf); 981 wp_wrapu(ess, apuch + 2, APUREG_APUTYPE, 982 (APUTYPE_INPUTMIXER << APU_APUTYPE_SHIFT) | 983 APU_DMA_ENABLED | 0xf); 984 if (nch > 1) 985 wp_wrapu(ess, apuch + 3, APUREG_APUTYPE, 986 (APUTYPE_RATECONV << APU_APUTYPE_SHIFT) | 987 APU_DMA_ENABLED | 0xf); 988 989 return 0; 990 } 991 992 993 int 994 esm_halt_output(void *sc) 995 { 996 struct esm_softc *ess = sc; 997 struct esm_chinfo *ch = &ess->pch; 998 999 DPRINTF(ESM_DEBUG_PARAM, ("esm_halt_output(%p)\n", sc)); 1000 1001 wp_wrapu(ess, (ch->num << 1), APUREG_APUTYPE, 1002 APUTYPE_INACTIVE << APU_APUTYPE_SHIFT); 1003 wp_wrapu(ess, (ch->num << 1) + 1, APUREG_APUTYPE, 1004 APUTYPE_INACTIVE << APU_APUTYPE_SHIFT); 1005 1006 ess->pactive = 0; 1007 if (!ess->ractive) 1008 wp_stoptimer(ess); 1009 1010 return 0; 1011 } 1012 1013 1014 int 1015 esm_halt_input(void *sc) 1016 { 1017 struct esm_softc *ess = sc; 1018 struct esm_chinfo *ch = &ess->rch; 1019 1020 DPRINTF(ESM_DEBUG_PARAM, ("esm_halt_input(%p)\n", sc)); 1021 1022 wp_wrapu(ess, (ch->num << 1), APUREG_APUTYPE, 1023 APUTYPE_INACTIVE << APU_APUTYPE_SHIFT); 1024 wp_wrapu(ess, (ch->num << 1) + 1, APUREG_APUTYPE, 1025 APUTYPE_INACTIVE << APU_APUTYPE_SHIFT); 1026 wp_wrapu(ess, (ch->num << 1) + 2, APUREG_APUTYPE, 1027 APUTYPE_INACTIVE << APU_APUTYPE_SHIFT); 1028 wp_wrapu(ess, (ch->num << 1) + 3, APUREG_APUTYPE, 1029 APUTYPE_INACTIVE << APU_APUTYPE_SHIFT); 1030 1031 ess->ractive = 0; 1032 if (!ess->pactive) 1033 wp_stoptimer(ess); 1034 1035 return 0; 1036 } 1037 1038 1039 static inline u_int 1040 calc_timer_freq(struct esm_chinfo *ch) 1041 { 1042 u_int freq; 1043 1044 freq = (ch->sample_rate + ch->apublk - 1) / ch->apublk; 1045 1046 DPRINTF(ESM_DEBUG_TIMER, 1047 ("calc_timer_freq(%p): rate = %u, blk = 0x%x (0x%x): freq = %u\n", 1048 ch, ch->sample_rate, ch->apublk, ch->blocksize, freq)); 1049 1050 return freq; 1051 } 1052 1053 static void 1054 set_timer(struct esm_softc *ess) 1055 { 1056 unsigned freq = 0, freq2; 1057 1058 if (ess->pactive) 1059 freq = calc_timer_freq(&ess->pch); 1060 1061 if (ess->ractive) { 1062 freq2 = calc_timer_freq(&ess->rch); 1063 if (freq2 > freq) 1064 freq = freq2; 1065 } 1066 1067 KASSERT(freq != 0); 1068 1069 for (; freq < MAESTRO_MINFREQ; freq <<= 1) 1070 ; 1071 1072 if (freq > 0) 1073 wp_settimer(ess, freq); 1074 } 1075 1076 1077 static void 1078 esmch_set_format(struct esm_chinfo *ch, struct audio_params *p) 1079 { 1080 u_int16_t wcreg_tpl = (ch->base - 16) & WAVCACHE_CHCTL_ADDRTAG_MASK; 1081 u_int16_t aputype = APUTYPE_16BITLINEAR; 1082 1083 if (p->channels == 2) { 1084 wcreg_tpl |= WAVCACHE_CHCTL_STEREO; 1085 aputype++; 1086 } 1087 if (p->precision * p->factor == 8) { 1088 aputype += 2; 1089 if (p->encoding == AUDIO_ENCODING_ULINEAR) 1090 wcreg_tpl |= WAVCACHE_CHCTL_U8; 1091 } 1092 ch->wcreg_tpl = wcreg_tpl; 1093 ch->aputype = aputype; 1094 ch->sample_rate = p->sample_rate; 1095 1096 DPRINTF(ESM_DEBUG_PARAM, ("esmch_set_format: " 1097 "numch=%d, prec=%d*%d, tpl=0x%x, aputype=%d, rate=%ld\n", 1098 p->channels, p->precision, p->factor, wcreg_tpl, aputype, 1099 p->sample_rate)); 1100 } 1101 1102 /* 1103 * Since we can't record in true stereo, this function combines 1104 * the separately recorded left and right channels into the final 1105 * buffer for the upper layer. 1106 */ 1107 static void 1108 esmch_combine_input(struct esm_softc *ess, struct esm_chinfo *ch) 1109 { 1110 u_int32_t *dst32s; 1111 size_t offset, resid, count; 1112 const u_int32_t *left32s, *right32s; 1113 u_int32_t left32, right32; 1114 1115 /* The current offset into the upper layer buffer. */ 1116 offset = ch->offset; 1117 1118 /* The number of bytes left to combine. */ 1119 resid = ch->blocksize; 1120 1121 while (resid > 0) { 1122 1123 /* The 32-bit words for the left channel. */ 1124 left32s = (const u_int32_t *)(ess->sc_dma.addr + 1125 MAESTRO_RECBUF_L_OFF + offset / 2); 1126 1127 /* The 32-bit words for the right channel. */ 1128 right32s = (const u_int32_t *)(ess->sc_dma.addr + 1129 MAESTRO_RECBUF_R_OFF + offset / 2); 1130 1131 /* The pointer to the 32-bit words we will write. */ 1132 dst32s = (u_int32_t *)(ch->buffer + offset); 1133 1134 /* Get the number of bytes we will combine now. */ 1135 count = ch->bufsize - offset; 1136 if (count > resid) 1137 count = resid; 1138 resid -= count; 1139 offset += count; 1140 if (offset == ch->bufsize) 1141 offset = 0; 1142 1143 /* Combine, writing two 32-bit words at a time. */ 1144 KASSERT((count & (sizeof(uint32_t) * 2 - 1)) == 0); 1145 count /= (sizeof(u_int32_t) * 2); 1146 while (count > 0) { 1147 left32 = *(left32s++); 1148 right32 = *(right32s++); 1149 /* XXX this endian handling is half-baked at best */ 1150 #if BYTE_ORDER == LITTLE_ENDIAN 1151 *(dst32s++) = (left32 & 0xFFFF) | (right32 << 16); 1152 *(dst32s++) = (left32 >> 16) | (right32 & 0xFFFF0000); 1153 #else /* BYTE_ORDER == BIG_ENDIAN */ 1154 *(dst32s++) = (left32 & 0xFFFF0000) | (right32 >> 16); 1155 *(dst32s++) = (left32 << 16) | (right32 & 0xFFFF); 1156 #endif /* BYTE_ORDER == BIG_ENDIAN */ 1157 count--; 1158 } 1159 } 1160 1161 /* Update the offset. */ 1162 ch->offset = offset; 1163 } 1164 1165 /* 1166 * Audio interface glue functions 1167 */ 1168 1169 int 1170 esm_open(void *sc, int flags) 1171 { 1172 DPRINTF(ESM_DEBUG_PARAM, ("esm_open(%p, 0x%x)\n", sc, flags)); 1173 1174 return 0; 1175 } 1176 1177 1178 void 1179 esm_close(void *sc) 1180 { 1181 DPRINTF(ESM_DEBUG_PARAM, ("esm_close(%p)\n", sc)); 1182 } 1183 1184 1185 int 1186 esm_getdev (void *sc, struct audio_device *adp) 1187 { 1188 *adp = esm_device; 1189 return 0; 1190 } 1191 1192 1193 int 1194 esm_round_blocksize (void *sc, int blk) 1195 { 1196 DPRINTF(ESM_DEBUG_PARAM, 1197 ("esm_round_blocksize(%p, 0x%x)", sc, blk)); 1198 1199 blk &= ~0x3f; /* keep good alignment */ 1200 1201 DPRINTF(ESM_DEBUG_PARAM, (" = 0x%x\n", blk)); 1202 1203 return blk; 1204 } 1205 1206 1207 int 1208 esm_query_encoding(void *sc, struct audio_encoding *fp) 1209 { 1210 DPRINTF(ESM_DEBUG_PARAM, 1211 ("esm_query_encoding(%p, %d)\n", sc, fp->index)); 1212 1213 if (fp->index < 0 || fp->index >= MAESTRO_NENCODINGS) 1214 return EINVAL; 1215 1216 *fp = esm_encoding[fp->index]; 1217 return 0; 1218 } 1219 1220 1221 int 1222 esm_set_params(void *sc, int setmode, int usemode, 1223 struct audio_params *play, struct audio_params *rec) 1224 { 1225 struct esm_softc *ess = sc; 1226 struct audio_params *p; 1227 int mode; 1228 1229 DPRINTF(ESM_DEBUG_PARAM, 1230 ("esm_set_params(%p, 0x%x, 0x%x, %p, %p)\n", 1231 sc, setmode, usemode, play, rec)); 1232 1233 for (mode = AUMODE_RECORD; mode != -1; 1234 mode = mode == AUMODE_RECORD ? AUMODE_PLAY : -1) { 1235 if ((setmode & mode) == 0) 1236 continue; 1237 1238 p = mode == AUMODE_PLAY ? play : rec; 1239 1240 if (p->sample_rate < 4000 || p->sample_rate > 48000 || 1241 (p->precision != 8 && p->precision != 16) || 1242 (p->channels != 1 && p->channels != 2)) 1243 return EINVAL; 1244 1245 p->factor = 1; 1246 p->sw_code = 0; 1247 switch (p->encoding) { 1248 case AUDIO_ENCODING_SLINEAR_BE: 1249 if (p->precision == 16) 1250 p->sw_code = swap_bytes; 1251 else 1252 p->sw_code = change_sign8; 1253 break; 1254 case AUDIO_ENCODING_SLINEAR_LE: 1255 if (p->precision != 16) 1256 p->sw_code = change_sign8; 1257 break; 1258 case AUDIO_ENCODING_ULINEAR_BE: 1259 if (p->precision == 16) { 1260 if (mode == AUMODE_PLAY) 1261 p->sw_code = swap_bytes_change_sign16_le; 1262 else 1263 p->sw_code = change_sign16_swap_bytes_le; 1264 } 1265 break; 1266 case AUDIO_ENCODING_ULINEAR_LE: 1267 if (p->precision == 16) 1268 p->sw_code = change_sign16_le; 1269 break; 1270 case AUDIO_ENCODING_ULAW: 1271 if (mode == AUMODE_PLAY) { 1272 p->factor = 2; 1273 p->sw_code = mulaw_to_slinear16_le; 1274 } else 1275 p->sw_code = ulinear8_to_mulaw; 1276 break; 1277 case AUDIO_ENCODING_ALAW: 1278 if (mode == AUMODE_PLAY) { 1279 p->factor = 2; 1280 p->sw_code = alaw_to_slinear16_le; 1281 } else 1282 p->sw_code = ulinear8_to_alaw; 1283 break; 1284 default: 1285 return EINVAL; 1286 } 1287 } 1288 1289 if (setmode & AUMODE_PLAY) 1290 esmch_set_format(&ess->pch, play); 1291 1292 if (setmode & AUMODE_RECORD) 1293 esmch_set_format(&ess->rch, rec); 1294 1295 return 0; 1296 } 1297 1298 1299 int 1300 esm_set_port(void *sc, mixer_ctrl_t *cp) 1301 { 1302 struct esm_softc *ess = sc; 1303 1304 return (ess->codec_if->vtbl->mixer_set_port(ess->codec_if, cp)); 1305 } 1306 1307 1308 int 1309 esm_get_port(void *sc, mixer_ctrl_t *cp) 1310 { 1311 struct esm_softc *ess = sc; 1312 1313 return (ess->codec_if->vtbl->mixer_get_port(ess->codec_if, cp)); 1314 } 1315 1316 1317 int 1318 esm_query_devinfo(void *sc, mixer_devinfo_t *dip) 1319 { 1320 struct esm_softc *ess = sc; 1321 1322 return (ess->codec_if->vtbl->query_devinfo(ess->codec_if, dip)); 1323 } 1324 1325 1326 void * 1327 esm_malloc(void *sc, int direction, size_t size, struct malloc_type *pool, 1328 int flags) 1329 { 1330 struct esm_softc *ess = sc; 1331 int off; 1332 1333 DPRINTF(ESM_DEBUG_DMA, 1334 ("esm_malloc(%p, %d, 0x%x, %p, 0x%x)", 1335 sc, direction, size, pool, flags)); 1336 1337 /* 1338 * Each buffer can only be allocated once. 1339 */ 1340 if (ess->rings_alloced & direction) { 1341 DPRINTF(ESM_DEBUG_DMA, (" = 0 (ENOMEM)\n")); 1342 return 0; 1343 } 1344 1345 /* 1346 * Mark this buffer as allocated and return its 1347 * kernel virtual address. 1348 */ 1349 ess->rings_alloced |= direction; 1350 off = (direction == AUMODE_PLAY ? 1351 MAESTRO_PLAYBUF_OFF : MAESTRO_RECBUF_OFF); 1352 DPRINTF(ESM_DEBUG_DMA, (" = %p (DMAADDR 0x%x)\n", 1353 ess->sc_dma.addr + off, 1354 (int)DMAADDR(&ess->sc_dma) + off)); 1355 return (ess->sc_dma.addr + off); 1356 } 1357 1358 1359 void 1360 esm_free(void *sc, void *ptr, struct malloc_type *pool) 1361 { 1362 struct esm_softc *ess = sc; 1363 1364 DPRINTF(ESM_DEBUG_DMA, 1365 ("esm_free(%p, %p, %p)\n", 1366 sc, ptr, pool)); 1367 1368 if ((caddr_t)ptr == ess->sc_dma.addr + MAESTRO_PLAYBUF_OFF) 1369 ess->rings_alloced &= ~AUMODE_PLAY; 1370 else if ((caddr_t)ptr == ess->sc_dma.addr + MAESTRO_RECBUF_OFF) 1371 ess->rings_alloced &= ~AUMODE_RECORD; 1372 } 1373 1374 1375 size_t 1376 esm_round_buffersize(void *sc, int direction, size_t size) 1377 { 1378 if (size > MAESTRO_PLAYBUF_SZ) 1379 size = MAESTRO_PLAYBUF_SZ; 1380 if (size > MAESTRO_RECBUF_SZ) 1381 size = MAESTRO_RECBUF_SZ; 1382 return size; 1383 } 1384 1385 1386 paddr_t 1387 esm_mappage(void *sc, void *mem, off_t off, int prot) 1388 { 1389 struct esm_softc *ess = sc; 1390 1391 DPRINTF(ESM_DEBUG_DMA, 1392 ("esm_mappage(%p, %p, 0x%lx, 0x%x)\n", 1393 sc, mem, (unsigned long)off, prot)); 1394 1395 if (off < 0) 1396 return (-1); 1397 1398 if ((caddr_t)mem == ess->sc_dma.addr + MAESTRO_PLAYBUF_OFF) 1399 off += MAESTRO_PLAYBUF_OFF; 1400 else if ((caddr_t)mem == ess->sc_dma.addr + MAESTRO_RECBUF_OFF) 1401 off += MAESTRO_RECBUF_OFF; 1402 else 1403 return -1; 1404 return bus_dmamem_mmap(ess->dmat, ess->sc_dma.segs, ess->sc_dma.nsegs, 1405 off, prot, BUS_DMA_WAITOK); 1406 } 1407 1408 1409 int 1410 esm_get_props(void *sc) 1411 { 1412 return AUDIO_PROP_MMAP | AUDIO_PROP_INDEPENDENT | AUDIO_PROP_FULLDUPLEX; 1413 } 1414 1415 1416 /* ----------------------------- 1417 * Bus space. 1418 */ 1419 1420 int 1421 esm_intr(void *sc) 1422 { 1423 struct esm_softc *ess = sc; 1424 u_int16_t status; 1425 u_int16_t pos; 1426 int ret = 0; 1427 1428 status = bus_space_read_1(ess->st, ess->sh, PORT_HOSTINT_STAT); 1429 if (!status) 1430 return 0; 1431 1432 /* Acknowledge all. */ 1433 bus_space_write_2(ess->st, ess->sh, PORT_INT_STAT, 1); 1434 bus_space_write_1(ess->st, ess->sh, PORT_HOSTINT_STAT, 0); 1435 #if 0 /* XXX - HWVOL */ 1436 if (status & HOSTINT_STAT_HWVOL) { 1437 u_int delta; 1438 delta = bus_space_read_1(ess->st, ess->sh, PORT_HWVOL_MASTER) 1439 - 0x88; 1440 if (delta & 0x11) 1441 mixer_set(device_get_softc(ess->dev), 1442 SOUND_MIXER_VOLUME, 0); 1443 else { 1444 mixer_set(device_get_softc(ess->dev), 1445 SOUND_MIXER_VOLUME, 1446 mixer_get(device_get_softc(ess->dev), 1447 SOUND_MIXER_VOLUME) 1448 + ((delta >> 5) & 0x7) - 4 1449 + ((delta << 7) & 0x700) - 0x400); 1450 } 1451 bus_space_write_1(ess->st, ess->sh, PORT_HWVOL_MASTER, 0x88); 1452 ret++; 1453 } 1454 #endif /* XXX - HWVOL */ 1455 1456 if (ess->pactive) { 1457 pos = wp_rdapu(ess, ess->pch.num << 1, APUREG_CURPTR); 1458 1459 DPRINTF(ESM_DEBUG_IRQ, (" %4.4x/%4.4x ", pos, 1460 wp_rdapu(ess, (ess->pch.num<<1)+1, APUREG_CURPTR))); 1461 1462 pos -= ess->pch.apubase; 1463 if (pos >= ess->pch.nextirq && 1464 pos - ess->pch.nextirq < ess->pch.apubuf / 2) { 1465 ess->pch.nextirq += ess->pch.apublk; 1466 1467 if (ess->pch.nextirq >= ess->pch.apubuf) 1468 ess->pch.nextirq = 0; 1469 1470 if (ess->sc_pintr) { 1471 DPRINTF(ESM_DEBUG_IRQ, ("P\n")); 1472 ess->sc_pintr(ess->sc_parg); 1473 } 1474 1475 } 1476 ret++; 1477 } 1478 1479 if (ess->ractive) { 1480 pos = wp_rdapu(ess, ess->rch.num << 1, APUREG_CURPTR); 1481 1482 DPRINTF(ESM_DEBUG_IRQ, (" %4.4x/%4.4x ", pos, 1483 wp_rdapu(ess, (ess->rch.num<<1)+1, APUREG_CURPTR))); 1484 1485 pos -= ess->rch.apubase; 1486 if (pos >= ess->rch.nextirq && 1487 pos - ess->rch.nextirq < ess->rch.apubuf / 2) { 1488 ess->rch.nextirq += ess->rch.apublk; 1489 1490 if (ess->rch.nextirq >= ess->rch.apubuf) 1491 ess->rch.nextirq = 0; 1492 1493 if (ess->sc_rintr) { 1494 DPRINTF(ESM_DEBUG_IRQ, ("R\n")); 1495 switch(ess->rch.aputype) { 1496 case APUTYPE_16BITSTEREO: 1497 esmch_combine_input(ess, &ess->rch); 1498 break; 1499 } 1500 ess->sc_rintr(ess->sc_rarg); 1501 } 1502 1503 } 1504 ret++; 1505 } 1506 1507 return ret; 1508 } 1509 1510 1511 int 1512 esm_allocmem(struct esm_softc *sc, size_t size, size_t align, 1513 struct esm_dma *p) 1514 { 1515 int error; 1516 1517 p->size = size; 1518 error = bus_dmamem_alloc(sc->dmat, p->size, align, 0, 1519 p->segs, sizeof(p->segs)/sizeof(p->segs[0]), 1520 &p->nsegs, BUS_DMA_NOWAIT); 1521 if (error) 1522 return error; 1523 1524 error = bus_dmamem_map(sc->dmat, p->segs, p->nsegs, p->size, 1525 &p->addr, BUS_DMA_NOWAIT|BUS_DMA_COHERENT); 1526 if (error) 1527 goto free; 1528 1529 error = bus_dmamap_create(sc->dmat, p->size, 1, p->size, 1530 0, BUS_DMA_NOWAIT, &p->map); 1531 if (error) 1532 goto unmap; 1533 1534 error = bus_dmamap_load(sc->dmat, p->map, p->addr, p->size, NULL, 1535 BUS_DMA_NOWAIT); 1536 if (error) 1537 goto destroy; 1538 1539 return 0; 1540 1541 destroy: 1542 bus_dmamap_destroy(sc->dmat, p->map); 1543 unmap: 1544 bus_dmamem_unmap(sc->dmat, p->addr, p->size); 1545 free: 1546 bus_dmamem_free(sc->dmat, p->segs, p->nsegs); 1547 1548 return error; 1549 } 1550 1551 1552 int 1553 esm_match(struct device *dev, struct cfdata *match, void *aux) 1554 { 1555 struct pci_attach_args *pa = (struct pci_attach_args *) aux; 1556 1557 switch (PCI_VENDOR(pa->pa_id)) { 1558 case PCI_VENDOR_ESSTECH: 1559 switch (PCI_PRODUCT(pa->pa_id)) { 1560 case PCI_PRODUCT_ESSTECH_MAESTRO1: 1561 case PCI_PRODUCT_ESSTECH_MAESTRO2: 1562 case PCI_PRODUCT_ESSTECH_MAESTRO2E: 1563 return 1; 1564 } 1565 1566 case PCI_VENDOR_ESSTECH2: 1567 switch (PCI_PRODUCT(pa->pa_id)) { 1568 case PCI_PRODUCT_ESSTECH2_MAESTRO1: 1569 return 1; 1570 } 1571 } 1572 return 0; 1573 } 1574 1575 void 1576 esm_attach(struct device *parent, struct device *self, void *aux) 1577 { 1578 struct esm_softc *ess = (struct esm_softc *)self; 1579 struct pci_attach_args *pa = (struct pci_attach_args *)aux; 1580 pci_chipset_tag_t pc = pa->pa_pc; 1581 pcitag_t tag = pa->pa_tag; 1582 pci_intr_handle_t ih; 1583 pcireg_t csr, data; 1584 u_int16_t codec_data; 1585 u_int16_t pcmbar; 1586 const char *intrstr; 1587 int revision; 1588 char devinfo[256]; 1589 1590 aprint_naive(": Audio controller\n"); 1591 1592 pci_devinfo(pa->pa_id, pa->pa_class, 0, devinfo); 1593 revision = PCI_REVISION(pa->pa_class); 1594 aprint_normal(": %s (rev. 0x%02x)\n", devinfo, revision); 1595 1596 /* Enable the device. */ 1597 csr = pci_conf_read(pc, tag, PCI_COMMAND_STATUS_REG); 1598 pci_conf_write(pc, tag, PCI_COMMAND_STATUS_REG, 1599 csr | PCI_COMMAND_MASTER_ENABLE | PCI_COMMAND_IO_ENABLE); 1600 1601 /* Map I/O register */ 1602 if (pci_mapreg_map(pa, PCI_CBIO, PCI_MAPREG_TYPE_IO, 0, 1603 &ess->st, &ess->sh, NULL, NULL)) { 1604 aprint_error("%s: can't map i/o space\n", ess->sc_dev.dv_xname); 1605 return; 1606 } 1607 1608 /* Initialize softc */ 1609 ess->pch.num = 0; 1610 ess->rch.num = 1; 1611 ess->dmat = pa->pa_dmat; 1612 ess->tag = tag; 1613 ess->pc = pc; 1614 ess->subid = pci_conf_read(pc, tag, PCI_SUBSYS_ID_REG); 1615 1616 DPRINTF(ESM_DEBUG_PCI, 1617 ("%s: sub-system vendor 0x%4.4x, product 0x%4.4x\n", 1618 ess->sc_dev.dv_xname, 1619 PCI_VENDOR(ess->subid), PCI_PRODUCT(ess->subid))); 1620 1621 /* Map and establish the interrupt. */ 1622 if (pci_intr_map(pa, &ih)) { 1623 aprint_error("%s: can't map interrupt\n", ess->sc_dev.dv_xname); 1624 return; 1625 } 1626 intrstr = pci_intr_string(pc, ih); 1627 ess->ih = pci_intr_establish(pc, ih, IPL_AUDIO, esm_intr, self); 1628 if (ess->ih == NULL) { 1629 aprint_error("%s: can't establish interrupt", 1630 ess->sc_dev.dv_xname); 1631 if (intrstr != NULL) 1632 aprint_normal(" at %s", intrstr); 1633 aprint_normal("\n"); 1634 return; 1635 } 1636 aprint_normal("%s: interrupting at %s\n", 1637 ess->sc_dev.dv_xname, intrstr); 1638 1639 /* 1640 * Setup PCI config registers 1641 */ 1642 1643 /* set to power state D0 */ 1644 esm_power(ess, PCI_PMCSR_STATE_D0); 1645 delay(100000); 1646 1647 /* Disable all legacy emulations. */ 1648 data = pci_conf_read(pc, tag, CONF_LEGACY); 1649 pci_conf_write(pc, tag, CONF_LEGACY, data | LEGACY_DISABLED); 1650 1651 /* Disconnect from CHI. (Makes Dell inspiron 7500 work?) 1652 * Enable posted write. 1653 * Prefer PCI timing rather than that of ISA. 1654 * Don't swap L/R. */ 1655 data = pci_conf_read(pc, tag, CONF_MAESTRO); 1656 data |= MAESTRO_CHIBUS | MAESTRO_POSTEDWRITE | MAESTRO_DMA_PCITIMING; 1657 data &= ~MAESTRO_SWAP_LR; 1658 pci_conf_write(pc, tag, CONF_MAESTRO, data); 1659 1660 /* initialize sound chip */ 1661 esm_init(ess); 1662 1663 esm_read_codec(ess, 0, &codec_data); 1664 if (codec_data == 0x80) { 1665 aprint_error("%s: PT101 codec detected!\n", 1666 ess->sc_dev.dv_xname); 1667 return; 1668 } 1669 1670 /* 1671 * Some cards and Notebooks appear to have left and right channels 1672 * reversed. Check if there is a corresponding quirk entry for 1673 * the subsystem vendor and product and if so, set the appropriate 1674 * codec flag. 1675 */ 1676 if (esm_get_quirks(ess->subid) & ESM_QUIRKF_SWAPPEDCH) { 1677 ess->codec_flags |= AC97_HOST_SWAPPED_CHANNELS; 1678 } 1679 ess->codec_flags |= AC97_HOST_DONT_READ; 1680 1681 /* initialize AC97 host interface */ 1682 ess->host_if.arg = self; 1683 ess->host_if.attach = esm_attach_codec; 1684 ess->host_if.read = esm_read_codec; 1685 ess->host_if.write = esm_write_codec; 1686 ess->host_if.reset = esm_reset_codec; 1687 ess->host_if.flags = esm_flags_codec; 1688 1689 if (ac97_attach(&ess->host_if) != 0) 1690 return; 1691 1692 /* allocate our DMA region */ 1693 if (esm_allocmem(ess, MAESTRO_DMA_SZ, MAESTRO_DMA_ALIGN, 1694 &ess->sc_dma)) { 1695 aprint_error("%s: couldn't allocate memory!\n", 1696 ess->sc_dev.dv_xname); 1697 return; 1698 } 1699 ess->rings_alloced = 0; 1700 1701 /* set DMA base address */ 1702 for (pcmbar = WAVCACHE_PCMBAR; pcmbar < WAVCACHE_PCMBAR + 4; pcmbar++) 1703 wc_wrreg(ess, pcmbar, 1704 DMAADDR(&ess->sc_dma) >> WAVCACHE_BASEADDR_SHIFT); 1705 1706 audio_attach_mi(&esm_hw_if, self, &ess->sc_dev); 1707 1708 ess->esm_suspend = PWR_RESUME; 1709 ess->esm_powerhook = powerhook_establish(esm_powerhook, ess); 1710 } 1711 1712 /* Power Hook */ 1713 void 1714 esm_powerhook(why, v) 1715 int why; 1716 void *v; 1717 { 1718 struct esm_softc *ess = (struct esm_softc *)v; 1719 1720 DPRINTF(ESM_DEBUG_PARAM, 1721 ("%s: ESS maestro 2E why=%d\n", ess->sc_dev.dv_xname, why)); 1722 switch (why) { 1723 case PWR_SUSPEND: 1724 case PWR_STANDBY: 1725 ess->esm_suspend = why; 1726 esm_suspend(ess); 1727 DPRINTF(ESM_DEBUG_RESUME, ("esm_suspend\n")); 1728 break; 1729 1730 case PWR_RESUME: 1731 ess->esm_suspend = why; 1732 esm_resume(ess); 1733 DPRINTF(ESM_DEBUG_RESUME, ("esm_resumed\n")); 1734 break; 1735 } 1736 } 1737 1738 int 1739 esm_suspend(struct esm_softc *ess) 1740 { 1741 int x; 1742 1743 x = splaudio(); 1744 wp_stoptimer(ess); 1745 bus_space_write_2(ess->st, ess->sh, PORT_HOSTINT_CTRL, 0); 1746 1747 esm_halt_output(ess); 1748 esm_halt_input(ess); 1749 splx(x); 1750 1751 /* Power down everything except clock. */ 1752 esm_write_codec(ess, AC97_REG_POWER, 0xdf00); 1753 delay(20); 1754 bus_space_write_4(ess->st, ess->sh, PORT_RINGBUS_CTRL, 0); 1755 delay(1); 1756 esm_power(ess, PCI_PMCSR_STATE_D3); 1757 1758 return 0; 1759 } 1760 1761 int 1762 esm_resume(struct esm_softc *ess) 1763 { 1764 int x; 1765 1766 esm_power(ess, PCI_PMCSR_STATE_D0); 1767 delay(100000); 1768 esm_init(ess); 1769 1770 (*ess->codec_if->vtbl->restore_ports)(ess->codec_if); 1771 #if 0 1772 if (mixer_reinit(dev)) { 1773 printf("%s: unable to reinitialize the mixer\n", 1774 ess->sc_dev.dv_xname); 1775 return ENXIO; 1776 } 1777 #endif 1778 1779 x = splaudio(); 1780 #if TODO 1781 if (ess->pactive) 1782 esm_start_output(ess); 1783 if (ess->ractive) 1784 esm_start_input(ess); 1785 #endif 1786 if (ess->pactive || ess->ractive) { 1787 set_timer(ess); 1788 wp_starttimer(ess); 1789 } 1790 splx(x); 1791 return 0; 1792 } 1793 1794 #if 0 1795 int 1796 esm_shutdown(struct esm_softc *ess) 1797 { 1798 int i; 1799 1800 wp_stoptimer(ess); 1801 bus_space_write_2(ess->st, ess->sh, PORT_HOSTINT_CTRL, 0); 1802 1803 esm_halt_output(ess); 1804 esm_halt_input(ess); 1805 1806 return 0; 1807 } 1808 #endif 1809