Annotation of sys/dev/pci/auixp.c, Revision 1.1
1.1 ! nbrk 1: /* $OpenBSD: auixp.c,v 1.10 2007/05/26 00:36:03 krw Exp $ */
! 2: /* $NetBSD: auixp.c,v 1.9 2005/06/27 21:13:09 thorpej Exp $ */
! 3:
! 4: /*
! 5: * Copyright (c) 2004, 2005 Reinoud Zandijk <reinoud@netbsd.org>
! 6: * All rights reserved.
! 7: *
! 8: * Redistribution and use in source and binary forms, with or without
! 9: * modification, are permitted provided that the following conditions
! 10: * are met:
! 11: * 1. Redistributions of source code must retain the above copyright
! 12: * notice, this list of conditions and the following disclaimer.
! 13: * 2. The name of the author may not be used to endorse or promote products
! 14: * derived from this software without specific prior written permission.
! 15: * 3. All advertising materials mentioning features or use of this software
! 16: * must display the following acknowledgement:
! 17: * This product includes software developed by the NetBSD
! 18: * Foundation, Inc. and its contributors.
! 19: * 4. Neither the name of The NetBSD Foundation nor the names of its
! 20: * contributors may be used to endorse or promote products derived
! 21: * from this software without specific prior written permission.
! 22: *
! 23: * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
! 24: * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
! 25: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
! 26: * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
! 27: * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
! 28: * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
! 29: * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
! 30: * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
! 31: * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
! 32: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
! 33: * SUCH DAMAGE.
! 34: */
! 35: /*
! 36: * Audio driver for ATI IXP-{150,200,...} audio driver hardware.
! 37: *
! 38: * Recording and playback has been tested OK on various sample rates and
! 39: * encodings.
! 40: *
! 41: * Known problems and issues :
! 42: * - SPDIF is untested and needs some work still (LED stays off)
! 43: * - 32 bit audio playback failed last time i tried but that might an AC'97
! 44: * codec support problem.
! 45: * - 32 bit recording works but can't try out playing: see above.
! 46: * - no suspend/resume support yet.
! 47: * - multiple codecs are `supported' but not tested; the implemetation needs
! 48: * some cleaning up.
! 49: */
! 50:
! 51: /*#define DEBUG_AUIXP*/
! 52:
! 53: #include <sys/types.h>
! 54: #include <sys/errno.h>
! 55: #include <sys/param.h>
! 56: #include <sys/systm.h>
! 57: #include <sys/malloc.h>
! 58: #include <sys/device.h>
! 59: #include <sys/conf.h>
! 60: #include <sys/exec.h>
! 61: #include <sys/selinfo.h>
! 62: #include <sys/audioio.h>
! 63: #include <sys/queue.h>
! 64:
! 65: #include <machine/bus.h>
! 66: #include <machine/intr.h>
! 67:
! 68: #include <dev/pci/pcidevs.h>
! 69: #include <dev/pci/pcivar.h>
! 70:
! 71: #include <dev/audio_if.h>
! 72: #include <dev/mulaw.h>
! 73: #include <dev/auconv.h>
! 74: #include <dev/ic/ac97.h>
! 75:
! 76: #include <dev/pci/auixpreg.h>
! 77: #include <dev/pci/auixpvar.h>
! 78:
! 79: /* codec detection constant indicating the interrupt flags */
! 80: #define ALL_CODECS_NOT_READY \
! 81: (ATI_REG_ISR_CODEC0_NOT_READY | ATI_REG_ISR_CODEC1_NOT_READY |\
! 82: ATI_REG_ISR_CODEC2_NOT_READY)
! 83: #define CODEC_CHECK_BITS (ALL_CODECS_NOT_READY|ATI_REG_ISR_NEW_FRAME)
! 84:
! 85: /* why isn't this base address register not in the headerfile? */
! 86: #define PCI_CBIO 0x10
! 87:
! 88: /* macro's used */
! 89: #define KERNADDR(p) ((void *)((p)->addr))
! 90: #define DMAADDR(p) ((p)->map->dm_segs[0].ds_addr)
! 91:
! 92: const struct pci_matchid auixp_pci_devices[] = {
! 93: { PCI_VENDOR_ATI, PCI_PRODUCT_ATI_IXP_AUDIO_200 },
! 94: { PCI_VENDOR_ATI, PCI_PRODUCT_ATI_IXP_AUDIO_300 },
! 95: { PCI_VENDOR_ATI, PCI_PRODUCT_ATI_IXP_AUDIO_400 },
! 96: };
! 97:
! 98: struct cfdriver auixp_cd = {
! 99: NULL, "auixp", DV_DULL
! 100: };
! 101:
! 102: int auixp_match( struct device *, void *, void *);
! 103: void auixp_attach(struct device *, struct device *, void *);
! 104: int auixp_detach(struct device *, int);
! 105:
! 106: struct cfattach auixp_ca = {
! 107: sizeof(struct auixp_softc), auixp_match, auixp_attach
! 108: };
! 109:
! 110: int auixp_open(void *v, int flags);
! 111: void auixp_close(void *v);
! 112: int auixp_query_encoding(void *, struct audio_encoding *);
! 113: int auixp_set_params(void *, int, int, struct audio_params *,
! 114: struct audio_params *);
! 115: int auixp_commit_settings(void *);
! 116: int auixp_round_blocksize(void *, int);
! 117: int auixp_trigger_output(void *, void *, void *, int,
! 118: void (*)(void *), void *, struct audio_params *);
! 119: int auixp_trigger_input(void *, void *, void *, int,
! 120: void (*)(void *), void *, struct audio_params *);
! 121: int auixp_halt_output(void *);
! 122: int auixp_halt_input(void *);
! 123: int auixp_set_port(void *, mixer_ctrl_t *);
! 124: int auixp_get_port(void *, mixer_ctrl_t *);
! 125: int auixp_query_devinfo(void *, mixer_devinfo_t *);
! 126: void * auixp_malloc(void *, int, size_t, int, int);
! 127: void auixp_free(void *, void *, int);
! 128: int auixp_getdev(void *, struct audio_device *);
! 129: size_t auixp_round_buffersize(void *, int, size_t);
! 130: int auixp_get_props(void *);
! 131: int auixp_intr(void *);
! 132: int auixp_allocmem(struct auixp_softc *, size_t, size_t,
! 133: struct auixp_dma *);
! 134: int auixp_freemem(struct auixp_softc *, struct auixp_dma *);
! 135: paddr_t auixp_mappage(void *, void *, off_t, int);
! 136:
! 137:
! 138: /* power management (do we support that already?) */
! 139: int auixp_power(struct auixp_softc *, int);
! 140: #if 0
! 141: void auixp_powerhook(int, void *);
! 142: int auixp_suspend(struct auixp_softc *);
! 143: int auixp_resume(struct auixp_softc *);
! 144: #endif
! 145:
! 146:
! 147: /* Supporting subroutines */
! 148: int auixp_init(struct auixp_softc *);
! 149: void auixp_autodetect_codecs(struct auixp_softc *);
! 150: void auixp_post_config(void *);
! 151:
! 152: void auixp_reset_aclink(struct auixp_softc *);
! 153: int auixp_attach_codec(void *, struct ac97_codec_if *);
! 154: int auixp_read_codec(void *, u_int8_t, u_int16_t *);
! 155: int auixp_write_codec(void *, u_int8_t, u_int16_t);
! 156: int auixp_wait_for_codecs(struct auixp_softc *, const char *);
! 157: void auixp_reset_codec(void *);
! 158: enum ac97_host_flags auixp_flags_codec(void *);
! 159:
! 160: void auixp_enable_dma(struct auixp_softc *, struct auixp_dma *);
! 161: void auixp_disable_dma(struct auixp_softc *, struct auixp_dma *);
! 162: void auixp_enable_interrupts(struct auixp_softc *);
! 163: void auixp_disable_interrupts(struct auixp_softc *);
! 164:
! 165: void auixp_link_daisychain(struct auixp_softc *,
! 166: struct auixp_dma *, struct auixp_dma *, int, int);
! 167: int auixp_allocate_dma_chain(struct auixp_softc *, struct auixp_dma **);
! 168: void auixp_program_dma_chain(struct auixp_softc *, struct auixp_dma *);
! 169: void auixp_dma_update(struct auixp_softc *, struct auixp_dma *);
! 170: void auixp_update_busbusy(struct auixp_softc *);
! 171:
! 172: #ifdef DEBUG_AUIXP
! 173: #define DPRINTF(x) printf x;
! 174: #else
! 175: #define DPRINTF(x)
! 176: #endif
! 177:
! 178: struct audio_hw_if auixp_hw_if = {
! 179: auixp_open,
! 180: auixp_close,
! 181: NULL, /* drain */
! 182: auixp_query_encoding,
! 183: auixp_set_params,
! 184: auixp_round_blocksize,
! 185: auixp_commit_settings,
! 186: NULL, /* init_output */
! 187: NULL, /* init_input */
! 188: NULL, /* start_output */
! 189: NULL, /* start_input */
! 190: auixp_halt_output,
! 191: auixp_halt_input,
! 192: NULL, /* speaker_ctl */
! 193: auixp_getdev,
! 194: NULL, /* getfd */
! 195: auixp_set_port,
! 196: auixp_get_port,
! 197: auixp_query_devinfo,
! 198: auixp_malloc,
! 199: auixp_free,
! 200: auixp_round_buffersize,
! 201: auixp_mappage,
! 202: auixp_get_props,
! 203: auixp_trigger_output,
! 204: auixp_trigger_input
! 205: };
! 206:
! 207: int
! 208: auixp_open(void *v, int flags)
! 209: {
! 210:
! 211: return 0;
! 212: }
! 213:
! 214: void
! 215: auixp_close(void *v)
! 216: {
! 217: }
! 218:
! 219: int
! 220: auixp_query_encoding(void *hdl, struct audio_encoding *aep)
! 221: {
! 222: switch (aep->index) {
! 223: case 0:
! 224: strlcpy(aep->name, AudioEulinear, sizeof aep->name);
! 225: aep->encoding = AUDIO_ENCODING_ULINEAR;
! 226: aep->precision = 8;
! 227: aep->flags = AUDIO_ENCODINGFLAG_EMULATED;
! 228: return (0);
! 229: case 1:
! 230: strlcpy(aep->name, AudioEmulaw, sizeof aep->name);
! 231: aep->encoding = AUDIO_ENCODING_ULAW;
! 232: aep->precision = 8;
! 233: aep->flags = AUDIO_ENCODINGFLAG_EMULATED;
! 234: return (0);
! 235: case 2:
! 236: strlcpy(aep->name, AudioEalaw, sizeof aep->name);
! 237: aep->encoding = AUDIO_ENCODING_ALAW;
! 238: aep->precision = 8;
! 239: aep->flags = AUDIO_ENCODINGFLAG_EMULATED;
! 240: return (0);
! 241: case 3:
! 242: strlcpy(aep->name, AudioEslinear, sizeof aep->name);
! 243: aep->encoding = AUDIO_ENCODING_SLINEAR;
! 244: aep->precision = 8;
! 245: aep->flags = AUDIO_ENCODINGFLAG_EMULATED;
! 246: return (0);
! 247: case 4:
! 248: strlcpy(aep->name, AudioEslinear_le, sizeof aep->name);
! 249: aep->encoding = AUDIO_ENCODING_SLINEAR_LE;
! 250: aep->precision = 16;
! 251: aep->flags = 0;
! 252: return (0);
! 253: case 5:
! 254: strlcpy(aep->name, AudioEulinear_le, sizeof aep->name);
! 255: aep->encoding = AUDIO_ENCODING_ULINEAR_LE;
! 256: aep->precision = 16;
! 257: aep->flags = AUDIO_ENCODINGFLAG_EMULATED;
! 258: return (0);
! 259: case 6:
! 260: strlcpy(aep->name, AudioEslinear_be, sizeof aep->name);
! 261: aep->encoding = AUDIO_ENCODING_SLINEAR_BE;
! 262: aep->precision = 16;
! 263: aep->flags = AUDIO_ENCODINGFLAG_EMULATED;
! 264: return (0);
! 265: case 7:
! 266: strlcpy(aep->name, AudioEulinear_be, sizeof aep->name);
! 267: aep->encoding = AUDIO_ENCODING_ULINEAR_BE;
! 268: aep->precision = 16;
! 269: aep->flags = AUDIO_ENCODINGFLAG_EMULATED;
! 270: return (0);
! 271: default:
! 272: return (EINVAL);
! 273: }
! 274: }
! 275:
! 276:
! 277: /* commit setting and program ATI IXP chip */
! 278: int
! 279: auixp_commit_settings(void *hdl)
! 280: {
! 281: struct auixp_codec *co;
! 282: struct auixp_softc *sc;
! 283: bus_space_tag_t iot;
! 284: bus_space_handle_t ioh;
! 285: struct audio_params *params;
! 286: u_int32_t value;
! 287:
! 288: /* XXX would it be better to stop interrupts first? XXX */
! 289: co = (struct auixp_codec *) hdl;
! 290: sc = co->sc;
! 291: iot = sc->sc_iot;
! 292: ioh = sc->sc_ioh;
! 293:
! 294: /* process input settings */
! 295: params = &sc->sc_play_params;
! 296:
! 297: /* set input interleaving (precision) */
! 298: value = bus_space_read_4(iot, ioh, ATI_REG_CMD);
! 299: value &= ~ATI_REG_CMD_INTERLEAVE_IN;
! 300: if (params->precision <= 16)
! 301: value |= ATI_REG_CMD_INTERLEAVE_IN;
! 302: bus_space_write_4(iot, ioh, ATI_REG_CMD, value);
! 303:
! 304: /* process output settings */
! 305: params = &sc->sc_play_params;
! 306:
! 307: value = bus_space_read_4(iot, ioh, ATI_REG_OUT_DMA_SLOT);
! 308: value &= ~ATI_REG_OUT_DMA_SLOT_MASK;
! 309:
! 310: /* TODO SPDIF case for 8 channels */
! 311: switch (params->channels) {
! 312: case 6:
! 313: value |= ATI_REG_OUT_DMA_SLOT_BIT(7) |
! 314: ATI_REG_OUT_DMA_SLOT_BIT(8);
! 315: /* FALLTHROUGH */
! 316: case 4:
! 317: value |= ATI_REG_OUT_DMA_SLOT_BIT(6) |
! 318: ATI_REG_OUT_DMA_SLOT_BIT(9);
! 319: /* FALLTHROUGH */
! 320: default:
! 321: value |= ATI_REG_OUT_DMA_SLOT_BIT(3) |
! 322: ATI_REG_OUT_DMA_SLOT_BIT(4);
! 323: break;
! 324: }
! 325: /* set output threshold */
! 326: value |= 0x04 << ATI_REG_OUT_DMA_THRESHOLD_SHIFT;
! 327: bus_space_write_4(iot, ioh, ATI_REG_OUT_DMA_SLOT, value);
! 328:
! 329: /* set output interleaving (precision) */
! 330: value = bus_space_read_4(iot, ioh, ATI_REG_CMD);
! 331: value &= ~ATI_REG_CMD_INTERLEAVE_OUT;
! 332: if (params->precision <= 16)
! 333: value |= ATI_REG_CMD_INTERLEAVE_OUT;
! 334: bus_space_write_4(iot, ioh, ATI_REG_CMD, value);
! 335:
! 336: /* enable 6 channel reordering */
! 337: value = bus_space_read_4(iot, ioh, ATI_REG_6CH_REORDER);
! 338: value &= ~ATI_REG_6CH_REORDER_EN;
! 339: if (params->channels == 6)
! 340: value |= ATI_REG_6CH_REORDER_EN;
! 341: bus_space_write_4(iot, ioh, ATI_REG_6CH_REORDER, value);
! 342:
! 343: if (sc->has_spdif) {
! 344: /* set SPDIF (if present) */
! 345: value = bus_space_read_4(iot, ioh, ATI_REG_CMD);
! 346: value &= ~ATI_REG_CMD_SPDF_CONFIG_MASK;
! 347: value |= ATI_REG_CMD_SPDF_CONFIG_34; /* NetBSD AC'97 default */
! 348:
! 349: /* XXX this is probably not necessary unless splitted XXX */
! 350: value &= ~ATI_REG_CMD_INTERLEAVE_SPDF;
! 351: if (params->precision <= 16)
! 352: value |= ATI_REG_CMD_INTERLEAVE_SPDF;
! 353: bus_space_write_4(iot, ioh, ATI_REG_CMD, value);
! 354: }
! 355:
! 356: return 0;
! 357: }
! 358:
! 359:
! 360: /* set audio properties in desired setting */
! 361: int
! 362: auixp_set_params(void *hdl, int setmode, int usemode,
! 363: struct audio_params *play, struct audio_params *rec)
! 364: {
! 365: struct auixp_codec *co;
! 366: struct auixp_softc *sc;
! 367: int error;
! 368:
! 369: co = (struct auixp_codec *) hdl;
! 370: sc = co->sc;
! 371: if (setmode & AUMODE_PLAY) {
! 372: play->factor = 1;
! 373: play->sw_code = NULL;
! 374: switch(play->encoding) {
! 375: case AUDIO_ENCODING_ULAW:
! 376: switch (play->channels) {
! 377: case 1:
! 378: play->factor = 4;
! 379: play->sw_code = mulaw_to_slinear16_mts;
! 380: break;
! 381: case 2:
! 382: play->factor = 2;
! 383: play->sw_code = mulaw_to_slinear16;
! 384: break;
! 385: default:
! 386: return (EINVAL);
! 387: }
! 388: break;
! 389: case AUDIO_ENCODING_SLINEAR_LE:
! 390: switch (play->precision) {
! 391: case 8:
! 392: switch (play->channels) {
! 393: case 1:
! 394: play->factor = 4;
! 395: play->sw_code = linear8_to_linear16_mts;
! 396: break;
! 397: case 2:
! 398: play->factor = 2;
! 399: play->sw_code = linear8_to_linear16;
! 400: break;
! 401: default:
! 402: return (EINVAL);
! 403: }
! 404: break;
! 405: case 16:
! 406: switch (play->channels) {
! 407: case 1:
! 408: play->factor = 2;
! 409: play->sw_code = noswap_bytes_mts;
! 410: break;
! 411: case 2:
! 412: break;
! 413: default:
! 414: return (EINVAL);
! 415: }
! 416: break;
! 417: default:
! 418: return (EINVAL);
! 419: }
! 420: break;
! 421: case AUDIO_ENCODING_ULINEAR_LE:
! 422: switch (play->precision) {
! 423: case 8:
! 424: switch (play->channels) {
! 425: case 1:
! 426: play->factor = 4;
! 427: play->sw_code = ulinear8_to_linear16_mts;
! 428: break;
! 429: case 2:
! 430: play->factor = 2;
! 431: play->sw_code = ulinear8_to_linear16;
! 432: break;
! 433: default:
! 434: return (EINVAL);
! 435: }
! 436: break;
! 437: case 16:
! 438: switch (play->channels) {
! 439: case 1:
! 440: play->factor = 2;
! 441: play->sw_code = change_sign16_mts;
! 442: break;
! 443: case 2:
! 444: play->sw_code = change_sign16;
! 445: break;
! 446: default:
! 447: return (EINVAL);
! 448: }
! 449: break;
! 450: default:
! 451: return (EINVAL);
! 452: }
! 453: break;
! 454: case AUDIO_ENCODING_ALAW:
! 455: switch (play->channels) {
! 456: case 1:
! 457: play->factor = 4;
! 458: play->sw_code = alaw_to_slinear16_mts;
! 459: break;
! 460: case 2:
! 461: play->factor = 2;
! 462: play->sw_code = alaw_to_slinear16;
! 463: break;
! 464: default:
! 465: return (EINVAL);
! 466: }
! 467: break;
! 468: case AUDIO_ENCODING_SLINEAR_BE:
! 469: switch (play->precision) {
! 470: case 8:
! 471: switch (play->channels) {
! 472: case 1:
! 473: play->factor = 4;
! 474: play->sw_code = linear8_to_linear16_mts;
! 475: break;
! 476: case 2:
! 477: play->factor = 2;
! 478: play->sw_code = linear8_to_linear16;
! 479: break;
! 480: default:
! 481: return (EINVAL);
! 482: }
! 483: break;
! 484: case 16:
! 485: switch (play->channels) {
! 486: case 1:
! 487: play->factor = 2;
! 488: play->sw_code = swap_bytes_mts;
! 489: break;
! 490: case 2:
! 491: play->sw_code = swap_bytes;
! 492: break;
! 493: default:
! 494: return (EINVAL);
! 495: }
! 496: break;
! 497: default:
! 498: return (EINVAL);
! 499: }
! 500: break;
! 501: case AUDIO_ENCODING_ULINEAR_BE:
! 502: switch (play->precision) {
! 503: case 8:
! 504: switch (play->channels) {
! 505: case 1:
! 506: play->factor = 4;
! 507: play->sw_code = ulinear8_to_linear16_mts;
! 508: break;
! 509: case 2:
! 510: play->factor = 2;
! 511: play->sw_code = ulinear8_to_linear16;
! 512: break;
! 513: default:
! 514: return (EINVAL);
! 515: }
! 516: break;
! 517: case 16:
! 518: switch (play->channels) {
! 519: case 1:
! 520: play->factor = 2;
! 521: play->sw_code = change_sign16_swap_bytes_mts;
! 522: break;
! 523: case 2:
! 524: play->sw_code = change_sign16_swap_bytes;
! 525: break;
! 526: default:
! 527: return (EINVAL);
! 528: }
! 529: break;
! 530: default:
! 531: return (EINVAL);
! 532: }
! 533: break;
! 534: default:
! 535: return (EINVAL);
! 536: }
! 537:
! 538: error = ac97_set_rate(co->codec_if, play, AUMODE_PLAY);
! 539: if (error)
! 540: return (error);
! 541: }
! 542:
! 543: if (setmode & AUMODE_RECORD) {
! 544: rec->factor = 1;
! 545: rec->sw_code = 0;
! 546: switch(rec->encoding) {
! 547: case AUDIO_ENCODING_ULAW:
! 548: rec->sw_code = ulinear8_to_mulaw;
! 549: break;
! 550: case AUDIO_ENCODING_SLINEAR_LE:
! 551: if (rec->precision == 8)
! 552: rec->sw_code = change_sign8;
! 553: break;
! 554: case AUDIO_ENCODING_ULINEAR_LE:
! 555: if (rec->precision == 16)
! 556: rec->sw_code = change_sign16;
! 557: break;
! 558: case AUDIO_ENCODING_ALAW:
! 559: rec->sw_code = ulinear8_to_alaw;
! 560: break;
! 561: case AUDIO_ENCODING_SLINEAR_BE:
! 562: if (rec->precision == 16)
! 563: rec->sw_code = swap_bytes;
! 564: else
! 565: rec->sw_code = change_sign8;
! 566: break;
! 567: case AUDIO_ENCODING_ULINEAR_BE:
! 568: if (rec->precision == 16)
! 569: rec->sw_code = swap_bytes_change_sign16;
! 570: break;
! 571: default:
! 572: return (EINVAL);
! 573: }
! 574:
! 575: error = ac97_set_rate(co->codec_if, rec, AUMODE_RECORD);
! 576: if (error)
! 577: return (error);
! 578: }
! 579:
! 580: return (0);
! 581: }
! 582:
! 583:
! 584: /* called to translate a requested blocksize to a hw-possible one */
! 585: int
! 586: auixp_round_blocksize(void *v, int blk)
! 587: {
! 588:
! 589: blk = (blk + 0x1f) & ~0x1f;
! 590: /* Be conservative; align to 32 bytes and maximise it to 64 kb */
! 591: if (blk > 0x10000)
! 592: blk = 0x10000;
! 593:
! 594: return blk;
! 595: }
! 596:
! 597:
! 598: /*
! 599: * allocate dma capable memory and record its information for later retrieval
! 600: * when we program the dma chain itself. The trigger routines passes on the
! 601: * kernel virtual address we return here as a reference to the mapping.
! 602: */
! 603: void *
! 604: auixp_malloc(void *hdl, int direction, size_t size, int pool, int flags)
! 605: {
! 606: struct auixp_codec *co;
! 607: struct auixp_softc *sc;
! 608: struct auixp_dma *dma;
! 609: int error;
! 610:
! 611: co = (struct auixp_codec *) hdl;
! 612: sc = co->sc;
! 613: /* get us a auixp_dma structure */
! 614: dma = malloc(sizeof(*dma), pool, flags);
! 615: if (!dma)
! 616: return NULL;
! 617:
! 618: /* get us a dma buffer itself */
! 619: error = auixp_allocmem(sc, size, 16, dma);
! 620: if (error) {
! 621: free(dma, pool);
! 622: printf("%s: auixp_malloc: not enough memory\n",
! 623: sc->sc_dev.dv_xname);
! 624: return NULL;
! 625: }
! 626: SLIST_INSERT_HEAD(&sc->sc_dma_list, dma, dma_chain);
! 627:
! 628: DPRINTF(("auixp_malloc: returning kern %p, hw 0x%08x for %d bytes "
! 629: "in %d segs\n", KERNADDR(dma), (u_int32_t) DMAADDR(dma), dma->size,
! 630: dma->nsegs)
! 631: );
! 632:
! 633: return KERNADDR(dma);
! 634: }
! 635:
! 636: /*
! 637: * free and release dma capable memory we allocated before and remove its
! 638: * recording
! 639: */
! 640: void
! 641: auixp_free(void *hdl, void *addr, int pool)
! 642: {
! 643: struct auixp_codec *co;
! 644: struct auixp_softc *sc;
! 645: struct auixp_dma *dma;
! 646:
! 647: co = (struct auixp_codec *) hdl;
! 648: sc = co->sc;
! 649: SLIST_FOREACH(dma, &sc->sc_dma_list, dma_chain) {
! 650: if (KERNADDR(dma) == addr) {
! 651: SLIST_REMOVE(&sc->sc_dma_list, dma, auixp_dma,
! 652: dma_chain);
! 653: auixp_freemem(sc, dma);
! 654: free(dma, pool);
! 655: return;
! 656: }
! 657: }
! 658: }
! 659:
! 660: int
! 661: auixp_getdev(void *v, struct audio_device *adp)
! 662: {
! 663: struct auixp_softc *sc = v;
! 664: *adp = sc->sc_audev;
! 665: return 0;
! 666: }
! 667:
! 668: /* pass request to AC'97 codec code */
! 669: int
! 670: auixp_set_port(void *hdl, mixer_ctrl_t *mc)
! 671: {
! 672: struct auixp_codec *co;
! 673:
! 674: co = (struct auixp_codec *) hdl;
! 675: return co->codec_if->vtbl->mixer_set_port(co->codec_if, mc);
! 676: }
! 677:
! 678:
! 679: /* pass request to AC'97 codec code */
! 680: int
! 681: auixp_get_port(void *hdl, mixer_ctrl_t *mc)
! 682: {
! 683: struct auixp_codec *co;
! 684:
! 685: co = (struct auixp_codec *) hdl;
! 686: return co->codec_if->vtbl->mixer_get_port(co->codec_if, mc);
! 687: }
! 688:
! 689: /* pass request to AC'97 codec code */
! 690: int
! 691: auixp_query_devinfo(void *hdl, mixer_devinfo_t *di)
! 692: {
! 693: struct auixp_codec *co;
! 694:
! 695: co = (struct auixp_codec *) hdl;
! 696: return co->codec_if->vtbl->query_devinfo(co->codec_if, di);
! 697: }
! 698:
! 699:
! 700: size_t
! 701: auixp_round_buffersize(void *hdl, int direction, size_t bufsize)
! 702: {
! 703:
! 704: /* XXX force maximum? i.e. 256 kb? */
! 705: return bufsize;
! 706: }
! 707:
! 708:
! 709: int
! 710: auixp_get_props(void *hdl)
! 711: {
! 712:
! 713: return AUDIO_PROP_MMAP | AUDIO_PROP_INDEPENDENT | AUDIO_PROP_FULLDUPLEX;
! 714: }
! 715:
! 716:
! 717: /*
! 718: * A dma descriptor has dma->nsegs segments defined in dma->segs set up when
! 719: * we claimed the memory.
! 720: *
! 721: * Due to our demand for one contiguous DMA area, we only have one segment. A
! 722: * c_dma structure is about 3 kb for the 256 entries we maximally program
! 723: * -arbitrary limit AFAIK- so all is most likely to be in one segment/page
! 724: * anyway.
! 725: *
! 726: * XXX ought to implement fragmented dma area XXX
! 727: *
! 728: * Note that _v variables depict kernel virtual addresses, _p variables depict
! 729: * physical addresses.
! 730: */
! 731: void
! 732: auixp_link_daisychain(struct auixp_softc *sc,
! 733: struct auixp_dma *c_dma, struct auixp_dma *s_dma,
! 734: int blksize, int blocks)
! 735: {
! 736: atiixp_dma_desc_t *caddr_v, *next_caddr_v;
! 737: u_int32_t caddr_p, next_caddr_p, saddr_p;
! 738: int i;
! 739:
! 740: /* just make sure we are not changing when its running */
! 741: auixp_disable_dma(sc, c_dma);
! 742:
! 743: /* setup dma chain start addresses */
! 744: caddr_v = KERNADDR(c_dma);
! 745: caddr_p = DMAADDR(c_dma);
! 746: saddr_p = DMAADDR(s_dma);
! 747:
! 748: /* program the requested number of blocks */
! 749: for (i = 0; i < blocks; i++) {
! 750: /* clear the block just in case */
! 751: bzero(caddr_v, sizeof(atiixp_dma_desc_t));
! 752:
! 753: /* round robin the chain dma addresses for its successor */
! 754: next_caddr_v = caddr_v + 1;
! 755: next_caddr_p = caddr_p + sizeof(atiixp_dma_desc_t);
! 756:
! 757: if (i == blocks-1) {
! 758: next_caddr_v = KERNADDR(c_dma);
! 759: next_caddr_p = DMAADDR(c_dma);
! 760: }
! 761:
! 762: /* fill in the hardware dma chain descriptor in little-endian */
! 763: caddr_v->addr = htole32(saddr_p);
! 764: caddr_v->status = htole16(0);
! 765: caddr_v->size = htole16((blksize >> 2)); /* in dwords (!!!) */
! 766: caddr_v->next = htole32(next_caddr_p);
! 767:
! 768: /* advance slot */
! 769: saddr_p += blksize; /* XXX assuming contiguous XXX */
! 770: caddr_v = next_caddr_v;
! 771: caddr_p = next_caddr_p;
! 772: }
! 773: }
! 774:
! 775:
! 776: int
! 777: auixp_allocate_dma_chain(struct auixp_softc *sc, struct auixp_dma **dmap)
! 778: {
! 779: struct auixp_dma *dma;
! 780: int error;
! 781:
! 782: /* allocate keeper of dma area */
! 783: *dmap = NULL;
! 784: dma = malloc(sizeof(*dma), M_DEVBUF, M_NOWAIT);
! 785: if (!dma)
! 786: return ENOMEM;
! 787: bzero(dma, sizeof(*dma));
! 788:
! 789: /* allocate for daisychain of IXP hardware-dma descriptors */
! 790: error = auixp_allocmem(sc, DMA_DESC_CHAIN * sizeof(atiixp_dma_desc_t),
! 791: 16, dma);
! 792: if (error) {
! 793: printf("%s: can't malloc dma descriptor chain\n",
! 794: sc->sc_dev.dv_xname);
! 795: free(dma, M_DEVBUF);
! 796: return ENOMEM;
! 797: }
! 798:
! 799: /* return info and initialise structure */
! 800: dma->intr = NULL;
! 801: dma->intrarg = NULL;
! 802:
! 803: *dmap = dma;
! 804: return 0;
! 805: }
! 806:
! 807:
! 808: /* program dma chain in its link address descriptor */
! 809: void
! 810: auixp_program_dma_chain(struct auixp_softc *sc, struct auixp_dma *dma)
! 811: {
! 812: bus_space_tag_t iot;
! 813: bus_space_handle_t ioh;
! 814: u_int32_t value;
! 815:
! 816: iot = sc->sc_iot;
! 817: ioh = sc->sc_ioh;
! 818: /* get hardware start address of DMA chain and set valid-flag in it */
! 819: /* XXX always at start? XXX */
! 820: value = DMAADDR(dma);
! 821: value = value | ATI_REG_LINKPTR_EN;
! 822:
! 823: /* reset linkpointer */
! 824: bus_space_write_4(iot, ioh, dma->linkptr, 0);
! 825:
! 826: /* reset this DMA engine */
! 827: auixp_disable_dma(sc, dma);
! 828: auixp_enable_dma(sc, dma);
! 829:
! 830: /* program new DMA linkpointer */
! 831: bus_space_write_4(iot, ioh, dma->linkptr, value);
! 832: }
! 833:
! 834:
! 835: /* called from interrupt code to signal end of one dma-slot */
! 836: void
! 837: auixp_dma_update(struct auixp_softc *sc, struct auixp_dma *dma)
! 838: {
! 839:
! 840: /* be very paranoid */
! 841: if (!dma)
! 842: panic("auixp: update: dma = NULL");
! 843: if (!dma->intr)
! 844: panic("auixp: update: dma->intr = NULL");
! 845:
! 846: /* request more input from upper layer */
! 847: (*dma->intr)(dma->intrarg);
! 848: }
! 849:
! 850:
! 851: /*
! 852: * The magic `busbusy' bit that needs to be set when dma is active; allowing
! 853: * busmastering?
! 854: */
! 855: void
! 856: auixp_update_busbusy(struct auixp_softc *sc)
! 857: {
! 858: bus_space_tag_t iot;
! 859: bus_space_handle_t ioh;
! 860: u_int32_t value;
! 861: int running;
! 862:
! 863: iot = sc->sc_iot;
! 864: ioh = sc->sc_ioh;
! 865: /* set bus-busy flag when either recording or playing is performed */
! 866: value = bus_space_read_4(iot, ioh, ATI_REG_IER);
! 867: value &= ~ATI_REG_IER_SET_BUS_BUSY;
! 868:
! 869: running = ((sc->sc_output_dma->running) || (sc->sc_input_dma->running));
! 870: if (running)
! 871: value |= ATI_REG_IER_SET_BUS_BUSY;
! 872:
! 873: bus_space_write_4(iot, ioh, ATI_REG_IER, value);
! 874:
! 875: }
! 876:
! 877:
! 878: /*
! 879: * Called from upper audio layer to request playing audio, only called once;
! 880: * audio is refilled by calling the intr() function when space is available
! 881: * again.
! 882: */
! 883: /* XXX almost literally a copy of trigger-input; could be factorised XXX */
! 884: int
! 885: auixp_trigger_output(void *hdl, void *start, void *end, int blksize,
! 886: void (*intr)(void *), void *intrarg, struct audio_params *param)
! 887: {
! 888: struct auixp_codec *co;
! 889: struct auixp_softc *sc;
! 890: struct auixp_dma *chain_dma;
! 891: struct auixp_dma *sound_dma;
! 892: u_int32_t blocks;
! 893:
! 894: co = (struct auixp_codec *) hdl;
! 895: sc = co->sc;
! 896: chain_dma = sc->sc_output_dma;
! 897: /* add functions to call back */
! 898: chain_dma->intr = intr;
! 899: chain_dma->intrarg = intrarg;
! 900:
! 901: /*
! 902: * Program output DMA chain with blocks from [start...end] with
! 903: * blksize fragments.
! 904: *
! 905: * NOTE, we can assume its in one block since we asked for it to be in
! 906: * one contiguous blob; XXX change this? XXX
! 907: */
! 908: blocks = (size_t) (((caddr_t) end) - ((caddr_t) start)) / blksize;
! 909:
! 910: /* lookup `start' address in our list of DMA area's */
! 911: SLIST_FOREACH(sound_dma, &sc->sc_dma_list, dma_chain) {
! 912: if (KERNADDR(sound_dma) == start)
! 913: break;
! 914: }
! 915:
! 916: /* not ours ? then bail out */
! 917: if (!sound_dma) {
! 918: printf("%s: auixp_trigger_output: bad sound addr %p\n",
! 919: sc->sc_dev.dv_xname, start);
! 920: return EINVAL;
! 921: }
! 922:
! 923: /* link round-robin daisychain and program hardware */
! 924: auixp_link_daisychain(sc, chain_dma, sound_dma, blksize, blocks);
! 925: auixp_program_dma_chain(sc, chain_dma);
! 926:
! 927: /* mark we are now able to run now */
! 928: chain_dma->running = 1;
! 929:
! 930: /* update bus-flags; XXX programs more flags XXX */
! 931: auixp_update_busbusy(sc);
! 932:
! 933: /* callbacks happen in interrupt routine */
! 934: return 0;
! 935: }
! 936:
! 937:
! 938: /* halt output of audio, just disable its dma and update bus state */
! 939: int
! 940: auixp_halt_output(void *hdl)
! 941: {
! 942: struct auixp_codec *co;
! 943: struct auixp_softc *sc;
! 944: struct auixp_dma *dma;
! 945:
! 946: co = (struct auixp_codec *) hdl;
! 947: sc = co->sc;
! 948: dma = sc->sc_output_dma;
! 949: auixp_disable_dma(sc, dma);
! 950:
! 951: dma->running = 0;
! 952: auixp_update_busbusy(sc);
! 953:
! 954: return 0;
! 955: }
! 956:
! 957:
! 958: /* XXX almost literally a copy of trigger-output; could be factorised XXX */
! 959: int
! 960: auixp_trigger_input(void *hdl, void *start, void *end, int blksize,
! 961: void (*intr)(void *), void *intrarg, struct audio_params *param)
! 962: {
! 963: struct auixp_codec *co;
! 964: struct auixp_softc *sc;
! 965: struct auixp_dma *chain_dma;
! 966: struct auixp_dma *sound_dma;
! 967: u_int32_t blocks;
! 968:
! 969: co = (struct auixp_codec *) hdl;
! 970: sc = co->sc;
! 971: chain_dma = sc->sc_input_dma;
! 972: /* add functions to call back */
! 973: chain_dma->intr = intr;
! 974: chain_dma->intrarg = intrarg;
! 975:
! 976: /*
! 977: * Program output DMA chain with blocks from [start...end] with
! 978: * blksize fragments.
! 979: *
! 980: * NOTE, we can assume its in one block since we asked for it to be in
! 981: * one contiguous blob; XXX change this? XXX
! 982: */
! 983: blocks = (size_t) (((caddr_t) end) - ((caddr_t) start)) / blksize;
! 984:
! 985: /* lookup `start' address in our list of DMA area's */
! 986: SLIST_FOREACH(sound_dma, &sc->sc_dma_list, dma_chain) {
! 987: if (KERNADDR(sound_dma) == start)
! 988: break;
! 989: }
! 990:
! 991: /* not ours ? then bail out */
! 992: if (!sound_dma) {
! 993: printf("%s: auixp_trigger_input: bad sound addr %p\n",
! 994: sc->sc_dev.dv_xname, start);
! 995: return EINVAL;
! 996: }
! 997:
! 998: /* link round-robin daisychain and program hardware */
! 999: auixp_link_daisychain(sc, chain_dma, sound_dma, blksize, blocks);
! 1000: auixp_program_dma_chain(sc, chain_dma);
! 1001:
! 1002: /* mark we are now able to run now */
! 1003: chain_dma->running = 1;
! 1004:
! 1005: /* update bus-flags; XXX programs more flags XXX */
! 1006: auixp_update_busbusy(sc);
! 1007:
! 1008: /* callbacks happen in interrupt routine */
! 1009: return 0;
! 1010: }
! 1011:
! 1012:
! 1013: /* halt sampling audio, just disable its dma and update bus state */
! 1014: int
! 1015: auixp_halt_input(void *hdl)
! 1016: {
! 1017: struct auixp_codec *co;
! 1018: struct auixp_softc *sc;
! 1019: struct auixp_dma *dma;
! 1020:
! 1021: co = (struct auixp_codec *) hdl;
! 1022: sc = co->sc;
! 1023: dma = sc->sc_input_dma;
! 1024: auixp_disable_dma(sc, dma);
! 1025:
! 1026: dma->running = 0;
! 1027: auixp_update_busbusy(sc);
! 1028:
! 1029: return 0;
! 1030: }
! 1031:
! 1032:
! 1033: /*
! 1034: * IXP audio interrupt handler
! 1035: *
! 1036: * note that we return the number of bits handled; the return value is not
! 1037: * documented but I saw it implemented in other drivers. Probably returning a
! 1038: * value > 0 means "I've dealt with it"
! 1039: *
! 1040: */
! 1041: int
! 1042: auixp_intr(void *softc)
! 1043: {
! 1044: struct auixp_softc *sc;
! 1045: bus_space_tag_t iot;
! 1046: bus_space_handle_t ioh;
! 1047: u_int32_t status, enable, detected_codecs;
! 1048: int ret;
! 1049:
! 1050: sc = softc;
! 1051: iot = sc->sc_iot;
! 1052: ioh = sc->sc_ioh;
! 1053: ret = 0;
! 1054: /* get status from the interrupt status register */
! 1055: status = bus_space_read_4(iot, ioh, ATI_REG_ISR);
! 1056:
! 1057: if (status == 0)
! 1058: return 0;
! 1059:
! 1060: DPRINTF(("%s: (status = %x)\n", sc->sc_dev.dv_xname, status));
! 1061:
! 1062: /* check DMA UPDATE flags for input & output */
! 1063: if (status & ATI_REG_ISR_IN_STATUS) {
! 1064: ret++; DPRINTF(("IN_STATUS\n"));
! 1065: auixp_dma_update(sc, sc->sc_input_dma);
! 1066: }
! 1067: if (status & ATI_REG_ISR_OUT_STATUS) {
! 1068: ret++; DPRINTF(("OUT_STATUS\n"));
! 1069: auixp_dma_update(sc, sc->sc_output_dma);
! 1070: }
! 1071:
! 1072: /* XXX XRUN flags not used/needed yet; should i implement it? XXX */
! 1073: /* acknowledge the interrupts nevertheless */
! 1074: if (status & ATI_REG_ISR_IN_XRUN) {
! 1075: ret++; DPRINTF(("IN_XRUN\n"));
! 1076: /* auixp_dma_xrun(sc, sc->sc_input_dma); */
! 1077: }
! 1078: if (status & ATI_REG_ISR_OUT_XRUN) {
! 1079: ret++; DPRINTF(("OUT_XRUN\n"));
! 1080: /* auixp_dma_xrun(sc, sc->sc_output_dma); */
! 1081: }
! 1082:
! 1083: /* check if we are looking for codec detection */
! 1084: if (status & CODEC_CHECK_BITS) {
! 1085: ret++;
! 1086: /* mark missing codecs as not ready */
! 1087: detected_codecs = status & CODEC_CHECK_BITS;
! 1088: sc->sc_codec_not_ready_bits |= detected_codecs;
! 1089:
! 1090: /* disable detected interrupt sources */
! 1091: enable = bus_space_read_4(iot, ioh, ATI_REG_IER);
! 1092: enable &= ~detected_codecs;
! 1093: bus_space_write_4(iot, ioh, ATI_REG_IER, enable);
! 1094: }
! 1095:
! 1096: /* acknowledge interrupt sources */
! 1097: bus_space_write_4(iot, ioh, ATI_REG_ISR, status);
! 1098:
! 1099: return ret;
! 1100: }
! 1101:
! 1102:
! 1103: /* allocate memory for dma purposes; on failure of any of the steps, roll back */
! 1104: int
! 1105: auixp_allocmem(struct auixp_softc *sc, size_t size,
! 1106: size_t align, struct auixp_dma *dma)
! 1107: {
! 1108: int error;
! 1109:
! 1110: /* remember size */
! 1111: dma->size = size;
! 1112:
! 1113: /* allocate DMA safe memory but in just one segment for now :( */
! 1114: error = bus_dmamem_alloc(sc->sc_dmat, dma->size, align, 0,
! 1115: dma->segs, sizeof(dma->segs) / sizeof(dma->segs[0]), &dma->nsegs,
! 1116: BUS_DMA_NOWAIT);
! 1117: if (error)
! 1118: return error;
! 1119:
! 1120: /*
! 1121: * map allocated memory into kernel virtual address space and keep it
! 1122: * coherent with the CPU.
! 1123: */
! 1124: error = bus_dmamem_map(sc->sc_dmat, dma->segs, dma->nsegs, dma->size,
! 1125: &dma->addr, BUS_DMA_NOWAIT | BUS_DMA_COHERENT);
! 1126: if (error)
! 1127: goto free;
! 1128:
! 1129: /* allocate associated dma handle and initialize it. */
! 1130: error = bus_dmamap_create(sc->sc_dmat, dma->size, 1, dma->size, 0,
! 1131: BUS_DMA_NOWAIT, &dma->map);
! 1132: if (error)
! 1133: goto unmap;
! 1134:
! 1135: /*
! 1136: * load the dma handle with mappings for a dma transfer; all pages
! 1137: * need to be wired.
! 1138: */
! 1139: error = bus_dmamap_load(sc->sc_dmat, dma->map, dma->addr, dma->size, NULL,
! 1140: BUS_DMA_NOWAIT);
! 1141: if (error)
! 1142: goto destroy;
! 1143:
! 1144: return 0;
! 1145:
! 1146: destroy:
! 1147: bus_dmamap_destroy(sc->sc_dmat, dma->map);
! 1148: unmap:
! 1149: bus_dmamem_unmap(sc->sc_dmat, dma->addr, dma->size);
! 1150: free:
! 1151: bus_dmamem_free(sc->sc_dmat, dma->segs, dma->nsegs);
! 1152:
! 1153: return error;
! 1154: }
! 1155:
! 1156:
! 1157: /* undo dma mapping and release memory allocated */
! 1158: int
! 1159: auixp_freemem(struct auixp_softc *sc, struct auixp_dma *p)
! 1160: {
! 1161:
! 1162: bus_dmamap_unload(sc->sc_dmat, p->map);
! 1163: bus_dmamap_destroy(sc->sc_dmat, p->map);
! 1164: bus_dmamem_unmap(sc->sc_dmat, p->addr, p->size);
! 1165: bus_dmamem_free(sc->sc_dmat, p->segs, p->nsegs);
! 1166:
! 1167: return 0;
! 1168: }
! 1169:
! 1170:
! 1171: /* memory map dma memory */
! 1172: paddr_t
! 1173: auixp_mappage(void *hdl, void *mem, off_t off, int prot)
! 1174: {
! 1175: struct auixp_codec *co;
! 1176: struct auixp_softc *sc;
! 1177: struct auixp_dma *p;
! 1178:
! 1179: co = (struct auixp_codec *) hdl;
! 1180: sc = co->sc;
! 1181: /* for sanity */
! 1182: if (off < 0)
! 1183: return -1;
! 1184:
! 1185: /* look up allocated DMA area */
! 1186: SLIST_FOREACH(p, &sc->sc_dma_list, dma_chain) {
! 1187: if (KERNADDR(p) == mem)
! 1188: break;
! 1189: }
! 1190:
! 1191: /* have we found it ? */
! 1192: if (!p)
! 1193: return -1;
! 1194:
! 1195: /* return mmap'd region */
! 1196: return bus_dmamem_mmap(sc->sc_dmat, p->segs, p->nsegs,
! 1197: off, prot, BUS_DMA_WAITOK);
! 1198: }
! 1199:
! 1200: int
! 1201: auixp_match(struct device *dev, void *match, void *aux)
! 1202: {
! 1203: return (pci_matchbyid((struct pci_attach_args *)aux, auixp_pci_devices,
! 1204: sizeof(auixp_pci_devices)/sizeof(auixp_pci_devices[0])));
! 1205: }
! 1206:
! 1207: void
! 1208: auixp_attach(struct device *parent, struct device *self, void *aux)
! 1209: {
! 1210: struct auixp_softc *sc;
! 1211: struct pci_attach_args *pa;
! 1212: pcitag_t tag;
! 1213: pci_chipset_tag_t pc;
! 1214: pci_intr_handle_t ih;
! 1215: const char *intrstr;
! 1216: int len;
! 1217:
! 1218: sc = (struct auixp_softc *)self;
! 1219: pa = (struct pci_attach_args *)aux;
! 1220: tag = pa->pa_tag;
! 1221: pc = pa->pa_pc;
! 1222:
! 1223: /* map memory; its not sized -> what is the size? max PCI slot size? */
! 1224: if (pci_mapreg_map(pa, PCI_CBIO, PCI_MAPREG_TYPE_MEM, 0,
! 1225: &sc->sc_iot, &sc->sc_ioh, &sc->sc_iob, &sc->sc_ios, 0)) {
! 1226: printf(": can't map memory space\n");
! 1227: return;
! 1228: }
! 1229:
! 1230: /* Initialize softc */
! 1231: sc->sc_tag = tag;
! 1232: sc->sc_pct = pc;
! 1233: sc->sc_dmat = pa->pa_dmat;
! 1234: SLIST_INIT(&sc->sc_dma_list);
! 1235:
! 1236: /* get us the auixp_dma structures */
! 1237: auixp_allocate_dma_chain(sc, &sc->sc_output_dma);
! 1238: auixp_allocate_dma_chain(sc, &sc->sc_input_dma);
! 1239:
! 1240: /* when that fails we are dead in the water */
! 1241: if (!sc->sc_output_dma || !sc->sc_input_dma)
! 1242: return;
! 1243:
! 1244: /* fill in the missing details about the dma channels. */
! 1245:
! 1246: /* for output */
! 1247: sc->sc_output_dma->linkptr = ATI_REG_OUT_DMA_LINKPTR;
! 1248: sc->sc_output_dma->dma_enable_bit = ATI_REG_CMD_OUT_DMA_EN |
! 1249: ATI_REG_CMD_SEND_EN;
! 1250: /* have spdif? then this too! XXX not seeing LED yet! XXX */
! 1251: if (sc->has_spdif)
! 1252: sc->sc_output_dma->dma_enable_bit |= ATI_REG_CMD_SPDF_OUT_EN;
! 1253:
! 1254: /* and for input */
! 1255: sc->sc_input_dma->linkptr = ATI_REG_IN_DMA_LINKPTR;
! 1256: sc->sc_input_dma->dma_enable_bit = ATI_REG_CMD_IN_DMA_EN |
! 1257: ATI_REG_CMD_RECEIVE_EN;
! 1258:
! 1259: #if 0
! 1260: /* could preliminary program DMA chain */
! 1261: auixp_program_dma_chain(sc, sc->sc_output_dma);
! 1262: auixp_program_dma_chain(sc, sc->sc_input_dma);
! 1263: #endif
! 1264:
! 1265: if (pci_intr_map(pa, &ih)) {
! 1266: printf(": can't map interrupt\n");
! 1267: return;
! 1268: }
! 1269: intrstr = pci_intr_string(pc, ih);
! 1270: sc->sc_ih = pci_intr_establish(pc, ih, IPL_AUDIO, auixp_intr, sc,
! 1271: sc->sc_dev.dv_xname);
! 1272: if (sc->sc_ih == NULL) {
! 1273: printf(": can't establish interrupt");
! 1274: if (intrstr != NULL)
! 1275: printf(" at %s", intrstr);
! 1276: printf("\n");
! 1277: return;
! 1278: }
! 1279: printf(": %s\n", intrstr);
! 1280:
! 1281: strlcpy(sc->sc_audev.name, "ATI IXP AC97", sizeof sc->sc_audev.name);
! 1282: snprintf(sc->sc_audev.version, sizeof sc->sc_audev.version, "0x%02x",
! 1283: PCI_REVISION(pa->pa_class));
! 1284: strlcpy(sc->sc_audev.config, sc->sc_dev.dv_xname,
! 1285: sizeof sc->sc_audev.config);
! 1286:
! 1287: /* power up chip */
! 1288: auixp_power(sc, PCI_PMCSR_STATE_D0);
! 1289:
! 1290: /* init chip */
! 1291: if (auixp_init(sc) == -1) {
! 1292: printf("%s: auixp_attach: unable to initialize the card\n",
! 1293: sc->sc_dev.dv_xname);
! 1294: return;
! 1295: }
! 1296:
! 1297: /* XXX set up power hooks; not implemented yet XXX */
! 1298:
! 1299: len = 1; /* shut up gcc */
! 1300: #ifdef notyet
! 1301: /* create suspend save area */
! 1302: len = sizeof(u_int16_t) * (ESA_REV_B_CODE_MEMORY_LENGTH
! 1303: + ESA_REV_B_DATA_MEMORY_LENGTH + 1);
! 1304: sc->savemem = (u_int16_t *)malloc(len, M_DEVBUF, M_NOWAIT | M_ZERO);
! 1305: if (sc->savemem == NULL) {
! 1306: printf("%s: unable to allocate suspend buffer\n",
! 1307: sc->sc_dev.dv_xname);
! 1308: return;
! 1309: }
! 1310:
! 1311: sc->powerhook = powerhook_establish(auixp_powerhook, sc);
! 1312: if (sc->powerhook == NULL)
! 1313: printf("%s: WARNING: unable to establish powerhook\n",
! 1314: sc->sc_dev.dv_xname);
! 1315:
! 1316: #endif
! 1317:
! 1318: /*
! 1319: * delay further configuration of codecs and audio after interrupts
! 1320: * are enabled.
! 1321: */
! 1322: mountroothook_establish(auixp_post_config, self);
! 1323: }
! 1324:
! 1325: /* called from autoconfigure system when interrupts are enabled */
! 1326: void
! 1327: auixp_post_config(void *self)
! 1328: {
! 1329: struct auixp_softc *sc;
! 1330: struct auixp_codec *codec;
! 1331: int codec_nr;
! 1332:
! 1333: sc = (struct auixp_softc *)self;
! 1334: /* detect the AC97 codecs */
! 1335: auixp_autodetect_codecs(sc);
! 1336:
! 1337: #if notyet
! 1338: /* copy formats and invalidate entries not suitable for codec0 */
! 1339: sc->has_4ch = AC97_IS_4CH(codec->codec_if);
! 1340: sc->has_6ch = AC97_IS_6CH(codec->codec_if);
! 1341: sc->is_fixed = AC97_IS_FIXED_RATE(codec->codec_if);
! 1342: sc->has_spdif = AC97_HAS_SPDIF(codec->codec_if);
! 1343: #endif
! 1344:
! 1345: /* attach audio devices for all detected codecs */
! 1346: for (codec_nr = 0; codec_nr < ATI_IXP_CODECS; codec_nr++) {
! 1347: codec = &sc->sc_codec[codec_nr];
! 1348: if (codec->present)
! 1349: audio_attach_mi(&auixp_hw_if, codec, &sc->sc_dev);
! 1350: }
! 1351:
! 1352: /* done! now enable all interrupts we can service */
! 1353: auixp_enable_interrupts(sc);
! 1354: }
! 1355:
! 1356: void
! 1357: auixp_enable_interrupts(struct auixp_softc *sc)
! 1358: {
! 1359: bus_space_tag_t iot;
! 1360: bus_space_handle_t ioh;
! 1361: u_int32_t value;
! 1362:
! 1363: iot = sc->sc_iot;
! 1364: ioh = sc->sc_ioh;
! 1365: /* clear all pending */
! 1366: bus_space_write_4(iot, ioh, ATI_REG_ISR, 0xffffffff);
! 1367:
! 1368: /* enable all relevant interrupt sources we can handle */
! 1369: value = bus_space_read_4(iot, ioh, ATI_REG_IER);
! 1370:
! 1371: value |= ATI_REG_IER_IO_STATUS_EN;
! 1372: #ifdef notyet
! 1373: value |= ATI_REG_IER_IN_XRUN_EN;
! 1374: value |= ATI_REG_IER_OUT_XRUN_EN;
! 1375:
! 1376: value |= ATI_REG_IER_SPDIF_XRUN_EN;
! 1377: value |= ATI_REG_IER_SPDF_STATUS_EN;
! 1378: #endif
! 1379:
! 1380: bus_space_write_4(iot, ioh, ATI_REG_IER, value);
! 1381: }
! 1382:
! 1383: void
! 1384: auixp_disable_interrupts(struct auixp_softc *sc)
! 1385: {
! 1386: bus_space_tag_t iot;
! 1387: bus_space_handle_t ioh;
! 1388:
! 1389: iot = sc->sc_iot;
! 1390: ioh = sc->sc_ioh;
! 1391: /* disable all interrupt sources */
! 1392: bus_space_write_4(iot, ioh, ATI_REG_IER, 0);
! 1393:
! 1394: /* clear all pending */
! 1395: bus_space_write_4(iot, ioh, ATI_REG_ISR, 0xffffffff);
! 1396: }
! 1397:
! 1398: /* dismantle what we've set up by undoing setup */
! 1399: int
! 1400: auixp_detach(struct device *self, int flags)
! 1401: {
! 1402: struct auixp_softc *sc;
! 1403:
! 1404: sc = (struct auixp_softc *)self;
! 1405: /* XXX shouldn't we just reset the chip? XXX */
! 1406: /*
! 1407: * should we explicitly disable interrupt generation and acknowledge
! 1408: * what's left on? better be safe than sorry.
! 1409: */
! 1410: auixp_disable_interrupts(sc);
! 1411:
! 1412: /* tear down .... */
! 1413: config_detach(&sc->sc_dev, flags); /* XXX OK? XXX */
! 1414:
! 1415: if (sc->sc_ih != NULL)
! 1416: pci_intr_disestablish(sc->sc_pct, sc->sc_ih);
! 1417: if (sc->sc_ios)
! 1418: bus_space_unmap(sc->sc_iot, sc->sc_ioh, sc->sc_ios);
! 1419:
! 1420: if (sc->savemem)
! 1421: free(sc->savemem, M_DEVBUF);
! 1422:
! 1423: return 0;
! 1424: }
! 1425:
! 1426:
! 1427: /*
! 1428: * codec handling
! 1429: *
! 1430: * IXP audio support can have upto 3 codecs! are they chained ? or
! 1431: * alternative outlets with the same audio feed i.e. with different mixer
! 1432: * settings? XXX does NetBSD support more than one audio codec? XXX
! 1433: */
! 1434:
! 1435:
! 1436: int
! 1437: auixp_attach_codec(void *aux, struct ac97_codec_if *codec_if)
! 1438: {
! 1439: struct auixp_codec *ixp_codec;
! 1440:
! 1441: ixp_codec = aux;
! 1442: ixp_codec->codec_if = codec_if;
! 1443: ixp_codec->present = 1;
! 1444:
! 1445: return 0;
! 1446: }
! 1447:
! 1448: int
! 1449: auixp_read_codec(void *aux, u_int8_t reg, u_int16_t *result)
! 1450: {
! 1451: struct auixp_codec *co;
! 1452: struct auixp_softc *sc;
! 1453: bus_space_tag_t iot;
! 1454: bus_space_handle_t ioh;
! 1455: u_int32_t data;
! 1456: int timeout;
! 1457:
! 1458: co = aux;
! 1459: sc = co->sc;
! 1460: iot = sc->sc_iot;
! 1461: ioh = sc->sc_ioh;
! 1462: if (auixp_wait_for_codecs(sc, "read_codec"))
! 1463: return 0xffff;
! 1464:
! 1465: /* build up command for reading codec register */
! 1466: data = (reg << ATI_REG_PHYS_OUT_ADDR_SHIFT) |
! 1467: ATI_REG_PHYS_OUT_ADDR_EN |
! 1468: ATI_REG_PHYS_OUT_RW |
! 1469: co->codec_nr;
! 1470:
! 1471: bus_space_write_4(iot, ioh, ATI_REG_PHYS_OUT_ADDR, data);
! 1472:
! 1473: if (auixp_wait_for_codecs(sc, "read_codec"))
! 1474: return 0xffff;
! 1475:
! 1476: /* wait until codec info is clocked in */
! 1477: timeout = 500; /* 500*2 usec -> 0.001 sec */
! 1478: do {
! 1479: data = bus_space_read_4(iot, ioh, ATI_REG_PHYS_IN_ADDR);
! 1480: if (data & ATI_REG_PHYS_IN_READ_FLAG) {
! 1481: DPRINTF(("read ac'97 codec reg 0x%x = 0x%08x\n",
! 1482: reg, data >> ATI_REG_PHYS_IN_DATA_SHIFT));
! 1483: *result = data >> ATI_REG_PHYS_IN_DATA_SHIFT;
! 1484: return 0;
! 1485: }
! 1486: DELAY(2);
! 1487: timeout--;
! 1488: } while (timeout > 0);
! 1489:
! 1490: if (reg < 0x7c)
! 1491: printf("%s: codec read timeout! (reg %x)\n",
! 1492: sc->sc_dev.dv_xname, reg);
! 1493:
! 1494: return 0xffff;
! 1495: }
! 1496:
! 1497: int
! 1498: auixp_write_codec(void *aux, u_int8_t reg, u_int16_t data)
! 1499: {
! 1500: struct auixp_codec *co;
! 1501: struct auixp_softc *sc;
! 1502: bus_space_tag_t iot;
! 1503: bus_space_handle_t ioh;
! 1504: u_int32_t value;
! 1505:
! 1506: DPRINTF(("write ac'97 codec reg 0x%x = 0x%08x\n", reg, data));
! 1507: co = aux;
! 1508: sc = co->sc;
! 1509: iot = sc->sc_iot;
! 1510: ioh = sc->sc_ioh;
! 1511: if (auixp_wait_for_codecs(sc, "write_codec"))
! 1512: return -1;
! 1513:
! 1514: /* build up command for writing codec register */
! 1515: value = (((u_int32_t) data) << ATI_REG_PHYS_OUT_DATA_SHIFT) |
! 1516: (((u_int32_t) reg) << ATI_REG_PHYS_OUT_ADDR_SHIFT) |
! 1517: ATI_REG_PHYS_OUT_ADDR_EN |
! 1518: co->codec_nr;
! 1519:
! 1520: bus_space_write_4(iot, ioh, ATI_REG_PHYS_OUT_ADDR, value);
! 1521:
! 1522: return 0;
! 1523: }
! 1524:
! 1525: void
! 1526: auixp_reset_codec(void *aux)
! 1527: {
! 1528:
! 1529: /* nothing to be done? */
! 1530: }
! 1531:
! 1532: enum ac97_host_flags
! 1533: auixp_flags_codec(void *aux)
! 1534: {
! 1535: struct auixp_codec *ixp_codec;
! 1536:
! 1537: ixp_codec = aux;
! 1538: return ixp_codec->codec_flags;
! 1539: }
! 1540:
! 1541: int
! 1542: auixp_wait_for_codecs(struct auixp_softc *sc, const char *func)
! 1543: {
! 1544: bus_space_tag_t iot;
! 1545: bus_space_handle_t ioh;
! 1546: u_int32_t value;
! 1547: int timeout;
! 1548:
! 1549: iot = sc->sc_iot;
! 1550: ioh = sc->sc_ioh;
! 1551: /* wait until all codec transfers are done */
! 1552: timeout = 500; /* 500*2 usec -> 0.001 sec */
! 1553: do {
! 1554: value = bus_space_read_4(iot, ioh, ATI_REG_PHYS_OUT_ADDR);
! 1555: if ((value & ATI_REG_PHYS_OUT_ADDR_EN) == 0)
! 1556: return 0;
! 1557:
! 1558: DELAY(2);
! 1559: timeout--;
! 1560: } while (timeout > 0);
! 1561:
! 1562: printf("%s: %s: timed out\n", func, sc->sc_dev.dv_xname);
! 1563: return -1;
! 1564: }
! 1565:
! 1566: void
! 1567: auixp_autodetect_codecs(struct auixp_softc *sc)
! 1568: {
! 1569: bus_space_tag_t iot;
! 1570: bus_space_handle_t ioh;
! 1571: pcireg_t subdev;
! 1572: struct auixp_codec *codec;
! 1573: int timeout, codec_nr;
! 1574:
! 1575: iot = sc->sc_iot;
! 1576: ioh = sc->sc_ioh;
! 1577: subdev = pci_conf_read(sc->sc_pct, sc->sc_tag, PCI_SUBSYS_ID_REG);
! 1578:
! 1579: /* ATI IXP can have upto 3 codecs; mark all codecs as not existing */
! 1580: sc->sc_codec_not_ready_bits = 0;
! 1581: sc->sc_num_codecs = 0;
! 1582:
! 1583: /* enable all codecs to interrupt as well as the new frame interrupt */
! 1584: bus_space_write_4(iot, ioh, ATI_REG_IER, CODEC_CHECK_BITS);
! 1585:
! 1586: /* wait for the interrupts to happen */
! 1587: timeout = 100; /* 100.000 usec -> 0.1 sec */
! 1588:
! 1589: while (timeout > 0) {
! 1590: DELAY(1000);
! 1591: if (sc->sc_codec_not_ready_bits)
! 1592: break;
! 1593: timeout--;
! 1594: }
! 1595:
! 1596: if (timeout == 0)
! 1597: printf("%s: WARNING: timeout during codec detection; "
! 1598: "codecs might be present but haven't interrupted\n",
! 1599: sc->sc_dev.dv_xname);
! 1600:
! 1601: /* disable all interrupts for now */
! 1602: auixp_disable_interrupts(sc);
! 1603:
! 1604: /* Attach AC97 host interfaces */
! 1605: for (codec_nr = 0; codec_nr < ATI_IXP_CODECS; codec_nr++) {
! 1606: codec = &sc->sc_codec[codec_nr];
! 1607: bzero(codec, sizeof(struct auixp_codec));
! 1608:
! 1609: codec->sc = sc;
! 1610: codec->codec_nr = codec_nr;
! 1611: codec->present = 0;
! 1612:
! 1613: codec->host_if.arg = codec;
! 1614: codec->host_if.attach = auixp_attach_codec;
! 1615: codec->host_if.read = auixp_read_codec;
! 1616: codec->host_if.write = auixp_write_codec;
! 1617: codec->host_if.reset = auixp_reset_codec;
! 1618: codec->host_if.flags = auixp_flags_codec;
! 1619: switch (subdev) {
! 1620: case 0x1311462: /* MSI S270 */
! 1621: codec->codec_flags = AC97_HOST_DONT_ENABLE_SPDIF;
! 1622: break;
! 1623: }
! 1624: }
! 1625:
! 1626: if (!(sc->sc_codec_not_ready_bits & ATI_REG_ISR_CODEC0_NOT_READY)) {
! 1627: /* codec 0 present */
! 1628: DPRINTF(("auixp : YAY! codec 0 present!\n"));
! 1629: if (ac97_attach(&sc->sc_codec[0].host_if) == 0)
! 1630: sc->sc_num_codecs++;
! 1631: }
! 1632:
! 1633: #ifdef notyet
! 1634: if (!(sc->sc_codec_not_ready_bits & ATI_REG_ISR_CODEC1_NOT_READY)) {
! 1635: /* codec 1 present */
! 1636: DPRINTF(("auixp : YAY! codec 1 present!\n"));
! 1637: if (ac97_attach(&sc->sc_codec[1].host_if, &sc->sc_dev) == 0)
! 1638: sc->sc_num_codecs++;
! 1639: }
! 1640:
! 1641: if (!(sc->sc_codec_not_ready_bits & ATI_REG_ISR_CODEC2_NOT_READY)) {
! 1642: /* codec 2 present */
! 1643: DPRINTF(("auixp : YAY! codec 2 present!\n"));
! 1644: if (ac97_attach(&sc->sc_codec[2].host_if, &sc->sc_dev) == 0)
! 1645: sc->sc_num_codecs++;
! 1646: }
! 1647: #endif
! 1648:
! 1649: if (sc->sc_num_codecs == 0) {
! 1650: printf("%s: no codecs detected or initialised\n",
! 1651: sc->sc_dev.dv_xname);
! 1652: return;
! 1653: }
! 1654: }
! 1655:
! 1656: void
! 1657: auixp_disable_dma(struct auixp_softc *sc, struct auixp_dma *dma)
! 1658: {
! 1659: bus_space_tag_t iot;
! 1660: bus_space_handle_t ioh;
! 1661: u_int32_t value;
! 1662:
! 1663: iot = sc->sc_iot;
! 1664: ioh = sc->sc_ioh;
! 1665: /* lets not stress the DMA engine more than necessary */
! 1666: value = bus_space_read_4(iot, ioh, ATI_REG_CMD);
! 1667: if (value & dma->dma_enable_bit) {
! 1668: value &= ~dma->dma_enable_bit;
! 1669: bus_space_write_4(iot, ioh, ATI_REG_CMD, value);
! 1670: }
! 1671: }
! 1672:
! 1673: void
! 1674: auixp_enable_dma(struct auixp_softc *sc, struct auixp_dma *dma)
! 1675: {
! 1676: bus_space_tag_t iot;
! 1677: bus_space_handle_t ioh;
! 1678: u_int32_t value;
! 1679:
! 1680: iot = sc->sc_iot;
! 1681: ioh = sc->sc_ioh;
! 1682: /* lets not stress the DMA engine more than necesssary */
! 1683: value = bus_space_read_4(iot, ioh, ATI_REG_CMD);
! 1684: if (!(value & dma->dma_enable_bit)) {
! 1685: value |= dma->dma_enable_bit;
! 1686: bus_space_write_4(iot, ioh, ATI_REG_CMD, value);
! 1687: }
! 1688: }
! 1689:
! 1690: void
! 1691: auixp_reset_aclink(struct auixp_softc *sc)
! 1692: {
! 1693: bus_space_tag_t iot;
! 1694: bus_space_handle_t ioh;
! 1695: u_int32_t value, timeout;
! 1696:
! 1697: iot = sc->sc_iot;
! 1698: ioh = sc->sc_ioh;
! 1699:
! 1700: /* if power is down, power it up */
! 1701: value = bus_space_read_4(iot, ioh, ATI_REG_CMD);
! 1702: if (value & ATI_REG_CMD_POWERDOWN) {
! 1703: printf("%s: powering up\n", sc->sc_dev.dv_xname);
! 1704:
! 1705: /* explicitly enable power */
! 1706: value &= ~ATI_REG_CMD_POWERDOWN;
! 1707: bus_space_write_4(iot, ioh, ATI_REG_CMD, value);
! 1708:
! 1709: /* have to wait at least 10 usec for it to initialise */
! 1710: DELAY(20);
! 1711: };
! 1712:
! 1713: printf("%s: soft resetting aclink\n", sc->sc_dev.dv_xname);
! 1714:
! 1715: /* perform a soft reset */
! 1716: value = bus_space_read_4(iot, ioh, ATI_REG_CMD);
! 1717: value |= ATI_REG_CMD_AC_SOFT_RESET;
! 1718: bus_space_write_4(iot, ioh, ATI_REG_CMD, value);
! 1719:
! 1720: /* need to read the CMD reg and wait aprox. 10 usec to init */
! 1721: value = bus_space_read_4(iot, ioh, ATI_REG_CMD);
! 1722: DELAY(20);
! 1723:
! 1724: /* clear soft reset flag again */
! 1725: value = bus_space_read_4(iot, ioh, ATI_REG_CMD);
! 1726: value &= ~ATI_REG_CMD_AC_SOFT_RESET;
! 1727: bus_space_write_4(iot, ioh, ATI_REG_CMD, value);
! 1728:
! 1729: /* check if the ac-link is working; reset device otherwise */
! 1730: timeout = 10;
! 1731: value = bus_space_read_4(iot, ioh, ATI_REG_CMD);
! 1732: while (!(value & ATI_REG_CMD_ACLINK_ACTIVE)) {
! 1733: printf("%s: not up; resetting aclink hardware\n",
! 1734: sc->sc_dev.dv_xname);
! 1735:
! 1736: /* dip aclink reset but keep the acsync */
! 1737: value &= ~ATI_REG_CMD_AC_RESET;
! 1738: value |= ATI_REG_CMD_AC_SYNC;
! 1739: bus_space_write_4(iot, ioh, ATI_REG_CMD, value);
! 1740:
! 1741: /* need to read CMD again and wait again (clocking in issue?) */
! 1742: value = bus_space_read_4(iot, ioh, ATI_REG_CMD);
! 1743: DELAY(20);
! 1744:
! 1745: /* assert aclink reset again */
! 1746: value = bus_space_read_4(iot, ioh, ATI_REG_CMD);
! 1747: value |= ATI_REG_CMD_AC_RESET;
! 1748: bus_space_write_4(iot, ioh, ATI_REG_CMD, value);
! 1749:
! 1750: /* check if its active now */
! 1751: value = bus_space_read_4(iot, ioh, ATI_REG_CMD);
! 1752:
! 1753: timeout--;
! 1754: if (timeout == 0) break;
! 1755: };
! 1756:
! 1757: if (timeout == 0) {
! 1758: printf("%s: giving up aclink reset\n", sc->sc_dev.dv_xname);
! 1759: };
! 1760: if (timeout != 10) {
! 1761: printf("%s: aclink hardware reset successful\n",
! 1762: sc->sc_dev.dv_xname);
! 1763: };
! 1764:
! 1765: /* assert reset and sync for safety */
! 1766: value = bus_space_read_4(iot, ioh, ATI_REG_CMD);
! 1767: value |= ATI_REG_CMD_AC_SYNC | ATI_REG_CMD_AC_RESET;
! 1768: bus_space_write_4(iot, ioh, ATI_REG_CMD, value);
! 1769: }
! 1770:
! 1771: /* chip hard init */
! 1772: int
! 1773: auixp_init(struct auixp_softc *sc)
! 1774: {
! 1775: bus_space_tag_t iot;
! 1776: bus_space_handle_t ioh;
! 1777: u_int32_t value;
! 1778:
! 1779: iot = sc->sc_iot;
! 1780: ioh = sc->sc_ioh;
! 1781: /* disable all interrupts and clear all sources */
! 1782: auixp_disable_interrupts(sc);
! 1783:
! 1784: /* clear all DMA enables (preserving rest of settings) */
! 1785: value = bus_space_read_4(iot, ioh, ATI_REG_CMD);
! 1786: value &= ~( ATI_REG_CMD_IN_DMA_EN |
! 1787: ATI_REG_CMD_OUT_DMA_EN |
! 1788: ATI_REG_CMD_SPDF_OUT_EN );
! 1789: bus_space_write_4(iot, ioh, ATI_REG_CMD, value);
! 1790:
! 1791: /* Reset AC-link */
! 1792: auixp_reset_aclink(sc);
! 1793:
! 1794: /*
! 1795: * codecs get auto-detected later
! 1796: *
! 1797: * note: we are NOT enabling interrupts yet, no codecs have been
! 1798: * detected yet nor is anything else set up
! 1799: */
! 1800:
! 1801: return 0;
! 1802: }
! 1803:
! 1804: /*
! 1805: * TODO power saving and suspend / resume support
! 1806: */
! 1807: int
! 1808: auixp_power(struct auixp_softc *sc, int state)
! 1809: {
! 1810: pcitag_t tag;
! 1811: pci_chipset_tag_t pc;
! 1812: pcireg_t data;
! 1813: int pmcapreg;
! 1814:
! 1815: tag = sc->sc_tag;
! 1816: pc = sc->sc_pct;
! 1817: if (pci_get_capability(pc, tag, PCI_CAP_PWRMGMT, &pmcapreg, 0)) {
! 1818: data = pci_conf_read(pc, tag, pmcapreg + PCI_PMCSR);
! 1819: if ((data & PCI_PMCSR_STATE_MASK) != state)
! 1820: pci_conf_write(pc, tag, pmcapreg + PCI_PMCSR, state);
! 1821: }
! 1822:
! 1823: return 0;
! 1824: }
! 1825:
! 1826: #if 0
! 1827: void
! 1828: auixp_powerhook(int why, void *hdl)
! 1829: {
! 1830: struct auixp_softc *sc;
! 1831:
! 1832: sc = (struct auixp_softc *)hdl;
! 1833: switch (why) {
! 1834: case PWR_SUSPEND:
! 1835: case PWR_STANDBY:
! 1836: auixp_suspend(sc);
! 1837: break;
! 1838: case PWR_RESUME:
! 1839: auixp_resume(sc);
! 1840: /* XXX fix me XXX */
! 1841: (sc->codec_if->vtbl->restore_ports)(sc->codec_if);
! 1842: break;
! 1843: }
! 1844: }
! 1845:
! 1846: int
! 1847: auixp_suspend(struct auixp_softc *sc)
! 1848: {
! 1849:
! 1850: /* XXX no power functions yet XXX */
! 1851: return 0;
! 1852: }
! 1853:
! 1854: int
! 1855: auixp_resume(struct auixp_softc *sc)
! 1856: {
! 1857:
! 1858: /* XXX no power functions yet XXX */
! 1859: return 0;
! 1860: }
! 1861: #endif /* 0 */
CVSweb