1098ca2bdSWarner Losh /*- 23f225978SCameron Grant * Copyright (c) 1999 Cameron Grant <cg@freebsd.org> 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 */ 26987e5972SCameron Grant 2790da2b28SAriff Abdullah #ifdef HAVE_KERNEL_OPTION_HEADERS 2890da2b28SAriff Abdullah #include "opt_snd.h" 2990da2b28SAriff Abdullah #endif 3090da2b28SAriff Abdullah 31ef9308b1SCameron Grant #include <dev/sound/pcm/sound.h> 32ef9308b1SCameron Grant #include <dev/sound/pcm/ac97.h> 33ef9308b1SCameron Grant #include <dev/sound/pci/t4dwave.h> 34987e5972SCameron Grant 3590cf0136SWarner Losh #include <dev/pci/pcireg.h> 3690cf0136SWarner Losh #include <dev/pci/pcivar.h> 37987e5972SCameron Grant 3867b1dce3SCameron Grant SND_DECLARE_FILE("$FreeBSD$"); 3967b1dce3SCameron Grant 40987e5972SCameron Grant /* -------------------------------------------------------------------- */ 41987e5972SCameron Grant 42dedf4424SCameron Grant #define TDX_PCI_ID 0x20001023 43dedf4424SCameron Grant #define TNX_PCI_ID 0x20011023 440a1e38d3SGeorge C A Reid #define ALI_PCI_ID 0x545110b9 45874f2e78SGeorge C A Reid #define SPA_PCI_ID 0x70181039 46dedf4424SCameron Grant 4739dbd126SCameron Grant #define TR_DEFAULT_BUFSZ 0x1000 48eb811915SMarius Strobl /* For ALi M5451 the DMA transfer size appears to be fixed to 64k. */ 49eb811915SMarius Strobl #define ALI_BUFSZ 0x10000 50eb811915SMarius Strobl #define TR_BUFALGN 0x8 51dedf4424SCameron Grant #define TR_TIMEOUT_CDC 0xffff 52eb811915SMarius Strobl #define TR_MAXHWCH 64 53eb811915SMarius Strobl #define ALI_MAXHWCH 32 54dedf4424SCameron Grant #define TR_MAXPLAYCH 4 55eb811915SMarius Strobl #define ALI_MAXPLAYCH 1 56a2f4d2f5SPyun YongHyeon /* 57eb811915SMarius Strobl * Though, it's not clearly documented in the 4DWAVE datasheet, the 58eb811915SMarius Strobl * DX and NX chips can't handle DMA addresses located above 1GB as the 59eb811915SMarius Strobl * LBA (loop begin address) register which holds the DMA base address 60eb811915SMarius Strobl * is 32-bit, but the two MSBs are used for other purposes. 61a2f4d2f5SPyun YongHyeon */ 62eb811915SMarius Strobl #define TR_MAXADDR ((1U << 30) - 1) 63eb811915SMarius Strobl #define ALI_MAXADDR ((1U << 31) - 1) 64dedf4424SCameron Grant 65987e5972SCameron Grant struct tr_info; 66987e5972SCameron Grant 67987e5972SCameron Grant /* channel registers */ 68987e5972SCameron Grant struct tr_chinfo { 69987e5972SCameron Grant u_int32_t cso, alpha, fms, fmc, ec; 70987e5972SCameron Grant u_int32_t lba; 71987e5972SCameron Grant u_int32_t eso, delta; 72987e5972SCameron Grant u_int32_t rvol, cvol; 73987e5972SCameron Grant u_int32_t gvsel, pan, vol, ctrl; 74a02ab104SMitsuru IWASAKI u_int32_t active:1, was_active:1; 757ed9700bSCameron Grant int index, bufhalf; 7666ef8af5SCameron Grant struct snd_dbuf *buffer; 7766ef8af5SCameron Grant struct pcm_channel *channel; 78987e5972SCameron Grant struct tr_info *parent; 79987e5972SCameron Grant }; 80987e5972SCameron Grant 81f0fb042eSCameron Grant struct tr_rchinfo { 82f0fb042eSCameron Grant u_int32_t delta; 83a02ab104SMitsuru IWASAKI u_int32_t active:1, was_active:1; 8466ef8af5SCameron Grant struct snd_dbuf *buffer; 8566ef8af5SCameron Grant struct pcm_channel *channel; 86f0fb042eSCameron Grant struct tr_info *parent; 87f0fb042eSCameron Grant }; 88f0fb042eSCameron Grant 89987e5972SCameron Grant /* device private data */ 90987e5972SCameron Grant struct tr_info { 91987e5972SCameron Grant u_int32_t type; 9289ca562eSOlivier Houchard u_int32_t rev; 93987e5972SCameron Grant 94987e5972SCameron Grant bus_space_tag_t st; 95987e5972SCameron Grant bus_space_handle_t sh; 96987e5972SCameron Grant bus_dma_tag_t parent_dmat; 97987e5972SCameron Grant 98987e5972SCameron Grant struct resource *reg, *irq; 99987e5972SCameron Grant int regtype, regid, irqid; 100987e5972SCameron Grant void *ih; 101987e5972SCameron Grant 10200acb133SCameron Grant struct mtx *lock; 10366ef8af5SCameron Grant 104eb811915SMarius Strobl u_int32_t hwchns; 105987e5972SCameron Grant u_int32_t playchns; 10639dbd126SCameron Grant unsigned int bufsz; 10739dbd126SCameron Grant 108987e5972SCameron Grant struct tr_chinfo chinfo[TR_MAXPLAYCH]; 109f0fb042eSCameron Grant struct tr_rchinfo recchinfo; 110987e5972SCameron Grant }; 111987e5972SCameron Grant 112987e5972SCameron Grant /* -------------------------------------------------------------------- */ 113987e5972SCameron Grant 114513693beSCameron Grant static u_int32_t tr_recfmt[] = { 11590da2b28SAriff Abdullah SND_FORMAT(AFMT_U8, 1, 0), 11690da2b28SAriff Abdullah SND_FORMAT(AFMT_U8, 2, 0), 11790da2b28SAriff Abdullah SND_FORMAT(AFMT_S8, 1, 0), 11890da2b28SAriff Abdullah SND_FORMAT(AFMT_S8, 2, 0), 11990da2b28SAriff Abdullah SND_FORMAT(AFMT_S16_LE, 1, 0), 12090da2b28SAriff Abdullah SND_FORMAT(AFMT_S16_LE, 2, 0), 12190da2b28SAriff Abdullah SND_FORMAT(AFMT_U16_LE, 1, 0), 12290da2b28SAriff Abdullah SND_FORMAT(AFMT_U16_LE, 2, 0), 123513693beSCameron Grant 0 124987e5972SCameron Grant }; 12566ef8af5SCameron Grant static struct pcmchan_caps tr_reccaps = {4000, 48000, tr_recfmt, 0}; 126987e5972SCameron Grant 127513693beSCameron Grant static u_int32_t tr_playfmt[] = { 12890da2b28SAriff Abdullah SND_FORMAT(AFMT_U8, 1, 0), 12990da2b28SAriff Abdullah SND_FORMAT(AFMT_U8, 2, 0), 13090da2b28SAriff Abdullah SND_FORMAT(AFMT_S8, 1, 0), 13190da2b28SAriff Abdullah SND_FORMAT(AFMT_S8, 2, 0), 13290da2b28SAriff Abdullah SND_FORMAT(AFMT_S16_LE, 1, 0), 13390da2b28SAriff Abdullah SND_FORMAT(AFMT_S16_LE, 2, 0), 13490da2b28SAriff Abdullah SND_FORMAT(AFMT_U16_LE, 1, 0), 13590da2b28SAriff Abdullah SND_FORMAT(AFMT_U16_LE, 2, 0), 136513693beSCameron Grant 0 137987e5972SCameron Grant }; 13866ef8af5SCameron Grant static struct pcmchan_caps tr_playcaps = {4000, 48000, tr_playfmt, 0}; 139987e5972SCameron Grant 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) { 185874f2e78SGeorge C A Reid case SPA_PCI_ID: 186874f2e78SGeorge C A Reid treg=SPA_REG_CODECRD; 187874f2e78SGeorge C A Reid trw=SPA_CDC_RWSTAT; 188874f2e78SGeorge C A Reid break; 1890a1e38d3SGeorge C A Reid case ALI_PCI_ID: 19089ca562eSOlivier Houchard if (tr->rev > 0x01) 19189ca562eSOlivier Houchard treg=TDX_REG_CODECWR; 19289ca562eSOlivier Houchard else 19389ca562eSOlivier Houchard treg=TDX_REG_CODECRD; 19489ca562eSOlivier Houchard trw=TDX_CDC_RWSTAT; 19589ca562eSOlivier Houchard break; 196987e5972SCameron Grant case TDX_PCI_ID: 197987e5972SCameron Grant treg=TDX_REG_CODECRD; 198987e5972SCameron Grant trw=TDX_CDC_RWSTAT; 199987e5972SCameron Grant break; 200987e5972SCameron Grant case TNX_PCI_ID: 201987e5972SCameron Grant treg=(regno & 0x100)? TNX_REG_CODEC2RD : TNX_REG_CODEC1RD; 202987e5972SCameron Grant trw=TNX_CDC_RWSTAT; 203987e5972SCameron Grant break; 204987e5972SCameron Grant default: 205987e5972SCameron Grant printf("!!! tr_rdcd defaulted !!!\n"); 2060f55ac6cSCameron Grant return -1; 207987e5972SCameron Grant } 208987e5972SCameron Grant 209e9be79d6SMarcel Moolenaar i = j = 0; 210e9be79d6SMarcel Moolenaar 211987e5972SCameron Grant regno &= 0x7f; 21266ef8af5SCameron Grant snd_mtxlock(tr->lock); 21389ca562eSOlivier Houchard if (tr->type == ALI_PCI_ID) { 21489ca562eSOlivier Houchard u_int32_t chk1, chk2; 21589ca562eSOlivier Houchard j = trw; 21689ca562eSOlivier Houchard for (i = TR_TIMEOUT_CDC; (i > 0) && (j & trw); i--) 21789ca562eSOlivier Houchard j = tr_rd(tr, treg, 4); 21889ca562eSOlivier Houchard if (i > 0) { 21989ca562eSOlivier Houchard chk1 = tr_rd(tr, 0xc8, 4); 22089ca562eSOlivier Houchard chk2 = tr_rd(tr, 0xc8, 4); 22189ca562eSOlivier Houchard for (i = TR_TIMEOUT_CDC; (i > 0) && (chk1 == chk2); 22289ca562eSOlivier Houchard i--) 22389ca562eSOlivier Houchard chk2 = tr_rd(tr, 0xc8, 4); 22489ca562eSOlivier Houchard } 22589ca562eSOlivier Houchard } 22689ca562eSOlivier Houchard if (tr->type != ALI_PCI_ID || i > 0) { 227987e5972SCameron Grant tr_wr(tr, treg, regno | trw, 4); 228987e5972SCameron Grant j=trw; 22989ca562eSOlivier Houchard for (i=TR_TIMEOUT_CDC; (i > 0) && (j & trw); i--) 23089ca562eSOlivier Houchard j=tr_rd(tr, treg, 4); 23189ca562eSOlivier Houchard } 23266ef8af5SCameron Grant snd_mtxunlock(tr->lock); 233987e5972SCameron Grant if (i == 0) printf("codec timeout during read of register %x\n", regno); 234987e5972SCameron Grant return (j >> TR_CDC_DATA) & 0xffff; 235987e5972SCameron Grant } 236987e5972SCameron Grant 2370f55ac6cSCameron Grant static int 2380f55ac6cSCameron Grant tr_wrcd(kobj_t obj, void *devinfo, int regno, u_int32_t data) 239987e5972SCameron Grant { 240987e5972SCameron Grant struct tr_info *tr = (struct tr_info *)devinfo; 241987e5972SCameron Grant int i, j, treg, trw; 242987e5972SCameron Grant 243987e5972SCameron Grant switch (tr->type) { 244874f2e78SGeorge C A Reid case SPA_PCI_ID: 245874f2e78SGeorge C A Reid treg=SPA_REG_CODECWR; 246874f2e78SGeorge C A Reid trw=SPA_CDC_RWSTAT; 247874f2e78SGeorge C A Reid break; 2480a1e38d3SGeorge C A Reid case ALI_PCI_ID: 249987e5972SCameron Grant case TDX_PCI_ID: 250987e5972SCameron Grant treg=TDX_REG_CODECWR; 251987e5972SCameron Grant trw=TDX_CDC_RWSTAT; 252987e5972SCameron Grant break; 253987e5972SCameron Grant case TNX_PCI_ID: 254987e5972SCameron Grant treg=TNX_REG_CODECWR; 255987e5972SCameron Grant trw=TNX_CDC_RWSTAT | ((regno & 0x100)? TNX_CDC_SEC : 0); 256987e5972SCameron Grant break; 257987e5972SCameron Grant default: 258987e5972SCameron Grant printf("!!! tr_wrcd defaulted !!!"); 2590f55ac6cSCameron Grant return -1; 260987e5972SCameron Grant } 261987e5972SCameron Grant 262e9be79d6SMarcel Moolenaar i = 0; 263e9be79d6SMarcel Moolenaar 264987e5972SCameron Grant regno &= 0x7f; 265987e5972SCameron Grant #if 0 266987e5972SCameron Grant printf("tr_wrcd: reg %x was %x", regno, tr_rdcd(devinfo, regno)); 267987e5972SCameron Grant #endif 268987e5972SCameron Grant j=trw; 26966ef8af5SCameron Grant snd_mtxlock(tr->lock); 27089ca562eSOlivier Houchard if (tr->type == ALI_PCI_ID) { 27189ca562eSOlivier Houchard j = trw; 27289ca562eSOlivier Houchard for (i = TR_TIMEOUT_CDC; (i > 0) && (j & trw); i--) 27389ca562eSOlivier Houchard j = tr_rd(tr, treg, 4); 27489ca562eSOlivier Houchard if (i > 0) { 27589ca562eSOlivier Houchard u_int32_t chk1, chk2; 27689ca562eSOlivier Houchard chk1 = tr_rd(tr, 0xc8, 4); 27789ca562eSOlivier Houchard chk2 = tr_rd(tr, 0xc8, 4); 27889ca562eSOlivier Houchard for (i = TR_TIMEOUT_CDC; (i > 0) && (chk1 == chk2); 27989ca562eSOlivier Houchard i--) 28089ca562eSOlivier Houchard chk2 = tr_rd(tr, 0xc8, 4); 28189ca562eSOlivier Houchard } 28289ca562eSOlivier Houchard } 28389ca562eSOlivier Houchard if (tr->type != ALI_PCI_ID || i > 0) { 28489ca562eSOlivier Houchard for (i=TR_TIMEOUT_CDC; (i>0) && (j & trw); i--) 28589ca562eSOlivier Houchard j=tr_rd(tr, treg, 4); 28689ca562eSOlivier Houchard if (tr->type == ALI_PCI_ID && tr->rev > 0x01) 28789ca562eSOlivier Houchard trw |= 0x0100; 288987e5972SCameron Grant tr_wr(tr, treg, (data << TR_CDC_DATA) | regno | trw, 4); 28989ca562eSOlivier Houchard } 290987e5972SCameron Grant #if 0 291987e5972SCameron Grant printf(" - wrote %x, now %x\n", data, tr_rdcd(devinfo, regno)); 292987e5972SCameron Grant #endif 29366ef8af5SCameron Grant snd_mtxunlock(tr->lock); 294987e5972SCameron Grant if (i==0) printf("codec timeout writing %x, data %x\n", regno, data); 2950f55ac6cSCameron Grant return (i > 0)? 0 : -1; 296987e5972SCameron Grant } 297987e5972SCameron Grant 2980f55ac6cSCameron Grant static kobj_method_t tr_ac97_methods[] = { 2990f55ac6cSCameron Grant KOBJMETHOD(ac97_read, tr_rdcd), 3000f55ac6cSCameron Grant KOBJMETHOD(ac97_write, tr_wrcd), 30190da2b28SAriff Abdullah KOBJMETHOD_END 3020f55ac6cSCameron Grant }; 3030f55ac6cSCameron Grant AC97_DECLARE(tr_ac97); 3040f55ac6cSCameron Grant 3050f55ac6cSCameron Grant /* -------------------------------------------------------------------- */ 306987e5972SCameron Grant /* playback channel interrupts */ 307987e5972SCameron Grant 3087ed9700bSCameron Grant #if 0 309987e5972SCameron Grant static u_int32_t 310f0fb042eSCameron Grant tr_testint(struct tr_chinfo *ch) 311987e5972SCameron Grant { 312f0fb042eSCameron Grant struct tr_info *tr = ch->parent; 313f0fb042eSCameron Grant int bank, chan; 314f0fb042eSCameron Grant 315f0fb042eSCameron Grant bank = (ch->index & 0x20) ? 1 : 0; 316f0fb042eSCameron Grant chan = ch->index & 0x1f; 317f0fb042eSCameron Grant return tr_rd(tr, bank? TR_REG_ADDRINTB : TR_REG_ADDRINTA, 4) & (1 << chan); 318987e5972SCameron Grant } 3197ed9700bSCameron Grant #endif 320987e5972SCameron Grant 321987e5972SCameron Grant static void 322f0fb042eSCameron Grant tr_clrint(struct tr_chinfo *ch) 323987e5972SCameron Grant { 324f0fb042eSCameron Grant struct tr_info *tr = ch->parent; 325f0fb042eSCameron Grant int bank, chan; 326f0fb042eSCameron Grant 327f0fb042eSCameron Grant bank = (ch->index & 0x20) ? 1 : 0; 328f0fb042eSCameron Grant chan = ch->index & 0x1f; 329f0fb042eSCameron Grant tr_wr(tr, bank? TR_REG_ADDRINTB : TR_REG_ADDRINTA, 1 << chan, 4); 330987e5972SCameron Grant } 331987e5972SCameron Grant 332987e5972SCameron Grant static void 333f0fb042eSCameron Grant tr_enaint(struct tr_chinfo *ch, int enable) 334987e5972SCameron Grant { 335f0fb042eSCameron Grant struct tr_info *tr = ch->parent; 336f0fb042eSCameron Grant u_int32_t i, reg; 337f0fb042eSCameron Grant int bank, chan; 338f0fb042eSCameron Grant 33966ef8af5SCameron Grant snd_mtxlock(tr->lock); 340f0fb042eSCameron Grant bank = (ch->index & 0x20) ? 1 : 0; 341f0fb042eSCameron Grant chan = ch->index & 0x1f; 342f0fb042eSCameron Grant reg = bank? TR_REG_INTENB : TR_REG_INTENA; 343f0fb042eSCameron Grant 344f0fb042eSCameron Grant i = tr_rd(tr, reg, 4); 345f0fb042eSCameron Grant i &= ~(1 << chan); 346f0fb042eSCameron Grant i |= (enable? 1 : 0) << chan; 347f0fb042eSCameron Grant 348f0fb042eSCameron Grant tr_clrint(ch); 349987e5972SCameron Grant tr_wr(tr, reg, i, 4); 35066ef8af5SCameron Grant snd_mtxunlock(tr->lock); 351987e5972SCameron Grant } 352987e5972SCameron Grant 353987e5972SCameron Grant /* playback channels */ 354987e5972SCameron Grant 355987e5972SCameron Grant static void 356f0fb042eSCameron Grant tr_selch(struct tr_chinfo *ch) 357987e5972SCameron Grant { 358f0fb042eSCameron Grant struct tr_info *tr = ch->parent; 359f0fb042eSCameron Grant int i; 360f0fb042eSCameron Grant 361f0fb042eSCameron Grant i = tr_rd(tr, TR_REG_CIR, 4); 362987e5972SCameron Grant i &= ~TR_CIR_MASK; 363f0fb042eSCameron Grant i |= ch->index & 0x3f; 364987e5972SCameron Grant tr_wr(tr, TR_REG_CIR, i, 4); 365987e5972SCameron Grant } 366987e5972SCameron Grant 367987e5972SCameron Grant static void 368f0fb042eSCameron Grant tr_startch(struct tr_chinfo *ch) 369987e5972SCameron Grant { 370f0fb042eSCameron Grant struct tr_info *tr = ch->parent; 371f0fb042eSCameron Grant int bank, chan; 372f0fb042eSCameron Grant 373f0fb042eSCameron Grant bank = (ch->index & 0x20) ? 1 : 0; 374f0fb042eSCameron Grant chan = ch->index & 0x1f; 375f0fb042eSCameron Grant tr_wr(tr, bank? TR_REG_STARTB : TR_REG_STARTA, 1 << chan, 4); 376987e5972SCameron Grant } 377987e5972SCameron Grant 378987e5972SCameron Grant static void 379f0fb042eSCameron Grant tr_stopch(struct tr_chinfo *ch) 380987e5972SCameron Grant { 381f0fb042eSCameron Grant struct tr_info *tr = ch->parent; 382f0fb042eSCameron Grant int bank, chan; 383f0fb042eSCameron Grant 384f0fb042eSCameron Grant bank = (ch->index & 0x20) ? 1 : 0; 385f0fb042eSCameron Grant chan = ch->index & 0x1f; 386f0fb042eSCameron Grant tr_wr(tr, bank? TR_REG_STOPB : TR_REG_STOPA, 1 << chan, 4); 387987e5972SCameron Grant } 388987e5972SCameron Grant 389987e5972SCameron Grant static void 390f0fb042eSCameron Grant tr_wrch(struct tr_chinfo *ch) 391987e5972SCameron Grant { 392f0fb042eSCameron Grant struct tr_info *tr = ch->parent; 393987e5972SCameron Grant u_int32_t cr[TR_CHN_REGS], i; 394987e5972SCameron Grant 395987e5972SCameron Grant ch->gvsel &= 0x00000001; 396987e5972SCameron Grant ch->fmc &= 0x00000003; 397987e5972SCameron Grant ch->fms &= 0x0000000f; 398987e5972SCameron Grant ch->ctrl &= 0x0000000f; 399987e5972SCameron Grant ch->pan &= 0x0000007f; 400987e5972SCameron Grant ch->rvol &= 0x0000007f; 401987e5972SCameron Grant ch->cvol &= 0x0000007f; 402987e5972SCameron Grant ch->vol &= 0x000000ff; 403987e5972SCameron Grant ch->ec &= 0x00000fff; 404987e5972SCameron Grant ch->alpha &= 0x00000fff; 405987e5972SCameron Grant ch->delta &= 0x0000ffff; 406eb811915SMarius Strobl if (tr->type == ALI_PCI_ID) 407eb811915SMarius Strobl ch->lba &= ALI_MAXADDR; 408eb811915SMarius Strobl else 409eb811915SMarius Strobl ch->lba &= TR_MAXADDR; 410987e5972SCameron Grant 411987e5972SCameron Grant cr[1]=ch->lba; 4127ed9700bSCameron Grant cr[3]=(ch->fmc<<14) | (ch->rvol<<7) | (ch->cvol); 413987e5972SCameron Grant cr[4]=(ch->gvsel<<31) | (ch->pan<<24) | (ch->vol<<16) | (ch->ctrl<<12) | (ch->ec); 414987e5972SCameron Grant 415987e5972SCameron Grant switch (tr->type) { 416874f2e78SGeorge C A Reid case SPA_PCI_ID: 4170a1e38d3SGeorge C A Reid case ALI_PCI_ID: 418987e5972SCameron Grant case TDX_PCI_ID: 419987e5972SCameron Grant ch->cso &= 0x0000ffff; 420987e5972SCameron Grant ch->eso &= 0x0000ffff; 421987e5972SCameron Grant cr[0]=(ch->cso<<16) | (ch->alpha<<4) | (ch->fms); 422987e5972SCameron Grant cr[2]=(ch->eso<<16) | (ch->delta); 423987e5972SCameron Grant break; 424987e5972SCameron Grant case TNX_PCI_ID: 425987e5972SCameron Grant ch->cso &= 0x00ffffff; 426987e5972SCameron Grant ch->eso &= 0x00ffffff; 427987e5972SCameron Grant cr[0]=((ch->delta & 0xff)<<24) | (ch->cso); 42848875621SCameron Grant cr[2]=((ch->delta>>8)<<24) | (ch->eso); 429987e5972SCameron Grant cr[3]|=(ch->alpha<<20) | (ch->fms<<16) | (ch->fmc<<14); 430987e5972SCameron Grant break; 431987e5972SCameron Grant } 43266ef8af5SCameron Grant snd_mtxlock(tr->lock); 433f0fb042eSCameron Grant tr_selch(ch); 434987e5972SCameron Grant for (i=0; i<TR_CHN_REGS; i++) 435987e5972SCameron Grant tr_wr(tr, TR_REG_CHNBASE+(i<<2), cr[i], 4); 43666ef8af5SCameron Grant snd_mtxunlock(tr->lock); 437987e5972SCameron Grant } 438987e5972SCameron Grant 439987e5972SCameron Grant static void 440f0fb042eSCameron Grant tr_rdch(struct tr_chinfo *ch) 441987e5972SCameron Grant { 442f0fb042eSCameron Grant struct tr_info *tr = ch->parent; 443987e5972SCameron Grant u_int32_t cr[5], i; 444f0fb042eSCameron Grant 44566ef8af5SCameron Grant snd_mtxlock(tr->lock); 446f0fb042eSCameron Grant tr_selch(ch); 447f0fb042eSCameron Grant for (i=0; i<5; i++) 448f0fb042eSCameron Grant cr[i]=tr_rd(tr, TR_REG_CHNBASE+(i<<2), 4); 44966ef8af5SCameron Grant snd_mtxunlock(tr->lock); 450f0fb042eSCameron Grant 451f0fb042eSCameron Grant 452eb811915SMarius Strobl if (tr->type == ALI_PCI_ID) 453eb811915SMarius Strobl ch->lba=(cr[1] & ALI_MAXADDR); 454eb811915SMarius Strobl else 455eb811915SMarius Strobl ch->lba=(cr[1] & TR_MAXADDR); 456987e5972SCameron Grant ch->fmc= (cr[3] & 0x0000c000) >> 14; 457987e5972SCameron Grant ch->rvol= (cr[3] & 0x00003f80) >> 7; 458987e5972SCameron Grant ch->cvol= (cr[3] & 0x0000007f); 459987e5972SCameron Grant ch->gvsel= (cr[4] & 0x80000000) >> 31; 460987e5972SCameron Grant ch->pan= (cr[4] & 0x7f000000) >> 24; 461987e5972SCameron Grant ch->vol= (cr[4] & 0x00ff0000) >> 16; 462987e5972SCameron Grant ch->ctrl= (cr[4] & 0x0000f000) >> 12; 463987e5972SCameron Grant ch->ec= (cr[4] & 0x00000fff); 464987e5972SCameron Grant switch(tr->type) { 465874f2e78SGeorge C A Reid case SPA_PCI_ID: 4660a1e38d3SGeorge C A Reid case ALI_PCI_ID: 467987e5972SCameron Grant case TDX_PCI_ID: 468987e5972SCameron Grant ch->cso= (cr[0] & 0xffff0000) >> 16; 469987e5972SCameron Grant ch->alpha= (cr[0] & 0x0000fff0) >> 4; 470987e5972SCameron Grant ch->fms= (cr[0] & 0x0000000f); 471987e5972SCameron Grant ch->eso= (cr[2] & 0xffff0000) >> 16; 472987e5972SCameron Grant ch->delta= (cr[2] & 0x0000ffff); 473987e5972SCameron Grant break; 474987e5972SCameron Grant case TNX_PCI_ID: 475987e5972SCameron Grant ch->cso= (cr[0] & 0x00ffffff); 476987e5972SCameron Grant ch->eso= (cr[2] & 0x00ffffff); 477f0fb042eSCameron Grant ch->delta= ((cr[2] & 0xff000000) >> 16) | ((cr[0] & 0xff000000) >> 24); 478987e5972SCameron Grant ch->alpha= (cr[3] & 0xfff00000) >> 20; 479987e5972SCameron Grant ch->fms= (cr[3] & 0x000f0000) >> 16; 480987e5972SCameron Grant break; 481987e5972SCameron Grant } 482987e5972SCameron Grant } 483987e5972SCameron Grant 4847ed9700bSCameron Grant static u_int32_t 4857ed9700bSCameron Grant tr_fmttobits(u_int32_t fmt) 4867ed9700bSCameron Grant { 4877ed9700bSCameron Grant u_int32_t bits; 4887ed9700bSCameron Grant 4897ed9700bSCameron Grant bits = 0; 4907ed9700bSCameron Grant bits |= (fmt & AFMT_SIGNED)? 0x2 : 0; 49190da2b28SAriff Abdullah bits |= (AFMT_CHANNEL(fmt) > 1)? 0x4 : 0; 4927ed9700bSCameron Grant bits |= (fmt & AFMT_16BIT)? 0x8 : 0; 4937ed9700bSCameron Grant 4947ed9700bSCameron Grant return bits; 4957ed9700bSCameron Grant } 4967ed9700bSCameron Grant 4970f55ac6cSCameron Grant /* -------------------------------------------------------------------- */ 498987e5972SCameron Grant /* channel interface */ 499987e5972SCameron Grant 5000f55ac6cSCameron Grant static void * 50166ef8af5SCameron Grant trpchan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel *c, int dir) 502987e5972SCameron Grant { 503987e5972SCameron Grant struct tr_info *tr = devinfo; 504987e5972SCameron Grant struct tr_chinfo *ch; 505f0fb042eSCameron Grant 506f0fb042eSCameron Grant KASSERT(dir == PCMDIR_PLAY, ("trpchan_init: bad direction")); 507987e5972SCameron Grant ch = &tr->chinfo[tr->playchns]; 508987e5972SCameron Grant ch->index = tr->playchns++; 509987e5972SCameron Grant ch->buffer = b; 510987e5972SCameron Grant ch->parent = tr; 511987e5972SCameron Grant ch->channel = c; 5122e334adfSAriff Abdullah if (sndbuf_alloc(ch->buffer, tr->parent_dmat, 0, tr->bufsz) != 0) 513f0fb042eSCameron Grant return NULL; 514f0fb042eSCameron Grant 515f0fb042eSCameron Grant return ch; 516987e5972SCameron Grant } 517987e5972SCameron Grant 518987e5972SCameron Grant static int 519f0fb042eSCameron Grant trpchan_setformat(kobj_t obj, void *data, u_int32_t format) 520987e5972SCameron Grant { 521987e5972SCameron Grant struct tr_chinfo *ch = data; 522f0fb042eSCameron Grant 523f0fb042eSCameron Grant ch->ctrl = tr_fmttobits(format) | 0x01; 524f0fb042eSCameron Grant 525f0fb042eSCameron Grant return 0; 526f0fb042eSCameron Grant } 527f0fb042eSCameron Grant 52890da2b28SAriff Abdullah static u_int32_t 529f0fb042eSCameron Grant trpchan_setspeed(kobj_t obj, void *data, u_int32_t speed) 530f0fb042eSCameron Grant { 531f0fb042eSCameron Grant struct tr_chinfo *ch = data; 532f0fb042eSCameron Grant 533f0fb042eSCameron Grant ch->delta = (speed << 12) / 48000; 534f0fb042eSCameron Grant return (ch->delta * 48000) >> 12; 535f0fb042eSCameron Grant } 536f0fb042eSCameron Grant 53790da2b28SAriff Abdullah static u_int32_t 538f0fb042eSCameron Grant trpchan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize) 539f0fb042eSCameron Grant { 540f0fb042eSCameron Grant struct tr_chinfo *ch = data; 541f0fb042eSCameron Grant 542f0fb042eSCameron Grant sndbuf_resize(ch->buffer, 2, blocksize); 543f0fb042eSCameron Grant return blocksize; 544f0fb042eSCameron Grant } 545f0fb042eSCameron Grant 546f0fb042eSCameron Grant static int 547f0fb042eSCameron Grant trpchan_trigger(kobj_t obj, void *data, int go) 548f0fb042eSCameron Grant { 549f0fb042eSCameron Grant struct tr_chinfo *ch = data; 550f0fb042eSCameron Grant 551bdfbdcecSAriff Abdullah if (!PCMTRIG_COMMON(go)) 552f0fb042eSCameron Grant return 0; 553f0fb042eSCameron Grant 554f0fb042eSCameron Grant if (go == PCMTRIG_START) { 5557ed9700bSCameron Grant ch->fmc = 3; 5567ed9700bSCameron Grant ch->fms = 0; 5577ed9700bSCameron Grant ch->ec = 0; 5587ed9700bSCameron Grant ch->alpha = 0; 55938cc9942SOlivier Houchard ch->lba = sndbuf_getbufaddr(ch->buffer); 560987e5972SCameron Grant ch->cso = 0; 56190da2b28SAriff Abdullah ch->eso = (sndbuf_getsize(ch->buffer) / sndbuf_getalign(ch->buffer)) - 1; 5627ed9700bSCameron Grant ch->rvol = ch->cvol = 0x7f; 563987e5972SCameron Grant ch->gvsel = 0; 564987e5972SCameron Grant ch->pan = 0; 565987e5972SCameron Grant ch->vol = 0; 5667ed9700bSCameron Grant ch->bufhalf = 0; 567f0fb042eSCameron Grant tr_wrch(ch); 568f0fb042eSCameron Grant tr_enaint(ch, 1); 569f0fb042eSCameron Grant tr_startch(ch); 570a02ab104SMitsuru IWASAKI ch->active = 1; 571a02ab104SMitsuru IWASAKI } else { 572f0fb042eSCameron Grant tr_stopch(ch); 573a02ab104SMitsuru IWASAKI ch->active = 0; 574a02ab104SMitsuru IWASAKI } 575f0fb042eSCameron Grant 576f0fb042eSCameron Grant return 0; 577f0fb042eSCameron Grant } 578f0fb042eSCameron Grant 57990da2b28SAriff Abdullah static u_int32_t 580f0fb042eSCameron Grant trpchan_getptr(kobj_t obj, void *data) 581f0fb042eSCameron Grant { 582f0fb042eSCameron Grant struct tr_chinfo *ch = data; 583f0fb042eSCameron Grant 584f0fb042eSCameron Grant tr_rdch(ch); 58590da2b28SAriff Abdullah return ch->cso * sndbuf_getalign(ch->buffer); 586f0fb042eSCameron Grant } 587f0fb042eSCameron Grant 58866ef8af5SCameron Grant static struct pcmchan_caps * 589f0fb042eSCameron Grant trpchan_getcaps(kobj_t obj, void *data) 590f0fb042eSCameron Grant { 591f0fb042eSCameron Grant return &tr_playcaps; 592f0fb042eSCameron Grant } 593f0fb042eSCameron Grant 594f0fb042eSCameron Grant static kobj_method_t trpchan_methods[] = { 595f0fb042eSCameron Grant KOBJMETHOD(channel_init, trpchan_init), 596f0fb042eSCameron Grant KOBJMETHOD(channel_setformat, trpchan_setformat), 597f0fb042eSCameron Grant KOBJMETHOD(channel_setspeed, trpchan_setspeed), 598f0fb042eSCameron Grant KOBJMETHOD(channel_setblocksize, trpchan_setblocksize), 599f0fb042eSCameron Grant KOBJMETHOD(channel_trigger, trpchan_trigger), 600f0fb042eSCameron Grant KOBJMETHOD(channel_getptr, trpchan_getptr), 601f0fb042eSCameron Grant KOBJMETHOD(channel_getcaps, trpchan_getcaps), 60290da2b28SAriff Abdullah KOBJMETHOD_END 603f0fb042eSCameron Grant }; 604f0fb042eSCameron Grant CHANNEL_DECLARE(trpchan); 605f0fb042eSCameron Grant 606f0fb042eSCameron Grant /* -------------------------------------------------------------------- */ 607f0fb042eSCameron Grant /* rec channel interface */ 608f0fb042eSCameron Grant 609f0fb042eSCameron Grant static void * 61066ef8af5SCameron Grant trrchan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel *c, int dir) 611f0fb042eSCameron Grant { 612f0fb042eSCameron Grant struct tr_info *tr = devinfo; 613f0fb042eSCameron Grant struct tr_rchinfo *ch; 614f0fb042eSCameron Grant 615f0fb042eSCameron Grant KASSERT(dir == PCMDIR_REC, ("trrchan_init: bad direction")); 616f0fb042eSCameron Grant ch = &tr->recchinfo; 617f0fb042eSCameron Grant ch->buffer = b; 618f0fb042eSCameron Grant ch->parent = tr; 619f0fb042eSCameron Grant ch->channel = c; 6202e334adfSAriff Abdullah if (sndbuf_alloc(ch->buffer, tr->parent_dmat, 0, tr->bufsz) != 0) 621f0fb042eSCameron Grant return NULL; 622f0fb042eSCameron Grant 623f0fb042eSCameron Grant return ch; 624f0fb042eSCameron Grant } 625f0fb042eSCameron Grant 626f0fb042eSCameron Grant static int 627f0fb042eSCameron Grant trrchan_setformat(kobj_t obj, void *data, u_int32_t format) 628f0fb042eSCameron Grant { 629f0fb042eSCameron Grant struct tr_rchinfo *ch = data; 630f0fb042eSCameron Grant struct tr_info *tr = ch->parent; 631f0fb042eSCameron Grant u_int32_t i, bits; 632f0fb042eSCameron Grant 633f0fb042eSCameron Grant bits = tr_fmttobits(format); 634f0fb042eSCameron Grant /* set # of samples between interrupts */ 6357ed9700bSCameron Grant i = (sndbuf_runsz(ch->buffer) >> ((bits & 0x08)? 1 : 0)) - 1; 636f0fb042eSCameron Grant tr_wr(tr, TR_REG_SBBL, i | (i << 16), 4); 637f0fb042eSCameron Grant /* set sample format */ 638f0fb042eSCameron Grant i = 0x18 | (bits << 4); 639f0fb042eSCameron Grant tr_wr(tr, TR_REG_SBCTRL, i, 1); 640f0fb042eSCameron Grant 641f0fb042eSCameron Grant return 0; 642f0fb042eSCameron Grant } 643f0fb042eSCameron Grant 64490da2b28SAriff Abdullah static u_int32_t 645f0fb042eSCameron Grant trrchan_setspeed(kobj_t obj, void *data, u_int32_t speed) 646f0fb042eSCameron Grant { 647f0fb042eSCameron Grant struct tr_rchinfo *ch = data; 648f0fb042eSCameron Grant struct tr_info *tr = ch->parent; 649f0fb042eSCameron Grant 650f0fb042eSCameron Grant /* setup speed */ 651f0fb042eSCameron Grant ch->delta = (48000 << 12) / speed; 652f0fb042eSCameron Grant tr_wr(tr, TR_REG_SBDELTA, ch->delta, 2); 653f0fb042eSCameron Grant 654f0fb042eSCameron Grant /* return closest possible speed */ 655f0fb042eSCameron Grant return (48000 << 12) / ch->delta; 656f0fb042eSCameron Grant } 657f0fb042eSCameron Grant 65890da2b28SAriff Abdullah static u_int32_t 659f0fb042eSCameron Grant trrchan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize) 660f0fb042eSCameron Grant { 661f0fb042eSCameron Grant struct tr_rchinfo *ch = data; 662f0fb042eSCameron Grant 663f0fb042eSCameron Grant sndbuf_resize(ch->buffer, 2, blocksize); 664f0fb042eSCameron Grant 665f0fb042eSCameron Grant return blocksize; 666f0fb042eSCameron Grant } 667f0fb042eSCameron Grant 668f0fb042eSCameron Grant static int 669f0fb042eSCameron Grant trrchan_trigger(kobj_t obj, void *data, int go) 670f0fb042eSCameron Grant { 671f0fb042eSCameron Grant struct tr_rchinfo *ch = data; 672f0fb042eSCameron Grant struct tr_info *tr = ch->parent; 673987e5972SCameron Grant u_int32_t i; 674f0fb042eSCameron Grant 675bdfbdcecSAriff Abdullah if (!PCMTRIG_COMMON(go)) 676f0fb042eSCameron Grant return 0; 677f0fb042eSCameron Grant 678f0fb042eSCameron Grant if (go == PCMTRIG_START) { 679f0fb042eSCameron Grant /* set up dma mode regs */ 680987e5972SCameron Grant tr_wr(tr, TR_REG_DMAR15, 0, 1); 681987e5972SCameron Grant i = tr_rd(tr, TR_REG_DMAR11, 1) & 0x03; 682987e5972SCameron Grant tr_wr(tr, TR_REG_DMAR11, i | 0x54, 1); 683987e5972SCameron Grant /* set up base address */ 68438cc9942SOlivier Houchard tr_wr(tr, TR_REG_DMAR0, sndbuf_getbufaddr(ch->buffer), 4); 685987e5972SCameron Grant /* set up buffer size */ 686987e5972SCameron Grant i = tr_rd(tr, TR_REG_DMAR4, 4) & ~0x00ffffff; 687f0fb042eSCameron Grant tr_wr(tr, TR_REG_DMAR4, i | (sndbuf_runsz(ch->buffer) - 1), 4); 688f0fb042eSCameron Grant /* start */ 689f0fb042eSCameron Grant tr_wr(tr, TR_REG_SBCTRL, tr_rd(tr, TR_REG_SBCTRL, 1) | 1, 1); 690a02ab104SMitsuru IWASAKI ch->active = 1; 691a02ab104SMitsuru IWASAKI } else { 692f0fb042eSCameron Grant tr_wr(tr, TR_REG_SBCTRL, tr_rd(tr, TR_REG_SBCTRL, 1) & ~7, 1); 693a02ab104SMitsuru IWASAKI ch->active = 0; 694a02ab104SMitsuru IWASAKI } 695f0fb042eSCameron Grant 696f0fb042eSCameron Grant /* return 0 if ok */ 697987e5972SCameron Grant return 0; 698987e5972SCameron Grant } 699987e5972SCameron Grant 70090da2b28SAriff Abdullah static u_int32_t 701f0fb042eSCameron Grant trrchan_getptr(kobj_t obj, void *data) 702987e5972SCameron Grant { 703f0fb042eSCameron Grant struct tr_rchinfo *ch = data; 704987e5972SCameron Grant struct tr_info *tr = ch->parent; 705987e5972SCameron Grant 706f0fb042eSCameron Grant /* return current byte offset of channel */ 70738cc9942SOlivier Houchard return tr_rd(tr, TR_REG_DMAR0, 4) - sndbuf_getbufaddr(ch->buffer); 708987e5972SCameron Grant } 709987e5972SCameron Grant 71066ef8af5SCameron Grant static struct pcmchan_caps * 711f0fb042eSCameron Grant trrchan_getcaps(kobj_t obj, void *data) 712987e5972SCameron Grant { 713f0fb042eSCameron Grant return &tr_reccaps; 714987e5972SCameron Grant } 715987e5972SCameron Grant 716f0fb042eSCameron Grant static kobj_method_t trrchan_methods[] = { 717f0fb042eSCameron Grant KOBJMETHOD(channel_init, trrchan_init), 718f0fb042eSCameron Grant KOBJMETHOD(channel_setformat, trrchan_setformat), 719f0fb042eSCameron Grant KOBJMETHOD(channel_setspeed, trrchan_setspeed), 720f0fb042eSCameron Grant KOBJMETHOD(channel_setblocksize, trrchan_setblocksize), 721f0fb042eSCameron Grant KOBJMETHOD(channel_trigger, trrchan_trigger), 722f0fb042eSCameron Grant KOBJMETHOD(channel_getptr, trrchan_getptr), 723f0fb042eSCameron Grant KOBJMETHOD(channel_getcaps, trrchan_getcaps), 72490da2b28SAriff Abdullah KOBJMETHOD_END 7250f55ac6cSCameron Grant }; 726f0fb042eSCameron Grant CHANNEL_DECLARE(trrchan); 7270f55ac6cSCameron Grant 7280f55ac6cSCameron Grant /* -------------------------------------------------------------------- */ 729987e5972SCameron Grant /* The interrupt handler */ 730987e5972SCameron Grant 731987e5972SCameron Grant static void 732987e5972SCameron Grant tr_intr(void *p) 733987e5972SCameron Grant { 734987e5972SCameron Grant struct tr_info *tr = (struct tr_info *)p; 735f0fb042eSCameron Grant struct tr_chinfo *ch; 7367ed9700bSCameron Grant u_int32_t active, mask, bufhalf, chnum, intsrc; 7377ed9700bSCameron Grant int tmp; 738987e5972SCameron Grant 739f0fb042eSCameron Grant intsrc = tr_rd(tr, TR_REG_MISCINT, 4); 740987e5972SCameron Grant if (intsrc & TR_INT_ADDR) { 7417ed9700bSCameron Grant chnum = 0; 742eb811915SMarius Strobl while (chnum < tr->hwchns) { 7437ed9700bSCameron Grant mask = 0x00000001; 7447ed9700bSCameron Grant active = tr_rd(tr, (chnum < 32)? TR_REG_ADDRINTA : TR_REG_ADDRINTB, 4); 7457ed9700bSCameron Grant bufhalf = tr_rd(tr, (chnum < 32)? TR_REG_CSPF_A : TR_REG_CSPF_B, 4); 7467ed9700bSCameron Grant if (active) { 7477ed9700bSCameron Grant do { 7487ed9700bSCameron Grant if (active & mask) { 7497ed9700bSCameron Grant tmp = (bufhalf & mask)? 1 : 0; 7507ed9700bSCameron Grant if (chnum < tr->playchns) { 7517ed9700bSCameron Grant ch = &tr->chinfo[chnum]; 7527ed9700bSCameron Grant /* printf("%d @ %d, ", chnum, trpchan_getptr(NULL, ch)); */ 7537ed9700bSCameron Grant if (ch->bufhalf != tmp) { 754f0fb042eSCameron Grant chn_intr(ch->channel); 7557ed9700bSCameron Grant ch->bufhalf = tmp; 75666ef8af5SCameron Grant } 757987e5972SCameron Grant } 758987e5972SCameron Grant } 7597ed9700bSCameron Grant chnum++; 7607ed9700bSCameron Grant mask <<= 1; 7617ed9700bSCameron Grant } while (chnum & 31); 7627ed9700bSCameron Grant } else 7637ed9700bSCameron Grant chnum += 32; 7647ed9700bSCameron Grant 7657ed9700bSCameron Grant tr_wr(tr, (chnum <= 32)? TR_REG_ADDRINTA : TR_REG_ADDRINTB, active, 4); 7667ed9700bSCameron Grant } 767987e5972SCameron Grant } 768987e5972SCameron Grant if (intsrc & TR_INT_SB) { 769987e5972SCameron Grant chn_intr(tr->recchinfo.channel); 770987e5972SCameron Grant tr_rd(tr, TR_REG_SBR9, 1); 771987e5972SCameron Grant tr_rd(tr, TR_REG_SBR10, 1); 772987e5972SCameron Grant } 773987e5972SCameron Grant } 774987e5972SCameron Grant 775987e5972SCameron Grant /* -------------------------------------------------------------------- */ 776987e5972SCameron Grant 777987e5972SCameron Grant /* 778987e5972SCameron Grant * Probe and attach the card 779987e5972SCameron Grant */ 780987e5972SCameron Grant 781987e5972SCameron Grant static int 782987e5972SCameron Grant tr_init(struct tr_info *tr) 783987e5972SCameron Grant { 784874f2e78SGeorge C A Reid switch (tr->type) { 785874f2e78SGeorge C A Reid case SPA_PCI_ID: 786874f2e78SGeorge C A Reid tr_wr(tr, SPA_REG_GPIO, 0, 4); 787874f2e78SGeorge C A Reid tr_wr(tr, SPA_REG_CODECST, SPA_RST_OFF, 4); 788874f2e78SGeorge C A Reid break; 789874f2e78SGeorge C A Reid case TDX_PCI_ID: 790987e5972SCameron Grant tr_wr(tr, TDX_REG_CODECST, TDX_CDC_ON, 4); 791874f2e78SGeorge C A Reid break; 792874f2e78SGeorge C A Reid case TNX_PCI_ID: 793874f2e78SGeorge C A Reid tr_wr(tr, TNX_REG_CODECST, TNX_CDC_ON, 4); 794874f2e78SGeorge C A Reid break; 795874f2e78SGeorge C A Reid } 796987e5972SCameron Grant 797987e5972SCameron Grant tr_wr(tr, TR_REG_CIR, TR_CIR_MIDENA | TR_CIR_ADDRENA, 4); 798987e5972SCameron Grant return 0; 799987e5972SCameron Grant } 800987e5972SCameron Grant 801987e5972SCameron Grant static int 802987e5972SCameron Grant tr_pci_probe(device_t dev) 803987e5972SCameron Grant { 804874f2e78SGeorge C A Reid switch (pci_get_devid(dev)) { 805874f2e78SGeorge C A Reid case SPA_PCI_ID: 806874f2e78SGeorge C A Reid device_set_desc(dev, "SiS 7018"); 807d2b677bbSWarner Losh return BUS_PROBE_DEFAULT; 8080a1e38d3SGeorge C A Reid case ALI_PCI_ID: 8090a1e38d3SGeorge C A Reid device_set_desc(dev, "Acer Labs M5451"); 810d2b677bbSWarner Losh return BUS_PROBE_DEFAULT; 811874f2e78SGeorge C A Reid case TDX_PCI_ID: 812987e5972SCameron Grant device_set_desc(dev, "Trident 4DWave DX"); 813d2b677bbSWarner Losh return BUS_PROBE_DEFAULT; 814874f2e78SGeorge C A Reid case TNX_PCI_ID: 815987e5972SCameron Grant device_set_desc(dev, "Trident 4DWave NX"); 816d2b677bbSWarner Losh return BUS_PROBE_DEFAULT; 817987e5972SCameron Grant } 818987e5972SCameron Grant 819987e5972SCameron Grant return ENXIO; 820987e5972SCameron Grant } 821987e5972SCameron Grant 822987e5972SCameron Grant static int 823987e5972SCameron Grant tr_pci_attach(device_t dev) 824987e5972SCameron Grant { 825987e5972SCameron Grant u_int32_t data; 826987e5972SCameron Grant struct tr_info *tr; 8277dfc9325SCameron Grant struct ac97_info *codec = 0; 828eb811915SMarius Strobl bus_addr_t lowaddr; 8298b187690SAriff Abdullah int i, dacn; 830987e5972SCameron Grant char status[SND_STATUSLEN]; 831eb811915SMarius Strobl #ifdef __sparc64__ 832eb811915SMarius Strobl device_t *children; 833eb811915SMarius Strobl int nchildren; 834eb811915SMarius Strobl #endif 835987e5972SCameron Grant 836082f6383SAriff Abdullah tr = malloc(sizeof(*tr), M_DEVBUF, M_WAITOK | M_ZERO); 837987e5972SCameron Grant tr->type = pci_get_devid(dev); 83889ca562eSOlivier Houchard tr->rev = pci_get_revid(dev); 8394582b3a1SAriff Abdullah tr->lock = snd_mtxcreate(device_get_nameunit(dev), "snd_t4dwave softc"); 840987e5972SCameron Grant 8418b187690SAriff Abdullah if (resource_int_value(device_get_name(dev), device_get_unit(dev), 8428b187690SAriff Abdullah "dac", &i) == 0) { 8438b187690SAriff Abdullah if (i < 1) 8448b187690SAriff Abdullah dacn = 1; 8458b187690SAriff Abdullah else if (i > TR_MAXPLAYCH) 8468b187690SAriff Abdullah dacn = TR_MAXPLAYCH; 8478b187690SAriff Abdullah else 8488b187690SAriff Abdullah dacn = i; 8498b187690SAriff Abdullah } else { 8508b187690SAriff Abdullah switch (tr->type) { 8518b187690SAriff Abdullah case ALI_PCI_ID: 852eb811915SMarius Strobl dacn = ALI_MAXPLAYCH; 8538b187690SAriff Abdullah break; 8548b187690SAriff Abdullah default: 8558b187690SAriff Abdullah dacn = TR_MAXPLAYCH; 8568b187690SAriff Abdullah break; 8578b187690SAriff Abdullah } 8588b187690SAriff Abdullah } 8598b187690SAriff Abdullah 860987e5972SCameron Grant data = pci_read_config(dev, PCIR_COMMAND, 2); 861987e5972SCameron Grant data |= (PCIM_CMD_PORTEN|PCIM_CMD_MEMEN|PCIM_CMD_BUSMASTEREN); 862987e5972SCameron Grant pci_write_config(dev, PCIR_COMMAND, data, 2); 863987e5972SCameron Grant data = pci_read_config(dev, PCIR_COMMAND, 2); 864987e5972SCameron Grant 865e27951b2SJohn Baldwin tr->regid = PCIR_BAR(0); 866987e5972SCameron Grant tr->regtype = SYS_RES_IOPORT; 8675f96beb9SNate Lawson tr->reg = bus_alloc_resource_any(dev, tr->regtype, &tr->regid, 8685f96beb9SNate Lawson RF_ACTIVE); 869987e5972SCameron Grant if (tr->reg) { 870987e5972SCameron Grant tr->st = rman_get_bustag(tr->reg); 871987e5972SCameron Grant tr->sh = rman_get_bushandle(tr->reg); 872f0fb042eSCameron Grant } else { 873987e5972SCameron Grant device_printf(dev, "unable to map register space\n"); 874987e5972SCameron Grant goto bad; 875987e5972SCameron Grant } 876987e5972SCameron Grant 877987e5972SCameron Grant if (tr_init(tr) == -1) { 878987e5972SCameron Grant device_printf(dev, "unable to initialize the card\n"); 879987e5972SCameron Grant goto bad; 880987e5972SCameron Grant } 881a02ab104SMitsuru IWASAKI tr->playchns = 0; 882987e5972SCameron Grant 8830f55ac6cSCameron Grant codec = AC97_CREATE(dev, tr, tr_ac97); 884987e5972SCameron Grant if (codec == NULL) goto bad; 8850f55ac6cSCameron Grant if (mixer_init(dev, ac97_getmixerclass(), codec) == -1) goto bad; 886987e5972SCameron Grant 887987e5972SCameron Grant tr->irqid = 0; 8885f96beb9SNate Lawson tr->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &tr->irqid, 8895f96beb9SNate Lawson RF_ACTIVE | RF_SHAREABLE); 8908fb9a995SBrian Feldman if (!tr->irq || snd_setup_intr(dev, tr->irq, 0, tr_intr, tr, &tr->ih)) { 891987e5972SCameron Grant device_printf(dev, "unable to map interrupt\n"); 892987e5972SCameron Grant goto bad; 893987e5972SCameron Grant } 894987e5972SCameron Grant 895eb811915SMarius Strobl if (tr->type == ALI_PCI_ID) { 896eb811915SMarius Strobl /* 897eb811915SMarius Strobl * The M5451 generates 31 bit of DMA and in order to do 898eb811915SMarius Strobl * 32-bit DMA, the 31st bit can be set via its accompanying 899eb811915SMarius Strobl * ISA bridge. Note that we can't predict whether bus_dma(9) 900eb811915SMarius Strobl * will actually supply us with a 32-bit buffer and even when 901eb811915SMarius Strobl * using a low address of BUS_SPACE_MAXADDR_32BIT for both 902eb811915SMarius Strobl * we might end up with the play buffer being in the 32-bit 903eb811915SMarius Strobl * range while the record buffer isn't or vice versa. So we 904eb811915SMarius Strobl * limit enabling the 31st bit to sparc64, where the IOMMU 905eb811915SMarius Strobl * guarantees that we're using a 32-bit address (and in turn 906eb811915SMarius Strobl * requires it). 907eb811915SMarius Strobl */ 908eb811915SMarius Strobl lowaddr = ALI_MAXADDR; 909eb811915SMarius Strobl #ifdef __sparc64__ 910eb811915SMarius Strobl if (device_get_children(device_get_parent(dev), &children, 911eb811915SMarius Strobl &nchildren) == 0) { 912eb811915SMarius Strobl for (i = 0; i < nchildren; i++) { 913eb811915SMarius Strobl if (pci_get_devid(children[i]) == 0x153310b9) { 914eb811915SMarius Strobl lowaddr = BUS_SPACE_MAXADDR_32BIT; 915eb811915SMarius Strobl data = pci_read_config(children[i], 916eb811915SMarius Strobl 0x7e, 1); 917eb811915SMarius Strobl if (bootverbose) 918eb811915SMarius Strobl device_printf(dev, 919eb811915SMarius Strobl "M1533 0x7e: 0x%x -> ", 920eb811915SMarius Strobl data); 921eb811915SMarius Strobl data |= 0x1; 922eb811915SMarius Strobl if (bootverbose) 923eb811915SMarius Strobl printf("0x%x\n", data); 924eb811915SMarius Strobl pci_write_config(children[i], 0x7e, 925eb811915SMarius Strobl data, 1); 926eb811915SMarius Strobl break; 927eb811915SMarius Strobl } 928eb811915SMarius Strobl } 929eb811915SMarius Strobl } 930eb811915SMarius Strobl free(children, M_TEMP); 931eb811915SMarius Strobl #endif 932eb811915SMarius Strobl tr->hwchns = ALI_MAXHWCH; 933eb811915SMarius Strobl tr->bufsz = ALI_BUFSZ; 934eb811915SMarius Strobl } else { 935eb811915SMarius Strobl lowaddr = TR_MAXADDR; 936eb811915SMarius Strobl tr->hwchns = TR_MAXHWCH; 937eb811915SMarius Strobl tr->bufsz = pcm_getbuffersize(dev, 4096, TR_DEFAULT_BUFSZ, 938eb811915SMarius Strobl 65536); 939eb811915SMarius Strobl } 940eb811915SMarius Strobl 941eb811915SMarius Strobl if (bus_dma_tag_create(/*parent*/bus_get_dma_tag(dev), 942eb811915SMarius Strobl /*alignment*/TR_BUFALGN, 9430b989078SAlexander Leidinger /*boundary*/0, 944eb811915SMarius Strobl /*lowaddr*/lowaddr, 945987e5972SCameron Grant /*highaddr*/BUS_SPACE_MAXADDR, 946987e5972SCameron Grant /*filter*/NULL, /*filterarg*/NULL, 947eb811915SMarius Strobl /*maxsize*/tr->bufsz, /*nsegments*/1, /*maxsegz*/tr->bufsz, 948f6b1c44dSScott Long /*flags*/0, /*lockfunc*/busdma_lock_mutex, 949f6b1c44dSScott Long /*lockarg*/&Giant, &tr->parent_dmat) != 0) { 950987e5972SCameron Grant device_printf(dev, "unable to create dma tag\n"); 951987e5972SCameron Grant goto bad; 952987e5972SCameron Grant } 953987e5972SCameron Grant 9540d8ed52eSMathew Kanner snprintf(status, 64, "at io 0x%lx irq %ld %s", 9550d8ed52eSMathew Kanner rman_get_start(tr->reg), rman_get_start(tr->irq),PCM_KLDSTRING(snd_t4dwave)); 956987e5972SCameron Grant 9578b187690SAriff Abdullah if (pcm_register(dev, tr, dacn, 1)) 9588b187690SAriff Abdullah goto bad; 959f0fb042eSCameron Grant pcm_addchan(dev, PCMDIR_REC, &trrchan_class, tr); 9608b187690SAriff Abdullah for (i = 0; i < dacn; i++) 961f0fb042eSCameron Grant pcm_addchan(dev, PCMDIR_PLAY, &trpchan_class, tr); 962987e5972SCameron Grant pcm_setstatus(dev, status); 963987e5972SCameron Grant 964987e5972SCameron Grant return 0; 965987e5972SCameron Grant 966987e5972SCameron Grant bad: 967306f91b6SCameron Grant if (codec) ac97_destroy(codec); 968987e5972SCameron Grant if (tr->reg) bus_release_resource(dev, tr->regtype, tr->regid, tr->reg); 969987e5972SCameron Grant if (tr->ih) bus_teardown_intr(dev, tr->irq, tr->ih); 970987e5972SCameron Grant if (tr->irq) bus_release_resource(dev, SYS_RES_IRQ, tr->irqid, tr->irq); 971306f91b6SCameron Grant if (tr->parent_dmat) bus_dma_tag_destroy(tr->parent_dmat); 97266ef8af5SCameron Grant if (tr->lock) snd_mtxfree(tr->lock); 973987e5972SCameron Grant free(tr, M_DEVBUF); 974987e5972SCameron Grant return ENXIO; 975987e5972SCameron Grant } 976987e5972SCameron Grant 977306f91b6SCameron Grant static int 978306f91b6SCameron Grant tr_pci_detach(device_t dev) 979306f91b6SCameron Grant { 980306f91b6SCameron Grant int r; 981306f91b6SCameron Grant struct tr_info *tr; 982306f91b6SCameron Grant 983306f91b6SCameron Grant r = pcm_unregister(dev); 984306f91b6SCameron Grant if (r) 985306f91b6SCameron Grant return r; 986306f91b6SCameron Grant 987306f91b6SCameron Grant tr = pcm_getdevinfo(dev); 988306f91b6SCameron Grant bus_release_resource(dev, tr->regtype, tr->regid, tr->reg); 989306f91b6SCameron Grant bus_teardown_intr(dev, tr->irq, tr->ih); 990306f91b6SCameron Grant bus_release_resource(dev, SYS_RES_IRQ, tr->irqid, tr->irq); 991306f91b6SCameron Grant bus_dma_tag_destroy(tr->parent_dmat); 99266ef8af5SCameron Grant snd_mtxfree(tr->lock); 993306f91b6SCameron Grant free(tr, M_DEVBUF); 994306f91b6SCameron Grant 995306f91b6SCameron Grant return 0; 996306f91b6SCameron Grant } 997306f91b6SCameron Grant 998a02ab104SMitsuru IWASAKI static int 999a02ab104SMitsuru IWASAKI tr_pci_suspend(device_t dev) 1000a02ab104SMitsuru IWASAKI { 1001a02ab104SMitsuru IWASAKI int i; 1002a02ab104SMitsuru IWASAKI struct tr_info *tr; 1003a02ab104SMitsuru IWASAKI 1004a02ab104SMitsuru IWASAKI tr = pcm_getdevinfo(dev); 1005a02ab104SMitsuru IWASAKI 1006a02ab104SMitsuru IWASAKI for (i = 0; i < tr->playchns; i++) { 1007a02ab104SMitsuru IWASAKI tr->chinfo[i].was_active = tr->chinfo[i].active; 1008a02ab104SMitsuru IWASAKI if (tr->chinfo[i].active) { 1009a02ab104SMitsuru IWASAKI trpchan_trigger(NULL, &tr->chinfo[i], PCMTRIG_STOP); 1010a02ab104SMitsuru IWASAKI } 1011a02ab104SMitsuru IWASAKI } 1012a02ab104SMitsuru IWASAKI 1013a02ab104SMitsuru IWASAKI tr->recchinfo.was_active = tr->recchinfo.active; 1014a02ab104SMitsuru IWASAKI if (tr->recchinfo.active) { 1015a02ab104SMitsuru IWASAKI trrchan_trigger(NULL, &tr->recchinfo, PCMTRIG_STOP); 1016a02ab104SMitsuru IWASAKI } 1017a02ab104SMitsuru IWASAKI 1018a02ab104SMitsuru IWASAKI return 0; 1019a02ab104SMitsuru IWASAKI } 1020a02ab104SMitsuru IWASAKI 1021a02ab104SMitsuru IWASAKI static int 1022a02ab104SMitsuru IWASAKI tr_pci_resume(device_t dev) 1023a02ab104SMitsuru IWASAKI { 1024a02ab104SMitsuru IWASAKI int i; 1025a02ab104SMitsuru IWASAKI struct tr_info *tr; 1026a02ab104SMitsuru IWASAKI 1027a02ab104SMitsuru IWASAKI tr = pcm_getdevinfo(dev); 1028a02ab104SMitsuru IWASAKI 1029a02ab104SMitsuru IWASAKI if (tr_init(tr) == -1) { 1030a02ab104SMitsuru IWASAKI device_printf(dev, "unable to initialize the card\n"); 1031a02ab104SMitsuru IWASAKI return ENXIO; 1032a02ab104SMitsuru IWASAKI } 1033a02ab104SMitsuru IWASAKI 1034a02ab104SMitsuru IWASAKI if (mixer_reinit(dev) == -1) { 1035a02ab104SMitsuru IWASAKI device_printf(dev, "unable to initialize the mixer\n"); 1036a02ab104SMitsuru IWASAKI return ENXIO; 1037a02ab104SMitsuru IWASAKI } 1038a02ab104SMitsuru IWASAKI 1039a02ab104SMitsuru IWASAKI for (i = 0; i < tr->playchns; i++) { 1040a02ab104SMitsuru IWASAKI if (tr->chinfo[i].was_active) { 1041a02ab104SMitsuru IWASAKI trpchan_trigger(NULL, &tr->chinfo[i], PCMTRIG_START); 1042a02ab104SMitsuru IWASAKI } 1043a02ab104SMitsuru IWASAKI } 1044a02ab104SMitsuru IWASAKI 1045a02ab104SMitsuru IWASAKI if (tr->recchinfo.was_active) { 1046a02ab104SMitsuru IWASAKI trrchan_trigger(NULL, &tr->recchinfo, PCMTRIG_START); 1047a02ab104SMitsuru IWASAKI } 1048a02ab104SMitsuru IWASAKI 1049a02ab104SMitsuru IWASAKI return 0; 1050a02ab104SMitsuru IWASAKI } 1051a02ab104SMitsuru IWASAKI 1052987e5972SCameron Grant static device_method_t tr_methods[] = { 1053987e5972SCameron Grant /* Device interface */ 1054987e5972SCameron Grant DEVMETHOD(device_probe, tr_pci_probe), 1055987e5972SCameron Grant DEVMETHOD(device_attach, tr_pci_attach), 1056306f91b6SCameron Grant DEVMETHOD(device_detach, tr_pci_detach), 1057a02ab104SMitsuru IWASAKI DEVMETHOD(device_suspend, tr_pci_suspend), 1058a02ab104SMitsuru IWASAKI DEVMETHOD(device_resume, tr_pci_resume), 1059987e5972SCameron Grant { 0, 0 } 1060987e5972SCameron Grant }; 1061987e5972SCameron Grant 1062987e5972SCameron Grant static driver_t tr_driver = { 1063987e5972SCameron Grant "pcm", 1064987e5972SCameron Grant tr_methods, 106567b1dce3SCameron Grant PCM_SOFTC_SIZE, 1066987e5972SCameron Grant }; 1067987e5972SCameron Grant 1068f314f3daSCameron Grant DRIVER_MODULE(snd_t4dwave, pci, tr_driver, pcm_devclass, 0, 0); 10690739ea1dSSeigo Tanimura MODULE_DEPEND(snd_t4dwave, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER); 1070f314f3daSCameron Grant MODULE_VERSION(snd_t4dwave, 1); 1071