1 /* $OpenBSD: auglx.c,v 1.13 2015/05/11 06:46:21 ratchov Exp $ */ 2 3 /* 4 * Copyright (c) 2008 Marc Balmer <mbalmer@openbsd.org> 5 * All rights reserved. 6 * 7 * Permission to use, copy, modify, and distribute this software for any 8 * purpose with or without fee is hereby granted, provided that the above 9 * copyright notice and this permission notice appear in all copies. 10 * 11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15 * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER IN 16 * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT 17 * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 */ 19 20 /* 21 * AMD CS5536 series AC'97 audio driver. 22 * 23 * The following datasheets were helpful in the development of this 24 * driver: 25 * 26 * AMD Geode LX Processors Data Book 27 * http://www.amd.com/files/connectivitysolutions/geode/geode_lx/\ 28 * 33234F_LX_databook.pdf 29 * 30 * AMD Geode CS5536 Companion Device Data Book 31 * http://www.amd.com/files/connectivitysolutions/geode/geode_lx/\ 32 * 33238G_cs5536_db.pdf 33 * 34 * Realtek ALC203 Two-Channel AC'97 2.3 Audio Codec 35 * ftp://202.65.194.211/pc/audio/ALC203_DataSheet_1.6.pdf 36 * 37 * This driver is inspired by the auich(4) and auixp(4) drivers, some 38 * of the hardware-independent functionality has been derived from them 39 * (e.g. memory allocation for the upper level, parameter setting). 40 */ 41 42 #include <sys/param.h> 43 #include <sys/systm.h> 44 #include <sys/device.h> 45 #include <sys/malloc.h> 46 #include <sys/audioio.h> 47 48 #include <machine/bus.h> 49 50 #include <dev/pci/pcireg.h> 51 #include <dev/pci/pcivar.h> 52 #include <dev/pci/pcidevs.h> 53 #include <dev/audio_if.h> 54 55 #include <dev/ic/ac97.h> 56 57 #define AUGLX_ACC_BAR 0x10 58 59 /* ACC Native Registers */ 60 #define ACC_GPIO_STATUS 0x00 61 #define ACC_GPIO_CNTL 0x04 62 #define ACC_CODEC_STATUS 0x08 63 #define ACC_CODEC_CNTL 0x0c 64 #define ACC_IRQ_STATUS 0x12 65 #define ACC_ENGINE_CNTL 0x14 66 #define ACC_BM0_CMD 0x20 /* Bus Master 0 Command */ 67 #define ACC_BM0_STATUS 0x21 /* Bus Master 0 IRQ Status */ 68 #define ACC_BM0_PRD 0x24 /* BM0 PRD Table Address */ 69 #define ACC_BM1_CMD 0x28 /* Bus Master 1 Command */ 70 #define ACC_BM1_STATUS 0x29 /* Bus Master 1 IRQ Status */ 71 #define ACC_BM1_PRD 0x2c /* BM1 PRD Table Address */ 72 #define ACC_BM2_CMD 0x30 /* Bus Master 2 Command */ 73 #define ACC_BM2_STATUS 0x31 /* Bus Master 2 IRQ Status */ 74 #define ACC_BM2_PRD 0x34 /* BM2 PRD Table Address */ 75 #define ACC_BM3_CMD 0x38 /* Bus Master 3 Command */ 76 #define ACC_BM3_STATUS 0x39 /* Bus Master 3 IRQ Status */ 77 #define ACC_BM3_PRD 0x3c /* BM3 PRD Table Address */ 78 #define ACC_BM4_CMD 0x40 /* Bus Master 4 Command */ 79 #define ACC_BM4_STATUS 0x41 /* Bus Master 4 IRQ Status */ 80 #define ACC_BM4_PRD 0x44 /* BM4 PRD Table Address */ 81 #define ACC_BM5_CMD 0x48 /* Bus Master 5 Command */ 82 #define ACC_BM5_STATUS 0x49 /* Bus Master 5 IRQ Status */ 83 #define ACC_BM5_PRD 0x4c /* BM5 PRD Table Address */ 84 #define ACC_BM6_CMD 0x50 /* Bus Master 6 Command */ 85 #define ACC_BM6_STATUS 0x51 /* Bus Master 6 IRQ Status */ 86 #define ACC_BM6_PRD 0x54 /* BM6 PRD Table Address */ 87 #define ACC_BM7_CMD 0x58 /* Bus Master 7 Command */ 88 #define ACC_BM7_STATUS 0x59 /* Bus Master 7 IRQ Status */ 89 #define ACC_BM7_PRD 0x5c /* BM7 PRD Table Address */ 90 #define ACC_BM0_PNTR 0x60 /* Bus Master 0 DMA Pointer */ 91 #define ACC_BM1_PNTR 0x64 /* Bus Master 1 DMA Pointer */ 92 #define ACC_BM2_PNTR 0x68 /* Bus Master 2 DMA Pointer */ 93 #define ACC_BM3_PNTR 0x6c /* Bus Master 3 DMA Pointer */ 94 #define ACC_BM4_PNTR 0x70 /* Bus Master 4 DMA Pointer */ 95 #define ACC_BM5_PNTR 0x74 /* Bus Master 5 DMA Pointer */ 96 #define ACC_BM6_PNTR 0x78 /* Bus Master 6 DMA Pointer */ 97 #define ACC_BM7_PNTR 0x7c /* Bus Master 7 DMA Pointer */ 98 99 /* ACC_IRQ_STATUS Bit Definitions */ 100 #define BM7_IRQ_STS 0x0200 /* Audio Bus Master 7 IRQ Status */ 101 #define BM6_IRQ_STS 0x0100 /* Audio Bus Master 6 IRQ Status */ 102 #define BM5_IRQ_STS 0x0080 /* Audio Bus Master 5 IRQ Status */ 103 #define BM4_IRQ_STS 0x0040 /* Audio Bus Master 4 IRQ Status */ 104 #define BM3_IRQ_STS 0x0020 /* Audio Bus Master 3 IRQ Status */ 105 #define BM2_IRQ_STS 0x0010 /* Audio Bus Master 2 IRQ Status */ 106 #define BM1_IRQ_STS 0x0008 /* Audio Bus Master 1 IRQ Status */ 107 #define BM0_IRQ_STS 0x0004 /* Audio Bus Master 0 IRQ Status */ 108 #define WU_IRQ_STS 0x0002 /* Codec GPIO Wakeup IRQ Status */ 109 #define IRQ_STS 0x0001 /* Codec GPIO IRQ Status */ 110 111 /* ACC_ENGINE_CNTL Bit Definitions */ 112 #define SSND_MODE 0x00000001 /* Surround Sound (5.1) Sync. Mode */ 113 114 /* ACC_BM[x]_CMD Bit Descriptions */ 115 #define BMx_CMD_RW 0x08 /* 0: Mem to codec, 1: codec to mem */ 116 #define BMx_CMD_BYTE_ORD 0x04 /* 0: Little Endian, 1: Big Endian */ 117 #define BMx_CMD_BM_CTL_DIS 0x00 /* Disable bus master */ 118 #define BMx_CMD_BM_CTL_EN 0x01 /* Enable bus master */ 119 #define BMx_CMD_BM_CTL_PAUSE 0x03 /* Pause bus master */ 120 121 /* ACC_BM[x]_STATUS Bit Definitions */ 122 #define BMx_BM_EOP_ERR 0x02 /* Bus master error */ 123 #define BMx_BM_EOP 0x01 /* End of page */ 124 125 /* ACC_CODEC_CNTL Bit Definitions */ 126 #define RW_CMD 0x80000000 127 #define PD_PRIM 0x00200000 128 #define PD_SEC 0x00100000 129 #define LNK_SHTDOWN 0x00040000 130 #define LNK_WRM_RST 0x00020000 131 #define CMD_NEW 0x00010000 132 133 /* ACC_CODEC_STATUS Bit Definitions */ 134 #define PRM_RDY_STS 0x00800000 135 #define SEC_RDY_STS 0x00400000 136 #define SDATAIN2_EN 0x00200000 137 #define BM5_SEL 0x00100000 138 #define BM4_SEL 0x00080000 139 #define STS_NEW 0x00020000 140 141 #define AUGLX_TOUT 1000 /* uSec */ 142 143 #define AUGLX_DMALIST_MAX 1 144 #define AUGLX_DMASEG_MAX 65536 145 146 struct auglx_prd { 147 u_int32_t base; 148 u_int32_t size; 149 #define AUGLX_PRD_EOT 0x80000000 150 #define AUGLX_PRD_EOP 0x40000000 151 #define AUGLX_PRD_JMP 0x20000000 152 }; 153 154 #define AUGLX_FIXED_RATE 48000 155 156 struct auglx_dma { 157 bus_dmamap_t map; 158 caddr_t addr; 159 bus_dma_segment_t segs[AUGLX_DMALIST_MAX]; 160 int nsegs; 161 size_t size; 162 struct auglx_dma *next; 163 }; 164 165 struct auglx_softc { 166 struct device sc_dev; 167 void *sc_ih; 168 169 audio_device_t sc_audev; 170 171 bus_space_tag_t sc_iot; 172 bus_space_handle_t sc_ioh; 173 bus_dma_tag_t sc_dmat; 174 175 /* 176 * The CS5536 ACC has eight bus masters to support 5.1 audio. 177 * This driver, however, only supports main playback and recording 178 * since I only have a Realtek ALC203 codec available for testing. 179 */ 180 struct auglx_ring { 181 bus_dmamap_t sc_prd; 182 struct auglx_prd *sc_vprd; 183 int sc_nprd; 184 185 size_t sc_size; 186 int nsegs; 187 bus_dma_segment_t seg; 188 189 void (*intr)(void *); 190 void *arg; 191 } bm0, bm1; /* bm0: output, bm1: input */ 192 193 struct auglx_dma *sc_dmas; 194 195 struct ac97_codec_if *codec_if; 196 struct ac97_host_if host_if; 197 198 int sc_dmamap_flags; 199 }; 200 201 #ifdef AUGLX_DEBUG 202 #define DPRINTF(l,x) do { if (auglx_debug & (l)) printf x; } while(0) 203 int auglx_debug = 0; 204 #define AUGLX_DBG_ACC 0x0001 205 #define AUGLX_DBG_DMA 0x0002 206 #define AUGLX_DBG_IRQ 0x0004 207 #else 208 #define DPRINTF(x,y) /* nothing */ 209 #endif 210 211 struct cfdriver auglx_cd = { 212 NULL, "auglx", DV_DULL 213 }; 214 215 int auglx_open(void *, int); 216 void auglx_close(void *); 217 int auglx_query_encoding(void *, struct audio_encoding *); 218 int auglx_set_params(void *, int, int, struct audio_params *, 219 struct audio_params *); 220 int auglx_round_blocksize(void *, int); 221 int auglx_halt_output(void *); 222 int auglx_halt_input(void *); 223 int auglx_getdev(void *, struct audio_device *); 224 int auglx_set_port(void *, mixer_ctrl_t *); 225 int auglx_get_port(void *, mixer_ctrl_t *); 226 int auglx_query_devinfo(void *, mixer_devinfo_t *); 227 void *auglx_allocm(void *, int, size_t, int, int); 228 void auglx_freem(void *, void *, int); 229 size_t auglx_round_buffersize(void *, int, size_t); 230 paddr_t auglx_mappage(void *, void *, off_t, int); 231 int auglx_get_props(void *); 232 int auglx_trigger_output(void *, void *, void *, int, void (*)(void *), 233 void *, struct audio_params *); 234 int auglx_trigger_input(void *, void *, void *, int, void (*)(void *), 235 void *, struct audio_params *); 236 int auglx_alloc_cdata(struct auglx_softc *); 237 int auglx_alloc_prd(struct auglx_softc *, size_t, struct auglx_ring *); 238 void auglx_free_prd(struct auglx_softc *sc, struct auglx_ring *bm); 239 int auglx_allocmem(struct auglx_softc *, size_t, size_t, struct auglx_dma *); 240 void auglx_freemem(struct auglx_softc *, struct auglx_dma *); 241 void auglx_get_default_params(void *, int, struct audio_params *); 242 243 struct audio_hw_if auglx_hw_if = { 244 auglx_open, 245 auglx_close, 246 NULL, /* drain */ 247 auglx_query_encoding, 248 auglx_set_params, 249 auglx_round_blocksize, 250 NULL, /* commit_setting */ 251 NULL, /* init_output */ 252 NULL, /* init_input */ 253 NULL, /* start_output */ 254 NULL, /* start_input */ 255 auglx_halt_output, 256 auglx_halt_input, 257 NULL, /* speaker_ctl */ 258 auglx_getdev, 259 NULL, /* getfd */ 260 auglx_set_port, 261 auglx_get_port, 262 auglx_query_devinfo, 263 auglx_allocm, 264 auglx_freem, 265 auglx_round_buffersize, 266 auglx_mappage, 267 auglx_get_props, 268 auglx_trigger_output, 269 auglx_trigger_input, 270 auglx_get_default_params 271 }; 272 273 int auglx_match(struct device *, void *, void *); 274 void auglx_attach(struct device *, struct device *, void *); 275 int auglx_activate(struct device *, int); 276 int auglx_intr(void *); 277 278 int auglx_attach_codec(void *, struct ac97_codec_if *); 279 int auglx_read_codec(void *, u_int8_t, u_int16_t *); 280 int auglx_write_codec(void *, u_int8_t, u_int16_t); 281 void auglx_reset_codec(void *); 282 enum ac97_host_flags auglx_flags_codec(void *); 283 284 struct cfattach auglx_ca = { 285 sizeof(struct auglx_softc), auglx_match, auglx_attach, NULL, 286 auglx_activate 287 }; 288 289 const struct pci_matchid auglx_devices[] = { 290 { PCI_VENDOR_AMD, PCI_PRODUCT_AMD_CS5536_AUDIO } 291 }; 292 293 int 294 auglx_match(struct device *parent, void *match, void *aux) 295 { 296 return (pci_matchbyid((struct pci_attach_args *)aux, auglx_devices, 297 sizeof(auglx_devices) / sizeof(auglx_devices[0]))); 298 } 299 300 void 301 auglx_attach(struct device *parent, struct device *self, void *aux) 302 { 303 struct auglx_softc *sc = (struct auglx_softc *)self; 304 struct pci_attach_args *pa = aux; 305 bus_size_t bar_size; 306 pci_intr_handle_t ih; 307 const char *intrstr; 308 309 if (pci_mapreg_map(pa, AUGLX_ACC_BAR, PCI_MAPREG_TYPE_IO, 0, 310 &sc->sc_iot, &sc->sc_ioh, NULL, &bar_size, 0)) { 311 printf(": can't map ACC I/O space\n"); 312 return; 313 } 314 315 sc->sc_dmat = pa->pa_dmat; 316 sc->sc_dmamap_flags = BUS_DMA_COHERENT; 317 318 if (pci_intr_map(pa, &ih)) { 319 printf(": can't map interrupt"); 320 bus_space_unmap(sc->sc_iot, sc->sc_ioh, bar_size); 321 return; 322 } 323 intrstr = pci_intr_string(pa->pa_pc, ih); 324 sc->sc_ih = pci_intr_establish(pa->pa_pc, ih, IPL_AUDIO | IPL_MPSAFE, 325 auglx_intr, sc, sc->sc_dev.dv_xname); 326 if (!sc->sc_ih) { 327 printf(": can't establish interrupt"); 328 if (intrstr) 329 printf(" at %s", intrstr); 330 printf("\n"); 331 bus_space_unmap(sc->sc_iot, sc->sc_ioh, bar_size); 332 return; 333 } 334 335 strlcpy(sc->sc_audev.name, "CS5536 AC97", sizeof sc->sc_audev.name); 336 snprintf(sc->sc_audev.version, sizeof sc->sc_audev.version, "0x%02x", 337 PCI_REVISION(pa->pa_class)); 338 strlcpy(sc->sc_audev.config, sc->sc_dev.dv_xname, 339 sizeof sc->sc_audev.config); 340 341 printf(": %s, %s\n", intrstr, sc->sc_audev.name); 342 343 sc->host_if.arg = sc; 344 sc->host_if.attach = auglx_attach_codec; 345 sc->host_if.read = auglx_read_codec; 346 sc->host_if.write = auglx_write_codec; 347 sc->host_if.reset = auglx_reset_codec; 348 sc->host_if.flags = auglx_flags_codec; 349 350 if (ac97_attach(&sc->host_if) != 0) { 351 bus_space_unmap(sc->sc_iot, sc->sc_ioh, bar_size); 352 return; 353 } 354 audio_attach_mi(&auglx_hw_if, sc, &sc->sc_dev); 355 } 356 357 /* Functions to communicate with the AC97 Codec via the ACC */ 358 int 359 auglx_read_codec(void *v, u_int8_t reg, u_int16_t *val) 360 { 361 struct auglx_softc *sc = v; 362 u_int32_t codec_cntl, codec_status; 363 int i; 364 365 codec_cntl = RW_CMD | ((u_int32_t)reg << 24) | CMD_NEW; 366 bus_space_write_4(sc->sc_iot, sc->sc_ioh, ACC_CODEC_CNTL, codec_cntl); 367 368 for (i = AUGLX_TOUT; i; i--) { 369 codec_cntl = bus_space_read_4(sc->sc_iot, sc->sc_ioh, 370 ACC_CODEC_CNTL); 371 if (!(codec_cntl & CMD_NEW)) 372 break; 373 delay(1); 374 } 375 if (codec_cntl & CMD_NEW) { 376 printf("%s: codec read timeout after write\n", 377 sc->sc_dev.dv_xname); 378 return -1; 379 } 380 381 for (i = AUGLX_TOUT; i; i--) { 382 codec_status = bus_space_read_4(sc->sc_iot, sc->sc_ioh, 383 ACC_CODEC_STATUS); 384 if ((codec_status & STS_NEW) && (codec_status >> 24 == reg)) 385 break; 386 delay(10); 387 } 388 if (i == 0) { 389 printf("%s: codec status read timeout, 0x%08x\n", 390 sc->sc_dev.dv_xname, codec_status); 391 return -1; 392 } 393 394 *val = codec_status & 0xffff; 395 DPRINTF(AUGLX_DBG_ACC, ("%s: read codec register 0x%02x: 0x%04x\n", 396 sc->sc_dev.dv_xname, reg, *val)); 397 return 0; 398 } 399 400 int 401 auglx_write_codec(void *v, u_int8_t reg, u_int16_t val) 402 { 403 struct auglx_softc *sc = v; 404 u_int32_t codec_cntl; 405 int i; 406 407 DPRINTF(AUGLX_DBG_ACC, ("%s: write codec register 0x%02x: 0x%04x\n", 408 sc->sc_dev.dv_xname, reg, val)); 409 410 411 codec_cntl = ((u_int32_t)reg << 24) | CMD_NEW | val; 412 bus_space_write_4(sc->sc_iot, sc->sc_ioh, ACC_CODEC_CNTL, codec_cntl); 413 414 for (i = AUGLX_TOUT; i; i--) { 415 codec_cntl = bus_space_read_4(sc->sc_iot, sc->sc_ioh, 416 ACC_CODEC_CNTL); 417 if (!(codec_cntl & CMD_NEW)) 418 break; 419 delay(1); 420 } 421 if (codec_cntl & CMD_NEW) { 422 printf("%s: codec write timeout\n", sc->sc_dev.dv_xname); 423 return -1; 424 } 425 426 return 0; 427 } 428 429 int 430 auglx_attach_codec(void *v, struct ac97_codec_if *cif) 431 { 432 struct auglx_softc *sc = v; 433 434 sc->codec_if = cif; 435 return 0; 436 } 437 438 void 439 auglx_reset_codec(void *v) 440 { 441 struct auglx_softc *sc = v; 442 u_int32_t codec_cntl; 443 int i; 444 445 codec_cntl = LNK_WRM_RST | CMD_NEW; 446 bus_space_write_4(sc->sc_iot, sc->sc_ioh, ACC_CODEC_CNTL, codec_cntl); 447 448 for (i = AUGLX_TOUT; i; i--) { 449 codec_cntl = bus_space_read_4(sc->sc_iot, sc->sc_ioh, 450 ACC_CODEC_CNTL); 451 if (!(codec_cntl & CMD_NEW)) 452 continue; 453 delay(1); 454 } 455 if (codec_cntl & CMD_NEW) 456 printf("%s: codec reset timeout\n", sc->sc_dev.dv_xname); 457 } 458 459 enum ac97_host_flags 460 auglx_flags_codec(void *v) 461 { 462 return 0; 463 } 464 465 /* 466 * Audio functions 467 */ 468 int 469 auglx_open(void *v, int flags) 470 { 471 return 0; 472 } 473 474 void 475 auglx_close(void *v) 476 { 477 } 478 479 480 int 481 auglx_query_encoding(void *v, struct audio_encoding *aep) 482 { 483 switch (aep->index) { 484 case 0: 485 strlcpy(aep->name, AudioEslinear_le, sizeof aep->name); 486 aep->encoding = AUDIO_ENCODING_SLINEAR_LE; 487 aep->precision = 16; 488 aep->flags = 0; 489 break; 490 default: 491 return EINVAL; 492 } 493 aep->bps = AUDIO_BPS(aep->precision); 494 aep->msb = 1; 495 496 return 0; 497 } 498 499 500 int 501 auglx_set_params(void *v, int setmode, int usemode, struct audio_params *play, 502 struct audio_params *rec) 503 { 504 struct auglx_softc *sc = v; 505 int error; 506 u_int orate; 507 508 if (setmode & AUMODE_PLAY) { 509 play->precision = 16; 510 play->channels = 2; 511 play->encoding = AUDIO_ENCODING_SLINEAR_LE; 512 play->bps = AUDIO_BPS(play->precision); 513 play->msb = 1; 514 515 orate = play->sample_rate; 516 517 play->sample_rate = orate; 518 error = ac97_set_rate(sc->codec_if, 519 AC97_REG_PCM_LFE_DAC_RATE, &play->sample_rate); 520 if (error) 521 return error; 522 523 play->sample_rate = orate; 524 error = ac97_set_rate(sc->codec_if, 525 AC97_REG_PCM_SURR_DAC_RATE, &play->sample_rate); 526 if (error) 527 return error; 528 529 play->sample_rate = orate; 530 error = ac97_set_rate(sc->codec_if, 531 AC97_REG_PCM_FRONT_DAC_RATE, &play->sample_rate); 532 if (error) 533 return error; 534 } 535 536 if (setmode & AUMODE_RECORD) { 537 rec->precision = 16; 538 rec->channels = 2; 539 rec->encoding = AUDIO_ENCODING_ULINEAR_LE; 540 rec->bps = AUDIO_BPS(rec->precision); 541 rec->msb = 1; 542 543 error = ac97_set_rate(sc->codec_if, AC97_REG_PCM_LR_ADC_RATE, 544 &rec->sample_rate); 545 if (error) 546 return error; 547 } 548 549 return 0; 550 } 551 552 int 553 auglx_round_blocksize(void *v, int blk) 554 { 555 return (blk + 0x3f) & ~0x3f; 556 } 557 558 int 559 auglx_halt_output(void *v) 560 { 561 struct auglx_softc *sc = v; 562 563 DPRINTF(AUGLX_DBG_DMA, ("%s: halt_output\n", sc->sc_dev.dv_xname)); 564 565 bus_space_write_1(sc->sc_iot, sc->sc_ioh, ACC_BM0_CMD, 0x00); 566 sc->bm0.intr = NULL; 567 return 0; 568 } 569 570 int 571 auglx_halt_input(void *v) 572 { 573 struct auglx_softc *sc = v; 574 575 DPRINTF(AUGLX_DBG_DMA, 576 ("%s: halt_input\n", sc->sc_dev.dv_xname)); 577 578 bus_space_write_1(sc->sc_iot, sc->sc_ioh, ACC_BM1_CMD, 0x00); 579 sc->bm1.intr = NULL; 580 return 0; 581 } 582 583 int 584 auglx_getdev(void *v, struct audio_device *adp) 585 { 586 struct auglx_softc *sc = v; 587 *adp = sc->sc_audev; 588 return 0; 589 } 590 591 int 592 auglx_set_port(void *v, mixer_ctrl_t *cp) 593 { 594 struct auglx_softc *sc = v; 595 return sc->codec_if->vtbl->mixer_set_port(sc->codec_if, cp); 596 } 597 598 int 599 auglx_get_port(void *v, mixer_ctrl_t *cp) 600 { 601 struct auglx_softc *sc = v; 602 return sc->codec_if->vtbl->mixer_get_port(sc->codec_if, cp); 603 } 604 605 int 606 auglx_query_devinfo(void *v, mixer_devinfo_t *dp) 607 { 608 struct auglx_softc *sc = v; 609 return sc->codec_if->vtbl->query_devinfo(sc->codec_if, dp); 610 } 611 612 void * 613 auglx_allocm(void *v, int direction, size_t size, int pool, int flags) 614 { 615 struct auglx_softc *sc = v; 616 struct auglx_dma *p; 617 int error; 618 619 DPRINTF(AUGLX_DBG_DMA, ("%s: request buffer of size %ld, dir %d\n", 620 sc->sc_dev.dv_xname, size, direction)); 621 622 /* can only use 1 segment */ 623 if (size > AUGLX_DMASEG_MAX) { 624 DPRINTF(AUGLX_DBG_DMA, 625 ("%s: requested buffer size too large: %d", \ 626 sc->sc_dev.dv_xname, size)); 627 return NULL; 628 } 629 630 p = malloc(sizeof(*p), pool, flags | M_ZERO); 631 if (!p) 632 return NULL; 633 634 error = auglx_allocmem(sc, size, PAGE_SIZE, p); 635 if (error) { 636 free(p, pool, 0); 637 return NULL; 638 } 639 640 p->next = sc->sc_dmas; 641 sc->sc_dmas = p; 642 643 return p->addr; 644 } 645 646 void 647 auglx_freem(void *v, void *ptr, int pool) 648 { 649 struct auglx_softc *sc; 650 struct auglx_dma *p, **pp; 651 652 sc = v; 653 for (pp = &sc->sc_dmas; (p = *pp) != NULL; pp = &p->next) { 654 if (p->addr == ptr) { 655 auglx_freemem(sc, p); 656 *pp = p->next; 657 free(p, pool, 0); 658 return; 659 } 660 } 661 } 662 663 664 size_t 665 auglx_round_buffersize(void *v, int direction, size_t size) 666 { 667 if (size > AUGLX_DMASEG_MAX) 668 size = AUGLX_DMASEG_MAX; 669 670 return size; 671 } 672 673 paddr_t 674 auglx_mappage(void *v, void *mem, off_t off, int prot) 675 { 676 struct auglx_softc *sc = v; 677 struct auglx_dma *p; 678 679 if (off < 0) 680 return -1; 681 682 for (p = sc->sc_dmas; p && p->addr != mem; p = p->next); 683 if (!p) 684 return -1; 685 686 return bus_dmamem_mmap(sc->sc_dmat, p->segs, p->nsegs, 687 off, prot, BUS_DMA_WAITOK); 688 } 689 690 int 691 auglx_get_props(void *v) 692 { 693 return AUDIO_PROP_MMAP | AUDIO_PROP_INDEPENDENT | AUDIO_PROP_FULLDUPLEX; 694 } 695 696 int 697 auglx_intr(void *v) 698 { 699 struct auglx_softc *sc = v; 700 u_int16_t irq_sts; 701 u_int8_t bm_sts; 702 703 mtx_enter(&audio_lock); 704 irq_sts = bus_space_read_2(sc->sc_iot, sc->sc_ioh, ACC_IRQ_STATUS); 705 if (irq_sts == 0) { 706 mtx_leave(&audio_lock); 707 return 0; 708 } 709 710 if (irq_sts & BM0_IRQ_STS) { 711 bm_sts = bus_space_read_1(sc->sc_iot, sc->sc_ioh, 712 ACC_BM0_STATUS); 713 if (sc->bm0.intr) { 714 sc->bm0.intr(sc->bm0.arg); 715 bus_space_write_1(sc->sc_iot, sc->sc_ioh, ACC_BM0_CMD, 716 BMx_CMD_BM_CTL_EN); 717 } 718 } else if (irq_sts & BM1_IRQ_STS) { 719 bm_sts = bus_space_read_1(sc->sc_iot, sc->sc_ioh, 720 ACC_BM1_STATUS); 721 if (sc->bm1.intr) { 722 sc->bm1.intr(sc->bm1.arg); 723 bus_space_write_1(sc->sc_iot, sc->sc_ioh, ACC_BM1_CMD, 724 BMx_CMD_RW | BMx_CMD_BM_CTL_EN); 725 } 726 } else { 727 DPRINTF(AUGLX_DBG_IRQ, ("%s: stray intr, status = 0x%04x\n", 728 sc->sc_dev.dv_xname, irq_sts)); 729 mtx_leave(&audio_lock); 730 return -1; 731 } 732 mtx_leave(&audio_lock); 733 return 1; 734 } 735 736 int 737 auglx_trigger_output(void *v, void *start, void *end, int blksize, 738 void (*intr)(void *), void *arg, struct audio_params *param) 739 { 740 struct auglx_softc *sc = v; 741 struct auglx_dma *p; 742 size_t size; 743 u_int32_t addr; 744 int i, nprd; 745 746 size = (size_t)((caddr_t)end - (caddr_t)start); 747 DPRINTF(AUGLX_DBG_DMA, ("%s: trigger_output, %p 0x%08x bytes, " 748 "blksize 0x%04x\n", sc->sc_dev.dv_xname, start, size, blksize)); 749 750 for (p = sc->sc_dmas; p && p->addr != start; p = p->next); 751 if (!p) { 752 DPRINTF(AUGLX_DBG_DMA, ("%s dma reg not found\n", 753 sc->sc_dev.dv_xname)); 754 return -1; 755 } 756 757 /* set up the PRDs */ 758 nprd = size / blksize; 759 if (sc->bm0.sc_nprd != nprd + 1) { 760 if (sc->bm0.sc_nprd > 0) 761 auglx_free_prd(sc, &sc->bm0); 762 sc->bm0.sc_nprd = nprd + 1; 763 auglx_alloc_prd(sc, 764 sc->bm0.sc_nprd * sizeof(struct auglx_prd), &sc->bm0); 765 } 766 DPRINTF(AUGLX_DBG_DMA, ("%s: nprd = %d\n", sc->sc_dev.dv_xname, 767 nprd)); 768 addr = p->segs->ds_addr; 769 for (i = 0; i < nprd; i++) { 770 sc->bm0.sc_vprd[i].base = addr; 771 sc->bm0.sc_vprd[i].size = blksize | AUGLX_PRD_EOP; 772 addr += blksize; 773 } 774 sc->bm0.sc_vprd[i].base = sc->bm0.sc_prd->dm_segs[0].ds_addr; 775 sc->bm0.sc_vprd[i].size = AUGLX_PRD_JMP; 776 777 #ifdef AUGLX_DEBUG 778 for (i = 0; i < sc->bm0.sc_nprd; i++) 779 DPRINTF(AUGLX_DBG_DMA, ("%s: PRD[%d].base = %p, size %p\n", 780 sc->sc_dev.dv_xname, i, sc->bm0.sc_vprd[i].base, 781 sc->bm0.sc_vprd[i].size)); 782 #endif 783 sc->bm0.intr = intr; 784 sc->bm0.arg = arg; 785 786 mtx_enter(&audio_lock); 787 /* Program the BM0 PRD register */ 788 bus_space_write_4(sc->sc_iot, sc->sc_ioh, ACC_BM0_PRD, 789 sc->bm0.sc_prd->dm_segs[0].ds_addr); 790 /* Start Audio Bus Master 0 */ 791 bus_space_write_1(sc->sc_iot, sc->sc_ioh, ACC_BM0_CMD, 792 BMx_CMD_BM_CTL_EN); 793 mtx_leave(&audio_lock); 794 return 0; 795 } 796 797 int 798 auglx_trigger_input(void *v, void *start, void *end, int blksize, 799 void (*intr)(void *), void * arg, struct audio_params *param) 800 { 801 struct auglx_softc *sc = v; 802 struct auglx_dma *p; 803 size_t size; 804 u_int32_t addr; 805 int i, nprd; 806 807 size = (size_t)((caddr_t)end - (caddr_t)start); 808 DPRINTF(AUGLX_DBG_DMA, ("%s: trigger_input, %p 0x%08x bytes, " 809 "blksize 0x%04x\n", sc->sc_dev.dv_xname, start, size, blksize)); 810 811 for (p = sc->sc_dmas; p && p->addr != start; p = p->next); 812 if (!p) { 813 DPRINTF(AUGLX_DBG_DMA, ("%s dma reg not found\n", 814 sc->sc_dev.dv_xname)); 815 return -1; 816 } 817 818 /* set up the PRDs */ 819 nprd = size / blksize; 820 if (sc->bm1.sc_nprd != nprd + 1) { 821 if (sc->bm1.sc_nprd > 0) 822 auglx_free_prd(sc, &sc->bm1); 823 sc->bm1.sc_nprd = nprd + 1; 824 auglx_alloc_prd(sc, 825 sc->bm1.sc_nprd * sizeof(struct auglx_prd), &sc->bm1); 826 } 827 DPRINTF(AUGLX_DBG_DMA, ("%s: nprd = %d\n", sc->sc_dev.dv_xname, 828 nprd)); 829 addr = p->segs->ds_addr; 830 for (i = 0; i < nprd; i++) { 831 sc->bm1.sc_vprd[i].base = addr; 832 sc->bm1.sc_vprd[i].size = blksize | AUGLX_PRD_EOP; 833 addr += blksize; 834 } 835 sc->bm1.sc_vprd[i].base = sc->bm1.sc_prd->dm_segs[0].ds_addr; 836 sc->bm1.sc_vprd[i].size = AUGLX_PRD_JMP; 837 838 #ifdef AUGLX_DEBUG 839 for (i = 0; i < sc->bm1.sc_nprd; i++) 840 DPRINTF(AUGLX_DBG_DMA, ("%s: PRD[%d].base = %p, size %p\n", 841 sc->sc_dev.dv_xname, i, sc->bm1.sc_vprd[i].base, 842 sc->bm1.sc_vprd[i].size)); 843 #endif 844 sc->bm1.intr = intr; 845 sc->bm1.arg = arg; 846 847 mtx_enter(&audio_lock); 848 /* Program the BM1 PRD register */ 849 bus_space_write_4(sc->sc_iot, sc->sc_ioh, ACC_BM1_PRD, 850 sc->bm1.sc_prd->dm_segs[0].ds_addr); 851 /* Start Audio Bus Master 0 */ 852 bus_space_write_1(sc->sc_iot, sc->sc_ioh, ACC_BM1_CMD, 853 BMx_CMD_RW | BMx_CMD_BM_CTL_EN); 854 mtx_leave(&audio_lock); 855 return 0; 856 } 857 858 int 859 auglx_allocmem(struct auglx_softc *sc, size_t size, size_t align, 860 struct auglx_dma *p) 861 { 862 int error; 863 864 p->size = size; 865 error = bus_dmamem_alloc(sc->sc_dmat, p->size, align, 0, p->segs, 1, 866 &p->nsegs, BUS_DMA_NOWAIT); 867 if (error) { 868 DPRINTF(AUGLX_DBG_DMA, 869 ("%s: bus_dmamem_alloc failed: error %d\n", 870 sc->sc_dev.dv_xname, error)); 871 return error; 872 } 873 874 error = bus_dmamem_map(sc->sc_dmat, p->segs, 1, p->size, &p->addr, 875 BUS_DMA_NOWAIT | sc->sc_dmamap_flags); 876 if (error) { 877 DPRINTF(AUGLX_DBG_DMA, 878 ("%s: bus_dmamem_map failed: error %d\n", 879 sc->sc_dev.dv_xname, error)); 880 goto free; 881 } 882 883 error = bus_dmamap_create(sc->sc_dmat, p->size, 1, p->size, 0, 884 BUS_DMA_NOWAIT, &p->map); 885 if (error) { 886 DPRINTF(AUGLX_DBG_DMA, 887 ("%s: bus_dmamap_create failed: error %d\n", 888 sc->sc_dev.dv_xname, error)); 889 goto unmap; 890 } 891 892 error = bus_dmamap_load(sc->sc_dmat, p->map, p->addr, p->size, NULL, 893 BUS_DMA_NOWAIT); 894 if (error) { 895 DPRINTF(AUGLX_DBG_DMA, 896 ("%s: bus_dmamap_load failed: error %d\n", 897 sc->sc_dev.dv_xname, error)); 898 goto destroy; 899 } 900 return 0; 901 902 destroy: 903 bus_dmamap_destroy(sc->sc_dmat, p->map); 904 unmap: 905 bus_dmamem_unmap(sc->sc_dmat, p->addr, p->size); 906 free: 907 bus_dmamem_free(sc->sc_dmat, p->segs, p->nsegs); 908 return error; 909 } 910 911 void 912 auglx_freemem(struct auglx_softc *sc, struct auglx_dma *p) 913 { 914 bus_dmamap_unload(sc->sc_dmat, p->map); 915 bus_dmamap_destroy(sc->sc_dmat, p->map); 916 bus_dmamem_unmap(sc->sc_dmat, p->addr, p->size); 917 bus_dmamem_free(sc->sc_dmat, p->segs, p->nsegs); 918 } 919 920 void 921 auglx_get_default_params(void *addr, int mode, struct audio_params *params) 922 { 923 ac97_get_default_params(params); 924 } 925 926 int 927 auglx_alloc_prd(struct auglx_softc *sc, size_t size, struct auglx_ring *bm) 928 { 929 int error, rseg; 930 931 /* 932 * Allocate PRD table structure, and create and load the 933 * DMA map for it. 934 */ 935 if ((error = bus_dmamem_alloc(sc->sc_dmat, size, 936 PAGE_SIZE, 0, &bm->seg, 1, &rseg, 0)) != 0) { 937 printf("%s: unable to allocate PRD, error = %d\n", 938 sc->sc_dev.dv_xname, error); 939 goto fail_0; 940 } 941 942 if ((error = bus_dmamem_map(sc->sc_dmat, &bm->seg, rseg, 943 size, (caddr_t *)&bm->sc_vprd, 944 sc->sc_dmamap_flags)) != 0) { 945 printf("%s: unable to map PRD, error = %d\n", 946 sc->sc_dev.dv_xname, error); 947 goto fail_1; 948 } 949 950 if ((error = bus_dmamap_create(sc->sc_dmat, size, 951 1, size, 0, 0, &bm->sc_prd)) != 0) { 952 printf("%s: unable to create PRD DMA map, " 953 "error = %d\n", sc->sc_dev.dv_xname, error); 954 goto fail_2; 955 } 956 957 if ((error = bus_dmamap_load(sc->sc_dmat, bm->sc_prd, bm->sc_vprd, 958 size, NULL, 0)) != 0) { 959 printf("%s: unable tp load control data DMA map, " 960 "error = %d\n", sc->sc_dev.dv_xname, error); 961 goto fail_3; 962 } 963 964 return 0; 965 966 fail_3: 967 bus_dmamap_destroy(sc->sc_dmat, bm->sc_prd); 968 fail_2: 969 bus_dmamem_unmap(sc->sc_dmat, (caddr_t)bm->sc_vprd, 970 sizeof(struct auglx_prd)); 971 fail_1: 972 bus_dmamem_free(sc->sc_dmat, &bm->seg, rseg); 973 fail_0: 974 return error; 975 } 976 977 void 978 auglx_free_prd(struct auglx_softc *sc, struct auglx_ring *bm) 979 { 980 bus_dmamap_unload(sc->sc_dmat, bm->sc_prd); 981 bus_dmamap_destroy(sc->sc_dmat, bm->sc_prd); 982 bus_dmamem_unmap(sc->sc_dmat, (caddr_t)bm->sc_vprd, bm->sc_size); 983 bus_dmamem_free(sc->sc_dmat, &bm->seg, bm->nsegs); 984 } 985 986 int 987 auglx_activate(struct device *self, int act) 988 { 989 struct auglx_softc *sc = (struct auglx_softc *)self; 990 int rv = 0; 991 992 switch (act) { 993 case DVACT_RESUME: 994 ac97_resume(&sc->host_if, sc->codec_if); 995 rv = config_activate_children(self, act); 996 break; 997 default: 998 rv = config_activate_children(self, act); 999 break; 1000 } 1001 return (rv); 1002 } 1003