1 /* $Id: imx23_digfilt.c,v 1.3 2020/04/19 08:18:19 isaki Exp $ */ 2 3 /* 4 * Copyright (c) 2014 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Petri Laakso. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 #include <sys/param.h> 33 #include <sys/cdefs.h> 34 #include <sys/types.h> 35 #include <sys/device.h> 36 #include <sys/errno.h> 37 #include <sys/systm.h> 38 #include <sys/bus.h> 39 #include <sys/mutex.h> 40 #include <sys/audioio.h> 41 #include <sys/mallocvar.h> 42 #include <dev/audio/audio_if.h> 43 #include <arm/imx/imx23_digfiltreg.h> 44 #include <arm/imx/imx23_rtcvar.h> 45 #include <arm/imx/imx23_clkctrlvar.h> 46 #include <arm/imx/imx23_apbdmavar.h> 47 #include <arm/imx/imx23_icollreg.h> 48 #include <arm/imx/imx23var.h> 49 50 #include <arm/pic/picvar.h> 51 52 /* Autoconf. */ 53 static int digfilt_match(device_t, cfdata_t, void *); 54 static void digfilt_attach(device_t, device_t, void *); 55 static int digfilt_activate(device_t, enum devact); 56 57 /* Audio driver interface. */ 58 static int digfilt_query_format(void *, audio_format_query_t *); 59 static int digfilt_set_format(void *, int, 60 const audio_params_t *, const audio_params_t *, 61 audio_filter_reg_t *, audio_filter_reg_t *); 62 static int digfilt_round_blocksize(void *, int, int, const audio_params_t *); 63 static int digfilt_init_output(void *, void *, int ); 64 static int digfilt_start_output(void *, void *, int, void (*)(void *), void *); 65 static int digfilt_halt_output(void *); 66 static int digfilt_getdev(void *, struct audio_device *); 67 static int digfilt_set_port(void *, mixer_ctrl_t *); 68 static int digfilt_get_port(void *, mixer_ctrl_t *); 69 static int digfilt_query_devinfo(void *, mixer_devinfo_t *); 70 static void *digfilt_allocm(void *, int, size_t); 71 static void digfilt_freem(void *, void *, size_t); 72 static size_t digfilt_round_buffersize(void *, int, size_t); 73 static int digfilt_get_props(void *); 74 static void digfilt_get_locks(void *, kmutex_t **, kmutex_t **); 75 76 /* IRQs */ 77 static int dac_error_intr(void *); 78 static int dac_dma_intr(void *); 79 80 struct digfilt_softc; 81 82 /* Audio out. */ 83 static void *digfilt_ao_alloc_dmachain(void *, size_t); 84 static void digfilt_ao_apply_mutes(struct digfilt_softc *); 85 static void digfilt_ao_init(struct digfilt_softc *); 86 static void digfilt_ao_reset(struct digfilt_softc *); 87 static void digfilt_ao_set_rate(struct digfilt_softc *, int); 88 89 /* Audio in. */ 90 #if 0 91 static void digfilt_ai_reset(struct digfilt_softc *); 92 #endif 93 94 #define DIGFILT_DMA_NSEGS 1 95 #define DIGFILT_BLOCKSIZE_MAX 4096 96 #define DIGFILT_BLOCKSIZE_ROUND 512 97 #define DIGFILT_DMA_CHAIN_LENGTH 3 98 #define DIGFILT_DMA_CHANNEL 1 99 #define DIGFILT_MUTE_DAC 1 100 #define DIGFILT_MUTE_HP 2 101 #define DIGFILT_MUTE_LINE 4 102 #define DIGFILT_SOFT_RST_LOOP 455 /* At least 1 us. */ 103 104 #define AO_RD(sc, reg) \ 105 bus_space_read_4(sc->sc_iot, sc->sc_aohdl, (reg)) 106 #define AO_WR(sc, reg, val) \ 107 bus_space_write_4(sc->sc_iot, sc->sc_aohdl, (reg), (val)) 108 #define AI_RD(sc, reg) \ 109 bus_space_read_4(sc->sc_iot, sc->sc_aihdl, (reg)) 110 #define AI_WR(sc, reg, val) \ 111 bus_space_write_4(sc->sc_iot, sc->sc_aihdl, (reg), (val)) 112 113 struct digfilt_softc { 114 device_t sc_dev; 115 device_t sc_audiodev; 116 struct audio_format sc_format; 117 bus_space_handle_t sc_aihdl; 118 bus_space_handle_t sc_aohdl; 119 apbdma_softc_t sc_dmac; 120 bus_dma_tag_t sc_dmat; 121 bus_dmamap_t sc_dmamp; 122 bus_dmamap_t sc_c_dmamp; 123 bus_dma_segment_t sc_ds[DIGFILT_DMA_NSEGS]; 124 bus_dma_segment_t sc_c_ds[DIGFILT_DMA_NSEGS]; 125 bus_space_handle_t sc_hdl; 126 kmutex_t sc_intr_lock; 127 bus_space_tag_t sc_iot; 128 kmutex_t sc_lock; 129 audio_params_t sc_pparam; 130 void *sc_buffer; 131 void *sc_dmachain; 132 void *sc_intarg; 133 void (*sc_intr)(void*); 134 uint8_t sc_mute; 135 uint8_t sc_cmd_index; 136 }; 137 138 CFATTACH_DECL3_NEW(digfilt, 139 sizeof(struct digfilt_softc), 140 digfilt_match, 141 digfilt_attach, 142 NULL, 143 digfilt_activate, 144 NULL, 145 NULL, 146 0); 147 148 static const struct audio_hw_if digfilt_hw_if = { 149 .open = NULL, 150 .close = NULL, 151 .query_format = digfilt_query_format, 152 .set_format = digfilt_set_format, 153 .round_blocksize = digfilt_round_blocksize, 154 .commit_settings = NULL, 155 .init_output = digfilt_init_output, 156 .init_input = NULL, 157 .start_output = digfilt_start_output, 158 .start_input = NULL, 159 .halt_output = digfilt_halt_output, 160 .speaker_ctl = NULL, 161 .getdev = digfilt_getdev, 162 .set_port = digfilt_set_port, 163 .get_port = digfilt_get_port, 164 .query_devinfo = digfilt_query_devinfo, 165 .allocm = digfilt_allocm, 166 .freem = digfilt_freem, 167 .round_buffersize = digfilt_round_buffersize, 168 .get_props = digfilt_get_props, 169 .trigger_output = NULL, 170 .trigger_input = NULL, 171 .dev_ioctl = NULL, 172 .get_locks = digfilt_get_locks 173 }; 174 175 enum { 176 DIGFILT_OUTPUT_CLASS, 177 DIGFILT_OUTPUT_DAC_VOLUME, 178 DIGFILT_OUTPUT_DAC_MUTE, 179 DIGFILT_OUTPUT_HP_VOLUME, 180 DIGFILT_OUTPUT_HP_MUTE, 181 DIGFILT_OUTPUT_LINE_VOLUME, 182 DIGFILT_OUTPUT_LINE_MUTE, 183 DIGFILT_ENUM_LAST 184 }; 185 186 static int 187 digfilt_match(device_t parent, cfdata_t match, void *aux) 188 { 189 struct apb_attach_args *aa = aux; 190 191 if (aa->aa_addr == HW_DIGFILT_BASE && aa->aa_size == HW_DIGFILT_SIZE) 192 return 1; 193 else 194 return 0; 195 } 196 197 static void 198 digfilt_attach(device_t parent, device_t self, void *aux) 199 { 200 struct apb_softc *sc_parent = device_private(parent); 201 struct digfilt_softc *sc = device_private(self); 202 struct apb_attach_args *aa = aux; 203 static int digfilt_attached = 0; 204 int error; 205 uint32_t v; 206 void *intr; 207 208 sc->sc_dev = self; 209 sc->sc_iot = aa->aa_iot; 210 sc->sc_dmat = aa->aa_dmat; 211 212 /* This driver requires DMA functionality from the bus. 213 * Parent bus passes handle to the DMA controller instance. */ 214 if (sc_parent->dmac == NULL) { 215 aprint_error_dev(sc->sc_dev, "DMA functionality missing\n"); 216 return; 217 } 218 sc->sc_dmac = device_private(sc_parent->dmac); 219 220 if (aa->aa_addr == HW_DIGFILT_BASE && digfilt_attached) { 221 aprint_error_dev(sc->sc_dev, "DIGFILT already attached\n"); 222 return; 223 } 224 225 /* Allocate DMA for audio buffer. */ 226 error = bus_dmamap_create(sc->sc_dmat, MAXPHYS, DIGFILT_DMA_NSEGS, 227 MAXPHYS, 0, BUS_DMA_NOWAIT|BUS_DMA_ALLOCNOW, &sc->sc_dmamp); 228 if (error) { 229 aprint_error_dev(sc->sc_dev, 230 "Unable to allocate DMA handle\n"); 231 return; 232 } 233 234 /* Allocate for DMA chain. */ 235 error = bus_dmamap_create(sc->sc_dmat, MAXPHYS, DIGFILT_DMA_NSEGS, 236 MAXPHYS, 0, BUS_DMA_NOWAIT|BUS_DMA_ALLOCNOW, &sc->sc_c_dmamp); 237 if (error) { 238 aprint_error_dev(sc->sc_dev, 239 "Unable to allocate DMA handle\n"); 240 return; 241 } 242 243 /* Map DIGFILT bus space. */ 244 if (bus_space_map(sc->sc_iot, HW_DIGFILT_BASE, HW_DIGFILT_SIZE, 0, 245 &sc->sc_hdl)) { 246 aprint_error_dev(sc->sc_dev, 247 "Unable to map DIGFILT bus space\n"); 248 return; 249 } 250 251 /* Map AUDIOOUT subregion from parent bus space. */ 252 if (bus_space_subregion(sc->sc_iot, sc->sc_hdl, 253 (HW_AUDIOOUT_BASE - HW_DIGFILT_BASE), HW_AUDIOOUT_SIZE, 254 &sc->sc_aohdl)) { 255 aprint_error_dev(sc->sc_dev, 256 "Unable to submap AUDIOOUT bus space\n"); 257 return; 258 } 259 260 /* Map AUDIOIN subregion from parent bus space. */ 261 if (bus_space_subregion(sc->sc_iot, sc->sc_hdl, 262 (HW_AUDIOIN_BASE - HW_DIGFILT_BASE), HW_AUDIOIN_SIZE, 263 &sc->sc_aihdl)) { 264 aprint_error_dev(sc->sc_dev, 265 "Unable to submap AUDIOIN bus space\n"); 266 return; 267 } 268 269 /* Enable clocks to the DIGFILT block. */ 270 clkctrl_en_filtclk(); 271 delay(10); 272 273 digfilt_ao_reset(sc); /* Reset AUDIOOUT. */ 274 /* Not yet: digfilt_ai_reset(sc); */ 275 276 v = AO_RD(sc, HW_AUDIOOUT_VERSION); 277 aprint_normal(": DIGFILT Block v%" __PRIuBIT ".%" __PRIuBIT 278 ".%" __PRIuBIT "\n", 279 __SHIFTOUT(v, HW_AUDIOOUT_VERSION_MAJOR), 280 __SHIFTOUT(v, HW_AUDIOOUT_VERSION_MINOR), 281 __SHIFTOUT(v, HW_AUDIOOUT_VERSION_STEP)); 282 283 digfilt_ao_init(sc); 284 digfilt_ao_set_rate(sc, 44100); /* Default sample rate 44.1 kHz. */ 285 286 mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_NONE); 287 mutex_init(&sc->sc_intr_lock, MUTEX_DEFAULT, IPL_SCHED); 288 289 /* HW supported formats. */ 290 sc->sc_format.mode = AUMODE_PLAY|AUMODE_RECORD; 291 sc->sc_format.encoding = AUDIO_ENCODING_SLINEAR_LE; 292 sc->sc_format.validbits = 16; 293 sc->sc_format.precision = 16; 294 sc->sc_format.channels = 2; 295 sc->sc_format.channel_mask = AUFMT_STEREO; 296 sc->sc_format.frequency_type = 8; 297 sc->sc_format.frequency[0] = 8000; 298 sc->sc_format.frequency[1] = 11025; 299 sc->sc_format.frequency[2] = 12000; 300 sc->sc_format.frequency[3] = 16000; 301 sc->sc_format.frequency[4] = 22050; 302 sc->sc_format.frequency[5] = 24000; 303 sc->sc_format.frequency[6] = 32000; 304 sc->sc_format.frequency[7] = 44100; 305 306 sc->sc_audiodev = audio_attach_mi(&digfilt_hw_if, sc, sc->sc_dev); 307 308 /* Default mutes. */ 309 sc->sc_mute = DIGFILT_MUTE_LINE; 310 digfilt_ao_apply_mutes(sc); 311 312 /* Allocate DMA safe memory for the DMA chain. */ 313 sc->sc_dmachain = digfilt_ao_alloc_dmachain(sc, 314 sizeof(struct apbdma_command) * DIGFILT_DMA_CHAIN_LENGTH); 315 if (sc->sc_dmachain == NULL) { 316 aprint_error_dev(self, "digfilt_ao_alloc_dmachain failed\n"); 317 return; 318 } 319 320 intr = intr_establish(IRQ_DAC_DMA, IPL_SCHED, IST_LEVEL, dac_dma_intr, 321 sc); 322 if (intr == NULL) { 323 aprint_error_dev(sc->sc_dev, 324 "Unable to establish IRQ for DAC_DMA\n"); 325 return; 326 } 327 328 intr = intr_establish(IRQ_DAC_ERROR, IPL_SCHED, IST_LEVEL, 329 dac_error_intr, sc); 330 if (intr == NULL) { 331 aprint_error_dev(sc->sc_dev, 332 "Unable to establish IRQ for DAC_ERROR\n"); 333 return; 334 } 335 336 /* Initialize DMA channel. */ 337 apbdma_chan_init(sc->sc_dmac, DIGFILT_DMA_CHANNEL); 338 339 digfilt_attached = 1; 340 341 return; 342 } 343 344 static int 345 digfilt_activate(device_t self, enum devact act) 346 { 347 return EOPNOTSUPP; 348 } 349 350 static int 351 digfilt_query_format(void *priv, audio_format_query_t *afp) 352 { 353 struct digfilt_softc *sc = priv; 354 355 return audio_query_format(&sc->sc_format, 1, afp); 356 } 357 358 static int 359 digfilt_set_format(void *priv, int setmode, 360 const audio_params_t *play, const audio_params_t *rec, 361 audio_filter_reg_t *pfil, audio_filter_reg_t *rfil) 362 { 363 struct digfilt_softc *sc = priv; 364 365 if ((setmode & AUMODE_PLAY)) { 366 /* At this point bitrate should be figured out. */ 367 digfilt_ao_set_rate(sc, sc->sc_pparam.sample_rate); 368 } 369 370 return 0; 371 } 372 373 static int 374 digfilt_round_blocksize(void *priv, int bs, int mode, 375 const audio_params_t *param) 376 { 377 int blocksize; 378 379 if (bs > DIGFILT_BLOCKSIZE_MAX) 380 blocksize = DIGFILT_BLOCKSIZE_MAX; 381 else 382 blocksize = bs & ~(DIGFILT_BLOCKSIZE_ROUND-1); 383 if (blocksize < DIGFILT_BLOCKSIZE_ROUND) 384 blocksize = DIGFILT_BLOCKSIZE_ROUND; 385 386 return blocksize; 387 } 388 389 static int 390 digfilt_init_output(void *priv, void *buffer, int size) 391 { 392 struct digfilt_softc *sc = priv; 393 apbdma_command_t dma_cmd; 394 int i; 395 dma_cmd = sc->sc_dmachain; 396 sc->sc_cmd_index = 0; 397 398 /* 399 * Build circular DMA command chain template for later use. 400 */ 401 for (i = 0; i < DIGFILT_DMA_CHAIN_LENGTH; i++) { 402 /* Last entry loops back to first. */ 403 if (i == DIGFILT_DMA_CHAIN_LENGTH - 1) 404 dma_cmd[i].next = (void *)(sc->sc_c_dmamp->dm_segs[0].ds_addr); 405 else 406 dma_cmd[i].next = (void *)(sc->sc_c_dmamp->dm_segs[0].ds_addr + (sizeof(struct apbdma_command) * (1 + i))); 407 408 dma_cmd[i].control = __SHIFTIN(DIGFILT_BLOCKSIZE_MAX, APBDMA_CMD_XFER_COUNT) | 409 __SHIFTIN(1, APBDMA_CMD_CMDPIOWORDS) | 410 APBDMA_CMD_SEMAPHORE | 411 APBDMA_CMD_IRQONCMPLT | 412 APBDMA_CMD_CHAIN | 413 __SHIFTIN(APBDMA_CMD_DMA_READ, APBDMA_CMD_COMMAND); 414 415 dma_cmd[i].buffer = (void *)(sc->sc_c_dmamp->dm_segs[0].ds_addr); 416 417 dma_cmd[i].pio_words[0] = HW_AUDIOOUT_CTRL_WORD_LENGTH | 418 HW_AUDIOOUT_CTRL_FIFO_ERROR_IRQ_EN | 419 HW_AUDIOOUT_CTRL_RUN; 420 421 } 422 423 apbdma_chan_set_chain(sc->sc_dmac, DIGFILT_DMA_CHANNEL, sc->sc_c_dmamp); 424 425 return 0; 426 } 427 428 static int 429 digfilt_start_output(void *priv, void *start, int bs, void (*intr)(void*), void *intarg) 430 { 431 struct digfilt_softc *sc = priv; 432 apbdma_command_t dma_cmd; 433 bus_addr_t offset; 434 435 sc->sc_intr = intr; 436 sc->sc_intarg = intarg; 437 dma_cmd = sc->sc_dmachain; 438 439 offset = (bus_addr_t)start - (bus_addr_t)sc->sc_buffer; 440 441 dma_cmd[sc->sc_cmd_index].buffer = 442 (void *)((bus_addr_t)sc->sc_dmamp->dm_segs[0].ds_addr + offset); 443 444 bus_dmamap_sync(sc->sc_dmat, sc->sc_dmamp, offset, bs, BUS_DMASYNC_PREWRITE); 445 bus_dmamap_sync(sc->sc_dmat, sc->sc_c_dmamp, 446 sizeof(struct apbdma_command) * sc->sc_cmd_index, sizeof(struct apbdma_command), BUS_DMASYNC_PREWRITE); 447 448 sc->sc_cmd_index++; 449 if (sc->sc_cmd_index > DIGFILT_DMA_CHAIN_LENGTH - 1) 450 sc->sc_cmd_index = 0; 451 452 apbdma_run(sc->sc_dmac, DIGFILT_DMA_CHANNEL); 453 454 return 0; 455 } 456 457 static int 458 digfilt_halt_output(void *priv) 459 { 460 struct digfilt_softc *sc = priv; 461 462 sc->sc_cmd_index = 0; 463 return 0; 464 } 465 466 static int 467 digfilt_getdev(void *priv, struct audio_device *ad) 468 { 469 struct digfilt_softc *sc = priv; 470 471 strncpy(ad->name, device_xname(sc->sc_dev), MAX_AUDIO_DEV_LEN); 472 strncpy(ad->version, "", MAX_AUDIO_DEV_LEN); 473 strncpy(ad->config, "", MAX_AUDIO_DEV_LEN); 474 475 return 0; 476 } 477 478 static int 479 digfilt_set_port(void *priv, mixer_ctrl_t *mc) 480 { 481 struct digfilt_softc *sc = priv; 482 uint32_t val; 483 uint8_t nvol; 484 485 switch (mc->dev) { 486 case DIGFILT_OUTPUT_DAC_VOLUME: 487 val = AO_RD(sc, HW_AUDIOOUT_DACVOLUME); 488 val &= ~(HW_AUDIOOUT_DACVOLUME_VOLUME_LEFT | 489 HW_AUDIOOUT_DACVOLUME_VOLUME_RIGHT); 490 491 /* DAC volume field is 8 bits. */ 492 nvol = mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT]; 493 if (nvol > 0xff) 494 nvol = 0xff; 495 496 val |= __SHIFTIN(nvol, HW_AUDIOOUT_DACVOLUME_VOLUME_LEFT); 497 498 nvol = mc->un.value.level[AUDIO_MIXER_LEVEL_RIGHT]; 499 if (nvol > 0xff) 500 nvol = 0xff; 501 502 val |= __SHIFTIN(nvol, HW_AUDIOOUT_DACVOLUME_VOLUME_RIGHT); 503 504 AO_WR(sc, HW_AUDIOOUT_DACVOLUME, val); 505 506 return 0; 507 508 case DIGFILT_OUTPUT_HP_VOLUME: 509 val = AO_RD(sc, HW_AUDIOOUT_HPVOL); 510 val &= ~(HW_AUDIOOUT_HPVOL_VOL_LEFT | 511 HW_AUDIOOUT_HPVOL_VOL_RIGHT); 512 513 /* HP volume field is 7 bits. */ 514 nvol = mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT]; 515 if (nvol > 0x7f) 516 nvol = 0x7f; 517 518 nvol = ~nvol; 519 val |= __SHIFTIN(nvol, HW_AUDIOOUT_HPVOL_VOL_LEFT); 520 521 nvol = mc->un.value.level[AUDIO_MIXER_LEVEL_RIGHT]; 522 if (nvol > 0x7f) 523 nvol = 0x7f; 524 525 nvol = ~nvol; 526 val |= __SHIFTIN(nvol, HW_AUDIOOUT_HPVOL_VOL_RIGHT); 527 528 AO_WR(sc, HW_AUDIOOUT_HPVOL, val); 529 530 return 0; 531 532 case DIGFILT_OUTPUT_LINE_VOLUME: 533 return 1; 534 535 case DIGFILT_OUTPUT_DAC_MUTE: 536 if (mc->un.ord) 537 sc->sc_mute |= DIGFILT_MUTE_DAC; 538 else 539 sc->sc_mute &= ~DIGFILT_MUTE_DAC; 540 541 digfilt_ao_apply_mutes(sc); 542 543 return 0; 544 545 case DIGFILT_OUTPUT_HP_MUTE: 546 if (mc->un.ord) 547 sc->sc_mute |= DIGFILT_MUTE_HP; 548 else 549 sc->sc_mute &= ~DIGFILT_MUTE_HP; 550 551 digfilt_ao_apply_mutes(sc); 552 553 return 0; 554 555 case DIGFILT_OUTPUT_LINE_MUTE: 556 if (mc->un.ord) 557 sc->sc_mute |= DIGFILT_MUTE_LINE; 558 else 559 sc->sc_mute &= ~DIGFILT_MUTE_LINE; 560 561 digfilt_ao_apply_mutes(sc); 562 563 return 0; 564 } 565 566 return ENXIO; 567 } 568 569 static int 570 digfilt_get_port(void *priv, mixer_ctrl_t *mc) 571 { 572 struct digfilt_softc *sc = priv; 573 uint32_t val; 574 uint8_t nvol; 575 576 switch (mc->dev) { 577 case DIGFILT_OUTPUT_DAC_VOLUME: 578 val = AO_RD(sc, HW_AUDIOOUT_DACVOLUME); 579 580 nvol = __SHIFTOUT(val, HW_AUDIOOUT_DACVOLUME_VOLUME_LEFT); 581 mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT] = nvol; 582 583 nvol = __SHIFTOUT(val, HW_AUDIOOUT_DACVOLUME_VOLUME_RIGHT); 584 mc->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = nvol; 585 586 return 0; 587 588 case DIGFILT_OUTPUT_HP_VOLUME: 589 val = AO_RD(sc, HW_AUDIOOUT_HPVOL); 590 591 nvol = __SHIFTOUT(val, HW_AUDIOOUT_HPVOL_VOL_LEFT); 592 mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT] = ~nvol & 0x7f; 593 594 nvol = __SHIFTOUT(val, HW_AUDIOOUT_HPVOL_VOL_RIGHT); 595 mc->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = ~nvol & 0x7f; 596 597 return 0; 598 599 case DIGFILT_OUTPUT_LINE_VOLUME: 600 mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT] = 255; 601 mc->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = 255; 602 603 return 0; 604 605 case DIGFILT_OUTPUT_DAC_MUTE: 606 val = AO_RD(sc, HW_AUDIOOUT_DACVOLUME); 607 608 mc->un.ord = (val & (HW_AUDIOOUT_DACVOLUME_MUTE_LEFT | 609 HW_AUDIOOUT_DACVOLUME_MUTE_RIGHT)) ? 1 : 0; 610 611 return 0; 612 613 case DIGFILT_OUTPUT_HP_MUTE: 614 val = AO_RD(sc, HW_AUDIOOUT_HPVOL); 615 616 mc->un.ord = (val & HW_AUDIOOUT_HPVOL_MUTE) ? 1 : 0; 617 618 return 0; 619 620 case DIGFILT_OUTPUT_LINE_MUTE: 621 val = AO_RD(sc, HW_AUDIOOUT_SPEAKERCTRL); 622 623 mc->un.ord = (val & HW_AUDIOOUT_SPEAKERCTRL_MUTE) ? 1 : 0; 624 625 return 0; 626 } 627 628 return ENXIO; 629 } 630 631 static int 632 digfilt_query_devinfo(void *priv, mixer_devinfo_t *di) 633 { 634 635 switch (di->index) { 636 case DIGFILT_OUTPUT_CLASS: 637 di->mixer_class = DIGFILT_OUTPUT_CLASS; 638 strcpy(di->label.name, AudioCoutputs); 639 di->type = AUDIO_MIXER_CLASS; 640 di->next = di->prev = AUDIO_MIXER_LAST; 641 return 0; 642 643 case DIGFILT_OUTPUT_DAC_VOLUME: 644 di->mixer_class = DIGFILT_OUTPUT_CLASS; 645 strcpy(di->label.name, AudioNdac); 646 di->type = AUDIO_MIXER_VALUE; 647 di->prev = AUDIO_MIXER_LAST; 648 di->next = DIGFILT_OUTPUT_DAC_MUTE; 649 di->un.v.num_channels = 2; 650 strcpy(di->un.v.units.name, AudioNvolume); 651 return 0; 652 653 case DIGFILT_OUTPUT_DAC_MUTE: 654 di->mixer_class = DIGFILT_OUTPUT_CLASS; 655 di->type = AUDIO_MIXER_ENUM; 656 di->prev = DIGFILT_OUTPUT_DAC_VOLUME; 657 di->next = AUDIO_MIXER_LAST; 658 mute: 659 strlcpy(di->label.name, AudioNmute, sizeof(di->label.name)); 660 di->un.e.num_mem = 2; 661 strlcpy(di->un.e.member[0].label.name, AudioNon, 662 sizeof(di->un.e.member[0].label.name)); 663 di->un.e.member[0].ord = 1; 664 strlcpy(di->un.e.member[1].label.name, AudioNoff, 665 sizeof(di->un.e.member[1].label.name)); 666 di->un.e.member[1].ord = 0; 667 return 0; 668 669 case DIGFILT_OUTPUT_HP_VOLUME: 670 di->mixer_class = DIGFILT_OUTPUT_CLASS; 671 strcpy(di->label.name, AudioNheadphone); 672 di->type = AUDIO_MIXER_VALUE; 673 di->prev = AUDIO_MIXER_LAST; 674 di->next = DIGFILT_OUTPUT_HP_MUTE; 675 di->un.v.num_channels = 2; 676 strcpy(di->un.v.units.name, AudioNvolume); 677 return 0; 678 679 case DIGFILT_OUTPUT_HP_MUTE: 680 di->mixer_class = DIGFILT_OUTPUT_CLASS; 681 di->type = AUDIO_MIXER_ENUM; 682 di->prev = DIGFILT_OUTPUT_HP_VOLUME; 683 di->next = AUDIO_MIXER_LAST; 684 goto mute; 685 686 case DIGFILT_OUTPUT_LINE_VOLUME: 687 di->mixer_class = DIGFILT_OUTPUT_CLASS; 688 strcpy(di->label.name, AudioNline); 689 di->type = AUDIO_MIXER_VALUE; 690 di->prev = AUDIO_MIXER_LAST; 691 di->next = DIGFILT_OUTPUT_LINE_MUTE; 692 di->un.v.num_channels = 2; 693 strcpy(di->un.v.units.name, AudioNvolume); 694 return 0; 695 696 case DIGFILT_OUTPUT_LINE_MUTE: 697 di->mixer_class = DIGFILT_OUTPUT_CLASS; 698 di->type = AUDIO_MIXER_ENUM; 699 di->prev = DIGFILT_OUTPUT_LINE_VOLUME; 700 di->next = AUDIO_MIXER_LAST; 701 goto mute; 702 } 703 704 return ENXIO; 705 } 706 707 static void * 708 digfilt_allocm(void *priv, int direction, size_t size) 709 { 710 struct digfilt_softc *sc = priv; 711 int rsegs; 712 int error; 713 714 sc->sc_buffer = NULL; 715 716 /* 717 * AUMODE_PLAY is DMA from memory to device. 718 */ 719 if (direction != AUMODE_PLAY) 720 return NULL; 721 722 error = bus_dmamem_alloc(sc->sc_dmat, size, PAGE_SIZE, 0, &sc->sc_ds[0], DIGFILT_DMA_NSEGS, &rsegs, BUS_DMA_NOWAIT); 723 if (error) { 724 aprint_error_dev(sc->sc_dev, 725 "bus_dmamem_alloc: %d\n", error); 726 goto out; 727 } 728 729 error = bus_dmamem_map(sc->sc_dmat, sc->sc_ds, DIGFILT_DMA_NSEGS, size, &sc->sc_buffer, BUS_DMA_NOWAIT); 730 if (error) { 731 aprint_error_dev(sc->sc_dev, "bus_dmamem_map: %d\n", error); 732 goto dmamem_free; 733 } 734 735 /* After load sc_dmamp is valid. */ 736 error = bus_dmamap_load(sc->sc_dmat, sc->sc_dmamp, sc->sc_buffer, size, NULL, BUS_DMA_NOWAIT|BUS_DMA_WRITE); 737 if (error) { 738 aprint_error_dev(sc->sc_dev, "bus_dmamap_load: %d\n", error); 739 goto dmamem_unmap; 740 } 741 742 memset(sc->sc_buffer, 0x00, size); 743 744 return sc->sc_buffer; 745 746 dmamem_unmap: 747 bus_dmamem_unmap(sc->sc_dmat, sc->sc_buffer, size); 748 dmamem_free: 749 bus_dmamem_free(sc->sc_dmat, sc->sc_ds, DIGFILT_DMA_NSEGS); 750 out: 751 return NULL; 752 } 753 754 static void 755 digfilt_freem(void *priv, void *kvap, size_t size) 756 { 757 struct digfilt_softc *sc = priv; 758 759 bus_dmamem_unmap(sc->sc_dmat, kvap, size); 760 bus_dmamem_free(sc->sc_dmat, sc->sc_ds, DIGFILT_DMA_NSEGS); 761 762 return; 763 } 764 765 static size_t 766 digfilt_round_buffersize(void *hdl, int direction, size_t bs) 767 { 768 int bufsize; 769 770 bufsize = bs & ~(DIGFILT_BLOCKSIZE_MAX-1); 771 772 return bufsize; 773 } 774 775 static int 776 digfilt_get_props(void *sc) 777 { 778 return (AUDIO_PROP_PLAYBACK | AUDIO_PROP_INDEPENDENT); 779 } 780 781 static void 782 digfilt_get_locks(void *priv, kmutex_t **intr, kmutex_t **thread) 783 { 784 struct digfilt_softc *sc = priv; 785 786 *intr = &sc->sc_intr_lock; 787 *thread = &sc->sc_lock; 788 789 return; 790 } 791 792 /* 793 * IRQ for DAC error. 794 */ 795 static int 796 dac_error_intr(void *arg) 797 { 798 struct digfilt_softc *sc = arg; 799 AO_WR(sc, HW_AUDIOOUT_CTRL_CLR, HW_AUDIOOUT_CTRL_FIFO_UNDERFLOW_IRQ); 800 return 1; 801 } 802 803 /* 804 * IRQ from DMA. 805 */ 806 static int 807 dac_dma_intr(void *arg) 808 { 809 struct digfilt_softc *sc = arg; 810 811 unsigned int dma_err; 812 813 mutex_enter(&sc->sc_intr_lock); 814 815 dma_err = apbdma_intr_status(sc->sc_dmac, DIGFILT_DMA_CHANNEL); 816 817 if (dma_err) { 818 apbdma_ack_error_intr(sc->sc_dmac, DIGFILT_DMA_CHANNEL); 819 } 820 821 sc->sc_intr(sc->sc_intarg); 822 apbdma_ack_intr(sc->sc_dmac, DIGFILT_DMA_CHANNEL); 823 824 mutex_exit(&sc->sc_intr_lock); 825 826 /* Return 1 to acknowledge IRQ. */ 827 return 1; 828 } 829 830 static void * 831 digfilt_ao_alloc_dmachain(void *priv, size_t size) 832 { 833 struct digfilt_softc *sc = priv; 834 int rsegs; 835 int error; 836 void *kvap; 837 838 kvap = NULL; 839 840 error = bus_dmamem_alloc(sc->sc_dmat, size, PAGE_SIZE, 0, &sc->sc_c_ds[0], DIGFILT_DMA_NSEGS, &rsegs, BUS_DMA_NOWAIT); 841 if (error) { 842 aprint_error_dev(sc->sc_dev, 843 "bus_dmamem_alloc: %d\n", error); 844 goto out; 845 } 846 847 error = bus_dmamem_map(sc->sc_dmat, sc->sc_c_ds, DIGFILT_DMA_NSEGS, size, &kvap, BUS_DMA_NOWAIT); 848 if (error) { 849 aprint_error_dev(sc->sc_dev, "bus_dmamem_map: %d\n", error); 850 goto dmamem_free; 851 } 852 853 /* After load sc_c_dmamp is valid. */ 854 error = bus_dmamap_load(sc->sc_dmat, sc->sc_c_dmamp, kvap, size, NULL, BUS_DMA_NOWAIT|BUS_DMA_WRITE); 855 if (error) { 856 aprint_error_dev(sc->sc_dev, "bus_dmamap_load: %d\n", error); 857 goto dmamem_unmap; 858 } 859 860 memset(kvap, 0x00, size); 861 862 return kvap; 863 864 dmamem_unmap: 865 bus_dmamem_unmap(sc->sc_dmat, kvap, size); 866 dmamem_free: 867 bus_dmamem_free(sc->sc_dmat, sc->sc_c_ds, DIGFILT_DMA_NSEGS); 868 out: 869 870 return kvap; 871 } 872 873 static void 874 digfilt_ao_apply_mutes(struct digfilt_softc *sc) 875 { 876 877 /* DAC. */ 878 if (sc->sc_mute & DIGFILT_MUTE_DAC) { 879 AO_WR(sc, HW_AUDIOOUT_DACVOLUME_SET, 880 HW_AUDIOOUT_DACVOLUME_MUTE_LEFT | 881 HW_AUDIOOUT_DACVOLUME_MUTE_RIGHT 882 ); 883 884 } else { 885 AO_WR(sc, HW_AUDIOOUT_DACVOLUME_CLR, 886 HW_AUDIOOUT_DACVOLUME_MUTE_LEFT | 887 HW_AUDIOOUT_DACVOLUME_MUTE_RIGHT 888 ); 889 } 890 891 /* HP. */ 892 if (sc->sc_mute & DIGFILT_MUTE_HP) 893 AO_WR(sc, HW_AUDIOOUT_HPVOL_SET, HW_AUDIOOUT_HPVOL_MUTE); 894 else 895 AO_WR(sc, HW_AUDIOOUT_HPVOL_CLR, HW_AUDIOOUT_HPVOL_MUTE); 896 897 /* Line. */ 898 if (sc->sc_mute & DIGFILT_MUTE_LINE) 899 AO_WR(sc, HW_AUDIOOUT_SPEAKERCTRL_SET, 900 HW_AUDIOOUT_SPEAKERCTRL_MUTE); 901 else 902 AO_WR(sc, HW_AUDIOOUT_SPEAKERCTRL_CLR, 903 HW_AUDIOOUT_SPEAKERCTRL_MUTE); 904 905 return; 906 } 907 908 /* 909 * Initialize audio system. 910 */ 911 static void 912 digfilt_ao_init(struct digfilt_softc *sc) 913 { 914 915 AO_WR(sc, HW_AUDIOOUT_ANACLKCTRL_CLR, HW_AUDIOOUT_ANACLKCTRL_CLKGATE); 916 while ((AO_RD(sc, HW_AUDIOOUT_ANACLKCTRL) & 917 HW_AUDIOOUT_ANACLKCTRL_CLKGATE)); 918 919 /* Hold headphones outputs at ground. */ 920 AO_WR(sc, HW_AUDIOOUT_ANACTRL_SET, HW_AUDIOOUT_ANACTRL_HP_HOLD_GND); 921 922 /* Remove pulldown resistors on headphone outputs. */ 923 rtc_release_gnd(1); 924 925 /* Release pull down */ 926 AO_WR(sc, HW_AUDIOOUT_ANACTRL_CLR, HW_AUDIOOUT_ANACTRL_HP_HOLD_GND); 927 928 AO_WR(sc, HW_AUDIOOUT_ANACTRL_SET, HW_AUDIOOUT_ANACTRL_HP_CLASSAB); 929 930 /* Enable Modules. */ 931 AO_WR(sc, HW_AUDIOOUT_PWRDN_CLR, 932 HW_AUDIOOUT_PWRDN_RIGHT_ADC | 933 HW_AUDIOOUT_PWRDN_DAC | 934 HW_AUDIOOUT_PWRDN_CAPLESS | 935 HW_AUDIOOUT_PWRDN_HEADPHONE 936 ); 937 938 return; 939 } 940 941 /* 942 * Reset the AUDIOOUT block. 943 * 944 * Inspired by i.MX23 RM "39.3.10 Correct Way to Soft Reset a Block" 945 */ 946 static void 947 digfilt_ao_reset(struct digfilt_softc *sc) 948 { 949 unsigned int loop; 950 951 /* Prepare for soft-reset by making sure that SFTRST is not currently 952 * asserted. Also clear CLKGATE so we can wait for its assertion below. 953 */ 954 AO_WR(sc, HW_AUDIOOUT_CTRL_CLR, HW_AUDIOOUT_CTRL_SFTRST); 955 956 /* Wait at least a microsecond for SFTRST to deassert. */ 957 loop = 0; 958 while ((AO_RD(sc, HW_AUDIOOUT_CTRL) & HW_AUDIOOUT_CTRL_SFTRST) || 959 (loop < DIGFILT_SOFT_RST_LOOP)) 960 loop++; 961 962 /* Clear CLKGATE so we can wait for its assertion below. */ 963 AO_WR(sc, HW_AUDIOOUT_CTRL_CLR, HW_AUDIOOUT_CTRL_CLKGATE); 964 965 /* Soft-reset the block. */ 966 AO_WR(sc, HW_AUDIOOUT_CTRL_SET, HW_AUDIOOUT_CTRL_SFTRST); 967 968 /* Wait until clock is in the gated state. */ 969 while (!(AO_RD(sc, HW_AUDIOOUT_CTRL) & HW_AUDIOOUT_CTRL_CLKGATE)); 970 971 /* Bring block out of reset. */ 972 AO_WR(sc, HW_AUDIOOUT_CTRL_CLR, HW_AUDIOOUT_CTRL_SFTRST); 973 974 loop = 0; 975 while ((AO_RD(sc, HW_AUDIOOUT_CTRL) & HW_AUDIOOUT_CTRL_SFTRST) || 976 (loop < DIGFILT_SOFT_RST_LOOP)) 977 loop++; 978 979 AO_WR(sc, HW_AUDIOOUT_CTRL_CLR, HW_AUDIOOUT_CTRL_CLKGATE); 980 981 /* Wait until clock is in the NON-gated state. */ 982 while (AO_RD(sc, HW_AUDIOOUT_CTRL) & HW_AUDIOOUT_CTRL_CLKGATE); 983 984 return; 985 } 986 987 static void 988 digfilt_ao_set_rate(struct digfilt_softc *sc, int sr) 989 { 990 uint32_t val; 991 992 993 val = AO_RD(sc, HW_AUDIOOUT_DACSRR); 994 995 996 val &= ~(HW_AUDIOOUT_DACSRR_BASEMULT | HW_AUDIOOUT_DACSRR_SRC_HOLD | 997 HW_AUDIOOUT_DACSRR_SRC_INT | HW_AUDIOOUT_DACSRR_SRC_FRAC); 998 999 switch(sr) { 1000 case 8000: 1001 val |= (__SHIFTIN(0x1 ,HW_AUDIOOUT_DACSRR_BASEMULT) | 1002 __SHIFTIN(0x3, HW_AUDIOOUT_DACSRR_SRC_HOLD) | 1003 __SHIFTIN(0x17, HW_AUDIOOUT_DACSRR_SRC_INT) | 1004 __SHIFTIN(0x0E00, HW_AUDIOOUT_DACSRR_SRC_FRAC)); 1005 break; 1006 case 11025: 1007 val |= (__SHIFTIN(0x1 ,HW_AUDIOOUT_DACSRR_BASEMULT) | 1008 __SHIFTIN(0x3, HW_AUDIOOUT_DACSRR_SRC_HOLD) | 1009 __SHIFTIN(0x11, HW_AUDIOOUT_DACSRR_SRC_INT) | 1010 __SHIFTIN(0x0037, HW_AUDIOOUT_DACSRR_SRC_FRAC)); 1011 break; 1012 case 12000: 1013 val |= (__SHIFTIN(0x1 ,HW_AUDIOOUT_DACSRR_BASEMULT) | 1014 __SHIFTIN(0x3, HW_AUDIOOUT_DACSRR_SRC_HOLD) | 1015 __SHIFTIN(0x0F, HW_AUDIOOUT_DACSRR_SRC_INT) | 1016 __SHIFTIN(0x13FF, HW_AUDIOOUT_DACSRR_SRC_FRAC)); 1017 break; 1018 case 16000: 1019 val |= (__SHIFTIN(0x1 ,HW_AUDIOOUT_DACSRR_BASEMULT) | 1020 __SHIFTIN(0x1, HW_AUDIOOUT_DACSRR_SRC_HOLD) | 1021 __SHIFTIN(0x17, HW_AUDIOOUT_DACSRR_SRC_INT) | 1022 __SHIFTIN(0x0E00, HW_AUDIOOUT_DACSRR_SRC_FRAC)); 1023 break; 1024 case 22050: 1025 val |= (__SHIFTIN(0x1 ,HW_AUDIOOUT_DACSRR_BASEMULT) | 1026 __SHIFTIN(0x1, HW_AUDIOOUT_DACSRR_SRC_HOLD) | 1027 __SHIFTIN(0x11, HW_AUDIOOUT_DACSRR_SRC_INT) | 1028 __SHIFTIN(0x0037, HW_AUDIOOUT_DACSRR_SRC_FRAC)); 1029 break; 1030 case 24000: 1031 val |= (__SHIFTIN(0x1 ,HW_AUDIOOUT_DACSRR_BASEMULT) | 1032 __SHIFTIN(0x1, HW_AUDIOOUT_DACSRR_SRC_HOLD) | 1033 __SHIFTIN(0x0F, HW_AUDIOOUT_DACSRR_SRC_INT) | 1034 __SHIFTIN(0x13FF, HW_AUDIOOUT_DACSRR_SRC_FRAC)); 1035 break; 1036 case 32000: 1037 val |= (__SHIFTIN(0x1 ,HW_AUDIOOUT_DACSRR_BASEMULT) | 1038 __SHIFTIN(0x0, HW_AUDIOOUT_DACSRR_SRC_HOLD) | 1039 __SHIFTIN(0x17, HW_AUDIOOUT_DACSRR_SRC_INT) | 1040 __SHIFTIN(0x0E00, HW_AUDIOOUT_DACSRR_SRC_FRAC)); 1041 break; 1042 default: 1043 aprint_error_dev(sc->sc_dev, "uknown sample rate: %d\n", sr); 1044 case 44100: 1045 val |= (__SHIFTIN(0x1 ,HW_AUDIOOUT_DACSRR_BASEMULT) | 1046 __SHIFTIN(0x0, HW_AUDIOOUT_DACSRR_SRC_HOLD) | 1047 __SHIFTIN(0x11, HW_AUDIOOUT_DACSRR_SRC_INT) | 1048 __SHIFTIN(0x0037, HW_AUDIOOUT_DACSRR_SRC_FRAC)); 1049 break; 1050 } 1051 1052 AO_WR(sc, HW_AUDIOOUT_DACSRR, val); 1053 1054 val = AO_RD(sc, HW_AUDIOOUT_DACSRR); 1055 1056 return; 1057 } 1058 #if 0 1059 /* 1060 * Reset the AUDIOIN block. 1061 * 1062 * Inspired by i.MX23 RM "39.3.10 Correct Way to Soft Reset a Block" 1063 */ 1064 static void 1065 digfilt_ai_reset(struct digfilt_softc *sc) 1066 { 1067 unsigned int loop; 1068 1069 /* Prepare for soft-reset by making sure that SFTRST is not currently 1070 * asserted. Also clear CLKGATE so we can wait for its assertion below. 1071 */ 1072 AI_WR(sc, HW_AUDIOIN_CTRL_CLR, HW_AUDIOIN_CTRL_SFTRST); 1073 1074 /* Wait at least a microsecond for SFTRST to deassert. */ 1075 loop = 0; 1076 while ((AI_RD(sc, HW_AUDIOIN_CTRL) & HW_AUDIOIN_CTRL_SFTRST) || 1077 (loop < DIGFILT_SOFT_RST_LOOP)) 1078 loop++; 1079 1080 /* Clear CLKGATE so we can wait for its assertion below. */ 1081 AI_WR(sc, HW_AUDIOIN_CTRL_CLR, HW_AUDIOIN_CTRL_CLKGATE); 1082 1083 /* Soft-reset the block. */ 1084 AI_WR(sc, HW_AUDIOIN_CTRL_SET, HW_AUDIOIN_CTRL_SFTRST); 1085 1086 /* Wait until clock is in the gated state. */ 1087 while (!(AI_RD(sc, HW_AUDIOIN_CTRL) & HW_AUDIOIN_CTRL_CLKGATE)); 1088 1089 /* Bring block out of reset. */ 1090 AI_WR(sc, HW_AUDIOIN_CTRL_CLR, HW_AUDIOIN_CTRL_SFTRST); 1091 1092 loop = 0; 1093 while ((AI_RD(sc, HW_AUDIOIN_CTRL) & HW_AUDIOIN_CTRL_SFTRST) || 1094 (loop < DIGFILT_SOFT_RST_LOOP)) 1095 loop++; 1096 1097 AI_WR(sc, HW_AUDIOIN_CTRL_CLR, HW_AUDIOIN_CTRL_CLKGATE); 1098 1099 /* Wait until clock is in the NON-gated state. */ 1100 while (AI_RD(sc, HW_AUDIOIN_CTRL) & HW_AUDIOIN_CTRL_CLKGATE); 1101 1102 return; 1103 } 1104 #endif 1105