1 /* $OpenBSD: auglx.c,v 1.23 2022/10/26 20:19:08 kn 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 bus_space_tag_t sc_iot; 170 bus_space_handle_t sc_ioh; 171 bus_dma_tag_t sc_dmat; 172 173 /* 174 * The CS5536 ACC has eight bus masters to support 5.1 audio. 175 * This driver, however, only supports main playback and recording 176 * since I only have a Realtek ALC203 codec available for testing. 177 */ 178 struct auglx_ring { 179 bus_dmamap_t sc_prd; 180 struct auglx_prd *sc_vprd; 181 int sc_nprd; 182 183 size_t sc_size; 184 int nsegs; 185 bus_dma_segment_t seg; 186 187 void (*intr)(void *); 188 void *arg; 189 } bm0, bm1; /* bm0: output, bm1: input */ 190 191 struct auglx_dma *sc_dmas; 192 193 struct ac97_codec_if *codec_if; 194 struct ac97_host_if host_if; 195 196 int sc_dmamap_flags; 197 }; 198 199 #ifdef AUGLX_DEBUG 200 #define DPRINTF(l,x) do { if (auglx_debug & (l)) printf x; } while(0) 201 int auglx_debug = 0; 202 #define AUGLX_DBG_ACC 0x0001 203 #define AUGLX_DBG_DMA 0x0002 204 #define AUGLX_DBG_IRQ 0x0004 205 #else 206 #define DPRINTF(x,y) /* nothing */ 207 #endif 208 209 struct cfdriver auglx_cd = { 210 NULL, "auglx", DV_DULL 211 }; 212 213 int auglx_open(void *, int); 214 void auglx_close(void *); 215 int auglx_set_params(void *, int, int, struct audio_params *, 216 struct audio_params *); 217 int auglx_round_blocksize(void *, int); 218 int auglx_halt_output(void *); 219 int auglx_halt_input(void *); 220 int auglx_set_port(void *, mixer_ctrl_t *); 221 int auglx_get_port(void *, mixer_ctrl_t *); 222 int auglx_query_devinfo(void *, mixer_devinfo_t *); 223 void *auglx_allocm(void *, int, size_t, int, int); 224 void auglx_freem(void *, void *, int); 225 size_t auglx_round_buffersize(void *, int, size_t); 226 int auglx_trigger_output(void *, void *, void *, int, void (*)(void *), 227 void *, struct audio_params *); 228 int auglx_trigger_input(void *, void *, void *, int, void (*)(void *), 229 void *, struct audio_params *); 230 int auglx_alloc_cdata(struct auglx_softc *); 231 int auglx_alloc_prd(struct auglx_softc *, size_t, struct auglx_ring *); 232 void auglx_free_prd(struct auglx_softc *sc, struct auglx_ring *bm); 233 int auglx_allocmem(struct auglx_softc *, size_t, size_t, struct auglx_dma *); 234 void auglx_freemem(struct auglx_softc *, struct auglx_dma *); 235 236 const struct audio_hw_if auglx_hw_if = { 237 .open = auglx_open, 238 .close = auglx_close, 239 .set_params = auglx_set_params, 240 .round_blocksize = auglx_round_blocksize, 241 .halt_output = auglx_halt_output, 242 .halt_input = auglx_halt_input, 243 .set_port = auglx_set_port, 244 .get_port = auglx_get_port, 245 .query_devinfo = auglx_query_devinfo, 246 .allocm = auglx_allocm, 247 .freem = auglx_freem, 248 .round_buffersize = auglx_round_buffersize, 249 .trigger_output = auglx_trigger_output, 250 .trigger_input = auglx_trigger_input, 251 }; 252 253 int auglx_match(struct device *, void *, void *); 254 void auglx_attach(struct device *, struct device *, void *); 255 int auglx_activate(struct device *, int); 256 int auglx_intr(void *); 257 258 int auglx_attach_codec(void *, struct ac97_codec_if *); 259 int auglx_read_codec(void *, u_int8_t, u_int16_t *); 260 int auglx_write_codec(void *, u_int8_t, u_int16_t); 261 void auglx_reset_codec(void *); 262 enum ac97_host_flags auglx_flags_codec(void *); 263 264 const struct cfattach auglx_ca = { 265 sizeof(struct auglx_softc), auglx_match, auglx_attach, NULL, 266 auglx_activate 267 }; 268 269 const struct pci_matchid auglx_devices[] = { 270 { PCI_VENDOR_AMD, PCI_PRODUCT_AMD_CS5536_AUDIO } 271 }; 272 273 int 274 auglx_match(struct device *parent, void *match, void *aux) 275 { 276 return (pci_matchbyid((struct pci_attach_args *)aux, auglx_devices, 277 sizeof(auglx_devices) / sizeof(auglx_devices[0]))); 278 } 279 280 void 281 auglx_attach(struct device *parent, struct device *self, void *aux) 282 { 283 struct auglx_softc *sc = (struct auglx_softc *)self; 284 struct pci_attach_args *pa = aux; 285 bus_size_t bar_size; 286 pci_intr_handle_t ih; 287 const char *intrstr; 288 289 if (pci_mapreg_map(pa, AUGLX_ACC_BAR, PCI_MAPREG_TYPE_IO, 0, 290 &sc->sc_iot, &sc->sc_ioh, NULL, &bar_size, 0)) { 291 printf(": can't map ACC I/O space\n"); 292 return; 293 } 294 295 sc->sc_dmat = pa->pa_dmat; 296 sc->sc_dmamap_flags = BUS_DMA_COHERENT; 297 298 if (pci_intr_map(pa, &ih)) { 299 printf(": can't map interrupt"); 300 bus_space_unmap(sc->sc_iot, sc->sc_ioh, bar_size); 301 return; 302 } 303 intrstr = pci_intr_string(pa->pa_pc, ih); 304 sc->sc_ih = pci_intr_establish(pa->pa_pc, ih, IPL_AUDIO | IPL_MPSAFE, 305 auglx_intr, sc, sc->sc_dev.dv_xname); 306 if (!sc->sc_ih) { 307 printf(": can't establish interrupt"); 308 if (intrstr) 309 printf(" at %s", intrstr); 310 printf("\n"); 311 bus_space_unmap(sc->sc_iot, sc->sc_ioh, bar_size); 312 return; 313 } 314 315 printf(": %s, %s\n", intrstr, "CS5536 AC97"); 316 317 sc->host_if.arg = sc; 318 sc->host_if.attach = auglx_attach_codec; 319 sc->host_if.read = auglx_read_codec; 320 sc->host_if.write = auglx_write_codec; 321 sc->host_if.reset = auglx_reset_codec; 322 sc->host_if.flags = auglx_flags_codec; 323 324 if (ac97_attach(&sc->host_if) != 0) { 325 bus_space_unmap(sc->sc_iot, sc->sc_ioh, bar_size); 326 return; 327 } 328 audio_attach_mi(&auglx_hw_if, sc, NULL, &sc->sc_dev); 329 } 330 331 /* Functions to communicate with the AC97 Codec via the ACC */ 332 int 333 auglx_read_codec(void *v, u_int8_t reg, u_int16_t *val) 334 { 335 struct auglx_softc *sc = v; 336 u_int32_t codec_cntl, codec_status; 337 int i; 338 339 codec_cntl = RW_CMD | ((u_int32_t)reg << 24) | CMD_NEW; 340 bus_space_write_4(sc->sc_iot, sc->sc_ioh, ACC_CODEC_CNTL, codec_cntl); 341 342 for (i = AUGLX_TOUT; i; i--) { 343 codec_cntl = bus_space_read_4(sc->sc_iot, sc->sc_ioh, 344 ACC_CODEC_CNTL); 345 if (!(codec_cntl & CMD_NEW)) 346 break; 347 delay(1); 348 } 349 if (codec_cntl & CMD_NEW) { 350 printf("%s: codec read timeout after write\n", 351 sc->sc_dev.dv_xname); 352 return -1; 353 } 354 355 for (i = AUGLX_TOUT; i; i--) { 356 codec_status = bus_space_read_4(sc->sc_iot, sc->sc_ioh, 357 ACC_CODEC_STATUS); 358 if ((codec_status & STS_NEW) && (codec_status >> 24 == reg)) 359 break; 360 delay(10); 361 } 362 if (i == 0) { 363 printf("%s: codec status read timeout, 0x%08x\n", 364 sc->sc_dev.dv_xname, codec_status); 365 return -1; 366 } 367 368 *val = codec_status & 0xffff; 369 DPRINTF(AUGLX_DBG_ACC, ("%s: read codec register 0x%02x: 0x%04x\n", 370 sc->sc_dev.dv_xname, reg, *val)); 371 return 0; 372 } 373 374 int 375 auglx_write_codec(void *v, u_int8_t reg, u_int16_t val) 376 { 377 struct auglx_softc *sc = v; 378 u_int32_t codec_cntl; 379 int i; 380 381 DPRINTF(AUGLX_DBG_ACC, ("%s: write codec register 0x%02x: 0x%04x\n", 382 sc->sc_dev.dv_xname, reg, val)); 383 384 385 codec_cntl = ((u_int32_t)reg << 24) | CMD_NEW | val; 386 bus_space_write_4(sc->sc_iot, sc->sc_ioh, ACC_CODEC_CNTL, codec_cntl); 387 388 for (i = AUGLX_TOUT; i; i--) { 389 codec_cntl = bus_space_read_4(sc->sc_iot, sc->sc_ioh, 390 ACC_CODEC_CNTL); 391 if (!(codec_cntl & CMD_NEW)) 392 break; 393 delay(1); 394 } 395 if (codec_cntl & CMD_NEW) { 396 printf("%s: codec write timeout\n", sc->sc_dev.dv_xname); 397 return -1; 398 } 399 400 return 0; 401 } 402 403 int 404 auglx_attach_codec(void *v, struct ac97_codec_if *cif) 405 { 406 struct auglx_softc *sc = v; 407 408 sc->codec_if = cif; 409 return 0; 410 } 411 412 void 413 auglx_reset_codec(void *v) 414 { 415 struct auglx_softc *sc = v; 416 u_int32_t codec_cntl; 417 int i; 418 419 codec_cntl = LNK_WRM_RST | CMD_NEW; 420 bus_space_write_4(sc->sc_iot, sc->sc_ioh, ACC_CODEC_CNTL, codec_cntl); 421 422 for (i = AUGLX_TOUT; i; i--) { 423 codec_cntl = bus_space_read_4(sc->sc_iot, sc->sc_ioh, 424 ACC_CODEC_CNTL); 425 if (!(codec_cntl & CMD_NEW)) 426 continue; 427 delay(1); 428 } 429 if (codec_cntl & CMD_NEW) 430 printf("%s: codec reset timeout\n", sc->sc_dev.dv_xname); 431 } 432 433 enum ac97_host_flags 434 auglx_flags_codec(void *v) 435 { 436 return 0; 437 } 438 439 /* 440 * Audio functions 441 */ 442 int 443 auglx_open(void *v, int flags) 444 { 445 return 0; 446 } 447 448 void 449 auglx_close(void *v) 450 { 451 } 452 453 int 454 auglx_set_params(void *v, int setmode, int usemode, struct audio_params *play, 455 struct audio_params *rec) 456 { 457 struct auglx_softc *sc = v; 458 int error; 459 u_int orate; 460 461 if (setmode & AUMODE_PLAY) { 462 play->precision = 16; 463 play->channels = 2; 464 play->encoding = AUDIO_ENCODING_SLINEAR_LE; 465 play->bps = AUDIO_BPS(play->precision); 466 play->msb = 1; 467 468 orate = play->sample_rate; 469 470 play->sample_rate = orate; 471 error = ac97_set_rate(sc->codec_if, 472 AC97_REG_PCM_LFE_DAC_RATE, &play->sample_rate); 473 if (error) 474 return error; 475 476 play->sample_rate = orate; 477 error = ac97_set_rate(sc->codec_if, 478 AC97_REG_PCM_SURR_DAC_RATE, &play->sample_rate); 479 if (error) 480 return error; 481 482 play->sample_rate = orate; 483 error = ac97_set_rate(sc->codec_if, 484 AC97_REG_PCM_FRONT_DAC_RATE, &play->sample_rate); 485 if (error) 486 return error; 487 } 488 489 if (setmode & AUMODE_RECORD) { 490 rec->precision = 16; 491 rec->channels = 2; 492 rec->encoding = AUDIO_ENCODING_ULINEAR_LE; 493 rec->bps = AUDIO_BPS(rec->precision); 494 rec->msb = 1; 495 496 error = ac97_set_rate(sc->codec_if, AC97_REG_PCM_LR_ADC_RATE, 497 &rec->sample_rate); 498 if (error) 499 return error; 500 } 501 502 return 0; 503 } 504 505 int 506 auglx_round_blocksize(void *v, int blk) 507 { 508 return (blk + 0x3f) & ~0x3f; 509 } 510 511 int 512 auglx_halt_output(void *v) 513 { 514 struct auglx_softc *sc = v; 515 516 DPRINTF(AUGLX_DBG_DMA, ("%s: halt_output\n", sc->sc_dev.dv_xname)); 517 518 bus_space_write_1(sc->sc_iot, sc->sc_ioh, ACC_BM0_CMD, 0x00); 519 sc->bm0.intr = NULL; 520 return 0; 521 } 522 523 int 524 auglx_halt_input(void *v) 525 { 526 struct auglx_softc *sc = v; 527 528 DPRINTF(AUGLX_DBG_DMA, 529 ("%s: halt_input\n", sc->sc_dev.dv_xname)); 530 531 bus_space_write_1(sc->sc_iot, sc->sc_ioh, ACC_BM1_CMD, 0x00); 532 sc->bm1.intr = NULL; 533 return 0; 534 } 535 536 int 537 auglx_set_port(void *v, mixer_ctrl_t *cp) 538 { 539 struct auglx_softc *sc = v; 540 return sc->codec_if->vtbl->mixer_set_port(sc->codec_if, cp); 541 } 542 543 int 544 auglx_get_port(void *v, mixer_ctrl_t *cp) 545 { 546 struct auglx_softc *sc = v; 547 return sc->codec_if->vtbl->mixer_get_port(sc->codec_if, cp); 548 } 549 550 int 551 auglx_query_devinfo(void *v, mixer_devinfo_t *dp) 552 { 553 struct auglx_softc *sc = v; 554 return sc->codec_if->vtbl->query_devinfo(sc->codec_if, dp); 555 } 556 557 void * 558 auglx_allocm(void *v, int direction, size_t size, int pool, int flags) 559 { 560 struct auglx_softc *sc = v; 561 struct auglx_dma *p; 562 int error; 563 564 DPRINTF(AUGLX_DBG_DMA, ("%s: request buffer of size %ld, dir %d\n", 565 sc->sc_dev.dv_xname, size, direction)); 566 567 /* can only use 1 segment */ 568 if (size > AUGLX_DMASEG_MAX) { 569 DPRINTF(AUGLX_DBG_DMA, 570 ("%s: requested buffer size too large: %d", \ 571 sc->sc_dev.dv_xname, size)); 572 return NULL; 573 } 574 575 p = malloc(sizeof(*p), pool, flags | M_ZERO); 576 if (!p) 577 return NULL; 578 579 error = auglx_allocmem(sc, size, PAGE_SIZE, p); 580 if (error) { 581 free(p, pool, sizeof(*p)); 582 return NULL; 583 } 584 585 p->next = sc->sc_dmas; 586 sc->sc_dmas = p; 587 588 return p->addr; 589 } 590 591 void 592 auglx_freem(void *v, void *ptr, int pool) 593 { 594 struct auglx_softc *sc; 595 struct auglx_dma *p, **pp; 596 597 sc = v; 598 for (pp = &sc->sc_dmas; (p = *pp) != NULL; pp = &p->next) { 599 if (p->addr == ptr) { 600 auglx_freemem(sc, p); 601 *pp = p->next; 602 free(p, pool, sizeof(*p)); 603 return; 604 } 605 } 606 } 607 608 609 size_t 610 auglx_round_buffersize(void *v, int direction, size_t size) 611 { 612 if (size > AUGLX_DMASEG_MAX) 613 size = AUGLX_DMASEG_MAX; 614 615 return size; 616 } 617 618 int 619 auglx_intr(void *v) 620 { 621 struct auglx_softc *sc = v; 622 u_int16_t irq_sts; 623 u_int8_t bm_sts; 624 625 mtx_enter(&audio_lock); 626 irq_sts = bus_space_read_2(sc->sc_iot, sc->sc_ioh, ACC_IRQ_STATUS); 627 if (irq_sts == 0) { 628 mtx_leave(&audio_lock); 629 return 0; 630 } 631 632 if (irq_sts & BM0_IRQ_STS) { 633 bm_sts = bus_space_read_1(sc->sc_iot, sc->sc_ioh, 634 ACC_BM0_STATUS); 635 if (sc->bm0.intr) { 636 sc->bm0.intr(sc->bm0.arg); 637 bus_space_write_1(sc->sc_iot, sc->sc_ioh, ACC_BM0_CMD, 638 BMx_CMD_BM_CTL_EN); 639 } 640 } else if (irq_sts & BM1_IRQ_STS) { 641 bm_sts = bus_space_read_1(sc->sc_iot, sc->sc_ioh, 642 ACC_BM1_STATUS); 643 if (sc->bm1.intr) { 644 sc->bm1.intr(sc->bm1.arg); 645 bus_space_write_1(sc->sc_iot, sc->sc_ioh, ACC_BM1_CMD, 646 BMx_CMD_RW | BMx_CMD_BM_CTL_EN); 647 } 648 } else { 649 DPRINTF(AUGLX_DBG_IRQ, ("%s: stray intr, status = 0x%04x\n", 650 sc->sc_dev.dv_xname, irq_sts)); 651 mtx_leave(&audio_lock); 652 return -1; 653 } 654 mtx_leave(&audio_lock); 655 return 1; 656 } 657 658 int 659 auglx_trigger_output(void *v, void *start, void *end, int blksize, 660 void (*intr)(void *), void *arg, struct audio_params *param) 661 { 662 struct auglx_softc *sc = v; 663 struct auglx_dma *p; 664 size_t size; 665 u_int32_t addr; 666 int i, nprd; 667 668 size = (size_t)((caddr_t)end - (caddr_t)start); 669 DPRINTF(AUGLX_DBG_DMA, ("%s: trigger_output, %p 0x%08x bytes, " 670 "blksize 0x%04x\n", sc->sc_dev.dv_xname, start, size, blksize)); 671 672 for (p = sc->sc_dmas; p && p->addr != start; p = p->next); 673 if (!p) { 674 DPRINTF(AUGLX_DBG_DMA, ("%s dma reg not found\n", 675 sc->sc_dev.dv_xname)); 676 return -1; 677 } 678 679 /* set up the PRDs */ 680 nprd = size / blksize; 681 if (sc->bm0.sc_nprd != nprd + 1) { 682 if (sc->bm0.sc_nprd > 0) 683 auglx_free_prd(sc, &sc->bm0); 684 sc->bm0.sc_nprd = nprd + 1; 685 auglx_alloc_prd(sc, 686 sc->bm0.sc_nprd * sizeof(struct auglx_prd), &sc->bm0); 687 } 688 DPRINTF(AUGLX_DBG_DMA, ("%s: nprd = %d\n", sc->sc_dev.dv_xname, 689 nprd)); 690 addr = p->segs->ds_addr; 691 for (i = 0; i < nprd; i++) { 692 sc->bm0.sc_vprd[i].base = addr; 693 sc->bm0.sc_vprd[i].size = blksize | AUGLX_PRD_EOP; 694 addr += blksize; 695 } 696 sc->bm0.sc_vprd[i].base = sc->bm0.sc_prd->dm_segs[0].ds_addr; 697 sc->bm0.sc_vprd[i].size = AUGLX_PRD_JMP; 698 699 #ifdef AUGLX_DEBUG 700 for (i = 0; i < sc->bm0.sc_nprd; i++) 701 DPRINTF(AUGLX_DBG_DMA, ("%s: PRD[%d].base = %p, size %p\n", 702 sc->sc_dev.dv_xname, i, sc->bm0.sc_vprd[i].base, 703 sc->bm0.sc_vprd[i].size)); 704 #endif 705 sc->bm0.intr = intr; 706 sc->bm0.arg = arg; 707 708 mtx_enter(&audio_lock); 709 /* Program the BM0 PRD register */ 710 bus_space_write_4(sc->sc_iot, sc->sc_ioh, ACC_BM0_PRD, 711 sc->bm0.sc_prd->dm_segs[0].ds_addr); 712 /* Start Audio Bus Master 0 */ 713 bus_space_write_1(sc->sc_iot, sc->sc_ioh, ACC_BM0_CMD, 714 BMx_CMD_BM_CTL_EN); 715 mtx_leave(&audio_lock); 716 return 0; 717 } 718 719 int 720 auglx_trigger_input(void *v, void *start, void *end, int blksize, 721 void (*intr)(void *), void * arg, struct audio_params *param) 722 { 723 struct auglx_softc *sc = v; 724 struct auglx_dma *p; 725 size_t size; 726 u_int32_t addr; 727 int i, nprd; 728 729 size = (size_t)((caddr_t)end - (caddr_t)start); 730 DPRINTF(AUGLX_DBG_DMA, ("%s: trigger_input, %p 0x%08x bytes, " 731 "blksize 0x%04x\n", sc->sc_dev.dv_xname, start, size, blksize)); 732 733 for (p = sc->sc_dmas; p && p->addr != start; p = p->next); 734 if (!p) { 735 DPRINTF(AUGLX_DBG_DMA, ("%s dma reg not found\n", 736 sc->sc_dev.dv_xname)); 737 return -1; 738 } 739 740 /* set up the PRDs */ 741 nprd = size / blksize; 742 if (sc->bm1.sc_nprd != nprd + 1) { 743 if (sc->bm1.sc_nprd > 0) 744 auglx_free_prd(sc, &sc->bm1); 745 sc->bm1.sc_nprd = nprd + 1; 746 auglx_alloc_prd(sc, 747 sc->bm1.sc_nprd * sizeof(struct auglx_prd), &sc->bm1); 748 } 749 DPRINTF(AUGLX_DBG_DMA, ("%s: nprd = %d\n", sc->sc_dev.dv_xname, 750 nprd)); 751 addr = p->segs->ds_addr; 752 for (i = 0; i < nprd; i++) { 753 sc->bm1.sc_vprd[i].base = addr; 754 sc->bm1.sc_vprd[i].size = blksize | AUGLX_PRD_EOP; 755 addr += blksize; 756 } 757 sc->bm1.sc_vprd[i].base = sc->bm1.sc_prd->dm_segs[0].ds_addr; 758 sc->bm1.sc_vprd[i].size = AUGLX_PRD_JMP; 759 760 #ifdef AUGLX_DEBUG 761 for (i = 0; i < sc->bm1.sc_nprd; i++) 762 DPRINTF(AUGLX_DBG_DMA, ("%s: PRD[%d].base = %p, size %p\n", 763 sc->sc_dev.dv_xname, i, sc->bm1.sc_vprd[i].base, 764 sc->bm1.sc_vprd[i].size)); 765 #endif 766 sc->bm1.intr = intr; 767 sc->bm1.arg = arg; 768 769 mtx_enter(&audio_lock); 770 /* Program the BM1 PRD register */ 771 bus_space_write_4(sc->sc_iot, sc->sc_ioh, ACC_BM1_PRD, 772 sc->bm1.sc_prd->dm_segs[0].ds_addr); 773 /* Start Audio Bus Master 0 */ 774 bus_space_write_1(sc->sc_iot, sc->sc_ioh, ACC_BM1_CMD, 775 BMx_CMD_RW | BMx_CMD_BM_CTL_EN); 776 mtx_leave(&audio_lock); 777 return 0; 778 } 779 780 int 781 auglx_allocmem(struct auglx_softc *sc, size_t size, size_t align, 782 struct auglx_dma *p) 783 { 784 int error; 785 786 p->size = size; 787 error = bus_dmamem_alloc(sc->sc_dmat, p->size, align, 0, p->segs, 1, 788 &p->nsegs, BUS_DMA_NOWAIT); 789 if (error) { 790 DPRINTF(AUGLX_DBG_DMA, 791 ("%s: bus_dmamem_alloc failed: error %d\n", 792 sc->sc_dev.dv_xname, error)); 793 return error; 794 } 795 796 error = bus_dmamem_map(sc->sc_dmat, p->segs, 1, p->size, &p->addr, 797 BUS_DMA_NOWAIT | sc->sc_dmamap_flags); 798 if (error) { 799 DPRINTF(AUGLX_DBG_DMA, 800 ("%s: bus_dmamem_map failed: error %d\n", 801 sc->sc_dev.dv_xname, error)); 802 goto free; 803 } 804 805 error = bus_dmamap_create(sc->sc_dmat, p->size, 1, p->size, 0, 806 BUS_DMA_NOWAIT, &p->map); 807 if (error) { 808 DPRINTF(AUGLX_DBG_DMA, 809 ("%s: bus_dmamap_create failed: error %d\n", 810 sc->sc_dev.dv_xname, error)); 811 goto unmap; 812 } 813 814 error = bus_dmamap_load(sc->sc_dmat, p->map, p->addr, p->size, NULL, 815 BUS_DMA_NOWAIT); 816 if (error) { 817 DPRINTF(AUGLX_DBG_DMA, 818 ("%s: bus_dmamap_load failed: error %d\n", 819 sc->sc_dev.dv_xname, error)); 820 goto destroy; 821 } 822 return 0; 823 824 destroy: 825 bus_dmamap_destroy(sc->sc_dmat, p->map); 826 unmap: 827 bus_dmamem_unmap(sc->sc_dmat, p->addr, p->size); 828 free: 829 bus_dmamem_free(sc->sc_dmat, p->segs, p->nsegs); 830 return error; 831 } 832 833 void 834 auglx_freemem(struct auglx_softc *sc, struct auglx_dma *p) 835 { 836 bus_dmamap_unload(sc->sc_dmat, p->map); 837 bus_dmamap_destroy(sc->sc_dmat, p->map); 838 bus_dmamem_unmap(sc->sc_dmat, p->addr, p->size); 839 bus_dmamem_free(sc->sc_dmat, p->segs, p->nsegs); 840 } 841 842 int 843 auglx_alloc_prd(struct auglx_softc *sc, size_t size, struct auglx_ring *bm) 844 { 845 int error, rseg; 846 847 /* 848 * Allocate PRD table structure, and create and load the 849 * DMA map for it. 850 */ 851 if ((error = bus_dmamem_alloc(sc->sc_dmat, size, 852 PAGE_SIZE, 0, &bm->seg, 1, &rseg, 0)) != 0) { 853 printf("%s: unable to allocate PRD, error = %d\n", 854 sc->sc_dev.dv_xname, error); 855 goto fail_0; 856 } 857 858 if ((error = bus_dmamem_map(sc->sc_dmat, &bm->seg, rseg, 859 size, (caddr_t *)&bm->sc_vprd, 860 sc->sc_dmamap_flags)) != 0) { 861 printf("%s: unable to map PRD, error = %d\n", 862 sc->sc_dev.dv_xname, error); 863 goto fail_1; 864 } 865 866 if ((error = bus_dmamap_create(sc->sc_dmat, size, 867 1, size, 0, 0, &bm->sc_prd)) != 0) { 868 printf("%s: unable to create PRD DMA map, " 869 "error = %d\n", sc->sc_dev.dv_xname, error); 870 goto fail_2; 871 } 872 873 if ((error = bus_dmamap_load(sc->sc_dmat, bm->sc_prd, bm->sc_vprd, 874 size, NULL, 0)) != 0) { 875 printf("%s: unable tp load control data DMA map, " 876 "error = %d\n", sc->sc_dev.dv_xname, error); 877 goto fail_3; 878 } 879 880 return 0; 881 882 fail_3: 883 bus_dmamap_destroy(sc->sc_dmat, bm->sc_prd); 884 fail_2: 885 bus_dmamem_unmap(sc->sc_dmat, (caddr_t)bm->sc_vprd, 886 sizeof(struct auglx_prd)); 887 fail_1: 888 bus_dmamem_free(sc->sc_dmat, &bm->seg, rseg); 889 fail_0: 890 return error; 891 } 892 893 void 894 auglx_free_prd(struct auglx_softc *sc, struct auglx_ring *bm) 895 { 896 bus_dmamap_unload(sc->sc_dmat, bm->sc_prd); 897 bus_dmamap_destroy(sc->sc_dmat, bm->sc_prd); 898 bus_dmamem_unmap(sc->sc_dmat, (caddr_t)bm->sc_vprd, bm->sc_size); 899 bus_dmamem_free(sc->sc_dmat, &bm->seg, bm->nsegs); 900 } 901 902 int 903 auglx_activate(struct device *self, int act) 904 { 905 struct auglx_softc *sc = (struct auglx_softc *)self; 906 907 if (act == DVACT_RESUME) 908 ac97_resume(&sc->host_if, sc->codec_if); 909 return (config_activate_children(self, act)); 910 } 911