1987e5972SCameron Grant /* 2987e5972SCameron Grant * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk> 3987e5972SCameron Grant * All rights reserved. 4987e5972SCameron Grant * 5987e5972SCameron Grant * Redistribution and use in source and binary forms, with or without 6987e5972SCameron Grant * modification, are permitted provided that the following conditions 7987e5972SCameron Grant * are met: 8987e5972SCameron Grant * 1. Redistributions of source code must retain the above copyright 9987e5972SCameron Grant * notice, this list of conditions and the following disclaimer. 10987e5972SCameron Grant * 2. Redistributions in binary form must reproduce the above copyright 11987e5972SCameron Grant * notice, this list of conditions and the following disclaimer in the 12987e5972SCameron Grant * documentation and/or other materials provided with the distribution. 13987e5972SCameron Grant * 14987e5972SCameron Grant * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15987e5972SCameron Grant * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16987e5972SCameron Grant * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17987e5972SCameron Grant * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18987e5972SCameron Grant * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19987e5972SCameron Grant * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20987e5972SCameron Grant * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21987e5972SCameron Grant * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHERIN CONTRACT, STRICT 22987e5972SCameron Grant * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23987e5972SCameron Grant * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THEPOSSIBILITY OF 24987e5972SCameron Grant * SUCH DAMAGE. 25987e5972SCameron Grant * 2653c5a968SPeter Wemm * $FreeBSD$ 27987e5972SCameron Grant */ 28987e5972SCameron Grant 29ef9308b1SCameron Grant #include <dev/sound/pcm/sound.h> 30ef9308b1SCameron Grant #include <dev/sound/pcm/ac97.h> 31ef9308b1SCameron Grant #include <dev/sound/pci/t4dwave.h> 32987e5972SCameron Grant 33987e5972SCameron Grant #include <pci/pcireg.h> 34987e5972SCameron Grant #include <pci/pcivar.h> 35987e5972SCameron Grant 36987e5972SCameron Grant /* -------------------------------------------------------------------- */ 37987e5972SCameron Grant 38dedf4424SCameron Grant #define TDX_PCI_ID 0x20001023 39dedf4424SCameron Grant #define TNX_PCI_ID 0x20011023 40dedf4424SCameron Grant 41dedf4424SCameron Grant #define TR_BUFFSIZE 0xf000 42dedf4424SCameron Grant #define TR_TIMEOUT_CDC 0xffff 43dedf4424SCameron Grant #define TR_INTSAMPLES 0x2000 44dedf4424SCameron Grant #define TR_MAXPLAYCH 4 45dedf4424SCameron Grant 46987e5972SCameron Grant struct tr_info; 47987e5972SCameron Grant 48987e5972SCameron Grant /* channel registers */ 49987e5972SCameron Grant struct tr_chinfo { 50987e5972SCameron Grant u_int32_t cso, alpha, fms, fmc, ec; 51987e5972SCameron Grant u_int32_t lba; 52987e5972SCameron Grant u_int32_t eso, delta; 53987e5972SCameron Grant u_int32_t rvol, cvol; 54987e5972SCameron Grant u_int32_t gvsel, pan, vol, ctrl; 5535f9e4a1SCameron Grant int index, ss; 56987e5972SCameron Grant snd_dbuf *buffer; 57987e5972SCameron Grant pcm_channel *channel; 58987e5972SCameron Grant struct tr_info *parent; 59987e5972SCameron Grant }; 60987e5972SCameron Grant 61987e5972SCameron Grant /* device private data */ 62987e5972SCameron Grant struct tr_info { 63987e5972SCameron Grant u_int32_t type; 64987e5972SCameron Grant 65987e5972SCameron Grant bus_space_tag_t st; 66987e5972SCameron Grant bus_space_handle_t sh; 67987e5972SCameron Grant bus_dma_tag_t parent_dmat; 68987e5972SCameron Grant 69987e5972SCameron Grant struct resource *reg, *irq; 70987e5972SCameron Grant int regtype, regid, irqid; 71987e5972SCameron Grant void *ih; 72987e5972SCameron Grant 73987e5972SCameron Grant u_int32_t playchns; 74987e5972SCameron Grant struct tr_chinfo chinfo[TR_MAXPLAYCH]; 75987e5972SCameron Grant struct tr_chinfo recchinfo; 76987e5972SCameron Grant }; 77987e5972SCameron Grant 78987e5972SCameron Grant /* -------------------------------------------------------------------- */ 79987e5972SCameron Grant 80987e5972SCameron Grant /* 81987e5972SCameron Grant * prototypes 82987e5972SCameron Grant */ 83987e5972SCameron Grant 84987e5972SCameron Grant /* stuff */ 85987e5972SCameron Grant static int tr_init(struct tr_info *); 86987e5972SCameron Grant static void tr_intr(void *); 87987e5972SCameron Grant 88987e5972SCameron Grant /* talk to the card */ 89987e5972SCameron Grant static u_int32_t tr_rd(struct tr_info *, int, int); 90987e5972SCameron Grant static void tr_wr(struct tr_info *, int, u_int32_t, int); 91987e5972SCameron Grant 92987e5972SCameron Grant /* manipulate playback channels */ 93987e5972SCameron Grant static void tr_clrint(struct tr_info *, char); 94987e5972SCameron Grant static void tr_enaint(struct tr_info *, char, int); 95987e5972SCameron Grant static u_int32_t tr_testint(struct tr_info *, char); 96987e5972SCameron Grant static void tr_rdch(struct tr_info *, char, struct tr_chinfo *); 97987e5972SCameron Grant static void tr_wrch(struct tr_info *, char, struct tr_chinfo *); 98987e5972SCameron Grant static void tr_selch(struct tr_info *, char); 99987e5972SCameron Grant static void tr_startch(struct tr_info *, char); 100987e5972SCameron Grant static void tr_stopch(struct tr_info *, char); 101987e5972SCameron Grant 102987e5972SCameron Grant /* -------------------------------------------------------------------- */ 103987e5972SCameron Grant 104513693beSCameron Grant static u_int32_t tr_recfmt[] = { 105513693beSCameron Grant AFMT_U8, 106513693beSCameron Grant AFMT_STEREO | AFMT_U8, 107513693beSCameron Grant AFMT_S8, 108513693beSCameron Grant AFMT_STEREO | AFMT_S8, 109513693beSCameron Grant AFMT_S16_LE, 110513693beSCameron Grant AFMT_STEREO | AFMT_S16_LE, 111513693beSCameron Grant AFMT_U16_LE, 112513693beSCameron Grant AFMT_STEREO | AFMT_U16_LE, 113513693beSCameron Grant 0 114987e5972SCameron Grant }; 115513693beSCameron Grant static pcmchan_caps tr_reccaps = {4000, 48000, tr_recfmt, 0}; 116987e5972SCameron Grant 117513693beSCameron Grant static u_int32_t tr_playfmt[] = { 118513693beSCameron Grant AFMT_U8, 119513693beSCameron Grant AFMT_STEREO | AFMT_U8, 120513693beSCameron Grant AFMT_S8, 121513693beSCameron Grant AFMT_STEREO | AFMT_S8, 122513693beSCameron Grant AFMT_S16_LE, 123513693beSCameron Grant AFMT_STEREO | AFMT_S16_LE, 124513693beSCameron Grant AFMT_U16_LE, 125513693beSCameron Grant AFMT_STEREO | AFMT_U16_LE, 126513693beSCameron Grant 0 127987e5972SCameron Grant }; 128513693beSCameron Grant static pcmchan_caps tr_playcaps = {4000, 48000, tr_playfmt, 0}; 129987e5972SCameron Grant 130987e5972SCameron Grant /* -------------------------------------------------------------------- */ 131987e5972SCameron Grant 132987e5972SCameron Grant static u_int32_t 133987e5972SCameron Grant tr_fmttobits(u_int32_t fmt) 134987e5972SCameron Grant { 135987e5972SCameron Grant u_int32_t bits = 0; 136987e5972SCameron Grant bits |= (fmt & AFMT_STEREO)? 0x4 : 0; 137987e5972SCameron Grant bits |= (fmt & (AFMT_S8 | AFMT_S16_LE))? 0x2 : 0; 138987e5972SCameron Grant bits |= (fmt & (AFMT_S16_LE | AFMT_U16_LE))? 0x8 : 0; 139987e5972SCameron Grant return bits; 140987e5972SCameron Grant } 141987e5972SCameron Grant 142987e5972SCameron Grant /* Hardware */ 143987e5972SCameron Grant 144987e5972SCameron Grant static u_int32_t 145987e5972SCameron Grant tr_rd(struct tr_info *tr, int regno, int size) 146987e5972SCameron Grant { 147987e5972SCameron Grant switch(size) { 148987e5972SCameron Grant case 1: 149987e5972SCameron Grant return bus_space_read_1(tr->st, tr->sh, regno); 150987e5972SCameron Grant case 2: 151987e5972SCameron Grant return bus_space_read_2(tr->st, tr->sh, regno); 152987e5972SCameron Grant case 4: 153987e5972SCameron Grant return bus_space_read_4(tr->st, tr->sh, regno); 154987e5972SCameron Grant default: 155987e5972SCameron Grant return 0xffffffff; 156987e5972SCameron Grant } 157987e5972SCameron Grant } 158987e5972SCameron Grant 159987e5972SCameron Grant static void 160987e5972SCameron Grant tr_wr(struct tr_info *tr, int regno, u_int32_t data, int size) 161987e5972SCameron Grant { 162987e5972SCameron Grant switch(size) { 163987e5972SCameron Grant case 1: 164987e5972SCameron Grant bus_space_write_1(tr->st, tr->sh, regno, data); 165987e5972SCameron Grant break; 166987e5972SCameron Grant case 2: 167987e5972SCameron Grant bus_space_write_2(tr->st, tr->sh, regno, data); 168987e5972SCameron Grant break; 169987e5972SCameron Grant case 4: 170987e5972SCameron Grant bus_space_write_4(tr->st, tr->sh, regno, data); 171987e5972SCameron Grant break; 172987e5972SCameron Grant } 173987e5972SCameron Grant } 174987e5972SCameron Grant 1750f55ac6cSCameron Grant /* -------------------------------------------------------------------- */ 176987e5972SCameron Grant /* ac97 codec */ 177987e5972SCameron Grant 1780f55ac6cSCameron Grant static int 1790f55ac6cSCameron Grant tr_rdcd(kobj_t obj, void *devinfo, int regno) 180987e5972SCameron Grant { 181987e5972SCameron Grant struct tr_info *tr = (struct tr_info *)devinfo; 182987e5972SCameron Grant int i, j, treg, trw; 183987e5972SCameron Grant 184987e5972SCameron Grant switch (tr->type) { 185987e5972SCameron Grant case TDX_PCI_ID: 186987e5972SCameron Grant treg=TDX_REG_CODECRD; 187987e5972SCameron Grant trw=TDX_CDC_RWSTAT; 188987e5972SCameron Grant break; 189987e5972SCameron Grant case TNX_PCI_ID: 190987e5972SCameron Grant treg=(regno & 0x100)? TNX_REG_CODEC2RD : TNX_REG_CODEC1RD; 191987e5972SCameron Grant trw=TNX_CDC_RWSTAT; 192987e5972SCameron Grant break; 193987e5972SCameron Grant default: 194987e5972SCameron Grant printf("!!! tr_rdcd defaulted !!!\n"); 1950f55ac6cSCameron Grant return -1; 196987e5972SCameron Grant } 197987e5972SCameron Grant 198987e5972SCameron Grant regno &= 0x7f; 199987e5972SCameron Grant tr_wr(tr, treg, regno | trw, 4); 200987e5972SCameron Grant j=trw; 201987e5972SCameron Grant for (i=TR_TIMEOUT_CDC; (i > 0) && (j & trw); i--) j=tr_rd(tr, treg, 4); 202987e5972SCameron Grant if (i == 0) printf("codec timeout during read of register %x\n", regno); 203987e5972SCameron Grant return (j >> TR_CDC_DATA) & 0xffff; 204987e5972SCameron Grant } 205987e5972SCameron Grant 2060f55ac6cSCameron Grant static int 2070f55ac6cSCameron Grant tr_wrcd(kobj_t obj, void *devinfo, int regno, u_int32_t data) 208987e5972SCameron Grant { 209987e5972SCameron Grant struct tr_info *tr = (struct tr_info *)devinfo; 210987e5972SCameron Grant int i, j, treg, trw; 211987e5972SCameron Grant 212987e5972SCameron Grant switch (tr->type) { 213987e5972SCameron Grant case TDX_PCI_ID: 214987e5972SCameron Grant treg=TDX_REG_CODECWR; 215987e5972SCameron Grant trw=TDX_CDC_RWSTAT; 216987e5972SCameron Grant break; 217987e5972SCameron Grant case TNX_PCI_ID: 218987e5972SCameron Grant treg=TNX_REG_CODECWR; 219987e5972SCameron Grant trw=TNX_CDC_RWSTAT | ((regno & 0x100)? TNX_CDC_SEC : 0); 220987e5972SCameron Grant break; 221987e5972SCameron Grant default: 222987e5972SCameron Grant printf("!!! tr_wrcd defaulted !!!"); 2230f55ac6cSCameron Grant return -1; 224987e5972SCameron Grant } 225987e5972SCameron Grant 226987e5972SCameron Grant regno &= 0x7f; 227987e5972SCameron Grant #if 0 228987e5972SCameron Grant printf("tr_wrcd: reg %x was %x", regno, tr_rdcd(devinfo, regno)); 229987e5972SCameron Grant #endif 230987e5972SCameron Grant j=trw; 231987e5972SCameron Grant for (i=TR_TIMEOUT_CDC; (i>0) && (j & trw); i--) j=tr_rd(tr, treg, 4); 232987e5972SCameron Grant tr_wr(tr, treg, (data << TR_CDC_DATA) | regno | trw, 4); 233987e5972SCameron Grant #if 0 234987e5972SCameron Grant printf(" - wrote %x, now %x\n", data, tr_rdcd(devinfo, regno)); 235987e5972SCameron Grant #endif 236987e5972SCameron Grant if (i==0) printf("codec timeout writing %x, data %x\n", regno, data); 2370f55ac6cSCameron Grant return (i > 0)? 0 : -1; 238987e5972SCameron Grant } 239987e5972SCameron Grant 2400f55ac6cSCameron Grant static kobj_method_t tr_ac97_methods[] = { 2410f55ac6cSCameron Grant KOBJMETHOD(ac97_read, tr_rdcd), 2420f55ac6cSCameron Grant KOBJMETHOD(ac97_write, tr_wrcd), 2430f55ac6cSCameron Grant { 0, 0 } 2440f55ac6cSCameron Grant }; 2450f55ac6cSCameron Grant AC97_DECLARE(tr_ac97); 2460f55ac6cSCameron Grant 2470f55ac6cSCameron Grant /* -------------------------------------------------------------------- */ 248987e5972SCameron Grant /* playback channel interrupts */ 249987e5972SCameron Grant 250987e5972SCameron Grant static u_int32_t 251987e5972SCameron Grant tr_testint(struct tr_info *tr, char channel) 252987e5972SCameron Grant { 253987e5972SCameron Grant return tr_rd(tr, (channel & 0x20)? TR_REG_ADDRINTB : TR_REG_ADDRINTA, 254987e5972SCameron Grant 4) & (1<<(channel & 0x1f)); 255987e5972SCameron Grant } 256987e5972SCameron Grant 257987e5972SCameron Grant static void 258987e5972SCameron Grant tr_clrint(struct tr_info *tr, char channel) 259987e5972SCameron Grant { 260987e5972SCameron Grant tr_wr(tr, (channel & 0x20)? TR_REG_ADDRINTB : TR_REG_ADDRINTA, 261987e5972SCameron Grant 1<<(channel & 0x1f), 4); 262987e5972SCameron Grant } 263987e5972SCameron Grant 264987e5972SCameron Grant static void 265987e5972SCameron Grant tr_enaint(struct tr_info *tr, char channel, int enable) 266987e5972SCameron Grant { 267987e5972SCameron Grant u_int32_t reg = (channel & 0x20)? TR_REG_INTENB : TR_REG_INTENA; 268987e5972SCameron Grant u_int32_t i = tr_rd(tr, reg, 4); 269987e5972SCameron Grant channel &= 0x1f; 270987e5972SCameron Grant i &= ~(1 << channel); 271987e5972SCameron Grant i |= (enable? 1 : 0) << channel; 272987e5972SCameron Grant tr_clrint(tr, channel); 273987e5972SCameron Grant tr_wr(tr, reg, i, 4); 274987e5972SCameron Grant } 275987e5972SCameron Grant 276987e5972SCameron Grant /* playback channels */ 277987e5972SCameron Grant 278987e5972SCameron Grant static void 279987e5972SCameron Grant tr_selch(struct tr_info *tr, char channel) 280987e5972SCameron Grant { 281987e5972SCameron Grant int i=tr_rd(tr, TR_REG_CIR, 4); 282987e5972SCameron Grant i &= ~TR_CIR_MASK; 283987e5972SCameron Grant i |= channel & 0x3f; 284987e5972SCameron Grant tr_wr(tr, TR_REG_CIR, i, 4); 285987e5972SCameron Grant } 286987e5972SCameron Grant 287987e5972SCameron Grant static void 288987e5972SCameron Grant tr_startch(struct tr_info *tr, char channel) 289987e5972SCameron Grant { 290987e5972SCameron Grant tr_wr(tr, (channel & 0x20)? TR_REG_STARTB : TR_REG_STARTA, 291987e5972SCameron Grant 1<<(channel & 0x1f), 4); 292987e5972SCameron Grant } 293987e5972SCameron Grant 294987e5972SCameron Grant static void 295987e5972SCameron Grant tr_stopch(struct tr_info *tr, char channel) 296987e5972SCameron Grant { 297987e5972SCameron Grant tr_wr(tr, (channel & 0x20)? TR_REG_STOPB : TR_REG_STOPA, 298987e5972SCameron Grant 1<<(channel & 0x1f), 4); 299987e5972SCameron Grant } 300987e5972SCameron Grant 301987e5972SCameron Grant static void 302987e5972SCameron Grant tr_wrch(struct tr_info *tr, char channel, struct tr_chinfo *ch) 303987e5972SCameron Grant { 304987e5972SCameron Grant u_int32_t cr[TR_CHN_REGS], i; 305987e5972SCameron Grant 306987e5972SCameron Grant ch->gvsel &= 0x00000001; 307987e5972SCameron Grant ch->fmc &= 0x00000003; 308987e5972SCameron Grant ch->fms &= 0x0000000f; 309987e5972SCameron Grant ch->ctrl &= 0x0000000f; 310987e5972SCameron Grant ch->pan &= 0x0000007f; 311987e5972SCameron Grant ch->rvol &= 0x0000007f; 312987e5972SCameron Grant ch->cvol &= 0x0000007f; 313987e5972SCameron Grant ch->vol &= 0x000000ff; 314987e5972SCameron Grant ch->ec &= 0x00000fff; 315987e5972SCameron Grant ch->alpha &= 0x00000fff; 316987e5972SCameron Grant ch->delta &= 0x0000ffff; 317987e5972SCameron Grant ch->lba &= 0x3fffffff; 318987e5972SCameron Grant 319987e5972SCameron Grant cr[1]=ch->lba; 320987e5972SCameron Grant cr[3]=(ch->rvol<<7) | (ch->cvol); 321987e5972SCameron Grant cr[4]=(ch->gvsel<<31)|(ch->pan<<24)|(ch->vol<<16)|(ch->ctrl<<12)|(ch->ec); 322987e5972SCameron Grant 323987e5972SCameron Grant switch (tr->type) { 324987e5972SCameron Grant case TDX_PCI_ID: 325987e5972SCameron Grant ch->cso &= 0x0000ffff; 326987e5972SCameron Grant ch->eso &= 0x0000ffff; 327987e5972SCameron Grant cr[0]=(ch->cso<<16) | (ch->alpha<<4) | (ch->fms); 328987e5972SCameron Grant cr[2]=(ch->eso<<16) | (ch->delta); 329987e5972SCameron Grant cr[3]|=0x0000c000; 330987e5972SCameron Grant break; 331987e5972SCameron Grant case TNX_PCI_ID: 332987e5972SCameron Grant ch->cso &= 0x00ffffff; 333987e5972SCameron Grant ch->eso &= 0x00ffffff; 334987e5972SCameron Grant cr[0]=((ch->delta & 0xff)<<24) | (ch->cso); 335987e5972SCameron Grant cr[2]=((ch->delta>>16)<<24) | (ch->eso); 336987e5972SCameron Grant cr[3]|=(ch->alpha<<20) | (ch->fms<<16) | (ch->fmc<<14); 337987e5972SCameron Grant break; 338987e5972SCameron Grant } 339987e5972SCameron Grant tr_selch(tr, channel); 340987e5972SCameron Grant for (i=0; i<TR_CHN_REGS; i++) 341987e5972SCameron Grant tr_wr(tr, TR_REG_CHNBASE+(i<<2), cr[i], 4); 342987e5972SCameron Grant } 343987e5972SCameron Grant 344987e5972SCameron Grant static void 345987e5972SCameron Grant tr_rdch(struct tr_info *tr, char channel, struct tr_chinfo *ch) 346987e5972SCameron Grant { 347987e5972SCameron Grant u_int32_t cr[5], i; 348987e5972SCameron Grant tr_selch(tr, channel); 349987e5972SCameron Grant for (i=0; i<5; i++) cr[i]=tr_rd(tr, TR_REG_CHNBASE+(i<<2), 4); 350987e5972SCameron Grant ch->lba= (cr[1] & 0x3fffffff); 351987e5972SCameron Grant ch->fmc= (cr[3] & 0x0000c000) >> 14; 352987e5972SCameron Grant ch->rvol= (cr[3] & 0x00003f80) >> 7; 353987e5972SCameron Grant ch->cvol= (cr[3] & 0x0000007f); 354987e5972SCameron Grant ch->gvsel= (cr[4] & 0x80000000) >> 31; 355987e5972SCameron Grant ch->pan= (cr[4] & 0x7f000000) >> 24; 356987e5972SCameron Grant ch->vol= (cr[4] & 0x00ff0000) >> 16; 357987e5972SCameron Grant ch->ctrl= (cr[4] & 0x0000f000) >> 12; 358987e5972SCameron Grant ch->ec= (cr[4] & 0x00000fff); 359987e5972SCameron Grant switch(tr->type) { 360987e5972SCameron Grant case TDX_PCI_ID: 361987e5972SCameron Grant ch->cso= (cr[0] & 0xffff0000) >> 16; 362987e5972SCameron Grant ch->alpha= (cr[0] & 0x0000fff0) >> 4; 363987e5972SCameron Grant ch->fms= (cr[0] & 0x0000000f); 364987e5972SCameron Grant ch->eso= (cr[2] & 0xffff0000) >> 16; 365987e5972SCameron Grant ch->delta= (cr[2] & 0x0000ffff); 366987e5972SCameron Grant break; 367987e5972SCameron Grant case TNX_PCI_ID: 368987e5972SCameron Grant ch->cso= (cr[0] & 0x00ffffff); 369987e5972SCameron Grant ch->eso= (cr[2] & 0x00ffffff); 370987e5972SCameron Grant ch->delta= ((cr[2] & 0xff000000) >> 16) | 371987e5972SCameron Grant ((cr[0] & 0xff000000) >> 24); 372987e5972SCameron Grant ch->alpha= (cr[3] & 0xfff00000) >> 20; 373987e5972SCameron Grant ch->fms= (cr[3] & 0x000f0000) >> 16; 374987e5972SCameron Grant break; 375987e5972SCameron Grant } 376987e5972SCameron Grant } 377987e5972SCameron Grant 3780f55ac6cSCameron Grant /* -------------------------------------------------------------------- */ 379987e5972SCameron Grant /* channel interface */ 380987e5972SCameron Grant 3810f55ac6cSCameron Grant static void * 3820f55ac6cSCameron Grant trchan_init(kobj_t obj, void *devinfo, snd_dbuf *b, pcm_channel *c, int dir) 383987e5972SCameron Grant { 384987e5972SCameron Grant struct tr_info *tr = devinfo; 385987e5972SCameron Grant struct tr_chinfo *ch; 386987e5972SCameron Grant if (dir == PCMDIR_PLAY) { 387987e5972SCameron Grant ch = &tr->chinfo[tr->playchns]; 388987e5972SCameron Grant ch->index = tr->playchns++; 389987e5972SCameron Grant } else { 390987e5972SCameron Grant ch = &tr->recchinfo; 391987e5972SCameron Grant ch->index = -1; 392987e5972SCameron Grant } 393987e5972SCameron Grant ch->buffer = b; 394987e5972SCameron Grant ch->buffer->bufsize = TR_BUFFSIZE; 395987e5972SCameron Grant ch->parent = tr; 396987e5972SCameron Grant ch->channel = c; 397987e5972SCameron Grant if (chn_allocbuf(ch->buffer, tr->parent_dmat) == -1) return NULL; 398987e5972SCameron Grant else return ch; 399987e5972SCameron Grant } 400987e5972SCameron Grant 401987e5972SCameron Grant static int 4020f55ac6cSCameron Grant trchan_setdir(kobj_t obj, void *data, int dir) 403987e5972SCameron Grant { 404987e5972SCameron Grant struct tr_chinfo *ch = data; 405987e5972SCameron Grant struct tr_info *tr = ch->parent; 406987e5972SCameron Grant if (dir == PCMDIR_PLAY && ch->index >= 0) { 407987e5972SCameron Grant ch->fmc = ch->fms = ch->ec = ch->alpha = 0; 408987e5972SCameron Grant ch->lba = vtophys(ch->buffer->buf); 409987e5972SCameron Grant ch->cso = 0; 410987e5972SCameron Grant ch->eso = ch->buffer->bufsize - 1; 411987e5972SCameron Grant ch->rvol = ch->cvol = 0; 412987e5972SCameron Grant ch->gvsel = 0; 413987e5972SCameron Grant ch->pan = 0; 414987e5972SCameron Grant ch->vol = 0; 415987e5972SCameron Grant ch->ctrl = 0x01; 416987e5972SCameron Grant ch->delta = 0; 417987e5972SCameron Grant tr_wrch(tr, ch->index, ch); 418987e5972SCameron Grant tr_enaint(tr, ch->index, 1); 419987e5972SCameron Grant } else if (dir == PCMDIR_REC && ch->index == -1) { 420987e5972SCameron Grant /* set up dma mode regs */ 421987e5972SCameron Grant u_int32_t i; 422987e5972SCameron Grant tr_wr(tr, TR_REG_DMAR15, 0, 1); 423987e5972SCameron Grant i = tr_rd(tr, TR_REG_DMAR11, 1) & 0x03; 424987e5972SCameron Grant tr_wr(tr, TR_REG_DMAR11, i | 0x54, 1); 425987e5972SCameron Grant /* set up base address */ 426987e5972SCameron Grant tr_wr(tr, TR_REG_DMAR0, vtophys(ch->buffer->buf), 4); 427987e5972SCameron Grant /* set up buffer size */ 428987e5972SCameron Grant i = tr_rd(tr, TR_REG_DMAR4, 4) & ~0x00ffffff; 429987e5972SCameron Grant tr_wr(tr, TR_REG_DMAR4, i | (ch->buffer->bufsize - 1), 4); 430987e5972SCameron Grant } else return -1; 431987e5972SCameron Grant return 0; 432987e5972SCameron Grant } 433987e5972SCameron Grant 434987e5972SCameron Grant static int 4350f55ac6cSCameron Grant trchan_setformat(kobj_t obj, void *data, u_int32_t format) 436987e5972SCameron Grant { 437987e5972SCameron Grant struct tr_chinfo *ch = data; 438987e5972SCameron Grant struct tr_info *tr = ch->parent; 439987e5972SCameron Grant u_int32_t bits = tr_fmttobits(format); 440987e5972SCameron Grant 44135f9e4a1SCameron Grant ch->ss = 1; 44235f9e4a1SCameron Grant ch->ss <<= (format & AFMT_STEREO)? 1 : 0; 44335f9e4a1SCameron Grant ch->ss <<= (format & AFMT_16BIT)? 1 : 0; 444987e5972SCameron Grant if (ch->index >= 0) { 445987e5972SCameron Grant tr_rdch(tr, ch->index, ch); 44635f9e4a1SCameron Grant ch->eso = (ch->buffer->bufsize / ch->ss) - 1; 447987e5972SCameron Grant ch->ctrl = bits | 0x01; 448987e5972SCameron Grant tr_wrch(tr, ch->index, ch); 449987e5972SCameron Grant } else { 450987e5972SCameron Grant u_int32_t i; 451987e5972SCameron Grant /* set # of samples between interrupts */ 452987e5972SCameron Grant i = (TR_INTSAMPLES >> ((bits & 0x08)? 1 : 0)) - 1; 453987e5972SCameron Grant tr_wr(tr, TR_REG_SBBL, i | (i << 16), 4); 454987e5972SCameron Grant /* set sample format */ 455987e5972SCameron Grant i = 0x18 | (bits << 4); 456987e5972SCameron Grant tr_wr(tr, TR_REG_SBCTRL, i, 1); 457987e5972SCameron Grant } 458987e5972SCameron Grant return 0; 459987e5972SCameron Grant } 460987e5972SCameron Grant 461987e5972SCameron Grant static int 4620f55ac6cSCameron Grant trchan_setspeed(kobj_t obj, void *data, u_int32_t speed) 463987e5972SCameron Grant { 464987e5972SCameron Grant struct tr_chinfo *ch = data; 465987e5972SCameron Grant struct tr_info *tr = ch->parent; 466987e5972SCameron Grant 467987e5972SCameron Grant if (ch->index >= 0) { 468987e5972SCameron Grant tr_rdch(tr, ch->index, ch); 469987e5972SCameron Grant ch->delta = (speed << 12) / 48000; 470987e5972SCameron Grant tr_wrch(tr, ch->index, ch); 471987e5972SCameron Grant return (ch->delta * 48000) >> 12; 472987e5972SCameron Grant } else { 473987e5972SCameron Grant /* setup speed */ 474987e5972SCameron Grant ch->delta = (48000 << 12) / speed; 475987e5972SCameron Grant tr_wr(tr, TR_REG_SBDELTA, ch->delta, 2); 476987e5972SCameron Grant return (48000 << 12) / ch->delta; 477987e5972SCameron Grant } 478987e5972SCameron Grant return 0; 479987e5972SCameron Grant } 480987e5972SCameron Grant 481987e5972SCameron Grant static int 4820f55ac6cSCameron Grant trchan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize) 483987e5972SCameron Grant { 484987e5972SCameron Grant struct tr_chinfo *ch = data; 485987e5972SCameron Grant return ch->buffer->bufsize / 2; 486987e5972SCameron Grant } 487987e5972SCameron Grant 488987e5972SCameron Grant static int 4890f55ac6cSCameron Grant trchan_trigger(kobj_t obj, void *data, int go) 490987e5972SCameron Grant { 491987e5972SCameron Grant struct tr_chinfo *ch = data; 492987e5972SCameron Grant struct tr_info *tr = ch->parent; 493674c45bdSCameron Grant 49435f9e4a1SCameron Grant if (go == PCMTRIG_EMLDMAWR || go == PCMTRIG_EMLDMARD) 49535f9e4a1SCameron Grant return 0; 49635f9e4a1SCameron Grant 497987e5972SCameron Grant if (ch->index >= 0) { 498674c45bdSCameron Grant if (go == PCMTRIG_START) { 499674c45bdSCameron Grant tr_rdch(tr, ch->index, ch); 500674c45bdSCameron Grant ch->cso = 0; 501674c45bdSCameron Grant tr_wrch(tr, ch->index, ch); 502674c45bdSCameron Grant tr_startch(tr, ch->index); 503674c45bdSCameron Grant } else tr_stopch(tr, ch->index); 504987e5972SCameron Grant } else { 505987e5972SCameron Grant u_int32_t i = tr_rd(tr, TR_REG_SBCTRL, 1) & ~7; 506987e5972SCameron Grant tr_wr(tr, TR_REG_SBCTRL, i | (go == PCMTRIG_START)? 1 : 0, 1); 507987e5972SCameron Grant } 508987e5972SCameron Grant return 0; 509987e5972SCameron Grant } 510987e5972SCameron Grant 511987e5972SCameron Grant static int 5120f55ac6cSCameron Grant trchan_getptr(kobj_t obj, void *data) 513987e5972SCameron Grant { 514987e5972SCameron Grant struct tr_chinfo *ch = data; 515987e5972SCameron Grant struct tr_info *tr = ch->parent; 51635f9e4a1SCameron Grant 517987e5972SCameron Grant if (ch->index >= 0) { 518987e5972SCameron Grant tr_rdch(tr, ch->index, ch); 51935f9e4a1SCameron Grant return ch->cso * ch->ss; 520987e5972SCameron Grant } else return tr_rd(tr, TR_REG_DMAR0, 4) - vtophys(ch->buffer->buf); 521987e5972SCameron Grant } 522987e5972SCameron Grant 523987e5972SCameron Grant static pcmchan_caps * 5240f55ac6cSCameron Grant trchan_getcaps(kobj_t obj, void *data) 525987e5972SCameron Grant { 526987e5972SCameron Grant struct tr_chinfo *ch = data; 527987e5972SCameron Grant return (ch->index >= 0)? &tr_playcaps : &tr_reccaps; 528987e5972SCameron Grant } 529987e5972SCameron Grant 5300f55ac6cSCameron Grant static kobj_method_t trchan_methods[] = { 5310f55ac6cSCameron Grant KOBJMETHOD(channel_init, trchan_init), 5320f55ac6cSCameron Grant KOBJMETHOD(channel_setdir, trchan_setdir), 5330f55ac6cSCameron Grant KOBJMETHOD(channel_setformat, trchan_setformat), 5340f55ac6cSCameron Grant KOBJMETHOD(channel_setspeed, trchan_setspeed), 5350f55ac6cSCameron Grant KOBJMETHOD(channel_setblocksize, trchan_setblocksize), 5360f55ac6cSCameron Grant KOBJMETHOD(channel_trigger, trchan_trigger), 5370f55ac6cSCameron Grant KOBJMETHOD(channel_getptr, trchan_getptr), 5380f55ac6cSCameron Grant KOBJMETHOD(channel_getcaps, trchan_getcaps), 5390f55ac6cSCameron Grant { 0, 0 } 5400f55ac6cSCameron Grant }; 5410f55ac6cSCameron Grant CHANNEL_DECLARE(trchan); 5420f55ac6cSCameron Grant 5430f55ac6cSCameron Grant /* -------------------------------------------------------------------- */ 544987e5972SCameron Grant /* The interrupt handler */ 545987e5972SCameron Grant 546987e5972SCameron Grant static void 547987e5972SCameron Grant tr_intr(void *p) 548987e5972SCameron Grant { 549987e5972SCameron Grant struct tr_info *tr = (struct tr_info *)p; 550987e5972SCameron Grant u_int32_t intsrc = tr_rd(tr, TR_REG_MISCINT, 4); 551987e5972SCameron Grant 552987e5972SCameron Grant if (intsrc & TR_INT_ADDR) { 553987e5972SCameron Grant int i; 554987e5972SCameron Grant for (i = 0; i < tr->playchns; i++) { 555987e5972SCameron Grant if (tr_testint(tr, i)) { 556987e5972SCameron Grant chn_intr(tr->chinfo[i].channel); 557987e5972SCameron Grant tr_clrint(tr, i); 558987e5972SCameron Grant } 559987e5972SCameron Grant } 560987e5972SCameron Grant } 561987e5972SCameron Grant if (intsrc & TR_INT_SB) { 562987e5972SCameron Grant chn_intr(tr->recchinfo.channel); 563987e5972SCameron Grant tr_rd(tr, TR_REG_SBR9, 1); 564987e5972SCameron Grant tr_rd(tr, TR_REG_SBR10, 1); 565987e5972SCameron Grant } 566987e5972SCameron Grant } 567987e5972SCameron Grant 568987e5972SCameron Grant /* -------------------------------------------------------------------- */ 569987e5972SCameron Grant 570987e5972SCameron Grant /* 571987e5972SCameron Grant * Probe and attach the card 572987e5972SCameron Grant */ 573987e5972SCameron Grant 574987e5972SCameron Grant static int 575987e5972SCameron Grant tr_init(struct tr_info *tr) 576987e5972SCameron Grant { 577987e5972SCameron Grant if (tr->type == TDX_PCI_ID) { 578987e5972SCameron Grant tr_wr(tr, TDX_REG_CODECST, TDX_CDC_ON, 4); 579987e5972SCameron Grant } else tr_wr(tr, TNX_REG_CODECST, TNX_CDC_ON, 4); 580987e5972SCameron Grant 581987e5972SCameron Grant tr_wr(tr, TR_REG_CIR, TR_CIR_MIDENA | TR_CIR_ADDRENA, 4); 582987e5972SCameron Grant tr->playchns = 0; 583987e5972SCameron Grant return 0; 584987e5972SCameron Grant } 585987e5972SCameron Grant 586987e5972SCameron Grant static int 587987e5972SCameron Grant tr_pci_probe(device_t dev) 588987e5972SCameron Grant { 589987e5972SCameron Grant if (pci_get_devid(dev) == TDX_PCI_ID) { 590987e5972SCameron Grant device_set_desc(dev, "Trident 4DWave DX"); 591987e5972SCameron Grant return 0; 592987e5972SCameron Grant } 593987e5972SCameron Grant if (pci_get_devid(dev) == TNX_PCI_ID) { 594987e5972SCameron Grant device_set_desc(dev, "Trident 4DWave NX"); 595987e5972SCameron Grant return 0; 596987e5972SCameron Grant } 597987e5972SCameron Grant 598987e5972SCameron Grant return ENXIO; 599987e5972SCameron Grant } 600987e5972SCameron Grant 601987e5972SCameron Grant static int 602987e5972SCameron Grant tr_pci_attach(device_t dev) 603987e5972SCameron Grant { 604987e5972SCameron Grant u_int32_t data; 605987e5972SCameron Grant struct tr_info *tr; 6067dfc9325SCameron Grant struct ac97_info *codec = 0; 607987e5972SCameron Grant int i; 608987e5972SCameron Grant int mapped; 609987e5972SCameron Grant char status[SND_STATUSLEN]; 610987e5972SCameron Grant 611987e5972SCameron Grant if ((tr = malloc(sizeof(*tr), M_DEVBUF, M_NOWAIT)) == NULL) { 612987e5972SCameron Grant device_printf(dev, "cannot allocate softc\n"); 613987e5972SCameron Grant return ENXIO; 614987e5972SCameron Grant } 615987e5972SCameron Grant 616987e5972SCameron Grant bzero(tr, sizeof(*tr)); 617987e5972SCameron Grant tr->type = pci_get_devid(dev); 618987e5972SCameron Grant 619987e5972SCameron Grant data = pci_read_config(dev, PCIR_COMMAND, 2); 620987e5972SCameron Grant data |= (PCIM_CMD_PORTEN|PCIM_CMD_MEMEN|PCIM_CMD_BUSMASTEREN); 621987e5972SCameron Grant pci_write_config(dev, PCIR_COMMAND, data, 2); 622987e5972SCameron Grant data = pci_read_config(dev, PCIR_COMMAND, 2); 623987e5972SCameron Grant 624987e5972SCameron Grant mapped = 0; 625987e5972SCameron Grant /* XXX dfr: is this strictly necessary? */ 626987e5972SCameron Grant for (i = 0; (mapped == 0) && (i < PCI_MAXMAPS_0); i++) { 627987e5972SCameron Grant tr->regid = PCIR_MAPS + i*4; 628987e5972SCameron Grant tr->regtype = SYS_RES_MEMORY; 629987e5972SCameron Grant tr->reg = bus_alloc_resource(dev, tr->regtype, &tr->regid, 630987e5972SCameron Grant 0, ~0, 1, RF_ACTIVE); 631987e5972SCameron Grant if (!tr->reg) { 632987e5972SCameron Grant tr->regtype = SYS_RES_IOPORT; 633987e5972SCameron Grant tr->reg = bus_alloc_resource(dev, tr->regtype, 634987e5972SCameron Grant &tr->regid, 0, ~0, 1, 635987e5972SCameron Grant RF_ACTIVE); 636987e5972SCameron Grant } 637987e5972SCameron Grant if (tr->reg) { 638987e5972SCameron Grant tr->st = rman_get_bustag(tr->reg); 639987e5972SCameron Grant tr->sh = rman_get_bushandle(tr->reg); 640987e5972SCameron Grant mapped++; 641987e5972SCameron Grant } 642987e5972SCameron Grant } 643987e5972SCameron Grant 644987e5972SCameron Grant if (mapped == 0) { 645987e5972SCameron Grant device_printf(dev, "unable to map register space\n"); 646987e5972SCameron Grant goto bad; 647987e5972SCameron Grant } 648987e5972SCameron Grant 649987e5972SCameron Grant if (tr_init(tr) == -1) { 650987e5972SCameron Grant device_printf(dev, "unable to initialize the card\n"); 651987e5972SCameron Grant goto bad; 652987e5972SCameron Grant } 653987e5972SCameron Grant 6540f55ac6cSCameron Grant codec = AC97_CREATE(dev, tr, tr_ac97); 655987e5972SCameron Grant if (codec == NULL) goto bad; 6560f55ac6cSCameron Grant if (mixer_init(dev, ac97_getmixerclass(), codec) == -1) goto bad; 657987e5972SCameron Grant 658987e5972SCameron Grant tr->irqid = 0; 659987e5972SCameron Grant tr->irq = bus_alloc_resource(dev, SYS_RES_IRQ, &tr->irqid, 660987e5972SCameron Grant 0, ~0, 1, RF_ACTIVE | RF_SHAREABLE); 661987e5972SCameron Grant if (!tr->irq || 662987e5972SCameron Grant bus_setup_intr(dev, tr->irq, INTR_TYPE_TTY, tr_intr, tr, &tr->ih)) { 663987e5972SCameron Grant device_printf(dev, "unable to map interrupt\n"); 664987e5972SCameron Grant goto bad; 665987e5972SCameron Grant } 666987e5972SCameron Grant 667987e5972SCameron Grant if (bus_dma_tag_create(/*parent*/NULL, /*alignment*/2, /*boundary*/0, 668987e5972SCameron Grant /*lowaddr*/BUS_SPACE_MAXADDR_32BIT, 669987e5972SCameron Grant /*highaddr*/BUS_SPACE_MAXADDR, 670987e5972SCameron Grant /*filter*/NULL, /*filterarg*/NULL, 671987e5972SCameron Grant /*maxsize*/TR_BUFFSIZE, /*nsegments*/1, /*maxsegz*/0x3ffff, 672987e5972SCameron Grant /*flags*/0, &tr->parent_dmat) != 0) { 673987e5972SCameron Grant device_printf(dev, "unable to create dma tag\n"); 674987e5972SCameron Grant goto bad; 675987e5972SCameron Grant } 676987e5972SCameron Grant 677987e5972SCameron Grant snprintf(status, 64, "at %s 0x%lx irq %ld", 678987e5972SCameron Grant (tr->regtype == SYS_RES_IOPORT)? "io" : "memory", 679987e5972SCameron Grant rman_get_start(tr->reg), rman_get_start(tr->irq)); 680987e5972SCameron Grant 681987e5972SCameron Grant if (pcm_register(dev, tr, TR_MAXPLAYCH, 1)) goto bad; 6820f55ac6cSCameron Grant pcm_addchan(dev, PCMDIR_REC, &trchan_class, tr); 683987e5972SCameron Grant for (i = 0; i < TR_MAXPLAYCH; i++) 6840f55ac6cSCameron Grant pcm_addchan(dev, PCMDIR_PLAY, &trchan_class, tr); 685987e5972SCameron Grant pcm_setstatus(dev, status); 686987e5972SCameron Grant 687987e5972SCameron Grant return 0; 688987e5972SCameron Grant 689987e5972SCameron Grant bad: 690306f91b6SCameron Grant if (codec) ac97_destroy(codec); 691987e5972SCameron Grant if (tr->reg) bus_release_resource(dev, tr->regtype, tr->regid, tr->reg); 692987e5972SCameron Grant if (tr->ih) bus_teardown_intr(dev, tr->irq, tr->ih); 693987e5972SCameron Grant if (tr->irq) bus_release_resource(dev, SYS_RES_IRQ, tr->irqid, tr->irq); 694306f91b6SCameron Grant if (tr->parent_dmat) bus_dma_tag_destroy(tr->parent_dmat); 695987e5972SCameron Grant free(tr, M_DEVBUF); 696987e5972SCameron Grant return ENXIO; 697987e5972SCameron Grant } 698987e5972SCameron Grant 699306f91b6SCameron Grant static int 700306f91b6SCameron Grant tr_pci_detach(device_t dev) 701306f91b6SCameron Grant { 702306f91b6SCameron Grant int r; 703306f91b6SCameron Grant struct tr_info *tr; 704306f91b6SCameron Grant 705306f91b6SCameron Grant r = pcm_unregister(dev); 706306f91b6SCameron Grant if (r) 707306f91b6SCameron Grant return r; 708306f91b6SCameron Grant 709306f91b6SCameron Grant tr = pcm_getdevinfo(dev); 710306f91b6SCameron Grant bus_release_resource(dev, tr->regtype, tr->regid, tr->reg); 711306f91b6SCameron Grant bus_teardown_intr(dev, tr->irq, tr->ih); 712306f91b6SCameron Grant bus_release_resource(dev, SYS_RES_IRQ, tr->irqid, tr->irq); 713306f91b6SCameron Grant bus_dma_tag_destroy(tr->parent_dmat); 714306f91b6SCameron Grant free(tr, M_DEVBUF); 715306f91b6SCameron Grant 716306f91b6SCameron Grant return 0; 717306f91b6SCameron Grant } 718306f91b6SCameron Grant 719987e5972SCameron Grant static device_method_t tr_methods[] = { 720987e5972SCameron Grant /* Device interface */ 721987e5972SCameron Grant DEVMETHOD(device_probe, tr_pci_probe), 722987e5972SCameron Grant DEVMETHOD(device_attach, tr_pci_attach), 723306f91b6SCameron Grant DEVMETHOD(device_detach, tr_pci_detach), 724987e5972SCameron Grant 725987e5972SCameron Grant { 0, 0 } 726987e5972SCameron Grant }; 727987e5972SCameron Grant 728987e5972SCameron Grant static driver_t tr_driver = { 729987e5972SCameron Grant "pcm", 730987e5972SCameron Grant tr_methods, 731987e5972SCameron Grant sizeof(snddev_info), 732987e5972SCameron Grant }; 733987e5972SCameron Grant 734987e5972SCameron Grant static devclass_t pcm_devclass; 735987e5972SCameron Grant 736f314f3daSCameron Grant DRIVER_MODULE(snd_t4dwave, pci, tr_driver, pcm_devclass, 0, 0); 737f314f3daSCameron Grant MODULE_DEPEND(snd_t4dwave, snd_pcm, PCM_MINVER, PCM_PREFVER, PCM_MAXVER); 738f314f3daSCameron Grant MODULE_VERSION(snd_t4dwave, 1); 739