Annotation of sys/dev/pci/auich.c, Revision 1.1
1.1 ! nbrk 1: /* $OpenBSD: auich.c,v 1.64 2007/08/02 07:43:41 jakemsr Exp $ */
! 2:
! 3: /*
! 4: * Copyright (c) 2000,2001 Michael Shalayeff
! 5: * All rights reserved.
! 6: *
! 7: * Redistribution and use in source and binary forms, with or without
! 8: * modification, are permitted provided that the following conditions
! 9: * are met:
! 10: * 1. Redistributions of source code must retain the above copyright
! 11: * notice, this list of conditions and the following disclaimer.
! 12: * 2. Redistributions in binary form must reproduce the above copyright
! 13: * notice, this list of conditions and the following disclaimer in the
! 14: * documentation and/or other materials provided with the distribution.
! 15: *
! 16: * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
! 17: * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
! 18: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
! 19: * IN NO EVENT SHALL THE AUTHOR OR HIS RELATIVES BE LIABLE FOR ANY DIRECT,
! 20: * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
! 21: * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
! 22: * SERVICES; LOSS OF MIND, USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
! 23: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
! 24: * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
! 25: * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
! 26: * THE POSSIBILITY OF SUCH DAMAGE.
! 27: */
! 28:
! 29: /* #define AUICH_DEBUG */
! 30: /*
! 31: * AC'97 audio found on Intel 810/815/820/440MX chipsets.
! 32: * http://developer.intel.com/design/chipsets/datashts/290655.htm
! 33: * http://developer.intel.com/design/chipsets/manuals/298028.htm
! 34: * http://www.intel.com/design/chipsets/datashts/290716.htm
! 35: * http://www.intel.com/design/chipsets/datashts/290744.htm
! 36: */
! 37:
! 38: #include <sys/param.h>
! 39: #include <sys/systm.h>
! 40: #include <sys/kernel.h>
! 41: #include <sys/malloc.h>
! 42: #include <sys/device.h>
! 43:
! 44: #include <dev/pci/pcidevs.h>
! 45: #include <dev/pci/pcivar.h>
! 46:
! 47: #include <sys/audioio.h>
! 48: #include <dev/audio_if.h>
! 49: #include <dev/mulaw.h>
! 50: #include <dev/auconv.h>
! 51:
! 52: #include <machine/bus.h>
! 53:
! 54: #include <dev/ic/ac97.h>
! 55:
! 56: /* 12.1.10 NAMBAR - native audio mixer base address register */
! 57: #define AUICH_NAMBAR 0x10
! 58: /* 12.1.11 NABMBAR - native audio bus mastering base address register */
! 59: #define AUICH_NABMBAR 0x14
! 60: #define AUICH_CFG 0x41
! 61: #define AUICH_CFG_IOSE 0x01
! 62: /* ICH4/ICH5/ICH6/ICH7 native audio mixer BAR */
! 63: #define AUICH_MMBAR 0x18
! 64: /* ICH4/ICH5/ICH6/ICH7 native bus mastering BAR */
! 65: #define AUICH_MBBAR 0x1c
! 66: #define AUICH_S2CR 0x10000000 /* tertiary codec ready */
! 67:
! 68: /* table 12-3. native audio bus master control registers */
! 69: #define AUICH_BDBAR 0x00 /* 8-byte aligned address */
! 70: #define AUICH_CIV 0x04 /* 5 bits current index value */
! 71: #define AUICH_LVI 0x05 /* 5 bits last valid index value */
! 72: #define AUICH_LVI_MASK 0x1f
! 73: #define AUICH_STS 0x06 /* 16 bits status */
! 74: #define AUICH_FIFOE 0x10 /* fifo error */
! 75: #define AUICH_BCIS 0x08 /* r- buf cmplt int sts; wr ack */
! 76: #define AUICH_LVBCI 0x04 /* r- last valid bci, wr ack */
! 77: #define AUICH_CELV 0x02 /* current equals last valid */
! 78: #define AUICH_DCH 0x01 /* dma halted */
! 79: #define AUICH_ISTS_BITS "\020\01dch\02celv\03lvbci\04bcis\05fifoe"
! 80: #define AUICH_PICB 0x08 /* 16 bits */
! 81: #define AUICH_PIV 0x0a /* 5 bits prefetched index value */
! 82: #define AUICH_CTRL 0x0b /* control */
! 83: #define AUICH_IOCE 0x10 /* int on completion enable */
! 84: #define AUICH_FEIE 0x08 /* fifo error int enable */
! 85: #define AUICH_LVBIE 0x04 /* last valid buf int enable */
! 86: #define AUICH_RR 0x02 /* 1 - reset regs */
! 87: #define AUICH_RPBM 0x01 /* 1 - run, 0 - pause */
! 88:
! 89: #define AUICH_PCMI 0x00
! 90: #define AUICH_PCMO 0x10
! 91: #define AUICH_MICI 0x20
! 92:
! 93: #define AUICH_GCTRL 0x2c
! 94: #define AUICH_SSM_78 0x40000000 /* S/PDIF slots 7 and 8 */
! 95: #define AUICH_SSM_69 0x80000000 /* S/PDIF slots 6 and 9 */
! 96: #define AUICH_SSM_1011 0xc0000000 /* S/PDIF slots 10 and 11 */
! 97: #define AUICH_POM16 0x000000 /* PCM out precision 16bit */
! 98: #define AUICH_POM20 0x400000 /* PCM out precision 20bit */
! 99: #define AUICH_PCM246_MASK 0x300000
! 100: #define AUICH_PCM2 0x000000 /* 2ch output */
! 101: #define AUICH_PCM4 0x100000 /* 4ch output */
! 102: #define AUICH_PCM6 0x200000 /* 6ch output */
! 103: #define AUICH_S2RIE 0x40 /* int when tertiary codec resume */
! 104: #define AUICH_SRIE 0x20 /* int when 2ndary codec resume */
! 105: #define AUICH_PRIE 0x10 /* int when primary codec resume */
! 106: #define AUICH_ACLSO 0x08 /* aclink shut off */
! 107: #define AUICH_WRESET 0x04 /* warm reset */
! 108: #define AUICH_CRESET 0x02 /* cold reset */
! 109: #define AUICH_GIE 0x01 /* gpi int enable */
! 110: #define AUICH_GSTS 0x30
! 111: #define AUICH_MD3 0x20000 /* pwr-dn semaphore for modem */
! 112: #define AUICH_AD3 0x10000 /* pwr-dn semaphore for audio */
! 113: #define AUICH_RCS 0x08000 /* read completion status */
! 114: #define AUICH_B3S12 0x04000 /* bit 3 of slot 12 */
! 115: #define AUICH_B2S12 0x02000 /* bit 2 of slot 12 */
! 116: #define AUICH_B1S12 0x01000 /* bit 1 of slot 12 */
! 117: #define AUICH_SRI 0x00800 /* secondary resume int */
! 118: #define AUICH_PRI 0x00400 /* primary resume int */
! 119: #define AUICH_SCR 0x00200 /* secondary codec ready */
! 120: #define AUICH_PCR 0x00100 /* primary codec ready */
! 121: #define AUICH_MINT 0x00080 /* mic in int */
! 122: #define AUICH_POINT 0x00040 /* pcm out int */
! 123: #define AUICH_PIINT 0x00020 /* pcm in int */
! 124: #define AUICH_MOINT 0x00004 /* modem out int */
! 125: #define AUICH_MIINT 0x00002 /* modem in int */
! 126: #define AUICH_GSCI 0x00001 /* gpi status change */
! 127: #define AUICH_GSTS_BITS "\020\01gsci\02miict\03moint\06piint\07point\010mint\011pcr\012scr\013pri\014sri\015b1s12\016b2s12\017b3s12\020rcs\021ad3\022md3"
! 128: #define AUICH_CAS 0x34 /* 1/8 bit */
! 129: #define AUICH_SEMATIMO 1000 /* us */
! 130: #define AUICH_RESETIMO 500000 /* us */
! 131:
! 132: #define ICH_SIS_NV_CTL 0x4c /* some SiS/NVIDIA register. From Linux */
! 133: #define ICH_SIS_CTL_UNMUTE 0x01 /* un-mute the output */
! 134:
! 135: /*
! 136: * according to the dev/audiovar.h AU_RING_SIZE is 2^16, what fits
! 137: * in our limits perfectly, i.e. setting it to higher value
! 138: * in your kernel config would improve perfomance, still 2^21 is the max
! 139: */
! 140: #define AUICH_DMALIST_MAX 32
! 141: #define AUICH_DMASEG_MAX (65536*2) /* 64k samples, 2x16 bit samples */
! 142: struct auich_dmalist {
! 143: u_int32_t base;
! 144: u_int32_t len;
! 145: #define AUICH_DMAF_IOC 0x80000000 /* 1-int on complete */
! 146: #define AUICH_DMAF_BUP 0x40000000 /* 0-retrans last, 1-transmit 0 */
! 147: };
! 148:
! 149: #define AUICH_FIXED_RATE 48000
! 150:
! 151: struct auich_dma {
! 152: bus_dmamap_t map;
! 153: caddr_t addr;
! 154: bus_dma_segment_t segs[AUICH_DMALIST_MAX];
! 155: int nsegs;
! 156: size_t size;
! 157: struct auich_dma *next;
! 158: };
! 159:
! 160: struct auich_softc {
! 161: struct device sc_dev;
! 162: void *sc_ih;
! 163:
! 164: audio_device_t sc_audev;
! 165:
! 166: bus_space_tag_t iot;
! 167: bus_space_tag_t iot_mix;
! 168: bus_space_handle_t mix_ioh;
! 169: bus_space_handle_t aud_ioh;
! 170: bus_dma_tag_t dmat;
! 171:
! 172: struct ac97_codec_if *codec_if;
! 173: struct ac97_host_if host_if;
! 174:
! 175: /* dma scatter-gather buffer lists, aligned to 8 bytes */
! 176: struct auich_dmalist *dmalist_pcmo, *dmap_pcmo;
! 177: struct auich_dmalist *dmalist_pcmi, *dmap_pcmi;
! 178: struct auich_dmalist *dmalist_mici, *dmap_mici;
! 179:
! 180: bus_dmamap_t dmalist_map;
! 181: bus_dma_segment_t dmalist_seg[2];
! 182: caddr_t dmalist_kva;
! 183: bus_addr_t dmalist_pcmo_pa;
! 184: bus_addr_t dmalist_pcmi_pa;
! 185: bus_addr_t dmalist_mici_pa;
! 186:
! 187: /* i/o buffer pointers */
! 188: u_int32_t pcmo_start, pcmo_p, pcmo_end;
! 189: int pcmo_blksize, pcmo_fifoe;
! 190: u_int32_t pcmi_start, pcmi_p, pcmi_end;
! 191: int pcmi_blksize, pcmi_fifoe;
! 192: u_int32_t mici_start, mici_p, mici_end;
! 193: int mici_blksize, mici_fifoe;
! 194: struct auich_dma *sc_dmas;
! 195:
! 196: void (*sc_pintr)(void *);
! 197: void *sc_parg;
! 198:
! 199: void (*sc_rintr)(void *);
! 200: void *sc_rarg;
! 201:
! 202: void *powerhook;
! 203: int suspend;
! 204: u_int16_t ext_ctrl;
! 205: int sc_sample_size;
! 206: int sc_sts_reg;
! 207: int sc_ignore_codecready;
! 208: int flags;
! 209: int sc_ac97rate;
! 210: };
! 211:
! 212: #ifdef AUICH_DEBUG
! 213: #define DPRINTF(l,x) do { if (auich_debug & (l)) printf x; } while(0)
! 214: int auich_debug = 0xfffe;
! 215: #define AUICH_DEBUG_CODECIO 0x0001
! 216: #define AUICH_DEBUG_DMA 0x0002
! 217: #define AUICH_DEBUG_PARAM 0x0004
! 218: #else
! 219: #define DPRINTF(x,y) /* nothing */
! 220: #endif
! 221:
! 222: struct cfdriver auich_cd = {
! 223: NULL, "auich", DV_DULL
! 224: };
! 225:
! 226: int auich_match(struct device *, void *, void *);
! 227: void auich_attach(struct device *, struct device *, void *);
! 228: int auich_intr(void *);
! 229:
! 230: struct cfattach auich_ca = {
! 231: sizeof(struct auich_softc), auich_match, auich_attach
! 232: };
! 233:
! 234: static const struct auich_devtype {
! 235: int vendor;
! 236: int product;
! 237: int options;
! 238: char name[8];
! 239: } auich_devices[] = {
! 240: { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_6300ESB_ACA, 0, "ESB" },
! 241: { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_6321ESB_ACA, 0, "ESB2" },
! 242: { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801AA_ACA, 0, "ICH" },
! 243: { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801AB_ACA, 0, "ICH0" },
! 244: { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801BA_ACA, 0, "ICH2" },
! 245: { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801CA_ACA, 0, "ICH3" },
! 246: { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801DB_ACA, 0, "ICH4" },
! 247: { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801EB_ACA, 0, "ICH5" },
! 248: { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801FB_ACA, 0, "ICH6" },
! 249: { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801GB_ACA, 0, "ICH7" },
! 250: { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82440MX_ACA, 0, "440MX" },
! 251: { PCI_VENDOR_SIS, PCI_PRODUCT_SIS_7012_ACA, 0, "SiS7012" },
! 252: { PCI_VENDOR_NVIDIA, PCI_PRODUCT_NVIDIA_NFORCE_ACA, 0, "nForce" },
! 253: { PCI_VENDOR_NVIDIA, PCI_PRODUCT_NVIDIA_NFORCE2_ACA, 0, "nForce2" },
! 254: { PCI_VENDOR_NVIDIA, PCI_PRODUCT_NVIDIA_NFORCE2_400_ACA,
! 255: 0, "nForce2" },
! 256: { PCI_VENDOR_NVIDIA, PCI_PRODUCT_NVIDIA_NFORCE3_ACA, 0, "nForce3" },
! 257: { PCI_VENDOR_NVIDIA, PCI_PRODUCT_NVIDIA_NFORCE3_250_ACA,
! 258: 0, "nForce3" },
! 259: { PCI_VENDOR_NVIDIA, PCI_PRODUCT_NVIDIA_NFORCE4_AC, 0, "nForce4" },
! 260: { PCI_VENDOR_NVIDIA, PCI_PRODUCT_NVIDIA_MCP04_AC97, 0, "MCP04" },
! 261: { PCI_VENDOR_NVIDIA, PCI_PRODUCT_NVIDIA_MCP51_ACA, 0, "MCP51" },
! 262: { PCI_VENDOR_AMD, PCI_PRODUCT_AMD_PBC768_ACA, 0, "AMD768" },
! 263: { PCI_VENDOR_AMD, PCI_PRODUCT_AMD_8111_ACA, 0, "AMD8111" },
! 264: };
! 265:
! 266: int auich_open(void *, int);
! 267: void auich_close(void *);
! 268: int auich_query_encoding(void *, struct audio_encoding *);
! 269: int auich_set_params(void *, int, int, struct audio_params *,
! 270: struct audio_params *);
! 271: int auich_round_blocksize(void *, int);
! 272: int auich_halt_output(void *);
! 273: int auich_halt_input(void *);
! 274: int auich_getdev(void *, struct audio_device *);
! 275: int auich_set_port(void *, mixer_ctrl_t *);
! 276: int auich_get_port(void *, mixer_ctrl_t *);
! 277: int auich_query_devinfo(void *, mixer_devinfo_t *);
! 278: void *auich_allocm(void *, int, size_t, int, int);
! 279: void auich_freem(void *, void *, int);
! 280: size_t auich_round_buffersize(void *, int, size_t);
! 281: paddr_t auich_mappage(void *, void *, off_t, int);
! 282: int auich_get_props(void *);
! 283: int auich_trigger_output(void *, void *, void *, int, void (*)(void *),
! 284: void *, struct audio_params *);
! 285: int auich_trigger_input(void *, void *, void *, int, void (*)(void *),
! 286: void *, struct audio_params *);
! 287:
! 288: void auich_powerhook(int, void *);
! 289:
! 290: struct audio_hw_if auich_hw_if = {
! 291: auich_open,
! 292: auich_close,
! 293: NULL, /* drain */
! 294: auich_query_encoding,
! 295: auich_set_params,
! 296: auich_round_blocksize,
! 297: NULL, /* commit_setting */
! 298: NULL, /* init_output */
! 299: NULL, /* init_input */
! 300: NULL, /* start_output */
! 301: NULL, /* start_input */
! 302: auich_halt_output,
! 303: auich_halt_input,
! 304: NULL, /* speaker_ctl */
! 305: auich_getdev,
! 306: NULL, /* getfd */
! 307: auich_set_port,
! 308: auich_get_port,
! 309: auich_query_devinfo,
! 310: auich_allocm,
! 311: auich_freem,
! 312: auich_round_buffersize,
! 313: auich_mappage,
! 314: auich_get_props,
! 315: auich_trigger_output,
! 316: auich_trigger_input
! 317: };
! 318:
! 319: int auich_attach_codec(void *, struct ac97_codec_if *);
! 320: int auich_read_codec(void *, u_int8_t, u_int16_t *);
! 321: int auich_write_codec(void *, u_int8_t, u_int16_t);
! 322: void auich_reset_codec(void *);
! 323: enum ac97_host_flags auich_flags_codec(void *);
! 324: unsigned int auich_calibrate(struct auich_softc *);
! 325:
! 326: int
! 327: auich_match(parent, match, aux)
! 328: struct device *parent;
! 329: void *match;
! 330: void *aux;
! 331: {
! 332: struct pci_attach_args *pa = aux;
! 333: int i;
! 334:
! 335: for (i = sizeof(auich_devices)/sizeof(auich_devices[0]); i--;)
! 336: if (PCI_VENDOR(pa->pa_id) == auich_devices[i].vendor &&
! 337: PCI_PRODUCT(pa->pa_id) == auich_devices[i].product)
! 338: return 1;
! 339:
! 340: return 0;
! 341: }
! 342:
! 343: void
! 344: auich_attach(parent, self, aux)
! 345: struct device *parent, *self;
! 346: void *aux;
! 347: {
! 348: struct auich_softc *sc = (struct auich_softc *)self;
! 349: struct pci_attach_args *pa = aux;
! 350: pci_intr_handle_t ih;
! 351: bus_size_t mix_size, aud_size;
! 352: pcireg_t csr;
! 353: const char *intrstr;
! 354: u_int32_t status;
! 355: bus_size_t dmasz;
! 356: int i, segs;
! 357:
! 358: if (PCI_VENDOR(pa->pa_id) == PCI_VENDOR_INTEL &&
! 359: (PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_INTEL_82801DB_ACA ||
! 360: PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_INTEL_82801EB_ACA ||
! 361: PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_INTEL_82801FB_ACA ||
! 362: PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_INTEL_82801GB_ACA)) {
! 363: /*
! 364: * Use native mode for ICH4/ICH5/ICH6/ICH7
! 365: */
! 366: if (pci_mapreg_map(pa, AUICH_MMBAR, PCI_MAPREG_TYPE_MEM, 0,
! 367: &sc->iot_mix, &sc->mix_ioh, NULL, &mix_size, 0)) {
! 368: csr = pci_conf_read(pa->pa_pc, pa->pa_tag, AUICH_CFG);
! 369: pci_conf_write(pa->pa_pc, pa->pa_tag, AUICH_CFG,
! 370: csr | AUICH_CFG_IOSE);
! 371: if (pci_mapreg_map(pa, AUICH_NAMBAR, PCI_MAPREG_TYPE_IO,
! 372: 0, &sc->iot_mix, &sc->mix_ioh, NULL, &mix_size, 0)) {
! 373: printf(": can't map codec mem/io space\n");
! 374: return;
! 375: }
! 376: }
! 377:
! 378: if (pci_mapreg_map(pa, AUICH_MBBAR, PCI_MAPREG_TYPE_MEM, 0,
! 379: &sc->iot, &sc->aud_ioh, NULL, &aud_size, 0)) {
! 380: csr = pci_conf_read(pa->pa_pc, pa->pa_tag, AUICH_CFG);
! 381: pci_conf_write(pa->pa_pc, pa->pa_tag, AUICH_CFG,
! 382: csr | AUICH_CFG_IOSE);
! 383: if (pci_mapreg_map(pa, AUICH_NABMBAR,
! 384: PCI_MAPREG_TYPE_IO, 0, &sc->iot,
! 385: &sc->aud_ioh, NULL, &aud_size, 0)) {
! 386: printf(": can't map device mem/io space\n");
! 387: bus_space_unmap(sc->iot_mix, sc->mix_ioh, mix_size);
! 388: return;
! 389: }
! 390: }
! 391: } else {
! 392: if (pci_mapreg_map(pa, AUICH_NAMBAR, PCI_MAPREG_TYPE_IO,
! 393: 0, &sc->iot_mix, &sc->mix_ioh, NULL, &mix_size, 0)) {
! 394: printf(": can't map codec i/o space\n");
! 395: return;
! 396: }
! 397:
! 398: if (pci_mapreg_map(pa, AUICH_NABMBAR, PCI_MAPREG_TYPE_IO,
! 399: 0, &sc->iot, &sc->aud_ioh, NULL, &aud_size, 0)) {
! 400: printf(": can't map device i/o space\n");
! 401: bus_space_unmap(sc->iot_mix, sc->mix_ioh, mix_size);
! 402: return;
! 403: }
! 404: }
! 405: sc->dmat = pa->pa_dmat;
! 406:
! 407: /* allocate dma memory */
! 408: dmasz = AUICH_DMALIST_MAX * 3 * sizeof(struct auich_dma);
! 409: segs = 1;
! 410: if (bus_dmamem_alloc(sc->dmat, dmasz, PAGE_SIZE, 0, sc->dmalist_seg,
! 411: segs, &segs, BUS_DMA_NOWAIT)) {
! 412: printf(": failed to alloc dmalist\n");
! 413: return;
! 414: }
! 415: if (bus_dmamem_map(sc->dmat, sc->dmalist_seg, segs, dmasz,
! 416: &sc->dmalist_kva, BUS_DMA_NOWAIT)) {
! 417: printf(": failed to map dmalist\n");
! 418: bus_dmamem_free(sc->dmat, sc->dmalist_seg, segs);
! 419: return;
! 420: }
! 421: if (bus_dmamap_create(sc->dmat, dmasz, segs, dmasz, 0, BUS_DMA_NOWAIT,
! 422: &sc->dmalist_map)) {
! 423: printf(": failed to create dmalist map\n");
! 424: bus_dmamem_unmap(sc->dmat, sc->dmalist_kva, dmasz);
! 425: bus_dmamem_free(sc->dmat, sc->dmalist_seg, segs);
! 426: return;
! 427: }
! 428: if (bus_dmamap_load_raw(sc->dmat, sc->dmalist_map, sc->dmalist_seg,
! 429: segs, dmasz, BUS_DMA_NOWAIT)) {
! 430: printf(": failed to load dmalist map: %d segs %lu size\n",
! 431: segs, (u_long)dmasz);
! 432: bus_dmamap_destroy(sc->dmat, sc->dmalist_map);
! 433: bus_dmamem_unmap(sc->dmat, sc->dmalist_kva, dmasz);
! 434: bus_dmamem_free(sc->dmat, sc->dmalist_seg, segs);
! 435: return;
! 436: }
! 437:
! 438: if (pci_intr_map(pa, &ih)) {
! 439: bus_space_unmap(sc->iot, sc->aud_ioh, aud_size);
! 440: bus_space_unmap(sc->iot_mix, sc->mix_ioh, mix_size);
! 441: return;
! 442: }
! 443: intrstr = pci_intr_string(pa->pa_pc, ih);
! 444: sc->sc_ih = pci_intr_establish(pa->pa_pc, ih, IPL_AUDIO, auich_intr,
! 445: sc, sc->sc_dev.dv_xname);
! 446: if (!sc->sc_ih) {
! 447: printf(": can't establish interrupt");
! 448: if (intrstr)
! 449: printf(" at %s", intrstr);
! 450: printf("\n");
! 451: bus_space_unmap(sc->iot, sc->aud_ioh, aud_size);
! 452: bus_space_unmap(sc->iot_mix, sc->mix_ioh, mix_size);
! 453: return;
! 454: }
! 455:
! 456: for (i = sizeof(auich_devices)/sizeof(auich_devices[0]); i--;)
! 457: if (PCI_PRODUCT(pa->pa_id) == auich_devices[i].product)
! 458: break;
! 459:
! 460: snprintf(sc->sc_audev.name, sizeof sc->sc_audev.name, "%s AC97",
! 461: auich_devices[i].name);
! 462: snprintf(sc->sc_audev.version, sizeof sc->sc_audev.version, "0x%02x",
! 463: PCI_REVISION(pa->pa_class));
! 464: strlcpy(sc->sc_audev.config, sc->sc_dev.dv_xname,
! 465: sizeof sc->sc_audev.config);
! 466:
! 467: printf(": %s, %s\n", intrstr, sc->sc_audev.name);
! 468:
! 469: /* SiS 7012 needs special handling */
! 470: if (PCI_VENDOR(pa->pa_id) == PCI_VENDOR_SIS &&
! 471: PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_SIS_7012_ACA) {
! 472: sc->sc_sts_reg = AUICH_PICB;
! 473: sc->sc_sample_size = 1;
! 474: /* un-mute output */
! 475: bus_space_write_4(sc->iot, sc->aud_ioh, ICH_SIS_NV_CTL,
! 476: bus_space_read_4(sc->iot, sc->aud_ioh, ICH_SIS_NV_CTL) |
! 477: ICH_SIS_CTL_UNMUTE);
! 478: } else {
! 479: sc->sc_sts_reg = AUICH_STS;
! 480: sc->sc_sample_size = 2;
! 481: }
! 482:
! 483: sc->dmalist_pcmo = (struct auich_dmalist *)(sc->dmalist_kva +
! 484: (0 * sizeof(struct auich_dmalist) + AUICH_DMALIST_MAX));
! 485: sc->dmalist_pcmo_pa = sc->dmalist_map->dm_segs[0].ds_addr +
! 486: (0 * sizeof(struct auich_dmalist) + AUICH_DMALIST_MAX);
! 487:
! 488: sc->dmalist_pcmi = (struct auich_dmalist *)(sc->dmalist_kva +
! 489: (1 * sizeof(struct auich_dmalist) + AUICH_DMALIST_MAX));
! 490: sc->dmalist_pcmi_pa = sc->dmalist_map->dm_segs[0].ds_addr +
! 491: (1 * sizeof(struct auich_dmalist) + AUICH_DMALIST_MAX);
! 492:
! 493: sc->dmalist_mici = (struct auich_dmalist *)(sc->dmalist_kva +
! 494: (2 * sizeof(struct auich_dmalist) + AUICH_DMALIST_MAX));
! 495: sc->dmalist_mici_pa = sc->dmalist_map->dm_segs[0].ds_addr +
! 496: (2 * sizeof(struct auich_dmalist) + AUICH_DMALIST_MAX);
! 497:
! 498: DPRINTF(AUICH_DEBUG_DMA, ("auich_attach: lists %p %p %p\n",
! 499: sc->dmalist_pcmo, sc->dmalist_pcmi, sc->dmalist_mici));
! 500:
! 501: /* Reset codec and AC'97 */
! 502: auich_reset_codec(sc);
! 503: status = bus_space_read_4(sc->iot, sc->aud_ioh, AUICH_GSTS);
! 504: if (!(status & AUICH_PCR)) { /* reset failure */
! 505: if (PCI_VENDOR(pa->pa_id) == PCI_VENDOR_INTEL &&
! 506: (PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_INTEL_82801DB_ACA ||
! 507: PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_INTEL_82801EB_ACA ||
! 508: PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_INTEL_82801FB_ACA ||
! 509: PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_INTEL_82801GB_ACA)) {
! 510: /* MSI 845G Max never return AUICH_PCR */
! 511: sc->sc_ignore_codecready = 1;
! 512: } else {
! 513: printf("%s: reset failed!\n", sc->sc_dev.dv_xname);
! 514: return;
! 515: }
! 516: }
! 517:
! 518: sc->host_if.arg = sc;
! 519: sc->host_if.attach = auich_attach_codec;
! 520: sc->host_if.read = auich_read_codec;
! 521: sc->host_if.write = auich_write_codec;
! 522: sc->host_if.reset = auich_reset_codec;
! 523: sc->host_if.flags = auich_flags_codec;
! 524: if (sc->sc_dev.dv_cfdata->cf_flags & 0x0001)
! 525: sc->flags = AC97_HOST_SWAPPED_CHANNELS;
! 526:
! 527: if (ac97_attach(&sc->host_if) != 0) {
! 528: pci_intr_disestablish(pa->pa_pc, sc->sc_ih);
! 529: bus_space_unmap(sc->iot, sc->aud_ioh, aud_size);
! 530: bus_space_unmap(sc->iot_mix, sc->mix_ioh, mix_size);
! 531: return;
! 532: }
! 533:
! 534: audio_attach_mi(&auich_hw_if, sc, &sc->sc_dev);
! 535:
! 536: /* Watch for power changes */
! 537: sc->suspend = PWR_RESUME;
! 538: sc->powerhook = powerhook_establish(auich_powerhook, sc);
! 539:
! 540: sc->sc_ac97rate = -1;
! 541: }
! 542:
! 543: int
! 544: auich_read_codec(v, reg, val)
! 545: void *v;
! 546: u_int8_t reg;
! 547: u_int16_t *val;
! 548: {
! 549: struct auich_softc *sc = v;
! 550: int i;
! 551:
! 552: /* wait for an access semaphore */
! 553: for (i = AUICH_SEMATIMO; i-- &&
! 554: bus_space_read_1(sc->iot, sc->aud_ioh, AUICH_CAS) & 1; DELAY(1));
! 555:
! 556: if (!sc->sc_ignore_codecready && i < 0) {
! 557: DPRINTF(AUICH_DEBUG_CODECIO,
! 558: ("%s: read_codec timeout\n", sc->sc_dev.dv_xname));
! 559: return (-1);
! 560: }
! 561:
! 562: *val = bus_space_read_2(sc->iot_mix, sc->mix_ioh, reg);
! 563: DPRINTF(AUICH_DEBUG_CODECIO, ("%s: read_codec(%x, %x)\n",
! 564: sc->sc_dev.dv_xname, reg, *val));
! 565: return (0);
! 566: }
! 567:
! 568: int
! 569: auich_write_codec(v, reg, val)
! 570: void *v;
! 571: u_int8_t reg;
! 572: u_int16_t val;
! 573: {
! 574: struct auich_softc *sc = v;
! 575: int i;
! 576:
! 577: /* wait for an access semaphore */
! 578: for (i = AUICH_SEMATIMO; i-- &&
! 579: bus_space_read_1(sc->iot, sc->aud_ioh, AUICH_CAS) & 1; DELAY(1));
! 580:
! 581: if (sc->sc_ignore_codecready || i >= 0) {
! 582: DPRINTF(AUICH_DEBUG_CODECIO, ("%s: write_codec(%x, %x)\n",
! 583: sc->sc_dev.dv_xname, reg, val));
! 584: bus_space_write_2(sc->iot_mix, sc->mix_ioh, reg, val);
! 585: return (0);
! 586: } else {
! 587: DPRINTF(AUICH_DEBUG_CODECIO,
! 588: ("%s: write_codec timeout\n", sc->sc_dev.dv_xname));
! 589: return (-1);
! 590: }
! 591: }
! 592:
! 593: int
! 594: auich_attach_codec(v, cif)
! 595: void *v;
! 596: struct ac97_codec_if *cif;
! 597: {
! 598: struct auich_softc *sc = v;
! 599:
! 600: sc->codec_if = cif;
! 601: return 0;
! 602: }
! 603:
! 604: void
! 605: auich_reset_codec(v)
! 606: void *v;
! 607: {
! 608: struct auich_softc *sc = v;
! 609: u_int32_t control;
! 610: int i;
! 611:
! 612: control = bus_space_read_4(sc->iot, sc->aud_ioh, AUICH_GCTRL);
! 613: control &= ~(AUICH_ACLSO | AUICH_PCM246_MASK);
! 614: control |= (control & AUICH_CRESET) ? AUICH_WRESET : AUICH_CRESET;
! 615: bus_space_write_4(sc->iot, sc->aud_ioh, AUICH_GCTRL, control);
! 616:
! 617: for (i = AUICH_RESETIMO; i-- &&
! 618: !(bus_space_read_4(sc->iot, sc->aud_ioh, AUICH_GSTS) & AUICH_PCR);
! 619: DELAY(1));
! 620:
! 621: if (i < 0)
! 622: DPRINTF(AUICH_DEBUG_CODECIO,
! 623: ("%s: reset_codec timeout\n", sc->sc_dev.dv_xname));
! 624: }
! 625:
! 626: enum ac97_host_flags
! 627: auich_flags_codec(void *v)
! 628: {
! 629: struct auich_softc *sc = v;
! 630:
! 631: return (sc->flags);
! 632: }
! 633:
! 634: int
! 635: auich_open(v, flags)
! 636: void *v;
! 637: int flags;
! 638: {
! 639: struct auich_softc *sc = v;
! 640:
! 641: if (sc->sc_ac97rate == -1)
! 642: sc->sc_ac97rate = auich_calibrate(sc);
! 643: return 0;
! 644: }
! 645:
! 646: void
! 647: auich_close(v)
! 648: void *v;
! 649: {
! 650: }
! 651:
! 652: int
! 653: auich_query_encoding(v, aep)
! 654: void *v;
! 655: struct audio_encoding *aep;
! 656: {
! 657: switch (aep->index) {
! 658: case 0:
! 659: strlcpy(aep->name, AudioEulinear, sizeof aep->name);
! 660: aep->encoding = AUDIO_ENCODING_ULINEAR;
! 661: aep->precision = 8;
! 662: aep->flags = 0;
! 663: return (0);
! 664: case 1:
! 665: strlcpy(aep->name, AudioEmulaw, sizeof aep->name);
! 666: aep->encoding = AUDIO_ENCODING_ULAW;
! 667: aep->precision = 8;
! 668: aep->flags = AUDIO_ENCODINGFLAG_EMULATED;
! 669: return (0);
! 670: case 2:
! 671: strlcpy(aep->name, AudioEalaw, sizeof aep->name);
! 672: aep->encoding = AUDIO_ENCODING_ALAW;
! 673: aep->precision = 8;
! 674: aep->flags = AUDIO_ENCODINGFLAG_EMULATED;
! 675: return (0);
! 676: case 3:
! 677: strlcpy(aep->name, AudioEslinear, sizeof aep->name);
! 678: aep->encoding = AUDIO_ENCODING_SLINEAR;
! 679: aep->precision = 8;
! 680: aep->flags = AUDIO_ENCODINGFLAG_EMULATED;
! 681: return (0);
! 682: case 4:
! 683: strlcpy(aep->name, AudioEslinear_le, sizeof aep->name);
! 684: aep->encoding = AUDIO_ENCODING_SLINEAR_LE;
! 685: aep->precision = 16;
! 686: aep->flags = 0;
! 687: return (0);
! 688: case 5:
! 689: strlcpy(aep->name, AudioEulinear_le, sizeof aep->name);
! 690: aep->encoding = AUDIO_ENCODING_ULINEAR_LE;
! 691: aep->precision = 16;
! 692: aep->flags = AUDIO_ENCODINGFLAG_EMULATED;
! 693: return (0);
! 694: case 6:
! 695: strlcpy(aep->name, AudioEslinear_be, sizeof aep->name);
! 696: aep->encoding = AUDIO_ENCODING_SLINEAR_BE;
! 697: aep->precision = 16;
! 698: aep->flags = AUDIO_ENCODINGFLAG_EMULATED;
! 699: return (0);
! 700: case 7:
! 701: strlcpy(aep->name, AudioEulinear_be, sizeof aep->name);
! 702: aep->encoding = AUDIO_ENCODING_ULINEAR_BE;
! 703: aep->precision = 16;
! 704: aep->flags = AUDIO_ENCODINGFLAG_EMULATED;
! 705: return (0);
! 706: default:
! 707: return (EINVAL);
! 708: }
! 709: }
! 710:
! 711: int
! 712: auich_set_params(v, setmode, usemode, play, rec)
! 713: void *v;
! 714: int setmode, usemode;
! 715: struct audio_params *play, *rec;
! 716: {
! 717: struct auich_softc *sc = v;
! 718: int error;
! 719: u_int orate;
! 720: u_int adj_rate;
! 721:
! 722: if (setmode & AUMODE_PLAY) {
! 723: play->factor = 1;
! 724: play->sw_code = NULL;
! 725: switch(play->encoding) {
! 726: case AUDIO_ENCODING_ULAW:
! 727: switch (play->channels) {
! 728: case 1:
! 729: play->factor = 4;
! 730: play->sw_code = mulaw_to_slinear16_mts;
! 731: break;
! 732: case 2:
! 733: play->factor = 2;
! 734: play->sw_code = mulaw_to_slinear16;
! 735: break;
! 736: default:
! 737: return (EINVAL);
! 738: }
! 739: break;
! 740: case AUDIO_ENCODING_SLINEAR_LE:
! 741: switch (play->precision) {
! 742: case 8:
! 743: switch (play->channels) {
! 744: case 1:
! 745: play->factor = 4;
! 746: play->sw_code = linear8_to_linear16_mts;
! 747: break;
! 748: case 2:
! 749: play->factor = 2;
! 750: play->sw_code = linear8_to_linear16;
! 751: break;
! 752: default:
! 753: return (EINVAL);
! 754: }
! 755: break;
! 756: case 16:
! 757: switch (play->channels) {
! 758: case 1:
! 759: play->factor = 2;
! 760: play->sw_code = noswap_bytes_mts;
! 761: break;
! 762: case 2:
! 763: break;
! 764: default:
! 765: return (EINVAL);
! 766: }
! 767: break;
! 768: default:
! 769: return (EINVAL);
! 770: }
! 771: break;
! 772: case AUDIO_ENCODING_ULINEAR_LE:
! 773: switch (play->precision) {
! 774: case 8:
! 775: switch (play->channels) {
! 776: case 1:
! 777: play->factor = 4;
! 778: play->sw_code = ulinear8_to_linear16_mts;
! 779: break;
! 780: case 2:
! 781: play->factor = 2;
! 782: play->sw_code = ulinear8_to_linear16;
! 783: break;
! 784: default:
! 785: return (EINVAL);
! 786: }
! 787: break;
! 788: case 16:
! 789: switch (play->channels) {
! 790: case 1:
! 791: play->factor = 2;
! 792: play->sw_code = change_sign16_mts;
! 793: break;
! 794: case 2:
! 795: play->sw_code = change_sign16;
! 796: break;
! 797: default:
! 798: return (EINVAL);
! 799: }
! 800: break;
! 801: default:
! 802: return (EINVAL);
! 803: }
! 804: break;
! 805: case AUDIO_ENCODING_ALAW:
! 806: switch (play->channels) {
! 807: case 1:
! 808: play->factor = 4;
! 809: play->sw_code = alaw_to_slinear16_mts;
! 810: break;
! 811: case 2:
! 812: play->factor = 2;
! 813: play->sw_code = alaw_to_slinear16;
! 814: break;
! 815: default:
! 816: return (EINVAL);
! 817: }
! 818: break;
! 819: case AUDIO_ENCODING_SLINEAR_BE:
! 820: switch (play->precision) {
! 821: case 8:
! 822: switch (play->channels) {
! 823: case 1:
! 824: play->factor = 4;
! 825: play->sw_code = linear8_to_linear16_mts;
! 826: break;
! 827: case 2:
! 828: play->factor = 2;
! 829: play->sw_code = linear8_to_linear16;
! 830: break;
! 831: default:
! 832: return (EINVAL);
! 833: }
! 834: break;
! 835: case 16:
! 836: switch (play->channels) {
! 837: case 1:
! 838: play->factor = 2;
! 839: play->sw_code = swap_bytes_mts;
! 840: break;
! 841: case 2:
! 842: play->sw_code = swap_bytes;
! 843: break;
! 844: default:
! 845: return (EINVAL);
! 846: }
! 847: break;
! 848: default:
! 849: return (EINVAL);
! 850: }
! 851: break;
! 852: case AUDIO_ENCODING_ULINEAR_BE:
! 853: switch (play->precision) {
! 854: case 8:
! 855: switch (play->channels) {
! 856: case 1:
! 857: play->factor = 4;
! 858: play->sw_code = ulinear8_to_linear16_mts;
! 859: break;
! 860: case 2:
! 861: play->factor = 2;
! 862: play->sw_code = ulinear8_to_linear16;
! 863: break;
! 864: default:
! 865: return (EINVAL);
! 866: }
! 867: break;
! 868: case 16:
! 869: switch (play->channels) {
! 870: case 1:
! 871: play->factor = 2;
! 872: play->sw_code = change_sign16_swap_bytes_mts;
! 873: break;
! 874: case 2:
! 875: play->sw_code = change_sign16_swap_bytes;
! 876: break;
! 877: default:
! 878: return (EINVAL);
! 879: }
! 880: break;
! 881: default:
! 882: return (EINVAL);
! 883: }
! 884: break;
! 885: default:
! 886: return (EINVAL);
! 887: }
! 888:
! 889: orate = adj_rate = play->sample_rate;
! 890: if (sc->sc_ac97rate != 0)
! 891: adj_rate = orate * AUICH_FIXED_RATE / sc->sc_ac97rate;
! 892: play->sample_rate = adj_rate;
! 893: error = ac97_set_rate(sc->codec_if, play, AUMODE_PLAY);
! 894: if (play->sample_rate == adj_rate)
! 895: play->sample_rate = orate;
! 896: if (error)
! 897: return (error);
! 898: }
! 899:
! 900: if (setmode & AUMODE_RECORD) {
! 901: rec->factor = 1;
! 902: rec->sw_code = 0;
! 903: switch(rec->encoding) {
! 904: case AUDIO_ENCODING_ULAW:
! 905: rec->sw_code = slinear16_to_mulaw_le;
! 906: rec->factor = 2;
! 907: break;
! 908: case AUDIO_ENCODING_ALAW:
! 909: rec->sw_code = slinear16_to_alaw_le;
! 910: rec->factor = 2;
! 911: break;
! 912: case AUDIO_ENCODING_SLINEAR_LE:
! 913: switch (rec->precision) {
! 914: case 8:
! 915: rec->sw_code = linear16_to_linear8_le;
! 916: rec->factor = 2;
! 917: break;
! 918: case 16:
! 919: break;
! 920: default:
! 921: return (EINVAL);
! 922: }
! 923: break;
! 924: case AUDIO_ENCODING_ULINEAR_LE:
! 925: switch (rec->precision) {
! 926: case 8:
! 927: rec->sw_code = linear16_to_ulinear8_le;
! 928: rec->factor = 2;
! 929: break;
! 930: case 16:
! 931: rec->sw_code = change_sign16_le;
! 932: break;
! 933: default:
! 934: return (EINVAL);
! 935: }
! 936: break;
! 937: case AUDIO_ENCODING_SLINEAR_BE:
! 938: switch (rec->precision) {
! 939: case 8:
! 940: rec->sw_code = linear16_to_linear8_le;
! 941: rec->factor = 2;
! 942: break;
! 943: case 16:
! 944: rec->sw_code = swap_bytes;
! 945: break;
! 946: default:
! 947: return (EINVAL);
! 948: }
! 949: break;
! 950: case AUDIO_ENCODING_ULINEAR_BE:
! 951: switch (rec->precision) {
! 952: case 8:
! 953: rec->sw_code = linear16_to_ulinear8_le;
! 954: rec->factor = 2;
! 955: break;
! 956: case 16:
! 957: rec->sw_code = change_sign16_swap_bytes_le;
! 958: break;
! 959: default:
! 960: return (EINVAL);
! 961: }
! 962: break;
! 963: default:
! 964: return (EINVAL);
! 965: }
! 966:
! 967: orate = rec->sample_rate;
! 968: if (sc->sc_ac97rate != 0)
! 969: rec->sample_rate = orate * AUICH_FIXED_RATE /
! 970: sc->sc_ac97rate;
! 971: error = ac97_set_rate(sc->codec_if, rec, AUMODE_RECORD);
! 972: rec->sample_rate = orate;
! 973: if (error)
! 974: return (error);
! 975: }
! 976:
! 977: return (0);
! 978: }
! 979:
! 980: int
! 981: auich_round_blocksize(v, blk)
! 982: void *v;
! 983: int blk;
! 984: {
! 985: return (blk + 0x3f) & ~0x3f;
! 986: }
! 987:
! 988: int
! 989: auich_halt_output(v)
! 990: void *v;
! 991: {
! 992: struct auich_softc *sc = v;
! 993:
! 994: DPRINTF(AUICH_DEBUG_DMA, ("%s: halt_output\n", sc->sc_dev.dv_xname));
! 995:
! 996: bus_space_write_1(sc->iot, sc->aud_ioh, AUICH_PCMO + AUICH_CTRL, AUICH_RR);
! 997:
! 998: return 0;
! 999: }
! 1000:
! 1001: int
! 1002: auich_halt_input(v)
! 1003: void *v;
! 1004: {
! 1005: struct auich_softc *sc = v;
! 1006:
! 1007: DPRINTF(AUICH_DEBUG_DMA,
! 1008: ("%s: halt_input\n", sc->sc_dev.dv_xname));
! 1009:
! 1010: /* XXX halt both unless known otherwise */
! 1011:
! 1012: bus_space_write_1(sc->iot, sc->aud_ioh, AUICH_PCMI + AUICH_CTRL, AUICH_RR);
! 1013: bus_space_write_1(sc->iot, sc->aud_ioh, AUICH_MICI + AUICH_CTRL, AUICH_RR);
! 1014:
! 1015: return 0;
! 1016: }
! 1017:
! 1018: int
! 1019: auich_getdev(v, adp)
! 1020: void *v;
! 1021: struct audio_device *adp;
! 1022: {
! 1023: struct auich_softc *sc = v;
! 1024: *adp = sc->sc_audev;
! 1025: return 0;
! 1026: }
! 1027:
! 1028: int
! 1029: auich_set_port(v, cp)
! 1030: void *v;
! 1031: mixer_ctrl_t *cp;
! 1032: {
! 1033: struct auich_softc *sc = v;
! 1034: return sc->codec_if->vtbl->mixer_set_port(sc->codec_if, cp);
! 1035: }
! 1036:
! 1037: int
! 1038: auich_get_port(v, cp)
! 1039: void *v;
! 1040: mixer_ctrl_t *cp;
! 1041: {
! 1042: struct auich_softc *sc = v;
! 1043: return sc->codec_if->vtbl->mixer_get_port(sc->codec_if, cp);
! 1044: }
! 1045:
! 1046: int
! 1047: auich_query_devinfo(v, dp)
! 1048: void *v;
! 1049: mixer_devinfo_t *dp;
! 1050: {
! 1051: struct auich_softc *sc = v;
! 1052: return sc->codec_if->vtbl->query_devinfo(sc->codec_if, dp);
! 1053: }
! 1054:
! 1055: void *
! 1056: auich_allocm(v, direction, size, pool, flags)
! 1057: void *v;
! 1058: int direction;
! 1059: size_t size;
! 1060: int pool, flags;
! 1061: {
! 1062: struct auich_softc *sc = v;
! 1063: struct auich_dma *p;
! 1064: int error;
! 1065:
! 1066: if (size > AUICH_DMALIST_MAX * AUICH_DMASEG_MAX)
! 1067: return NULL;
! 1068:
! 1069: p = malloc(sizeof(*p), pool, flags);
! 1070: if (!p)
! 1071: return NULL;
! 1072: bzero(p, sizeof(*p));
! 1073:
! 1074: p->size = size;
! 1075: if ((error = bus_dmamem_alloc(sc->dmat, p->size, NBPG, 0, p->segs,
! 1076: 1, &p->nsegs, BUS_DMA_NOWAIT)) != 0) {
! 1077: printf("%s: unable to allocate dma, error = %d\n",
! 1078: sc->sc_dev.dv_xname, error);
! 1079: free(p, pool);
! 1080: return NULL;
! 1081: }
! 1082:
! 1083: if ((error = bus_dmamem_map(sc->dmat, p->segs, p->nsegs, p->size,
! 1084: &p->addr, BUS_DMA_NOWAIT | BUS_DMA_COHERENT)) != 0) {
! 1085: printf("%s: unable to map dma, error = %d\n",
! 1086: sc->sc_dev.dv_xname, error);
! 1087: bus_dmamem_free(sc->dmat, p->segs, p->nsegs);
! 1088: free(p, pool);
! 1089: return NULL;
! 1090: }
! 1091:
! 1092: if ((error = bus_dmamap_create(sc->dmat, p->size, 1,
! 1093: p->size, 0, BUS_DMA_NOWAIT, &p->map)) != 0) {
! 1094: printf("%s: unable to create dma map, error = %d\n",
! 1095: sc->sc_dev.dv_xname, error);
! 1096: bus_dmamem_unmap(sc->dmat, p->addr, size);
! 1097: bus_dmamem_free(sc->dmat, p->segs, p->nsegs);
! 1098: free(p, pool);
! 1099: return NULL;
! 1100: }
! 1101:
! 1102: if ((error = bus_dmamap_load(sc->dmat, p->map, p->addr, p->size,
! 1103: NULL, BUS_DMA_NOWAIT)) != 0) {
! 1104: printf("%s: unable to load dma map, error = %d\n",
! 1105: sc->sc_dev.dv_xname, error);
! 1106: bus_dmamap_destroy(sc->dmat, p->map);
! 1107: bus_dmamem_unmap(sc->dmat, p->addr, size);
! 1108: bus_dmamem_free(sc->dmat, p->segs, p->nsegs);
! 1109: free(p, pool);
! 1110: return NULL;
! 1111: }
! 1112:
! 1113: p->next = sc->sc_dmas;
! 1114: sc->sc_dmas = p;
! 1115:
! 1116: return p->addr;
! 1117: }
! 1118:
! 1119: void
! 1120: auich_freem(v, ptr, pool)
! 1121: void *v;
! 1122: void *ptr;
! 1123: int pool;
! 1124: {
! 1125: struct auich_softc *sc = v;
! 1126: struct auich_dma *p;
! 1127:
! 1128: for (p = sc->sc_dmas; p->addr != ptr; p = p->next)
! 1129: if (p->next == NULL) {
! 1130: printf("auich_freem: trying to free not allocated memory");
! 1131: return;
! 1132: }
! 1133:
! 1134: bus_dmamap_unload(sc->dmat, p->map);
! 1135: bus_dmamap_destroy(sc->dmat, p->map);
! 1136: bus_dmamem_unmap(sc->dmat, p->addr, p->size);
! 1137: bus_dmamem_free(sc->dmat, p->segs, p->nsegs);
! 1138: free(p, pool);
! 1139: }
! 1140:
! 1141: size_t
! 1142: auich_round_buffersize(v, direction, size)
! 1143: void *v;
! 1144: int direction;
! 1145: size_t size;
! 1146: {
! 1147: if (size > AUICH_DMALIST_MAX * AUICH_DMASEG_MAX)
! 1148: size = AUICH_DMALIST_MAX * AUICH_DMASEG_MAX;
! 1149:
! 1150: return size;
! 1151: }
! 1152:
! 1153: paddr_t
! 1154: auich_mappage(v, mem, off, prot)
! 1155: void *v;
! 1156: void *mem;
! 1157: off_t off;
! 1158: int prot;
! 1159: {
! 1160: struct auich_softc *sc = v;
! 1161: struct auich_dma *p;
! 1162:
! 1163: if (off < 0)
! 1164: return -1;
! 1165:
! 1166: for (p = sc->sc_dmas; p && p->addr != mem; p = p->next);
! 1167: if (!p)
! 1168: return -1;
! 1169:
! 1170: return bus_dmamem_mmap(sc->dmat, p->segs, p->nsegs,
! 1171: off, prot, BUS_DMA_WAITOK);
! 1172: }
! 1173:
! 1174: int
! 1175: auich_get_props(v)
! 1176: void *v;
! 1177: {
! 1178: return AUDIO_PROP_MMAP | AUDIO_PROP_INDEPENDENT | AUDIO_PROP_FULLDUPLEX;
! 1179: }
! 1180:
! 1181: int
! 1182: auich_intr(v)
! 1183: void *v;
! 1184: {
! 1185: struct auich_softc *sc = v;
! 1186: int ret = 0, sts, gsts, i;
! 1187:
! 1188: gsts = bus_space_read_2(sc->iot, sc->aud_ioh, AUICH_GSTS);
! 1189: DPRINTF(AUICH_DEBUG_DMA, ("auich_intr: gsts=%b\n", gsts, AUICH_GSTS_BITS));
! 1190:
! 1191: if (gsts & AUICH_POINT) {
! 1192: sts = bus_space_read_2(sc->iot, sc->aud_ioh,
! 1193: AUICH_PCMO + sc->sc_sts_reg);
! 1194: DPRINTF(AUICH_DEBUG_DMA,
! 1195: ("auich_intr: osts=%b\n", sts, AUICH_ISTS_BITS));
! 1196:
! 1197: #ifdef AUICH_DEBUG
! 1198: if (sts & AUICH_FIFOE) {
! 1199: printf("%s: fifo underrun # %u\n",
! 1200: sc->sc_dev.dv_xname, ++sc->pcmo_fifoe);
! 1201: }
! 1202: #endif
! 1203: i = bus_space_read_1(sc->iot, sc->aud_ioh, AUICH_PCMO + AUICH_CIV);
! 1204: if (sts & (AUICH_LVBCI | AUICH_CELV)) {
! 1205: struct auich_dmalist *q, *qe;
! 1206:
! 1207: q = sc->dmap_pcmo;
! 1208: qe = &sc->dmalist_pcmo[i];
! 1209:
! 1210: while (q != qe) {
! 1211:
! 1212: q->base = sc->pcmo_p;
! 1213: q->len = (sc->pcmo_blksize /
! 1214: sc->sc_sample_size) | AUICH_DMAF_IOC;
! 1215: DPRINTF(AUICH_DEBUG_DMA,
! 1216: ("auich_intr: %p, %p = %x @ %p\n",
! 1217: qe, q, sc->pcmo_blksize /
! 1218: sc->sc_sample_size, sc->pcmo_p));
! 1219:
! 1220: sc->pcmo_p += sc->pcmo_blksize;
! 1221: if (sc->pcmo_p >= sc->pcmo_end)
! 1222: sc->pcmo_p = sc->pcmo_start;
! 1223:
! 1224: if (++q == &sc->dmalist_pcmo[AUICH_DMALIST_MAX])
! 1225: q = sc->dmalist_pcmo;
! 1226: }
! 1227:
! 1228: sc->dmap_pcmo = q;
! 1229: bus_space_write_1(sc->iot, sc->aud_ioh,
! 1230: AUICH_PCMO + AUICH_LVI,
! 1231: (sc->dmap_pcmo - sc->dmalist_pcmo - 1) &
! 1232: AUICH_LVI_MASK);
! 1233: }
! 1234:
! 1235: if (sts & AUICH_BCIS && sc->sc_pintr)
! 1236: sc->sc_pintr(sc->sc_parg);
! 1237:
! 1238: /* int ack */
! 1239: bus_space_write_2(sc->iot, sc->aud_ioh,
! 1240: AUICH_PCMO + sc->sc_sts_reg, sts &
! 1241: (AUICH_LVBCI | AUICH_CELV | AUICH_BCIS | AUICH_FIFOE));
! 1242: bus_space_write_2(sc->iot, sc->aud_ioh, AUICH_GSTS, AUICH_POINT);
! 1243: ret++;
! 1244: }
! 1245:
! 1246: if (gsts & AUICH_PIINT) {
! 1247: sts = bus_space_read_2(sc->iot, sc->aud_ioh,
! 1248: AUICH_PCMI + sc->sc_sts_reg);
! 1249: DPRINTF(AUICH_DEBUG_DMA,
! 1250: ("auich_intr: ists=%b\n", sts, AUICH_ISTS_BITS));
! 1251:
! 1252: #ifdef AUICH_DEBUG
! 1253: if (sts & AUICH_FIFOE) {
! 1254: printf("%s: in fifo overrun # %u\n",
! 1255: sc->sc_dev.dv_xname, ++sc->pcmi_fifoe);
! 1256: }
! 1257: #endif
! 1258: i = bus_space_read_1(sc->iot, sc->aud_ioh, AUICH_PCMI + AUICH_CIV);
! 1259: if (sts & (AUICH_LVBCI | AUICH_CELV)) {
! 1260: struct auich_dmalist *q, *qe;
! 1261:
! 1262: q = sc->dmap_pcmi;
! 1263: qe = &sc->dmalist_pcmi[i];
! 1264:
! 1265: while (q != qe) {
! 1266:
! 1267: q->base = sc->pcmi_p;
! 1268: q->len = (sc->pcmi_blksize /
! 1269: sc->sc_sample_size) | AUICH_DMAF_IOC;
! 1270: DPRINTF(AUICH_DEBUG_DMA,
! 1271: ("auich_intr: %p, %p = %x @ %p\n",
! 1272: qe, q, sc->pcmi_blksize /
! 1273: sc->sc_sample_size, sc->pcmi_p));
! 1274:
! 1275: sc->pcmi_p += sc->pcmi_blksize;
! 1276: if (sc->pcmi_p >= sc->pcmi_end)
! 1277: sc->pcmi_p = sc->pcmi_start;
! 1278:
! 1279: if (++q == &sc->dmalist_pcmi[AUICH_DMALIST_MAX])
! 1280: q = sc->dmalist_pcmi;
! 1281: }
! 1282:
! 1283: sc->dmap_pcmi = q;
! 1284: bus_space_write_1(sc->iot, sc->aud_ioh,
! 1285: AUICH_PCMI + AUICH_LVI,
! 1286: (sc->dmap_pcmi - sc->dmalist_pcmi - 1) &
! 1287: AUICH_LVI_MASK);
! 1288: }
! 1289:
! 1290: if (sts & AUICH_BCIS && sc->sc_rintr)
! 1291: sc->sc_rintr(sc->sc_rarg);
! 1292:
! 1293: /* int ack */
! 1294: bus_space_write_2(sc->iot, sc->aud_ioh,
! 1295: AUICH_PCMI + sc->sc_sts_reg, sts &
! 1296: (AUICH_LVBCI | AUICH_CELV | AUICH_BCIS | AUICH_FIFOE));
! 1297: bus_space_write_2(sc->iot, sc->aud_ioh, AUICH_GSTS, AUICH_PIINT);
! 1298: ret++;
! 1299: }
! 1300:
! 1301: if (gsts & AUICH_MIINT) {
! 1302: sts = bus_space_read_2(sc->iot, sc->aud_ioh,
! 1303: AUICH_MICI + sc->sc_sts_reg);
! 1304: DPRINTF(AUICH_DEBUG_DMA,
! 1305: ("auich_intr: ists=%b\n", sts, AUICH_ISTS_BITS));
! 1306: #ifdef AUICH_DEBUG
! 1307: if (sts & AUICH_FIFOE)
! 1308: printf("%s: mic fifo overrun\n", sc->sc_dev.dv_xname);
! 1309: #endif
! 1310:
! 1311: /* TODO mic input dma */
! 1312:
! 1313: bus_space_write_2(sc->iot, sc->aud_ioh, AUICH_GSTS, AUICH_MIINT);
! 1314: }
! 1315:
! 1316: return ret;
! 1317: }
! 1318:
! 1319: int
! 1320: auich_trigger_output(v, start, end, blksize, intr, arg, param)
! 1321: void *v;
! 1322: void *start, *end;
! 1323: int blksize;
! 1324: void (*intr)(void *);
! 1325: void *arg;
! 1326: struct audio_params *param;
! 1327: {
! 1328: struct auich_softc *sc = v;
! 1329: struct auich_dmalist *q;
! 1330: struct auich_dma *p;
! 1331:
! 1332: DPRINTF(AUICH_DEBUG_DMA,
! 1333: ("auich_trigger_output(%x, %x, %d, %p, %p, %p)\n",
! 1334: start, end, blksize, intr, arg, param));
! 1335:
! 1336: for (p = sc->sc_dmas; p && p->addr != start; p = p->next);
! 1337: if (!p)
! 1338: return -1;
! 1339:
! 1340: sc->sc_pintr = intr;
! 1341: sc->sc_parg = arg;
! 1342:
! 1343: /*
! 1344: * The logic behind this is:
! 1345: * setup one buffer to play, then LVI dump out the rest
! 1346: * to the scatter-gather chain.
! 1347: */
! 1348: sc->pcmo_start = p->segs->ds_addr;
! 1349: sc->pcmo_p = sc->pcmo_start + blksize;
! 1350: sc->pcmo_end = sc->pcmo_start + ((char *)end - (char *)start);
! 1351: sc->pcmo_blksize = blksize;
! 1352:
! 1353: q = sc->dmap_pcmo = sc->dmalist_pcmo;
! 1354: q->base = sc->pcmo_start;
! 1355: q->len = (blksize / sc->sc_sample_size) | AUICH_DMAF_IOC;
! 1356: if (++q == &sc->dmalist_pcmo[AUICH_DMALIST_MAX])
! 1357: q = sc->dmalist_pcmo;
! 1358: sc->dmap_pcmo = q;
! 1359:
! 1360: bus_space_write_4(sc->iot, sc->aud_ioh, AUICH_PCMO + AUICH_BDBAR,
! 1361: sc->dmalist_pcmo_pa);
! 1362: bus_space_write_1(sc->iot, sc->aud_ioh, AUICH_PCMO + AUICH_CTRL,
! 1363: AUICH_IOCE | AUICH_FEIE | AUICH_LVBIE | AUICH_RPBM);
! 1364: bus_space_write_1(sc->iot, sc->aud_ioh, AUICH_PCMO + AUICH_LVI,
! 1365: (sc->dmap_pcmo - 1 - sc->dmalist_pcmo) & AUICH_LVI_MASK);
! 1366:
! 1367: return 0;
! 1368: }
! 1369:
! 1370: int
! 1371: auich_trigger_input(v, start, end, blksize, intr, arg, param)
! 1372: void *v;
! 1373: void *start, *end;
! 1374: int blksize;
! 1375: void (*intr)(void *);
! 1376: void *arg;
! 1377: struct audio_params *param;
! 1378: {
! 1379: struct auich_softc *sc = v;
! 1380: struct auich_dmalist *q;
! 1381: struct auich_dma *p;
! 1382:
! 1383: DPRINTF(AUICH_DEBUG_DMA,
! 1384: ("auich_trigger_input(%x, %x, %d, %p, %p, %p)\n",
! 1385: start, end, blksize, intr, arg, param));
! 1386:
! 1387: for (p = sc->sc_dmas; p && p->addr != start; p = p->next);
! 1388: if (!p)
! 1389: return -1;
! 1390:
! 1391: sc->sc_rintr = intr;
! 1392: sc->sc_rarg = arg;
! 1393:
! 1394: /*
! 1395: * The logic behind this is:
! 1396: * setup one buffer to play, then LVI dump out the rest
! 1397: * to the scatter-gather chain.
! 1398: */
! 1399: sc->pcmi_start = p->segs->ds_addr;
! 1400: sc->pcmi_p = sc->pcmi_start + blksize;
! 1401: sc->pcmi_end = sc->pcmi_start + ((char *)end - (char *)start);
! 1402: sc->pcmi_blksize = blksize;
! 1403:
! 1404: q = sc->dmap_pcmi = sc->dmalist_pcmi;
! 1405: q->base = sc->pcmi_start;
! 1406: q->len = (blksize / sc->sc_sample_size) | AUICH_DMAF_IOC;
! 1407: if (++q == &sc->dmalist_pcmi[AUICH_DMALIST_MAX])
! 1408: q = sc->dmalist_pcmi;
! 1409: sc->dmap_pcmi = q;
! 1410:
! 1411: bus_space_write_4(sc->iot, sc->aud_ioh, AUICH_PCMI + AUICH_BDBAR,
! 1412: sc->dmalist_pcmi_pa);
! 1413: bus_space_write_1(sc->iot, sc->aud_ioh, AUICH_PCMI + AUICH_CTRL,
! 1414: AUICH_IOCE | AUICH_FEIE | AUICH_LVBIE | AUICH_RPBM);
! 1415: bus_space_write_1(sc->iot, sc->aud_ioh, AUICH_PCMI + AUICH_LVI,
! 1416: (sc->dmap_pcmi - 1 - sc->dmalist_pcmi) & AUICH_LVI_MASK);
! 1417:
! 1418: return 0;
! 1419: }
! 1420:
! 1421: void
! 1422: auich_powerhook(why, self)
! 1423: int why;
! 1424: void *self;
! 1425: {
! 1426: struct auich_softc *sc = (struct auich_softc *)self;
! 1427:
! 1428: if (why != PWR_RESUME) {
! 1429: /* Power down */
! 1430: DPRINTF(1, ("auich: power down\n"));
! 1431: sc->suspend = why;
! 1432: auich_read_codec(sc, AC97_REG_EXT_AUDIO_CTRL, &sc->ext_ctrl);
! 1433:
! 1434: } else {
! 1435: /* Wake up */
! 1436: DPRINTF(1, ("auich: power resume\n"));
! 1437: if (sc->suspend == PWR_RESUME) {
! 1438: printf("%s: resume without suspend?\n",
! 1439: sc->sc_dev.dv_xname);
! 1440: sc->suspend = why;
! 1441: return;
! 1442: }
! 1443: sc->suspend = why;
! 1444: auich_reset_codec(sc);
! 1445: DELAY(1000);
! 1446: (sc->codec_if->vtbl->restore_ports)(sc->codec_if);
! 1447: auich_write_codec(sc, AC97_REG_EXT_AUDIO_CTRL, sc->ext_ctrl);
! 1448: }
! 1449: }
! 1450:
! 1451:
! 1452:
! 1453: /* -------------------------------------------------------------------- */
! 1454: /* Calibrate card (some boards are overclocked and need scaling) */
! 1455:
! 1456: unsigned int
! 1457: auich_calibrate(struct auich_softc *sc)
! 1458: {
! 1459: struct timeval t1, t2;
! 1460: u_int8_t ociv, nciv;
! 1461: u_int32_t wait_us, actual_48k_rate, bytes, ac97rate;
! 1462: void *temp_buffer;
! 1463: struct auich_dma *p;
! 1464: int i;
! 1465:
! 1466: ac97rate = AUICH_FIXED_RATE;
! 1467: /*
! 1468: * Grab audio from input for fixed interval and compare how
! 1469: * much we actually get with what we expect. Interval needs
! 1470: * to be sufficiently short that no interrupts are
! 1471: * generated.
! 1472: */
! 1473:
! 1474: /* Setup a buffer */
! 1475: bytes = 16000;
! 1476: temp_buffer = auich_allocm(sc, AUMODE_RECORD, bytes, M_DEVBUF,
! 1477: M_NOWAIT);
! 1478: if (temp_buffer == NULL)
! 1479: return (ac97rate);
! 1480: for (p = sc->sc_dmas; p && p->addr != temp_buffer; p = p->next)
! 1481: ;
! 1482: if (p == NULL) {
! 1483: printf("auich_calibrate: bad address %p\n", temp_buffer);
! 1484: return (ac97rate);
! 1485: }
! 1486:
! 1487: for (i = 0; i < AUICH_DMALIST_MAX; i++) {
! 1488: sc->dmalist_pcmi[i].base = p->map->dm_segs[0].ds_addr;
! 1489: sc->dmalist_pcmi[i].len = bytes / sc->sc_sample_size;
! 1490: }
! 1491:
! 1492: /*
! 1493: * our data format is stereo, 16 bit so each sample is 4 bytes.
! 1494: * assuming we get 48000 samples per second, we get 192000 bytes/sec.
! 1495: * we're going to start recording with interrupts disabled and measure
! 1496: * the time taken for one block to complete. we know the block size,
! 1497: * we know the time in microseconds, we calculate the sample rate:
! 1498: *
! 1499: * actual_rate [bps] = bytes / (time [s] * 4)
! 1500: * actual_rate [bps] = (bytes * 1000000) / (time [us] * 4)
! 1501: * actual_rate [Hz] = (bytes * 250000) / time [us]
! 1502: */
! 1503:
! 1504: /* prepare */
! 1505: ociv = bus_space_read_1(sc->iot, sc->aud_ioh, AUICH_PCMI + AUICH_CIV);
! 1506: nciv = ociv;
! 1507: bus_space_write_4(sc->iot, sc->aud_ioh, AUICH_PCMI + AUICH_BDBAR,
! 1508: sc->dmalist_pcmi_pa);
! 1509: bus_space_write_1(sc->iot, sc->aud_ioh, AUICH_PCMI + AUICH_LVI,
! 1510: (0 - 1) & AUICH_LVI_MASK);
! 1511:
! 1512: /* start */
! 1513: microuptime(&t1);
! 1514: bus_space_write_1(sc->iot, sc->aud_ioh, AUICH_PCMI + AUICH_CTRL,
! 1515: AUICH_RPBM);
! 1516:
! 1517: /* wait */
! 1518: while (nciv == ociv) {
! 1519: microuptime(&t2);
! 1520: if (t2.tv_sec - t1.tv_sec > 1)
! 1521: break;
! 1522: nciv = bus_space_read_1(sc->iot, sc->aud_ioh,
! 1523: AUICH_PCMI + AUICH_CIV);
! 1524: }
! 1525: microuptime(&t2);
! 1526:
! 1527: /* reset */
! 1528: bus_space_write_1(sc->iot, sc->aud_ioh, AUICH_PCMI + AUICH_CTRL, AUICH_RR);
! 1529: bus_space_write_1(sc->iot, sc->aud_ioh, AUICH_MICI + AUICH_CTRL, AUICH_RR);
! 1530: DELAY(100);
! 1531:
! 1532: /* turn time delta into us */
! 1533: wait_us = ((t2.tv_sec - t1.tv_sec) * 1000000) + t2.tv_usec - t1.tv_usec;
! 1534:
! 1535: #if 0
! 1536: auich_freem(sc, temp_buffer, M_DEVBUF);
! 1537: #endif
! 1538:
! 1539: if (nciv == ociv) {
! 1540: printf("%s: ac97 link rate calibration timed out after %d us\n",
! 1541: sc->sc_dev.dv_xname, wait_us);
! 1542: return (ac97rate);
! 1543: }
! 1544:
! 1545: actual_48k_rate = (bytes * 250000) / wait_us;
! 1546:
! 1547: if (actual_48k_rate <= 48500)
! 1548: ac97rate = AUICH_FIXED_RATE;
! 1549: else
! 1550: ac97rate = actual_48k_rate;
! 1551:
! 1552: printf("%s: measured ac97 link rate at %d Hz",
! 1553: sc->sc_dev.dv_xname, actual_48k_rate);
! 1554: if (ac97rate != actual_48k_rate)
! 1555: printf(", will use %d Hz", ac97rate);
! 1556: printf("\n");
! 1557:
! 1558: return (ac97rate);
! 1559: }
CVSweb