Annotation of sys/dev/pci/cs4281.c, Revision 1.1
1.1 ! nbrk 1: /* $OpenBSD: cs4281.c,v 1.19 2005/11/29 05:42:17 tedu Exp $ */
! 2: /* $Tera: cs4281.c,v 1.18 2000/12/27 14:24:45 tacha Exp $ */
! 3:
! 4: /*
! 5: * Copyright (c) 2000 Tatoku Ogaito. 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: * 3. All advertising materials mentioning features or use of this software
! 16: * must display the following acknowledgement:
! 17: * This product includes software developed by Tatoku Ogaito
! 18: * for the NetBSD Project.
! 19: * 4. The name of the author may not be used to endorse or promote products
! 20: * derived from this software without specific prior written permission
! 21: *
! 22: * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
! 23: * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
! 24: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
! 25: * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
! 26: * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
! 27: * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
! 28: * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
! 29: * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
! 30: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
! 31: * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
! 32: */
! 33:
! 34: /*
! 35: * Cirrus Logic CS4281 driver.
! 36: * Data sheets can be found
! 37: * http://www.cirrus.com/pubs/4281.pdf?DocumentID=30
! 38: * ftp://ftp.alsa-project.org/pub/manuals/cirrus/cs4281tm.pdf
! 39: *
! 40: * TODO:
! 41: * 1: midi and FM support
! 42: */
! 43:
! 44: #include <sys/param.h>
! 45: #include <sys/systm.h>
! 46: #include <sys/kernel.h>
! 47: #include <sys/malloc.h>
! 48: #include <sys/fcntl.h>
! 49: #include <sys/device.h>
! 50:
! 51: #include <dev/pci/pcidevs.h>
! 52: #include <dev/pci/pcivar.h>
! 53: #include <dev/pci/cs4281reg.h>
! 54:
! 55: #include <sys/audioio.h>
! 56: #include <dev/audio_if.h>
! 57: #include <dev/midi_if.h>
! 58: #include <dev/mulaw.h>
! 59: #include <dev/auconv.h>
! 60:
! 61: #include <dev/ic/ac97.h>
! 62:
! 63: #include <machine/bus.h>
! 64:
! 65: #define CSCC_PCI_BA0 0x10
! 66: #define CSCC_PCI_BA1 0x14
! 67:
! 68: struct cs4281_dma {
! 69: bus_dmamap_t map;
! 70: caddr_t addr; /* real dma buffer */
! 71: caddr_t dum; /* dummy buffer for audio driver */
! 72: bus_dma_segment_t segs[1];
! 73: int nsegs;
! 74: size_t size;
! 75: struct cs4281_dma *next;
! 76: };
! 77: #define DMAADDR(p) ((p)->map->dm_segs[0].ds_addr)
! 78: #define BUFADDR(p) ((void *)((p)->dum))
! 79: #define KERNADDR(p) ((void *)((p)->addr))
! 80:
! 81: /*
! 82: * Software state
! 83: */
! 84: struct cs4281_softc {
! 85: struct device sc_dev;
! 86:
! 87: pci_intr_handle_t *sc_ih;
! 88:
! 89: /* I/O (BA0) */
! 90: bus_space_tag_t ba0t;
! 91: bus_space_handle_t ba0h;
! 92:
! 93: /* BA1 */
! 94: bus_space_tag_t ba1t;
! 95: bus_space_handle_t ba1h;
! 96:
! 97: /* DMA */
! 98: bus_dma_tag_t sc_dmatag;
! 99: struct cs4281_dma *sc_dmas;
! 100: size_t dma_size;
! 101: size_t dma_align;
! 102:
! 103: int hw_blocksize;
! 104:
! 105: /* playback */
! 106: void (*sc_pintr)(void *); /* dma completion intr handler */
! 107: void *sc_parg; /* arg for sc_intr() */
! 108: char *sc_ps, *sc_pe, *sc_pn;
! 109: int sc_pcount;
! 110: int sc_pi;
! 111: struct cs4281_dma *sc_pdma;
! 112: char *sc_pbuf;
! 113: int (*halt_output)(void *);
! 114: #ifdef DIAGNOSTIC
! 115: char sc_prun;
! 116: #endif
! 117:
! 118: /* capturing */
! 119: void (*sc_rintr)(void *); /* dma completion intr handler */
! 120: void *sc_rarg; /* arg for sc_intr() */
! 121: char *sc_rs, *sc_re, *sc_rn;
! 122: int sc_rcount;
! 123: int sc_ri;
! 124: struct cs4281_dma *sc_rdma;
! 125: char *sc_rbuf;
! 126: int sc_rparam; /* record format */
! 127: int (*halt_input)(void *);
! 128: #ifdef DIAGNOSTIC
! 129: char sc_rrun;
! 130: #endif
! 131:
! 132: #if NMIDI > 0
! 133: void (*sc_iintr)(void *, int); /* midi input ready handler */
! 134: void (*sc_ointr)(void *); /* midi output ready handler */
! 135: void *sc_arg;
! 136: #endif
! 137:
! 138: /* AC97 CODEC */
! 139: struct ac97_codec_if *codec_if;
! 140: struct ac97_host_if host_if;
! 141:
! 142: /* Power Management */
! 143: char sc_suspend;
! 144: void *sc_powerhook; /* Power hook */
! 145: u_int16_t ac97_reg[CS4281_SAVE_REG_MAX + 1]; /* Save ac97 registers */
! 146: };
! 147:
! 148: #define BA0READ4(sc, r) bus_space_read_4((sc)->ba0t, (sc)->ba0h, (r))
! 149: #define BA0WRITE4(sc, r, x) bus_space_write_4((sc)->ba0t, (sc)->ba0h, (r), (x))
! 150:
! 151: #if defined(ENABLE_SECONDARY_CODEC)
! 152: #define MAX_CHANNELS (4)
! 153: #define MAX_FIFO_SIZE 32 /* 128/4 channels */
! 154: #else
! 155: #define MAX_CHANNELS (2)
! 156: #define MAX_FIFO_SIZE 64 /* 128/2 channels */
! 157: #endif
! 158:
! 159: int cs4281_match(struct device *, void *, void *);
! 160: void cs4281_attach(struct device *, struct device *, void *);
! 161: int cs4281_intr(void *);
! 162: int cs4281_query_encoding(void *, struct audio_encoding *);
! 163: int cs4281_set_params(void *, int, int, struct audio_params *,
! 164: struct audio_params *);
! 165: int cs4281_halt_output(void *);
! 166: int cs4281_halt_input(void *);
! 167: int cs4281_getdev(void *, struct audio_device *);
! 168: int cs4281_trigger_output(void *, void *, void *, int, void (*)(void *),
! 169: void *, struct audio_params *);
! 170: int cs4281_trigger_input(void *, void *, void *, int, void (*)(void *),
! 171: void *, struct audio_params *);
! 172: u_int8_t cs4281_sr2regval(int);
! 173: void cs4281_set_dac_rate(struct cs4281_softc *, int);
! 174: void cs4281_set_adc_rate(struct cs4281_softc *, int);
! 175: int cs4281_init(struct cs4281_softc *);
! 176:
! 177: int cs4281_open(void *, int);
! 178: void cs4281_close(void *);
! 179: int cs4281_round_blocksize(void *, int);
! 180: int cs4281_get_props(void *);
! 181: int cs4281_attach_codec(void *, struct ac97_codec_if *);
! 182: int cs4281_read_codec(void *, u_int8_t , u_int16_t *);
! 183: int cs4281_write_codec(void *, u_int8_t, u_int16_t);
! 184: void cs4281_reset_codec(void *);
! 185:
! 186: void cs4281_power(int, void *);
! 187:
! 188: int cs4281_mixer_set_port(void *, mixer_ctrl_t *);
! 189: int cs4281_mixer_get_port(void *, mixer_ctrl_t *);
! 190: int cs4281_query_devinfo(void *, mixer_devinfo_t *);
! 191: void *cs4281_malloc(void *, int, size_t, int, int);
! 192: size_t cs4281_round_buffersize(void *, int, size_t);
! 193: void cs4281_free(void *, void *, int);
! 194: paddr_t cs4281_mappage(void *, void *, off_t, int);
! 195:
! 196: int cs4281_allocmem(struct cs4281_softc *, size_t, int, int,
! 197: struct cs4281_dma *);
! 198: int cs4281_src_wait(struct cs4281_softc *);
! 199:
! 200: #if defined(CS4281_DEBUG)
! 201: #undef DPRINTF
! 202: #undef DPRINTFN
! 203: #define DPRINTF(x) if (cs4281_debug) printf x
! 204: #define DPRINTFN(n,x) if (cs4281_debug>(n)) printf x
! 205: int cs4281_debug = 5;
! 206: #else
! 207: #define DPRINTF(x)
! 208: #define DPRINTFN(n,x)
! 209: #endif
! 210:
! 211: struct audio_hw_if cs4281_hw_if = {
! 212: cs4281_open,
! 213: cs4281_close,
! 214: NULL,
! 215: cs4281_query_encoding,
! 216: cs4281_set_params,
! 217: cs4281_round_blocksize,
! 218: NULL,
! 219: NULL,
! 220: NULL,
! 221: NULL,
! 222: NULL,
! 223: cs4281_halt_output,
! 224: cs4281_halt_input,
! 225: NULL,
! 226: cs4281_getdev,
! 227: NULL,
! 228: cs4281_mixer_set_port,
! 229: cs4281_mixer_get_port,
! 230: cs4281_query_devinfo,
! 231: cs4281_malloc,
! 232: cs4281_free,
! 233: cs4281_round_buffersize,
! 234: NULL, /* cs4281_mappage, */
! 235: cs4281_get_props,
! 236: cs4281_trigger_output,
! 237: cs4281_trigger_input,
! 238: };
! 239:
! 240: #if NMIDI > 0
! 241: /* Midi Interface */
! 242: void cs4281_midi_close(void *);
! 243: void cs4281_midi_getinfo(void *, struct midi_info *);
! 244: int cs4281_midi_open(void *, int, void (*)(void *, int),
! 245: void (*)(void *), void *);
! 246: int cs4281_midi_output(void *, int);
! 247:
! 248: struct midi_hw_if cs4281_midi_hw_if = {
! 249: cs4281_midi_open,
! 250: cs4281_midi_close,
! 251: cs4281_midi_output,
! 252: cs4281_midi_getinfo,
! 253: 0,
! 254: };
! 255: #endif
! 256:
! 257: struct cfattach clct_ca = {
! 258: sizeof(struct cs4281_softc), cs4281_match, cs4281_attach
! 259: };
! 260:
! 261: struct cfdriver clct_cd = {
! 262: NULL, "clct", DV_DULL
! 263: };
! 264:
! 265: struct audio_device cs4281_device = {
! 266: "CS4281",
! 267: "",
! 268: "cs4281"
! 269: };
! 270:
! 271:
! 272: int
! 273: cs4281_match(parent, match, aux)
! 274: struct device *parent;
! 275: void *match;
! 276: void *aux;
! 277: {
! 278: struct pci_attach_args *pa = (struct pci_attach_args *)aux;
! 279:
! 280: if (PCI_VENDOR(pa->pa_id) != PCI_VENDOR_CIRRUS ||
! 281: PCI_PRODUCT(pa->pa_id) != PCI_PRODUCT_CIRRUS_CS4281)
! 282: return (0);
! 283:
! 284: return (1);
! 285: }
! 286:
! 287: void
! 288: cs4281_attach(parent, self, aux)
! 289: struct device *parent;
! 290: struct device *self;
! 291: void *aux;
! 292: {
! 293: struct cs4281_softc *sc = (struct cs4281_softc *)self;
! 294: struct pci_attach_args *pa = (struct pci_attach_args *)aux;
! 295: pci_chipset_tag_t pc = pa->pa_pc;
! 296: char const *intrstr;
! 297: pci_intr_handle_t ih;
! 298: int pci_pwrmgmt_cap_reg, pci_pwrmgmt_csr_reg;
! 299:
! 300: /* Map I/O register */
! 301: if (pci_mapreg_map(pa, CSCC_PCI_BA0,
! 302: PCI_MAPREG_TYPE_MEM|PCI_MAPREG_MEM_TYPE_32BIT, 0, &sc->ba0t,
! 303: &sc->ba0h, NULL, NULL, 0)) {
! 304: printf("%s: can't map BA0 space\n", sc->sc_dev.dv_xname);
! 305: return;
! 306: }
! 307: if (pci_mapreg_map(pa, CSCC_PCI_BA1,
! 308: PCI_MAPREG_TYPE_MEM|PCI_MAPREG_MEM_TYPE_32BIT, 0, &sc->ba1t,
! 309: &sc->ba1h, NULL, NULL, 0)) {
! 310: printf("%s: can't map BA1 space\n", sc->sc_dev.dv_xname);
! 311: return;
! 312: }
! 313:
! 314: sc->sc_dmatag = pa->pa_dmat;
! 315:
! 316: /*
! 317: * Set Power State D0.
! 318: * Without doing this, 0xffffffff is read from all registers after
! 319: * using Windows and rebooting into OpenBSD.
! 320: * On my IBM ThinkPad X20, it is set to D3 after using Windows2000.
! 321: */
! 322: if (pci_get_capability(pa->pa_pc, pa->pa_tag, PCI_CAP_PWRMGMT,
! 323: &pci_pwrmgmt_cap_reg, 0)) {
! 324: pcireg_t reg;
! 325:
! 326: pci_pwrmgmt_csr_reg = pci_pwrmgmt_cap_reg + PCI_PMCSR;
! 327: reg = pci_conf_read(pa->pa_pc, pa->pa_tag, pci_pwrmgmt_csr_reg);
! 328: if ((reg & PCI_PMCSR_STATE_MASK) != PCI_PMCSR_STATE_D0) {
! 329: pci_conf_write(pc, pa->pa_tag, pci_pwrmgmt_csr_reg,
! 330: (reg & ~PCI_PMCSR_STATE_MASK) |
! 331: PCI_PMCSR_STATE_D0);
! 332: }
! 333: }
! 334:
! 335: /* Map and establish the interrupt. */
! 336: if (pci_intr_map(pa, &ih)) {
! 337: printf("%s: couldn't map interrupt\n", sc->sc_dev.dv_xname);
! 338: return;
! 339: }
! 340: intrstr = pci_intr_string(pc, ih);
! 341:
! 342: sc->sc_ih = pci_intr_establish(pc, ih, IPL_AUDIO, cs4281_intr, sc,
! 343: sc->sc_dev.dv_xname);
! 344: if (sc->sc_ih == NULL) {
! 345: printf("%s: couldn't establish interrupt",sc->sc_dev.dv_xname);
! 346: if (intrstr != NULL)
! 347: printf(" at %s", intrstr);
! 348: printf("\n");
! 349: return;
! 350: }
! 351: printf(" %s\n", intrstr);
! 352:
! 353: /*
! 354: * Sound System start-up
! 355: */
! 356: if (cs4281_init(sc) != 0)
! 357: return;
! 358:
! 359: sc->halt_input = cs4281_halt_input;
! 360: sc->halt_output = cs4281_halt_output;
! 361:
! 362: sc->dma_size = CS4281_BUFFER_SIZE / MAX_CHANNELS;
! 363: sc->dma_align = 0x10;
! 364: sc->hw_blocksize = sc->dma_size / 2;
! 365:
! 366: /* AC 97 attachment */
! 367: sc->host_if.arg = sc;
! 368: sc->host_if.attach = cs4281_attach_codec;
! 369: sc->host_if.read = cs4281_read_codec;
! 370: sc->host_if.write = cs4281_write_codec;
! 371: sc->host_if.reset = cs4281_reset_codec;
! 372: if (ac97_attach(&sc->host_if) != 0) {
! 373: printf("%s: ac97_attach failed\n", sc->sc_dev.dv_xname);
! 374: return;
! 375: }
! 376: audio_attach_mi(&cs4281_hw_if, sc, &sc->sc_dev);
! 377:
! 378: #if NMIDI > 0
! 379: midi_attach_mi(&cs4281_midi_hw_if, sc, &sc->sc_dev);
! 380: #endif
! 381:
! 382: sc->sc_suspend = PWR_RESUME;
! 383: sc->sc_powerhook = powerhook_establish(cs4281_power, sc);
! 384: }
! 385:
! 386:
! 387: int
! 388: cs4281_intr(p)
! 389: void *p;
! 390: {
! 391: struct cs4281_softc *sc = p;
! 392: u_int32_t intr, val;
! 393: char *empty_dma;
! 394:
! 395: intr = BA0READ4(sc, CS4281_HISR);
! 396: if (!(intr & (HISR_DMA0 | HISR_DMA1 | HISR_MIDI))) {
! 397: BA0WRITE4(sc, CS4281_HICR, HICR_IEV | HICR_CHGM);
! 398: return (0);
! 399: }
! 400: DPRINTF(("cs4281_intr:"));
! 401:
! 402: if (intr & HISR_DMA0)
! 403: val = BA0READ4(sc, CS4281_HDSR0); /* clear intr condition */
! 404: if (intr & HISR_DMA1)
! 405: val = BA0READ4(sc, CS4281_HDSR1); /* clear intr condition */
! 406: BA0WRITE4(sc, CS4281_HICR, HICR_IEV | HICR_CHGM);
! 407:
! 408: /* Playback Interrupt */
! 409: if (intr & HISR_DMA0) {
! 410: DPRINTF((" PB DMA 0x%x(%d)", (int)BA0READ4(sc, CS4281_DCA0),
! 411: (int)BA0READ4(sc, CS4281_DCC0)));
! 412: if (sc->sc_pintr) {
! 413: if ((sc->sc_pi%sc->sc_pcount) == 0)
! 414: sc->sc_pintr(sc->sc_parg);
! 415: } else {
! 416: printf("unexpected play intr\n");
! 417: }
! 418: /* copy buffer */
! 419: ++sc->sc_pi;
! 420: empty_dma = sc->sc_pdma->addr;
! 421: if (sc->sc_pi&1)
! 422: empty_dma += sc->hw_blocksize;
! 423: memcpy(empty_dma, sc->sc_pn, sc->hw_blocksize);
! 424: sc->sc_pn += sc->hw_blocksize;
! 425: if (sc->sc_pn >= sc->sc_pe)
! 426: sc->sc_pn = sc->sc_ps;
! 427: }
! 428: if (intr & HISR_DMA1) {
! 429: val = BA0READ4(sc, CS4281_HDSR1);
! 430: /* copy from dma */
! 431: DPRINTF((" CP DMA 0x%x(%d)", (int)BA0READ4(sc, CS4281_DCA1),
! 432: (int)BA0READ4(sc, CS4281_DCC1)));
! 433: ++sc->sc_ri;
! 434: empty_dma = sc->sc_rdma->addr;
! 435: if ((sc->sc_ri & 1) == 0)
! 436: empty_dma += sc->hw_blocksize;
! 437: memcpy(sc->sc_rn, empty_dma, sc->hw_blocksize);
! 438: if (sc->sc_rn >= sc->sc_re)
! 439: sc->sc_rn = sc->sc_rs;
! 440: if (sc->sc_rintr) {
! 441: if ((sc->sc_ri % sc->sc_rcount) == 0)
! 442: sc->sc_rintr(sc->sc_rarg);
! 443: } else {
! 444: printf("unexpected record intr\n");
! 445: }
! 446: }
! 447: DPRINTF(("\n"));
! 448: return (1);
! 449: }
! 450:
! 451: int
! 452: cs4281_query_encoding(addr, fp)
! 453: void *addr;
! 454: struct audio_encoding *fp;
! 455: {
! 456: switch (fp->index) {
! 457: case 0:
! 458: strlcpy(fp->name, AudioEulinear, sizeof fp->name);
! 459: fp->encoding = AUDIO_ENCODING_ULINEAR;
! 460: fp->precision = 8;
! 461: fp->flags = 0;
! 462: break;
! 463: case 1:
! 464: strlcpy(fp->name, AudioEmulaw, sizeof fp->name);
! 465: fp->encoding = AUDIO_ENCODING_ULAW;
! 466: fp->precision = 8;
! 467: fp->flags = AUDIO_ENCODINGFLAG_EMULATED;
! 468: break;
! 469: case 2:
! 470: strlcpy(fp->name, AudioEalaw, sizeof fp->name);
! 471: fp->encoding = AUDIO_ENCODING_ALAW;
! 472: fp->precision = 8;
! 473: fp->flags = AUDIO_ENCODINGFLAG_EMULATED;
! 474: break;
! 475: case 3:
! 476: strlcpy(fp->name, AudioEslinear, sizeof fp->name);
! 477: fp->encoding = AUDIO_ENCODING_SLINEAR;
! 478: fp->precision = 8;
! 479: fp->flags = 0;
! 480: break;
! 481: case 4:
! 482: strlcpy(fp->name, AudioEslinear_le, sizeof fp->name);
! 483: fp->encoding = AUDIO_ENCODING_SLINEAR_LE;
! 484: fp->precision = 16;
! 485: fp->flags = 0;
! 486: break;
! 487: case 5:
! 488: strlcpy(fp->name, AudioEulinear_le, sizeof fp->name);
! 489: fp->encoding = AUDIO_ENCODING_ULINEAR_LE;
! 490: fp->precision = 16;
! 491: fp->flags = 0;
! 492: break;
! 493: case 6:
! 494: strlcpy(fp->name, AudioEslinear_be, sizeof fp->name);
! 495: fp->encoding = AUDIO_ENCODING_SLINEAR_BE;
! 496: fp->precision = 16;
! 497: fp->flags = 0;
! 498: break;
! 499: case 7:
! 500: strlcpy(fp->name, AudioEulinear_be, sizeof fp->name);
! 501: fp->encoding = AUDIO_ENCODING_ULINEAR_BE;
! 502: fp->precision = 16;
! 503: fp->flags = 0;
! 504: break;
! 505: default:
! 506: return EINVAL;
! 507: }
! 508: return (0);
! 509: }
! 510:
! 511: int
! 512: cs4281_set_params(addr, setmode, usemode, play, rec)
! 513: void *addr;
! 514: int setmode, usemode;
! 515: struct audio_params *play, *rec;
! 516: {
! 517: struct cs4281_softc *sc = addr;
! 518: struct audio_params *p;
! 519: int mode;
! 520:
! 521: for (mode = AUMODE_RECORD; mode != -1;
! 522: mode = mode == AUMODE_RECORD ? AUMODE_PLAY : -1) {
! 523: if ((setmode & mode) == 0)
! 524: continue;
! 525:
! 526: p = mode == AUMODE_PLAY ? play : rec;
! 527:
! 528: if (p == play) {
! 529: DPRINTFN(5,("play: samp=%ld precision=%d channels=%d\n",
! 530: p->sample_rate, p->precision, p->channels));
! 531: if (p->sample_rate < 6023 || p->sample_rate > 48000 ||
! 532: (p->precision != 8 && p->precision != 16) ||
! 533: (p->channels != 1 && p->channels != 2)) {
! 534: return (EINVAL);
! 535: }
! 536: } else {
! 537: DPRINTFN(5,("rec: samp=%ld precision=%d channels=%d\n",
! 538: p->sample_rate, p->precision, p->channels));
! 539: if (p->sample_rate < 6023 || p->sample_rate > 48000 ||
! 540: (p->precision != 8 && p->precision != 16) ||
! 541: (p->channels != 1 && p->channels != 2)) {
! 542: return (EINVAL);
! 543: }
! 544: }
! 545: p->factor = 1;
! 546: p->sw_code = 0;
! 547:
! 548: switch (p->encoding) {
! 549: case AUDIO_ENCODING_SLINEAR_BE:
! 550: break;
! 551: case AUDIO_ENCODING_SLINEAR_LE:
! 552: break;
! 553: case AUDIO_ENCODING_ULINEAR_BE:
! 554: break;
! 555: case AUDIO_ENCODING_ULINEAR_LE:
! 556: break;
! 557: case AUDIO_ENCODING_ULAW:
! 558: if (mode == AUMODE_PLAY) {
! 559: p->sw_code = mulaw_to_slinear8;
! 560: } else {
! 561: p->sw_code = slinear8_to_mulaw;
! 562: }
! 563: break;
! 564: case AUDIO_ENCODING_ALAW:
! 565: if (mode == AUMODE_PLAY) {
! 566: p->sw_code = alaw_to_slinear8;
! 567: } else {
! 568: p->sw_code = slinear8_to_alaw;
! 569: }
! 570: break;
! 571: default:
! 572: return (EINVAL);
! 573: }
! 574: }
! 575:
! 576: /* set sample rate */
! 577: cs4281_set_dac_rate(sc, play->sample_rate);
! 578: cs4281_set_adc_rate(sc, rec->sample_rate);
! 579: return (0);
! 580: }
! 581:
! 582: int
! 583: cs4281_halt_output(addr)
! 584: void *addr;
! 585: {
! 586: struct cs4281_softc *sc = addr;
! 587:
! 588: BA0WRITE4(sc, CS4281_DCR0, BA0READ4(sc, CS4281_DCR0) | DCRn_MSK);
! 589: #ifdef DIAGNOSTIC
! 590: sc->sc_prun = 0;
! 591: #endif
! 592: return (0);
! 593: }
! 594:
! 595: int
! 596: cs4281_halt_input(addr)
! 597: void *addr;
! 598: {
! 599: struct cs4281_softc *sc = addr;
! 600:
! 601: BA0WRITE4(sc, CS4281_DCR1, BA0READ4(sc, CS4281_DCR1) | DCRn_MSK);
! 602: #ifdef DIAGNOSTIC
! 603: sc->sc_rrun = 0;
! 604: #endif
! 605: return (0);
! 606: }
! 607:
! 608: /* trivial */
! 609: int
! 610: cs4281_getdev(addr, retp)
! 611: void *addr;
! 612: struct audio_device *retp;
! 613: {
! 614: *retp = cs4281_device;
! 615: return (0);
! 616: }
! 617:
! 618:
! 619: int
! 620: cs4281_trigger_output(addr, start, end, blksize, intr, arg, param)
! 621: void *addr;
! 622: void *start, *end;
! 623: int blksize;
! 624: void (*intr)(void *);
! 625: void *arg;
! 626: struct audio_params *param;
! 627: {
! 628: struct cs4281_softc *sc = addr;
! 629: u_int32_t fmt=0;
! 630: struct cs4281_dma *p;
! 631: int dma_count;
! 632:
! 633: #ifdef DIAGNOSTIC
! 634: if (sc->sc_prun)
! 635: printf("cs4281_trigger_output: already running\n");
! 636: sc->sc_prun = 1;
! 637: #endif
! 638:
! 639: DPRINTF(("cs4281_trigger_output: sc=%p start=%p end=%p "
! 640: "blksize=%d intr=%p(%p)\n", addr, start, end, blksize, intr, arg));
! 641: sc->sc_pintr = intr;
! 642: sc->sc_parg = arg;
! 643:
! 644: /* stop playback DMA */
! 645: BA0WRITE4(sc, CS4281_DCR0, BA0READ4(sc, CS4281_DCR0) | DCRn_MSK);
! 646:
! 647: DPRINTF(("param: precision=%d factor=%d channels=%d encoding=%d\n",
! 648: param->precision, param->factor, param->channels,
! 649: param->encoding));
! 650: for (p = sc->sc_dmas; p != NULL && BUFADDR(p) != start; p = p->next)
! 651: ;
! 652: if (p == NULL) {
! 653: printf("cs4281_trigger_output: bad addr %p\n", start);
! 654: return (EINVAL);
! 655: }
! 656:
! 657: sc->sc_pcount = blksize / sc->hw_blocksize;
! 658: sc->sc_ps = (char *)start;
! 659: sc->sc_pe = (char *)end;
! 660: sc->sc_pdma = p;
! 661: sc->sc_pbuf = KERNADDR(p);
! 662: sc->sc_pi = 0;
! 663: sc->sc_pn = sc->sc_ps;
! 664: if (blksize >= sc->dma_size) {
! 665: sc->sc_pn = sc->sc_ps + sc->dma_size;
! 666: memcpy(sc->sc_pbuf, start, sc->dma_size);
! 667: ++sc->sc_pi;
! 668: } else {
! 669: sc->sc_pn = sc->sc_ps + sc->hw_blocksize;
! 670: memcpy(sc->sc_pbuf, start, sc->hw_blocksize);
! 671: }
! 672:
! 673: dma_count = sc->dma_size;
! 674: if (param->precision * param->factor != 8)
! 675: dma_count /= 2; /* 16 bit */
! 676: if (param->channels > 1)
! 677: dma_count /= 2; /* Stereo */
! 678:
! 679: DPRINTF(("cs4281_trigger_output: DMAADDR(p)=0x%x count=%d\n",
! 680: (int)DMAADDR(p), dma_count));
! 681: BA0WRITE4(sc, CS4281_DBA0, DMAADDR(p));
! 682: BA0WRITE4(sc, CS4281_DBC0, dma_count-1);
! 683:
! 684: /* set playback format */
! 685: fmt = BA0READ4(sc, CS4281_DMR0) & ~DMRn_FMTMSK;
! 686: if (param->precision * param->factor == 8)
! 687: fmt |= DMRn_SIZE8;
! 688: if (param->channels == 1)
! 689: fmt |= DMRn_MONO;
! 690: if (param->encoding == AUDIO_ENCODING_ULINEAR_BE ||
! 691: param->encoding == AUDIO_ENCODING_SLINEAR_BE)
! 692: fmt |= DMRn_BEND;
! 693: if (param->encoding == AUDIO_ENCODING_ULINEAR_BE ||
! 694: param->encoding == AUDIO_ENCODING_ULINEAR_LE)
! 695: fmt |= DMRn_USIGN;
! 696: BA0WRITE4(sc, CS4281_DMR0, fmt);
! 697:
! 698: /* set sample rate */
! 699: cs4281_set_dac_rate(sc, param->sample_rate);
! 700:
! 701: /* start DMA */
! 702: BA0WRITE4(sc, CS4281_DCR0, BA0READ4(sc, CS4281_DCR0) & ~DCRn_MSK);
! 703: /* Enable interrupts */
! 704: BA0WRITE4(sc, CS4281_HICR, HICR_IEV | HICR_CHGM);
! 705:
! 706: BA0WRITE4(sc, CS4281_PPRVC, 7);
! 707: BA0WRITE4(sc, CS4281_PPLVC, 7);
! 708:
! 709: DPRINTF(("HICR =0x%08x(expected 0x00000001)\n", BA0READ4(sc, CS4281_HICR)));
! 710: DPRINTF(("HIMR =0x%08x(expected 0x00f0fc3f)\n", BA0READ4(sc, CS4281_HIMR)));
! 711: DPRINTF(("DMR0 =0x%08x(expected 0x2???0018)\n", BA0READ4(sc, CS4281_DMR0)));
! 712: DPRINTF(("DCR0 =0x%08x(expected 0x00030000)\n", BA0READ4(sc, CS4281_DCR0)));
! 713: DPRINTF(("FCR0 =0x%08x(expected 0x81000f00)\n", BA0READ4(sc, CS4281_FCR0)));
! 714: DPRINTF(("DACSR=0x%08x(expected 1 for 44kHz 5 for 8kHz)\n",
! 715: BA0READ4(sc, CS4281_DACSR)));
! 716: DPRINTF(("SRCSA=0x%08x(expected 0x0b0a0100)\n", BA0READ4(sc, CS4281_SRCSA)));
! 717: DPRINTF(("SSPM&SSPM_PSRCEN =0x%08x(expected 0x00000010)\n",
! 718: BA0READ4(sc, CS4281_SSPM) & SSPM_PSRCEN));
! 719:
! 720: return (0);
! 721: }
! 722:
! 723: int
! 724: cs4281_trigger_input(addr, start, end, blksize, intr, arg, param)
! 725: void *addr;
! 726: void *start, *end;
! 727: int blksize;
! 728: void (*intr)(void *);
! 729: void *arg;
! 730: struct audio_params *param;
! 731: {
! 732: struct cs4281_softc *sc = addr;
! 733: struct cs4281_dma *p;
! 734: u_int32_t fmt=0;
! 735: int dma_count;
! 736:
! 737: printf("cs4281_trigger_input: not implemented yet\n");
! 738: #ifdef DIAGNOSTIC
! 739: if (sc->sc_rrun)
! 740: printf("cs4281_trigger_input: already running\n");
! 741: sc->sc_rrun = 1;
! 742: #endif
! 743: DPRINTF(("cs4281_trigger_input: sc=%p start=%p end=%p "
! 744: "blksize=%d intr=%p(%p)\n", addr, start, end, blksize, intr, arg));
! 745: sc->sc_rintr = intr;
! 746: sc->sc_rarg = arg;
! 747:
! 748: /* stop recording DMA */
! 749: BA0WRITE4(sc, CS4281_DCR1, BA0READ4(sc, CS4281_DCR1) | DCRn_MSK);
! 750:
! 751: for (p = sc->sc_dmas; p && BUFADDR(p) != start; p = p->next)
! 752: ;
! 753: if (!p) {
! 754: printf("cs4281_trigger_input: bad addr %p\n", start);
! 755: return (EINVAL);
! 756: }
! 757:
! 758: sc->sc_rcount = blksize / sc->hw_blocksize;
! 759: sc->sc_rs = (char *)start;
! 760: sc->sc_re = (char *)end;
! 761: sc->sc_rdma = p;
! 762: sc->sc_rbuf = KERNADDR(p);
! 763: sc->sc_ri = 0;
! 764: sc->sc_rn = sc->sc_rs;
! 765:
! 766: dma_count = sc->dma_size;
! 767: if (param->precision * param->factor == 8)
! 768: dma_count /= 2;
! 769: if (param->channels > 1)
! 770: dma_count /= 2;
! 771:
! 772: DPRINTF(("cs4281_trigger_input: DMAADDR(p)=0x%x count=%d\n",
! 773: (int)DMAADDR(p), dma_count));
! 774: BA0WRITE4(sc, CS4281_DBA1, DMAADDR(p));
! 775: BA0WRITE4(sc, CS4281_DBC1, dma_count-1);
! 776:
! 777: /* set recording format */
! 778: fmt = BA0READ4(sc, CS4281_DMR1) & ~DMRn_FMTMSK;
! 779: if (param->precision * param->factor == 8)
! 780: fmt |= DMRn_SIZE8;
! 781: if (param->channels == 1)
! 782: fmt |= DMRn_MONO;
! 783: if (param->encoding == AUDIO_ENCODING_ULINEAR_BE ||
! 784: param->encoding == AUDIO_ENCODING_SLINEAR_BE)
! 785: fmt |= DMRn_BEND;
! 786: if (param->encoding == AUDIO_ENCODING_ULINEAR_BE ||
! 787: param->encoding == AUDIO_ENCODING_ULINEAR_LE)
! 788: fmt |= DMRn_USIGN;
! 789: BA0WRITE4(sc, CS4281_DMR1, fmt);
! 790:
! 791: /* set sample rate */
! 792: cs4281_set_adc_rate(sc, param->sample_rate);
! 793:
! 794: /* Start DMA */
! 795: BA0WRITE4(sc, CS4281_DCR1, BA0READ4(sc, CS4281_DCR1) & ~DCRn_MSK);
! 796: /* Enable interrupts */
! 797: BA0WRITE4(sc, CS4281_HICR, HICR_IEV | HICR_CHGM);
! 798:
! 799: DPRINTF(("HICR=0x%08x\n", BA0READ4(sc, CS4281_HICR)));
! 800: DPRINTF(("HIMR=0x%08x\n", BA0READ4(sc, CS4281_HIMR)));
! 801: DPRINTF(("DMR1=0x%08x\n", BA0READ4(sc, CS4281_DMR1)));
! 802: DPRINTF(("DCR1=0x%08x\n", BA0READ4(sc, CS4281_DCR1)));
! 803:
! 804: return (0);
! 805: }
! 806:
! 807: /* convert sample rate to register value */
! 808: u_int8_t
! 809: cs4281_sr2regval(rate)
! 810: int rate;
! 811: {
! 812: u_int8_t retval;
! 813:
! 814: /* We don't have to change here. but anyway ... */
! 815: if (rate > 48000)
! 816: rate = 48000;
! 817: if (rate < 6023)
! 818: rate = 6023;
! 819:
! 820: switch (rate) {
! 821: case 8000:
! 822: retval = 5;
! 823: break;
! 824: case 11025:
! 825: retval = 4;
! 826: break;
! 827: case 16000:
! 828: retval = 3;
! 829: break;
! 830: case 22050:
! 831: retval = 2;
! 832: break;
! 833: case 44100:
! 834: retval = 1;
! 835: break;
! 836: case 48000:
! 837: retval = 0;
! 838: break;
! 839: default:
! 840: retval = 1536000/rate; /* == 24576000/(rate*16) */
! 841: }
! 842: return (retval);
! 843: }
! 844:
! 845:
! 846: void
! 847: cs4281_set_dac_rate(sc, rate)
! 848: struct cs4281_softc *sc;
! 849: int rate;
! 850: {
! 851: BA0WRITE4(sc, CS4281_DACSR, cs4281_sr2regval(rate));
! 852: }
! 853:
! 854: void
! 855: cs4281_set_adc_rate(sc, rate)
! 856: struct cs4281_softc *sc;
! 857: int rate;
! 858: {
! 859: BA0WRITE4(sc, CS4281_ADCSR, cs4281_sr2regval(rate));
! 860: }
! 861:
! 862: int
! 863: cs4281_init(sc)
! 864: struct cs4281_softc *sc;
! 865: {
! 866: int n;
! 867: u_int16_t data;
! 868: u_int32_t dat32;
! 869:
! 870: /* set "Configuration Write Protect" register to
! 871: * 0x4281 to allow to write */
! 872: BA0WRITE4(sc, CS4281_CWPR, 0x4281);
! 873:
! 874: /*
! 875: * Unset "Full Power-Down bit of Extended PCI Power Management
! 876: * Control" register to release the reset state.
! 877: */
! 878: dat32 = BA0READ4(sc, CS4281_EPPMC);
! 879: if (dat32 & EPPMC_FPDN)
! 880: BA0WRITE4(sc, CS4281_EPPMC, dat32 & ~EPPMC_FPDN);
! 881:
! 882: /* Start PLL out in known state */
! 883: BA0WRITE4(sc, CS4281_CLKCR1, 0);
! 884: /* Start serial ports out in known state */
! 885: BA0WRITE4(sc, CS4281_SERMC, 0);
! 886:
! 887: /* Reset codec */
! 888: BA0WRITE4(sc, CS4281_ACCTL, 0);
! 889: delay(50); /* delay 50us */
! 890:
! 891: BA0WRITE4(sc, CS4281_SPMC, 0);
! 892: delay(100); /* delay 100us */
! 893: BA0WRITE4(sc, CS4281_SPMC, SPMC_RSTN);
! 894: #if defined(ENABLE_SECONDARY_CODEC)
! 895: BA0WRITE4(sc, CS4281_SPMC, SPMC_RSTN | SPCM_ASDIN2E);
! 896: BA0WRITE4(sc, CS4281_SERMC, SERMC_TCID);
! 897: #endif
! 898: delay(50000); /* XXX: delay 50ms */
! 899:
! 900: /* Turn on Sound System clocks based on ABITCLK */
! 901: BA0WRITE4(sc, CS4281_CLKCR1, CLKCR1_DLLP);
! 902: delay(50000); /* XXX: delay 50ms */
! 903: BA0WRITE4(sc, CS4281_CLKCR1, CLKCR1_SWCE | CLKCR1_DLLP);
! 904:
! 905: /* Set enables for sections that are needed in the SSPM registers */
! 906: BA0WRITE4(sc, CS4281_SSPM,
! 907: SSPM_MIXEN | /* Mixer */
! 908: SSPM_CSRCEN | /* Capture SRC */
! 909: SSPM_PSRCEN | /* Playback SRC */
! 910: SSPM_JSEN | /* Joystick */
! 911: SSPM_ACLEN | /* AC LINK */
! 912: SSPM_FMEN /* FM */
! 913: );
! 914:
! 915: /* Wait for clock stabilization */
! 916: n = 0;
! 917: while ((BA0READ4(sc, CS4281_CLKCR1)& (CLKCR1_DLLRDY | CLKCR1_CLKON))
! 918: != (CLKCR1_DLLRDY | CLKCR1_CLKON)) {
! 919: delay(100);
! 920: if (++n > 1000)
! 921: return (-1);
! 922: }
! 923:
! 924: /* Enable ASYNC generation */
! 925: BA0WRITE4(sc, CS4281_ACCTL, ACCTL_ESYN);
! 926:
! 927: /* Wait for Codec ready. Linux driver wait 50ms here */
! 928: n = 0;
! 929: while((BA0READ4(sc, CS4281_ACSTS) & ACSTS_CRDY) == 0) {
! 930: delay(100);
! 931: if (++n > 1000)
! 932: return (-1);
! 933: }
! 934:
! 935: #if defined(ENABLE_SECONDARY_CODEC)
! 936: /* secondary codec ready*/
! 937: n = 0;
! 938: while((BA0READ4(sc, CS4281_ACSTS2) & ACSTS2_CRDY2) == 0) {
! 939: delay(100);
! 940: if (++n > 1000)
! 941: return (-1);
! 942: }
! 943: #endif
! 944:
! 945: /* Set the serial timing configuration */
! 946: /* XXX: undocumented but the Linux driver do this */
! 947: BA0WRITE4(sc, CS4281_SERMC, SERMC_PTCAC97);
! 948:
! 949: /* Wait for Codec ready signal */
! 950: n = 0;
! 951: do {
! 952: delay(1000);
! 953: if (++n > 1000) {
! 954: printf("%s: Timeout waiting for Codec ready\n",
! 955: sc->sc_dev.dv_xname);
! 956: return -1;
! 957: }
! 958: dat32 = BA0READ4(sc, CS4281_ACSTS) & ACSTS_CRDY;
! 959: } while (dat32 == 0);
! 960:
! 961: /* Enable Valid Frame output on ASDOUT */
! 962: BA0WRITE4(sc, CS4281_ACCTL, ACCTL_ESYN | ACCTL_VFRM);
! 963:
! 964: /* Wait until Codec Calibration is finished. Codec register 26h */
! 965: n = 0;
! 966: do {
! 967: delay(1);
! 968: if (++n > 1000) {
! 969: printf("%s: Timeout waiting for Codec calibration\n",
! 970: sc->sc_dev.dv_xname);
! 971: return -1;
! 972: }
! 973: cs4281_read_codec(sc, AC97_REG_POWER, &data);
! 974: } while ((data & 0x0f) != 0x0f);
! 975:
! 976: /* Set the serial timing configuration again */
! 977: /* XXX: undocumented but the Linux driver do this */
! 978: BA0WRITE4(sc, CS4281_SERMC, SERMC_PTCAC97);
! 979:
! 980: /* Wait until we've sampled input slots 3 & 4 as valid */
! 981: n = 0;
! 982: do {
! 983: delay(1000);
! 984: if (++n > 1000) {
! 985: printf("%s: Timeout waiting for sampled input slots as valid\n",
! 986: sc->sc_dev.dv_xname);
! 987: return -1;
! 988: }
! 989: dat32 = BA0READ4(sc, CS4281_ACISV) & (ACISV_ISV3 | ACISV_ISV4);
! 990: } while (dat32 != (ACISV_ISV3 | ACISV_ISV4));
! 991:
! 992: /* Start digital data transfer of audio data to the codec */
! 993: BA0WRITE4(sc, CS4281_ACOSV, (ACOSV_SLV3 | ACOSV_SLV4));
! 994:
! 995: cs4281_write_codec(sc, AC97_REG_HEADPHONE_VOLUME, 0);
! 996: cs4281_write_codec(sc, AC97_REG_MASTER_VOLUME, 0);
! 997:
! 998: /* Power on the DAC */
! 999: cs4281_read_codec(sc, AC97_REG_POWER, &data);
! 1000: cs4281_write_codec(sc, AC97_REG_POWER, data &= 0xfdff);
! 1001:
! 1002: /* Wait until we sample a DAC ready state.
! 1003: * Not documented, but Linux driver does.
! 1004: */
! 1005: for (n = 0; n < 32; ++n) {
! 1006: delay(1000);
! 1007: cs4281_read_codec(sc, AC97_REG_POWER, &data);
! 1008: if (data & 0x02)
! 1009: break;
! 1010: }
! 1011:
! 1012: /* Power on the ADC */
! 1013: cs4281_read_codec(sc, AC97_REG_POWER, &data);
! 1014: cs4281_write_codec(sc, AC97_REG_POWER, data &= 0xfeff);
! 1015:
! 1016: /* Wait until we sample ADC ready state.
! 1017: * Not documented, but Linux driver does.
! 1018: */
! 1019: for (n = 0; n < 32; ++n) {
! 1020: delay(1000);
! 1021: cs4281_read_codec(sc, AC97_REG_POWER, &data);
! 1022: if (data & 0x01)
! 1023: break;
! 1024: }
! 1025:
! 1026: #if 0
! 1027: /* Initialize SSCR register features */
! 1028: /* XXX: hardware volume setting */
! 1029: BA0WRITE4(sc, CS4281_SSCR, ~SSCR_HVC); /* disable HW volume setting */
! 1030: #endif
! 1031:
! 1032: /* disable Sound Blaster Pro emulation */
! 1033: /* XXX:
! 1034: * Cannot set since the documents does not describe which bit is
! 1035: * correspond to SSCR_SB. Since the reset value of SSCR is 0,
! 1036: * we can ignore it.*/
! 1037: #if 0
! 1038: BA0WRITE4(sc, CS4281_SSCR, SSCR_SB);
! 1039: #endif
! 1040:
! 1041: /* map AC97 PCM playback to DMA Channel 0 */
! 1042: /* Reset FEN bit to setup first */
! 1043: BA0WRITE4(sc, CS4281_FCR0, (BA0READ4(sc,CS4281_FCR0) & ~FCRn_FEN));
! 1044: /*
! 1045: *| RS[4:0]/| |
! 1046: *| LS[4:0] | AC97 | Slot Function
! 1047: *|---------+--------+--------------------
! 1048: *| 0 | 3 | Left PCM Playback
! 1049: *| 1 | 4 | Right PCM Playback
! 1050: *| 2 | 5 | Phone Line 1 DAC
! 1051: *| 3 | 6 | Center PCM Playback
! 1052: *....
! 1053: * quoted from Table 29(p109)
! 1054: */
! 1055: dat32 = 0x01 << 24 | /* RS[4:0] = 1 see above */
! 1056: 0x00 << 16 | /* LS[4:0] = 0 see above */
! 1057: 0x0f << 8 | /* SZ[6:0] = 15 size of buffer */
! 1058: 0x00 << 0 ; /* OF[6:0] = 0 offset */
! 1059: BA0WRITE4(sc, CS4281_FCR0, dat32);
! 1060: BA0WRITE4(sc, CS4281_FCR0, dat32 | FCRn_FEN);
! 1061:
! 1062: /* map AC97 PCM record to DMA Channel 1 */
! 1063: /* Reset FEN bit to setup first */
! 1064: BA0WRITE4(sc, CS4281_FCR1, (BA0READ4(sc,CS4281_FCR1) & ~FCRn_FEN));
! 1065: /*
! 1066: *| RS[4:0]/|
! 1067: *| LS[4:0] | AC97 | Slot Function
! 1068: *|---------+------+-------------------
! 1069: *| 10 | 3 | Left PCM Record
! 1070: *| 11 | 4 | Right PCM Record
! 1071: *| 12 | 5 | Phone Line 1 ADC
! 1072: *| 13 | 6 | Mic ADC
! 1073: *....
! 1074: * quoted from Table 30(p109)
! 1075: */
! 1076: dat32 = 0x0b << 24 | /* RS[4:0] = 11 See above */
! 1077: 0x0a << 16 | /* LS[4:0] = 10 See above */
! 1078: 0x0f << 8 | /* SZ[6:0] = 15 Size of buffer */
! 1079: 0x10 << 0 ; /* OF[6:0] = 16 offset */
! 1080:
! 1081: /* XXX: I cannot understand why FCRn_PSH is needed here. */
! 1082: BA0WRITE4(sc, CS4281_FCR1, dat32 | FCRn_PSH);
! 1083: BA0WRITE4(sc, CS4281_FCR1, dat32 | FCRn_FEN);
! 1084:
! 1085: #if 0
! 1086: /* Disable DMA Channel 2, 3 */
! 1087: BA0WRITE4(sc, CS4281_FCR2, (BA0READ4(sc,CS4281_FCR2) & ~FCRn_FEN));
! 1088: BA0WRITE4(sc, CS4281_FCR3, (BA0READ4(sc,CS4281_FCR3) & ~FCRn_FEN));
! 1089: #endif
! 1090:
! 1091: /* Set the SRC Slot Assignment accordingly */
! 1092: /*| PLSS[4:0]/
! 1093: *| PRSS[4:0] | AC97 | Slot Function
! 1094: *|-----------+------+----------------
! 1095: *| 0 | 3 | Left PCM Playback
! 1096: *| 1 | 4 | Right PCM Playback
! 1097: *| 2 | 5 | phone line 1 DAC
! 1098: *| 3 | 6 | Center PCM Playback
! 1099: *| 4 | 7 | Left Surround PCM Playback
! 1100: *| 5 | 8 | Right Surround PCM Playback
! 1101: *......
! 1102: *
! 1103: *| CLSS[4:0]/
! 1104: *| CRSS[4:0] | AC97 | Codec |Slot Function
! 1105: *|-----------+------+-------+-----------------
! 1106: *| 10 | 3 |Primary| Left PCM Record
! 1107: *| 11 | 4 |Primary| Right PCM Record
! 1108: *| 12 | 5 |Primary| Phone Line 1 ADC
! 1109: *| 13 | 6 |Primary| Mic ADC
! 1110: *|.....
! 1111: *| 20 | 3 | Sec. | Left PCM Record
! 1112: *| 21 | 4 | Sec. | Right PCM Record
! 1113: *| 22 | 5 | Sec. | Phone Line 1 ADC
! 1114: *| 23 | 6 | Sec. | Mic ADC
! 1115: */
! 1116: dat32 = 0x0b << 24 | /* CRSS[4:0] Right PCM Record(primary) */
! 1117: 0x0a << 16 | /* CLSS[4:0] Left PCM Record(primary) */
! 1118: 0x01 << 8 | /* PRSS[4:0] Right PCM Playback */
! 1119: 0x00 << 0; /* PLSS[4:0] Left PCM Playback */
! 1120: BA0WRITE4(sc, CS4281_SRCSA, dat32);
! 1121:
! 1122: /* Set interrupt to occurred at Half and Full terminal
! 1123: * count interrupt enable for DMA channel 0 and 1.
! 1124: * To keep DMA stop, set MSK.
! 1125: */
! 1126: dat32 = DCRn_HTCIE | DCRn_TCIE | DCRn_MSK;
! 1127: BA0WRITE4(sc, CS4281_DCR0, dat32);
! 1128: BA0WRITE4(sc, CS4281_DCR1, dat32);
! 1129:
! 1130: /* Set Auto-Initialize Control enable */
! 1131: BA0WRITE4(sc, CS4281_DMR0,
! 1132: DMRn_DMA | DMRn_AUTO | DMRn_TR_READ);
! 1133: BA0WRITE4(sc, CS4281_DMR1,
! 1134: DMRn_DMA | DMRn_AUTO | DMRn_TR_WRITE);
! 1135:
! 1136: /* Clear DMA Mask in HIMR */
! 1137: dat32 = BA0READ4(sc, CS4281_HIMR) & 0xfffbfcff;
! 1138: BA0WRITE4(sc, CS4281_HIMR, dat32);
! 1139: return (0);
! 1140: }
! 1141:
! 1142: void
! 1143: cs4281_power(why, v)
! 1144: int why;
! 1145: void *v;
! 1146: {
! 1147: struct cs4281_softc *sc = (struct cs4281_softc *)v;
! 1148: int i;
! 1149:
! 1150: DPRINTF(("%s: cs4281_power why=%d\n", sc->sc_dev.dv_xname, why));
! 1151: if (why != PWR_RESUME) {
! 1152: sc->sc_suspend = why;
! 1153:
! 1154: cs4281_halt_output(sc);
! 1155: cs4281_halt_input(sc);
! 1156: /* Save AC97 registers */
! 1157: for (i = 1; i <= CS4281_SAVE_REG_MAX; i++) {
! 1158: if (i == 0x04) /* AC97_REG_MASTER_TONE */
! 1159: continue;
! 1160: cs4281_read_codec(sc, 2*i, &sc->ac97_reg[i>>1]);
! 1161: }
! 1162: /* should I powerdown here ? */
! 1163: cs4281_write_codec(sc, AC97_REG_POWER, CS4281_POWER_DOWN_ALL);
! 1164: } else {
! 1165: if (sc->sc_suspend == PWR_RESUME) {
! 1166: printf("cs4281_power: odd, resume without suspend.\n");
! 1167: sc->sc_suspend = why;
! 1168: return;
! 1169: }
! 1170: sc->sc_suspend = why;
! 1171: cs4281_init(sc);
! 1172: cs4281_reset_codec(sc);
! 1173:
! 1174: /* restore ac97 registers */
! 1175: for (i = 1; i <= CS4281_SAVE_REG_MAX; i++) {
! 1176: if (i == 0x04) /* AC97_REG_MASTER_TONE */
! 1177: continue;
! 1178: cs4281_write_codec(sc, 2*i, sc->ac97_reg[i>>1]);
! 1179: }
! 1180: }
! 1181: }
! 1182:
! 1183: void
! 1184: cs4281_reset_codec(void *addr)
! 1185: {
! 1186: struct cs4281_softc *sc;
! 1187: u_int16_t data;
! 1188: u_int32_t dat32;
! 1189: int n;
! 1190:
! 1191: sc = addr;
! 1192:
! 1193: DPRINTFN(3,("cs4281_reset_codec\n"));
! 1194:
! 1195: /* Reset codec */
! 1196: BA0WRITE4(sc, CS4281_ACCTL, 0);
! 1197: delay(50); /* delay 50us */
! 1198:
! 1199: BA0WRITE4(sc, CS4281_SPMC, 0);
! 1200: delay(100); /* delay 100us */
! 1201: BA0WRITE4(sc, CS4281_SPMC, SPMC_RSTN);
! 1202: #if defined(ENABLE_SECONDARY_CODEC)
! 1203: BA0WRITE4(sc, CS4281_SPMC, SPMC_RSTN | SPCM_ASDIN2E);
! 1204: BA0WRITE4(sc, CS4281_SERMC, SERMC_TCID);
! 1205: #endif
! 1206: delay(50000); /* XXX: delay 50ms */
! 1207:
! 1208: /* Enable ASYNC generation */
! 1209: BA0WRITE4(sc, CS4281_ACCTL, ACCTL_ESYN);
! 1210:
! 1211: /* Wait for Codec ready. Linux driver wait 50ms here */
! 1212: n = 0;
! 1213: while((BA0READ4(sc, CS4281_ACSTS) & ACSTS_CRDY) == 0) {
! 1214: delay(100);
! 1215: if (++n > 1000) {
! 1216: printf("reset_codec: AC97 codec ready timeout\n");
! 1217: return;
! 1218: }
! 1219: }
! 1220: #if defined(ENABLE_SECONDARY_CODEC)
! 1221: /* secondary codec ready*/
! 1222: n = 0;
! 1223: while((BA0READ4(sc, CS4281_ACSTS2) & ACSTS2_CRDY2) == 0) {
! 1224: delay(100);
! 1225: if (++n > 1000)
! 1226: return;
! 1227: }
! 1228: #endif
! 1229: /* Set the serial timing configuration */
! 1230: /* XXX: undocumented but the Linux driver do this */
! 1231: BA0WRITE4(sc, CS4281_SERMC, SERMC_PTCAC97);
! 1232:
! 1233: /* Wait for Codec ready signal */
! 1234: n = 0;
! 1235: do {
! 1236: delay(1000);
! 1237: if (++n > 1000) {
! 1238: printf("%s: Timeout waiting for Codec ready\n",
! 1239: sc->sc_dev.dv_xname);
! 1240: return;
! 1241: }
! 1242: dat32 = BA0READ4(sc, CS4281_ACSTS) & ACSTS_CRDY;
! 1243: } while (dat32 == 0);
! 1244:
! 1245: /* Enable Valid Frame output on ASDOUT */
! 1246: BA0WRITE4(sc, CS4281_ACCTL, ACCTL_ESYN | ACCTL_VFRM);
! 1247:
! 1248: /* Wait until Codec Calibration is finished. Codec register 26h */
! 1249: n = 0;
! 1250: do {
! 1251: delay(1);
! 1252: if (++n > 1000) {
! 1253: printf("%s: Timeout waiting for Codec calibration\n",
! 1254: sc->sc_dev.dv_xname);
! 1255: return ;
! 1256: }
! 1257: cs4281_read_codec(sc, AC97_REG_POWER, &data);
! 1258: } while ((data & 0x0f) != 0x0f);
! 1259:
! 1260: /* Set the serial timing configuration again */
! 1261: /* XXX: undocumented but the Linux driver do this */
! 1262: BA0WRITE4(sc, CS4281_SERMC, SERMC_PTCAC97);
! 1263:
! 1264: /* Wait until we've sampled input slots 3 & 4 as valid */
! 1265: n = 0;
! 1266: do {
! 1267: delay(1000);
! 1268: if (++n > 1000) {
! 1269: printf("%s: Timeout waiting for sampled input slots as valid\n",
! 1270: sc->sc_dev.dv_xname);
! 1271: return;
! 1272: }
! 1273: dat32 = BA0READ4(sc, CS4281_ACISV) & (ACISV_ISV3 | ACISV_ISV4) ;
! 1274: } while (dat32 != (ACISV_ISV3 | ACISV_ISV4));
! 1275:
! 1276: /* Start digital data transfer of audio data to the codec */
! 1277: BA0WRITE4(sc, CS4281_ACOSV, (ACOSV_SLV3 | ACOSV_SLV4));
! 1278: }
! 1279:
! 1280: int
! 1281: cs4281_open(void *addr, int flags)
! 1282: {
! 1283: return (0);
! 1284: }
! 1285:
! 1286: void
! 1287: cs4281_close(void *addr)
! 1288: {
! 1289: struct cs4281_softc *sc;
! 1290:
! 1291: sc = addr;
! 1292:
! 1293: (*sc->halt_output)(sc);
! 1294: (*sc->halt_input)(sc);
! 1295:
! 1296: sc->sc_pintr = 0;
! 1297: sc->sc_rintr = 0;
! 1298: }
! 1299:
! 1300: int
! 1301: cs4281_round_blocksize(void *addr, int blk)
! 1302: {
! 1303: struct cs4281_softc *sc;
! 1304: int retval;
! 1305:
! 1306: DPRINTFN(5,("cs4281_round_blocksize blk=%d -> ", blk));
! 1307:
! 1308: sc=addr;
! 1309: if (blk < sc->hw_blocksize)
! 1310: retval = sc->hw_blocksize;
! 1311: else
! 1312: retval = blk & -(sc->hw_blocksize);
! 1313:
! 1314: DPRINTFN(5,("%d\n", retval));
! 1315:
! 1316: return (retval);
! 1317: }
! 1318:
! 1319: int
! 1320: cs4281_mixer_set_port(void *addr, mixer_ctrl_t *cp)
! 1321: {
! 1322: struct cs4281_softc *sc;
! 1323: int val;
! 1324:
! 1325: sc = addr;
! 1326: val = sc->codec_if->vtbl->mixer_set_port(sc->codec_if, cp);
! 1327: DPRINTFN(3,("mixer_set_port: val=%d\n", val));
! 1328: return (val);
! 1329: }
! 1330:
! 1331: int
! 1332: cs4281_mixer_get_port(void *addr, mixer_ctrl_t *cp)
! 1333: {
! 1334: struct cs4281_softc *sc;
! 1335:
! 1336: sc = addr;
! 1337: return (sc->codec_if->vtbl->mixer_get_port(sc->codec_if, cp));
! 1338: }
! 1339:
! 1340:
! 1341: int
! 1342: cs4281_query_devinfo(void *addr, mixer_devinfo_t *dip)
! 1343: {
! 1344: struct cs4281_softc *sc;
! 1345:
! 1346: sc = addr;
! 1347: return (sc->codec_if->vtbl->query_devinfo(sc->codec_if, dip));
! 1348: }
! 1349:
! 1350: void *
! 1351: cs4281_malloc(void *addr, int direction, size_t size, int pool, int flags)
! 1352: {
! 1353: struct cs4281_softc *sc;
! 1354: struct cs4281_dma *p;
! 1355: int error;
! 1356:
! 1357: sc = addr;
! 1358:
! 1359: p = malloc(sizeof(*p), pool, flags);
! 1360: if (!p)
! 1361: return (0);
! 1362:
! 1363: error = cs4281_allocmem(sc, size, pool, flags, p);
! 1364:
! 1365: if (error) {
! 1366: free(p, pool);
! 1367: return (0);
! 1368: }
! 1369:
! 1370: p->next = sc->sc_dmas;
! 1371: sc->sc_dmas = p;
! 1372: return (BUFADDR(p));
! 1373: }
! 1374:
! 1375:
! 1376:
! 1377: void
! 1378: cs4281_free(void *addr, void *ptr, int pool)
! 1379: {
! 1380: struct cs4281_softc *sc;
! 1381: struct cs4281_dma **pp, *p;
! 1382:
! 1383: sc = addr;
! 1384: for (pp = &sc->sc_dmas; (p = *pp) != NULL; pp = &p->next) {
! 1385: if (BUFADDR(p) == ptr) {
! 1386: bus_dmamap_unload(sc->sc_dmatag, p->map);
! 1387: bus_dmamap_destroy(sc->sc_dmatag, p->map);
! 1388: bus_dmamem_unmap(sc->sc_dmatag, p->addr, p->size);
! 1389: bus_dmamem_free(sc->sc_dmatag, p->segs, p->nsegs);
! 1390: free(p->dum, pool);
! 1391: *pp = p->next;
! 1392: free(p, pool);
! 1393: return;
! 1394: }
! 1395: }
! 1396: }
! 1397:
! 1398: size_t
! 1399: cs4281_round_buffersize(void *addr, int direction, size_t size)
! 1400: {
! 1401: /* The real dma buffersize are 4KB for CS4280
! 1402: * and 64kB/MAX_CHANNELS for CS4281.
! 1403: * But they are too small for high quality audio,
! 1404: * let the upper layer(audio) use a larger buffer.
! 1405: * (originally suggested by Lennart Augustsson.)
! 1406: */
! 1407: return (size);
! 1408: }
! 1409:
! 1410: paddr_t
! 1411: cs4281_mappage(void *addr, void *mem, off_t off, int prot)
! 1412: {
! 1413: struct cs4281_softc *sc;
! 1414: struct cs4281_dma *p;
! 1415:
! 1416: sc = addr;
! 1417: if (off < 0)
! 1418: return -1;
! 1419:
! 1420: for (p = sc->sc_dmas; p && BUFADDR(p) != mem; p = p->next)
! 1421: ;
! 1422:
! 1423: if (!p) {
! 1424: DPRINTF(("cs4281_mappage: bad buffer address\n"));
! 1425: return (-1);
! 1426: }
! 1427:
! 1428: return (bus_dmamem_mmap(sc->sc_dmatag, p->segs, p->nsegs, off, prot,
! 1429: BUS_DMA_WAITOK));
! 1430: }
! 1431:
! 1432:
! 1433: int
! 1434: cs4281_get_props(void *addr)
! 1435: {
! 1436: int retval;
! 1437:
! 1438: retval = AUDIO_PROP_INDEPENDENT | AUDIO_PROP_FULLDUPLEX;
! 1439: #ifdef MMAP_READY
! 1440: retval |= AUDIO_PROP_MMAP;
! 1441: #endif
! 1442: return (retval);
! 1443: }
! 1444:
! 1445: /* AC97 */
! 1446: int
! 1447: cs4281_attach_codec(void *addr, struct ac97_codec_if *codec_if)
! 1448: {
! 1449: struct cs4281_softc *sc;
! 1450:
! 1451: DPRINTF(("cs4281_attach_codec:\n"));
! 1452: sc = addr;
! 1453: sc->codec_if = codec_if;
! 1454: return (0);
! 1455: }
! 1456:
! 1457:
! 1458: int
! 1459: cs4281_read_codec(void *addr, u_int8_t ac97_addr, u_int16_t *ac97_data)
! 1460: {
! 1461: struct cs4281_softc *sc;
! 1462: u_int32_t acctl;
! 1463: int n;
! 1464:
! 1465: sc = addr;
! 1466:
! 1467: DPRINTFN(5,("read_codec: add=0x%02x ", ac97_addr));
! 1468: /*
! 1469: * Make sure that there is not data sitting around from a preivous
! 1470: * uncompleted access.
! 1471: */
! 1472: BA0READ4(sc, CS4281_ACSDA);
! 1473:
! 1474: /* Set up AC97 control registers. */
! 1475: BA0WRITE4(sc, CS4281_ACCAD, ac97_addr);
! 1476: BA0WRITE4(sc, CS4281_ACCDA, 0);
! 1477:
! 1478: acctl = ACCTL_ESYN | ACCTL_VFRM | ACCTL_CRW | ACCTL_DCV;
! 1479: BA0WRITE4(sc, CS4281_ACCTL, acctl);
! 1480:
! 1481: if (cs4281_src_wait(sc) < 0) {
! 1482: printf("%s: AC97 read prob. (DCV!=0) for add=0x%0x\n",
! 1483: sc->sc_dev.dv_xname, ac97_addr);
! 1484: return 1;
! 1485: }
! 1486:
! 1487: /* wait for valid status bit is active */
! 1488: n = 0;
! 1489: while ((BA0READ4(sc, CS4281_ACSTS) & ACSTS_VSTS) == 0) {
! 1490: delay(1);
! 1491: while (++n > 1000) {
! 1492: printf("%s: AC97 read fail (VSTS==0) for add=0x%0x\n",
! 1493: sc->sc_dev.dv_xname, ac97_addr);
! 1494: return 1;
! 1495: }
! 1496: }
! 1497: *ac97_data = BA0READ4(sc, CS4281_ACSDA);
! 1498: DPRINTFN(5,("data=0x%04x\n", *ac97_data));
! 1499: return (0);
! 1500: }
! 1501:
! 1502: int
! 1503: cs4281_write_codec(void *addr, u_int8_t ac97_addr, u_int16_t ac97_data)
! 1504: {
! 1505: struct cs4281_softc *sc;
! 1506: u_int32_t acctl;
! 1507:
! 1508: sc = addr;
! 1509:
! 1510: DPRINTFN(5,("write_codec: add=0x%02x data=0x%04x\n", ac97_addr, ac97_data));
! 1511: BA0WRITE4(sc, CS4281_ACCAD, ac97_addr);
! 1512: BA0WRITE4(sc, CS4281_ACCDA, ac97_data);
! 1513:
! 1514: acctl = ACCTL_ESYN | ACCTL_VFRM | ACCTL_DCV;
! 1515: BA0WRITE4(sc, CS4281_ACCTL, acctl);
! 1516:
! 1517: if (cs4281_src_wait(sc) < 0) {
! 1518: printf("%s: AC97 write fail (DCV!=0) for add=0x%02x data="
! 1519: "0x%04x\n", sc->sc_dev.dv_xname, ac97_addr, ac97_data);
! 1520: return (1);
! 1521: }
! 1522: return (0);
! 1523: }
! 1524:
! 1525: int
! 1526: cs4281_allocmem(struct cs4281_softc *sc, size_t size, int pool, int flags,
! 1527: struct cs4281_dma *p)
! 1528: {
! 1529: int error;
! 1530: size_t align;
! 1531:
! 1532: align = sc->dma_align;
! 1533: p->size = sc->dma_size;
! 1534: /* allocate memory for upper audio driver */
! 1535: p->dum = malloc(size, pool, flags);
! 1536: if (!p->dum)
! 1537: return (1);
! 1538: error = bus_dmamem_alloc(sc->sc_dmatag, p->size, align, 0,
! 1539: p->segs, sizeof(p->segs)/sizeof(p->segs[0]),
! 1540: &p->nsegs, BUS_DMA_NOWAIT);
! 1541: if (error) {
! 1542: printf("%s: unable to allocate dma. error=%d\n",
! 1543: sc->sc_dev.dv_xname, error);
! 1544: return (error);
! 1545: }
! 1546:
! 1547: error = bus_dmamem_map(sc->sc_dmatag, p->segs, p->nsegs, p->size,
! 1548: &p->addr, BUS_DMA_NOWAIT|BUS_DMA_COHERENT);
! 1549: if (error) {
! 1550: printf("%s: unable to map dma, error=%d\n",
! 1551: sc->sc_dev.dv_xname, error);
! 1552: goto free;
! 1553: }
! 1554:
! 1555: error = bus_dmamap_create(sc->sc_dmatag, p->size, 1, p->size,
! 1556: 0, BUS_DMA_NOWAIT, &p->map);
! 1557: if (error) {
! 1558: printf("%s: unable to create dma map, error=%d\n",
! 1559: sc->sc_dev.dv_xname, error);
! 1560: goto unmap;
! 1561: }
! 1562:
! 1563: error = bus_dmamap_load(sc->sc_dmatag, p->map, p->addr, p->size, NULL,
! 1564: BUS_DMA_NOWAIT);
! 1565: if (error) {
! 1566: printf("%s: unable to load dma map, error=%d\n",
! 1567: sc->sc_dev.dv_xname, error);
! 1568: goto destroy;
! 1569: }
! 1570: return (0);
! 1571:
! 1572: destroy:
! 1573: bus_dmamap_destroy(sc->sc_dmatag, p->map);
! 1574: unmap:
! 1575: bus_dmamem_unmap(sc->sc_dmatag, p->addr, p->size);
! 1576: free:
! 1577: bus_dmamem_free(sc->sc_dmatag, p->segs, p->nsegs);
! 1578: return (error);
! 1579: }
! 1580:
! 1581:
! 1582: int
! 1583: cs4281_src_wait(sc)
! 1584: struct cs4281_softc *sc;
! 1585: {
! 1586: int n;
! 1587:
! 1588: n = 0;
! 1589: while ((BA0READ4(sc, CS4281_ACCTL) & ACCTL_DCV)) {
! 1590: delay(1000);
! 1591: if (++n > 1000)
! 1592: return (-1);
! 1593: }
! 1594: return (0);
! 1595: }
CVSweb