t4dwave.c revision 105100
1275970Scy/* 2275970Scy * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk> 3275970Scy * All rights reserved. 4275970Scy * 5275970Scy * Redistribution and use in source and binary forms, with or without 6275970Scy * modification, are permitted provided that the following conditions 7275970Scy * are met: 8275970Scy * 1. Redistributions of source code must retain the above copyright 9275970Scy * notice, this list of conditions and the following disclaimer. 10275970Scy * 2. Redistributions in binary form must reproduce the above copyright 11275970Scy * notice, this list of conditions and the following disclaimer in the 12275970Scy * documentation and/or other materials provided with the distribution. 13275970Scy * 14275970Scy * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15275970Scy * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16275970Scy * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17275970Scy * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18275970Scy * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19275970Scy * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20275970Scy * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21275970Scy * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHERIN CONTRACT, STRICT 22275970Scy * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23275970Scy * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THEPOSSIBILITY OF 24275970Scy * SUCH DAMAGE. 25275970Scy */ 26275970Scy 27275970Scy#include <dev/sound/pcm/sound.h> 28275970Scy#include <dev/sound/pcm/ac97.h> 29275970Scy#include <dev/sound/pci/t4dwave.h> 30275970Scy 31275970Scy#include <pci/pcireg.h> 32275970Scy#include <pci/pcivar.h> 33275970Scy 34275970ScySND_DECLARE_FILE("$FreeBSD: head/sys/dev/sound/pci/t4dwave.c 105100 2002-10-14 11:47:37Z cognet $"); 35275970Scy 36275970Scy/* -------------------------------------------------------------------- */ 37275970Scy 38275970Scy#define TDX_PCI_ID 0x20001023 39275970Scy#define TNX_PCI_ID 0x20011023 40275970Scy#define ALI_PCI_ID 0x545110b9 41275970Scy#define SPA_PCI_ID 0x70181039 42275970Scy 43275970Scy#define TR_DEFAULT_BUFSZ 0x1000 44275970Scy#define TR_TIMEOUT_CDC 0xffff 45275970Scy#define TR_MAXPLAYCH 4 46275970Scy 47330567Sgordonstruct tr_info; 48275970Scy 49275970Scy/* channel registers */ 50275970Scystruct tr_chinfo { 51275970Scy u_int32_t cso, alpha, fms, fmc, ec; 52275970Scy u_int32_t lba; 53275970Scy u_int32_t eso, delta; 54275970Scy u_int32_t rvol, cvol; 55275970Scy u_int32_t gvsel, pan, vol, ctrl; 56275970Scy u_int32_t active:1, was_active:1; 57275970Scy int index, bufhalf; 58275970Scy struct snd_dbuf *buffer; 59275970Scy struct pcm_channel *channel; 60275970Scy struct tr_info *parent; 61275970Scy}; 62275970Scy 63275970Scystruct tr_rchinfo { 64275970Scy u_int32_t delta; 65275970Scy u_int32_t active:1, was_active:1; 66275970Scy struct snd_dbuf *buffer; 67275970Scy struct pcm_channel *channel; 68275970Scy struct tr_info *parent; 69275970Scy}; 70275970Scy 71275970Scy/* device private data */ 72275970Scystruct tr_info { 73275970Scy u_int32_t type; 74275970Scy u_int32_t rev; 75275970Scy 76275970Scy bus_space_tag_t st; 77275970Scy bus_space_handle_t sh; 78275970Scy bus_dma_tag_t parent_dmat; 79275970Scy 80275970Scy struct resource *reg, *irq; 81275970Scy int regtype, regid, irqid; 82275970Scy void *ih; 83275970Scy 84275970Scy void *lock; 85275970Scy 86275970Scy u_int32_t playchns; 87275970Scy unsigned int bufsz; 88275970Scy 89275970Scy struct tr_chinfo chinfo[TR_MAXPLAYCH]; 90275970Scy struct tr_rchinfo recchinfo; 91275970Scy}; 92275970Scy 93275970Scy/* -------------------------------------------------------------------- */ 94275970Scy 95275970Scystatic u_int32_t tr_recfmt[] = { 96275970Scy AFMT_U8, 97275970Scy AFMT_STEREO | AFMT_U8, 98275970Scy AFMT_S8, 99275970Scy AFMT_STEREO | AFMT_S8, 100330567Sgordon AFMT_S16_LE, 101330567Sgordon AFMT_STEREO | AFMT_S16_LE, 102275970Scy AFMT_U16_LE, 103275970Scy AFMT_STEREO | AFMT_U16_LE, 104275970Scy 0 105275970Scy}; 106275970Scystatic struct pcmchan_caps tr_reccaps = {4000, 48000, tr_recfmt, 0}; 107275970Scy 108275970Scystatic u_int32_t tr_playfmt[] = { 109275970Scy AFMT_U8, 110275970Scy AFMT_STEREO | AFMT_U8, 111275970Scy AFMT_S8, 112275970Scy AFMT_STEREO | AFMT_S8, 113275970Scy AFMT_S16_LE, 114275970Scy AFMT_STEREO | AFMT_S16_LE, 115275970Scy AFMT_U16_LE, 116275970Scy AFMT_STEREO | AFMT_U16_LE, 117275970Scy 0 118275970Scy}; 119275970Scystatic struct pcmchan_caps tr_playcaps = {4000, 48000, tr_playfmt, 0}; 120275970Scy 121275970Scy/* -------------------------------------------------------------------- */ 122275970Scy 123275970Scy/* Hardware */ 124275970Scy 125275970Scystatic u_int32_t 126275970Scytr_rd(struct tr_info *tr, int regno, int size) 127275970Scy{ 128275970Scy switch(size) { 129275970Scy case 1: 130275970Scy return bus_space_read_1(tr->st, tr->sh, regno); 131275970Scy case 2: 132275970Scy return bus_space_read_2(tr->st, tr->sh, regno); 133275970Scy case 4: 134275970Scy return bus_space_read_4(tr->st, tr->sh, regno); 135275970Scy default: 136275970Scy return 0xffffffff; 137275970Scy } 138275970Scy} 139275970Scy 140275970Scystatic void 141275970Scytr_wr(struct tr_info *tr, int regno, u_int32_t data, int size) 142275970Scy{ 143275970Scy switch(size) { 144275970Scy case 1: 145275970Scy bus_space_write_1(tr->st, tr->sh, regno, data); 146330567Sgordon break; 147330567Sgordon case 2: 148330567Sgordon bus_space_write_2(tr->st, tr->sh, regno, data); 149330567Sgordon break; 150330567Sgordon case 4: 151330567Sgordon bus_space_write_4(tr->st, tr->sh, regno, data); 152330567Sgordon break; 153330567Sgordon } 154330567Sgordon} 155330567Sgordon 156275970Scy/* -------------------------------------------------------------------- */ 157275970Scy/* ac97 codec */ 158275970Scy 159275970Scystatic int 160275970Scytr_rdcd(kobj_t obj, void *devinfo, int regno) 161275970Scy{ 162275970Scy struct tr_info *tr = (struct tr_info *)devinfo; 163275970Scy int i, j, treg, trw; 164275970Scy 165275970Scy switch (tr->type) { 166275970Scy case SPA_PCI_ID: 167275970Scy treg=SPA_REG_CODECRD; 168275970Scy trw=SPA_CDC_RWSTAT; 169275970Scy break; 170275970Scy case ALI_PCI_ID: 171275970Scy if (tr->rev > 0x01) 172275970Scy treg=TDX_REG_CODECWR; 173275970Scy else 174275970Scy treg=TDX_REG_CODECRD; 175275970Scy trw=TDX_CDC_RWSTAT; 176275970Scy break; 177275970Scy case TDX_PCI_ID: 178275970Scy treg=TDX_REG_CODECRD; 179275970Scy trw=TDX_CDC_RWSTAT; 180330567Sgordon break; 181275970Scy case TNX_PCI_ID: 182275970Scy treg=(regno & 0x100)? TNX_REG_CODEC2RD : TNX_REG_CODEC1RD; 183275970Scy trw=TNX_CDC_RWSTAT; 184330567Sgordon break; 185275970Scy default: 186330567Sgordon printf("!!! tr_rdcd defaulted !!!\n"); 187330567Sgordon return -1; 188275970Scy } 189275970Scy 190275970Scy regno &= 0x7f; 191330567Sgordon snd_mtxlock(tr->lock); 192275970Scy if (tr->type == ALI_PCI_ID) { 193330567Sgordon u_int32_t chk1, chk2; 194275970Scy j = trw; 195330567Sgordon for (i = TR_TIMEOUT_CDC; (i > 0) && (j & trw); i--) 196275970Scy j = tr_rd(tr, treg, 4); 197330567Sgordon if (i > 0) { 198275970Scy chk1 = tr_rd(tr, 0xc8, 4); 199275970Scy chk2 = tr_rd(tr, 0xc8, 4); 200275970Scy for (i = TR_TIMEOUT_CDC; (i > 0) && (chk1 == chk2); 201275970Scy i--) 202330567Sgordon chk2 = tr_rd(tr, 0xc8, 4); 203330567Sgordon } 204275970Scy } 205275970Scy if (tr->type != ALI_PCI_ID || i > 0) { 206275970Scy tr_wr(tr, treg, regno | trw, 4); 207275970Scy j=trw; 208275970Scy for (i=TR_TIMEOUT_CDC; (i > 0) && (j & trw); i--) 209275970Scy j=tr_rd(tr, treg, 4); 210275970Scy } 211275970Scy snd_mtxunlock(tr->lock); 212275970Scy if (i == 0) printf("codec timeout during read of register %x\n", regno); 213275970Scy return (j >> TR_CDC_DATA) & 0xffff; 214275970Scy} 215275970Scy 216275970Scystatic int 217275970Scytr_wrcd(kobj_t obj, void *devinfo, int regno, u_int32_t data) 218330567Sgordon{ 219275970Scy struct tr_info *tr = (struct tr_info *)devinfo; 220275970Scy int i, j, treg, trw; 221275970Scy 222275970Scy switch (tr->type) { 223275970Scy case SPA_PCI_ID: 224330567Sgordon treg=SPA_REG_CODECWR; 225275970Scy trw=SPA_CDC_RWSTAT; 226275970Scy break; 227275970Scy case ALI_PCI_ID: 228275970Scy case TDX_PCI_ID: 229330567Sgordon treg=TDX_REG_CODECWR; 230275970Scy trw=TDX_CDC_RWSTAT; 231275970Scy break; 232275970Scy case TNX_PCI_ID: 233275970Scy treg=TNX_REG_CODECWR; 234275970Scy trw=TNX_CDC_RWSTAT | ((regno & 0x100)? TNX_CDC_SEC : 0); 235330567Sgordon break; 236275970Scy default: 237330567Sgordon printf("!!! tr_wrcd defaulted !!!"); 238275970Scy return -1; 239275970Scy } 240275970Scy 241330567Sgordon regno &= 0x7f; 242275970Scy#if 0 243275970Scy printf("tr_wrcd: reg %x was %x", regno, tr_rdcd(devinfo, regno)); 244275970Scy#endif 245275970Scy j=trw; 246275970Scy snd_mtxlock(tr->lock); 247275970Scy if (tr->type == ALI_PCI_ID) { 248275970Scy j = trw; 249330567Sgordon for (i = TR_TIMEOUT_CDC; (i > 0) && (j & trw); i--) 250275970Scy j = tr_rd(tr, treg, 4); 251275970Scy if (i > 0) { 252330567Sgordon u_int32_t chk1, chk2; 253330567Sgordon chk1 = tr_rd(tr, 0xc8, 4); 254330567Sgordon chk2 = tr_rd(tr, 0xc8, 4); 255275970Scy for (i = TR_TIMEOUT_CDC; (i > 0) && (chk1 == chk2); 256275970Scy i--) 257275970Scy chk2 = tr_rd(tr, 0xc8, 4); 258275970Scy } 259275970Scy } 260275970Scy if (tr->type != ALI_PCI_ID || i > 0) { 261275970Scy for (i=TR_TIMEOUT_CDC; (i>0) && (j & trw); i--) 262330567Sgordon j=tr_rd(tr, treg, 4); 263330567Sgordon if (tr->type == ALI_PCI_ID && tr->rev > 0x01) 264330567Sgordon trw |= 0x0100; 265330567Sgordon tr_wr(tr, treg, (data << TR_CDC_DATA) | regno | trw, 4); 266275970Scy } 267275970Scy#if 0 268330567Sgordon printf(" - wrote %x, now %x\n", data, tr_rdcd(devinfo, regno)); 269330567Sgordon#endif 270330567Sgordon snd_mtxunlock(tr->lock); 271275970Scy if (i==0) printf("codec timeout writing %x, data %x\n", regno, data); 272330567Sgordon return (i > 0)? 0 : -1; 273330567Sgordon} 274275970Scy 275275970Scystatic kobj_method_t tr_ac97_methods[] = { 276275970Scy KOBJMETHOD(ac97_read, tr_rdcd), 277275970Scy KOBJMETHOD(ac97_write, tr_wrcd), 278275970Scy { 0, 0 } 279275970Scy}; 280275970ScyAC97_DECLARE(tr_ac97); 281275970Scy 282275970Scy/* -------------------------------------------------------------------- */ 283275970Scy/* playback channel interrupts */ 284275970Scy 285275970Scy#if 0 286275970Scystatic u_int32_t 287330567Sgordontr_testint(struct tr_chinfo *ch) 288330567Sgordon{ 289330567Sgordon struct tr_info *tr = ch->parent; 290275970Scy int bank, chan; 291275970Scy 292285612Sdelphij bank = (ch->index & 0x20) ? 1 : 0; 293285612Sdelphij chan = ch->index & 0x1f; 294285612Sdelphij return tr_rd(tr, bank? TR_REG_ADDRINTB : TR_REG_ADDRINTA, 4) & (1 << chan); 295275970Scy} 296330567Sgordon#endif 297330567Sgordon 298330567Sgordonstatic void 299330567Sgordontr_clrint(struct tr_chinfo *ch) 300275970Scy{ 301275970Scy struct tr_info *tr = ch->parent; 302330567Sgordon int bank, chan; 303330567Sgordon 304330567Sgordon bank = (ch->index & 0x20) ? 1 : 0; 305275970Scy chan = ch->index & 0x1f; 306330567Sgordon tr_wr(tr, bank? TR_REG_ADDRINTB : TR_REG_ADDRINTA, 1 << chan, 4); 307330567Sgordon} 308330567Sgordon 309275970Scystatic void 310275970Scytr_enaint(struct tr_chinfo *ch, int enable) 311275970Scy{ 312275970Scy struct tr_info *tr = ch->parent; 313275970Scy u_int32_t i, reg; 314275970Scy int bank, chan; 315275970Scy 316275970Scy snd_mtxlock(tr->lock); 317275970Scy bank = (ch->index & 0x20) ? 1 : 0; 318275970Scy chan = ch->index & 0x1f; 319275970Scy reg = bank? TR_REG_INTENB : TR_REG_INTENA; 320275970Scy 321275970Scy i = tr_rd(tr, reg, 4); 322275970Scy i &= ~(1 << chan); 323275970Scy i |= (enable? 1 : 0) << chan; 324330567Sgordon 325275970Scy tr_clrint(ch); 326330567Sgordon tr_wr(tr, reg, i, 4); 327330567Sgordon snd_mtxunlock(tr->lock); 328275970Scy} 329275970Scy 330275970Scy/* playback channels */ 331275970Scy 332275970Scystatic void 333275970Scytr_selch(struct tr_chinfo *ch) 334275970Scy{ 335330567Sgordon struct tr_info *tr = ch->parent; 336275970Scy int i; 337275970Scy 338330567Sgordon i = tr_rd(tr, TR_REG_CIR, 4); 339275970Scy i &= ~TR_CIR_MASK; 340275970Scy i |= ch->index & 0x3f; 341275970Scy tr_wr(tr, TR_REG_CIR, i, 4); 342275970Scy} 343330567Sgordon 344275970Scystatic void 345275970Scytr_startch(struct tr_chinfo *ch) 346275970Scy{ 347275970Scy struct tr_info *tr = ch->parent; 348275970Scy int bank, chan; 349275970Scy 350330567Sgordon bank = (ch->index & 0x20) ? 1 : 0; 351330567Sgordon chan = ch->index & 0x1f; 352330567Sgordon tr_wr(tr, bank? TR_REG_STARTB : TR_REG_STARTA, 1 << chan, 4); 353330567Sgordon} 354330567Sgordon 355330567Sgordonstatic void 356275970Scytr_stopch(struct tr_chinfo *ch) 357275970Scy{ 358275970Scy struct tr_info *tr = ch->parent; 359275970Scy int bank, chan; 360275970Scy 361275970Scy bank = (ch->index & 0x20) ? 1 : 0; 362275970Scy chan = ch->index & 0x1f; 363275970Scy tr_wr(tr, bank? TR_REG_STOPB : TR_REG_STOPA, 1 << chan, 4); 364275970Scy} 365330567Sgordon 366330567Sgordonstatic void 367330567Sgordontr_wrch(struct tr_chinfo *ch) 368330567Sgordon{ 369330567Sgordon struct tr_info *tr = ch->parent; 370330567Sgordon u_int32_t cr[TR_CHN_REGS], i; 371330567Sgordon 372330567Sgordon ch->gvsel &= 0x00000001; 373330567Sgordon ch->fmc &= 0x00000003; 374275970Scy ch->fms &= 0x0000000f; 375275970Scy ch->ctrl &= 0x0000000f; 376275970Scy ch->pan &= 0x0000007f; 377330567Sgordon ch->rvol &= 0x0000007f; 378330567Sgordon ch->cvol &= 0x0000007f; 379330567Sgordon ch->vol &= 0x000000ff; 380330567Sgordon ch->ec &= 0x00000fff; 381330567Sgordon ch->alpha &= 0x00000fff; 382330567Sgordon ch->delta &= 0x0000ffff; 383330567Sgordon ch->lba &= 0x3fffffff; 384330567Sgordon 385330567Sgordon cr[1]=ch->lba; 386330567Sgordon cr[3]=(ch->fmc<<14) | (ch->rvol<<7) | (ch->cvol); 387330567Sgordon cr[4]=(ch->gvsel<<31) | (ch->pan<<24) | (ch->vol<<16) | (ch->ctrl<<12) | (ch->ec); 388330567Sgordon 389330567Sgordon switch (tr->type) { 390330567Sgordon case SPA_PCI_ID: 391275970Scy case ALI_PCI_ID: 392330567Sgordon case TDX_PCI_ID: 393330567Sgordon ch->cso &= 0x0000ffff; 394330567Sgordon ch->eso &= 0x0000ffff; 395330567Sgordon cr[0]=(ch->cso<<16) | (ch->alpha<<4) | (ch->fms); 396330567Sgordon cr[2]=(ch->eso<<16) | (ch->delta); 397330567Sgordon break; 398330567Sgordon case TNX_PCI_ID: 399330567Sgordon ch->cso &= 0x00ffffff; 400330567Sgordon ch->eso &= 0x00ffffff; 401330567Sgordon cr[0]=((ch->delta & 0xff)<<24) | (ch->cso); 402330567Sgordon cr[2]=((ch->delta>>8)<<24) | (ch->eso); 403330567Sgordon cr[3]|=(ch->alpha<<20) | (ch->fms<<16) | (ch->fmc<<14); 404330567Sgordon break; 405330567Sgordon } 406330567Sgordon snd_mtxlock(tr->lock); 407330567Sgordon tr_selch(ch); 408330567Sgordon for (i=0; i<TR_CHN_REGS; i++) 409330567Sgordon tr_wr(tr, TR_REG_CHNBASE+(i<<2), cr[i], 4); 410330567Sgordon snd_mtxunlock(tr->lock); 411330567Sgordon} 412330567Sgordon 413330567Sgordonstatic void 414330567Sgordontr_rdch(struct tr_chinfo *ch) 415330567Sgordon{ 416330567Sgordon struct tr_info *tr = ch->parent; 417330567Sgordon u_int32_t cr[5], i; 418330567Sgordon 419330567Sgordon snd_mtxlock(tr->lock); 420330567Sgordon tr_selch(ch); 421330567Sgordon for (i=0; i<5; i++) 422330567Sgordon cr[i]=tr_rd(tr, TR_REG_CHNBASE+(i<<2), 4); 423330567Sgordon snd_mtxunlock(tr->lock); 424330567Sgordon 425330567Sgordon 426330567Sgordon ch->lba= (cr[1] & 0x3fffffff); 427330567Sgordon ch->fmc= (cr[3] & 0x0000c000) >> 14; 428330567Sgordon ch->rvol= (cr[3] & 0x00003f80) >> 7; 429330567Sgordon ch->cvol= (cr[3] & 0x0000007f); 430330567Sgordon ch->gvsel= (cr[4] & 0x80000000) >> 31; 431330567Sgordon ch->pan= (cr[4] & 0x7f000000) >> 24; 432330567Sgordon ch->vol= (cr[4] & 0x00ff0000) >> 16; 433330567Sgordon ch->ctrl= (cr[4] & 0x0000f000) >> 12; 434330567Sgordon ch->ec= (cr[4] & 0x00000fff); 435330567Sgordon switch(tr->type) { 436275970Scy case SPA_PCI_ID: 437330567Sgordon case ALI_PCI_ID: 438275970Scy case TDX_PCI_ID: 439330567Sgordon ch->cso= (cr[0] & 0xffff0000) >> 16; 440275970Scy ch->alpha= (cr[0] & 0x0000fff0) >> 4; 441275970Scy ch->fms= (cr[0] & 0x0000000f); 442275970Scy ch->eso= (cr[2] & 0xffff0000) >> 16; 443275970Scy ch->delta= (cr[2] & 0x0000ffff); 444330567Sgordon break; 445330567Sgordon case TNX_PCI_ID: 446275970Scy ch->cso= (cr[0] & 0x00ffffff); 447275970Scy ch->eso= (cr[2] & 0x00ffffff); 448275970Scy ch->delta= ((cr[2] & 0xff000000) >> 16) | ((cr[0] & 0xff000000) >> 24); 449275970Scy ch->alpha= (cr[3] & 0xfff00000) >> 20; 450275970Scy ch->fms= (cr[3] & 0x000f0000) >> 16; 451275970Scy break; 452275970Scy } 453275970Scy} 454275970Scy 455275970Scystatic u_int32_t 456275970Scytr_fmttobits(u_int32_t fmt) 457275970Scy{ 458275970Scy u_int32_t bits; 459275970Scy 460275970Scy bits = 0; 461275970Scy bits |= (fmt & AFMT_SIGNED)? 0x2 : 0; 462275970Scy bits |= (fmt & AFMT_STEREO)? 0x4 : 0; 463275970Scy bits |= (fmt & AFMT_16BIT)? 0x8 : 0; 464275970Scy 465275970Scy return bits; 466330567Sgordon} 467275970Scy 468330567Sgordon/* -------------------------------------------------------------------- */ 469275970Scy/* channel interface */ 470275970Scy 471330567Sgordonstatic void * 472330567Sgordontrpchan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel *c, int dir) 473330567Sgordon{ 474275970Scy struct tr_info *tr = devinfo; 475275970Scy struct tr_chinfo *ch; 476275970Scy 477330567Sgordon KASSERT(dir == PCMDIR_PLAY, ("trpchan_init: bad direction")); 478330567Sgordon ch = &tr->chinfo[tr->playchns]; 479275970Scy ch->index = tr->playchns++; 480275970Scy ch->buffer = b; 481275970Scy ch->parent = tr; 482275970Scy ch->channel = c; 483275970Scy if (sndbuf_alloc(ch->buffer, tr->parent_dmat, tr->bufsz) == -1) 484330567Sgordon return NULL; 485330567Sgordon 486275970Scy return ch; 487275970Scy} 488275970Scy 489275970Scystatic int 490275970Scytrpchan_setformat(kobj_t obj, void *data, u_int32_t format) 491275970Scy{ 492275970Scy struct tr_chinfo *ch = data; 493275970Scy 494275970Scy ch->ctrl = tr_fmttobits(format) | 0x01; 495275970Scy 496275970Scy return 0; 497330567Sgordon} 498275970Scy 499275970Scystatic int 500330567Sgordontrpchan_setspeed(kobj_t obj, void *data, u_int32_t speed) 501330567Sgordon{ 502330567Sgordon struct tr_chinfo *ch = data; 503275970Scy 504330567Sgordon ch->delta = (speed << 12) / 48000; 505275970Scy return (ch->delta * 48000) >> 12; 506275970Scy} 507275970Scy 508275970Scystatic int 509275970Scytrpchan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize) 510275970Scy{ 511275970Scy struct tr_chinfo *ch = data; 512275970Scy 513275970Scy sndbuf_resize(ch->buffer, 2, blocksize); 514275970Scy return blocksize; 515275970Scy} 516275970Scy 517275970Scystatic int 518275970Scytrpchan_trigger(kobj_t obj, void *data, int go) 519330567Sgordon{ 520330567Sgordon struct tr_chinfo *ch = data; 521275970Scy 522330567Sgordon if (go == PCMTRIG_EMLDMAWR || go == PCMTRIG_EMLDMARD) 523330567Sgordon return 0; 524330567Sgordon 525330567Sgordon if (go == PCMTRIG_START) { 526275970Scy ch->fmc = 3; 527330567Sgordon ch->fms = 0; 528330567Sgordon ch->ec = 0; 529275970Scy ch->alpha = 0; 530275970Scy ch->lba = vtophys(sndbuf_getbuf(ch->buffer)); 531275970Scy ch->cso = 0; 532275970Scy ch->eso = (sndbuf_getsize(ch->buffer) / sndbuf_getbps(ch->buffer)) - 1; 533330567Sgordon ch->rvol = ch->cvol = 0x7f; 534330567Sgordon ch->gvsel = 0; 535275970Scy ch->pan = 0; 536275970Scy ch->vol = 0; 537275970Scy ch->bufhalf = 0; 538275970Scy tr_wrch(ch); 539275970Scy tr_enaint(ch, 1); 540275970Scy tr_startch(ch); 541275970Scy ch->active = 1; 542275970Scy } else { 543275970Scy tr_stopch(ch); 544275970Scy ch->active = 0; 545330567Sgordon } 546330567Sgordon 547330567Sgordon return 0; 548330567Sgordon} 549330567Sgordon 550275970Scystatic int 551275970Scytrpchan_getptr(kobj_t obj, void *data) 552275970Scy{ 553330567Sgordon struct tr_chinfo *ch = data; 554275970Scy 555330567Sgordon tr_rdch(ch); 556330567Sgordon return ch->cso * sndbuf_getbps(ch->buffer); 557330567Sgordon} 558330567Sgordon 559330567Sgordonstatic struct pcmchan_caps * 560330567Sgordontrpchan_getcaps(kobj_t obj, void *data) 561330567Sgordon{ 562330567Sgordon return &tr_playcaps; 563330567Sgordon} 564330567Sgordon 565330567Sgordonstatic kobj_method_t trpchan_methods[] = { 566275970Scy KOBJMETHOD(channel_init, trpchan_init), 567330567Sgordon KOBJMETHOD(channel_setformat, trpchan_setformat), 568275970Scy KOBJMETHOD(channel_setspeed, trpchan_setspeed), 569275970Scy KOBJMETHOD(channel_setblocksize, trpchan_setblocksize), 570275970Scy KOBJMETHOD(channel_trigger, trpchan_trigger), 571275970Scy KOBJMETHOD(channel_getptr, trpchan_getptr), 572275970Scy KOBJMETHOD(channel_getcaps, trpchan_getcaps), 573275970Scy { 0, 0 } 574330567Sgordon}; 575275970ScyCHANNEL_DECLARE(trpchan); 576275970Scy 577275970Scy/* -------------------------------------------------------------------- */ 578330567Sgordon/* rec channel interface */ 579330567Sgordon 580330567Sgordonstatic void * 581330567Sgordontrrchan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel *c, int dir) 582275970Scy{ 583275970Scy struct tr_info *tr = devinfo; 584275970Scy struct tr_rchinfo *ch; 585275970Scy 586330567Sgordon KASSERT(dir == PCMDIR_REC, ("trrchan_init: bad direction")); 587330567Sgordon ch = &tr->recchinfo; 588330567Sgordon ch->buffer = b; 589330567Sgordon ch->parent = tr; 590330567Sgordon ch->channel = c; 591330567Sgordon if (sndbuf_alloc(ch->buffer, tr->parent_dmat, tr->bufsz) == -1) 592330567Sgordon return NULL; 593330567Sgordon 594330567Sgordon return ch; 595330567Sgordon} 596330567Sgordon 597275970Scystatic int 598330567Sgordontrrchan_setformat(kobj_t obj, void *data, u_int32_t format) 599330567Sgordon{ 600330567Sgordon struct tr_rchinfo *ch = data; 601275970Scy struct tr_info *tr = ch->parent; 602275970Scy u_int32_t i, bits; 603275970Scy 604330567Sgordon bits = tr_fmttobits(format); 605275970Scy /* set # of samples between interrupts */ 606275970Scy i = (sndbuf_runsz(ch->buffer) >> ((bits & 0x08)? 1 : 0)) - 1; 607275970Scy tr_wr(tr, TR_REG_SBBL, i | (i << 16), 4); 608275970Scy /* set sample format */ 609330567Sgordon i = 0x18 | (bits << 4); 610275970Scy tr_wr(tr, TR_REG_SBCTRL, i, 1); 611275970Scy 612275970Scy return 0; 613330567Sgordon 614330567Sgordon} 615330567Sgordon 616330567Sgordonstatic int 617330567Sgordontrrchan_setspeed(kobj_t obj, void *data, u_int32_t speed) 618330567Sgordon{ 619275970Scy struct tr_rchinfo *ch = data; 620275970Scy struct tr_info *tr = ch->parent; 621330567Sgordon 622330567Sgordon /* setup speed */ 623330567Sgordon ch->delta = (48000 << 12) / speed; 624330567Sgordon tr_wr(tr, TR_REG_SBDELTA, ch->delta, 2); 625330567Sgordon 626330567Sgordon /* return closest possible speed */ 627330567Sgordon return (48000 << 12) / ch->delta; 628330567Sgordon} 629330567Sgordon 630330567Sgordonstatic int 631330567Sgordontrrchan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize) 632330567Sgordon{ 633330567Sgordon struct tr_rchinfo *ch = data; 634330567Sgordon 635330567Sgordon sndbuf_resize(ch->buffer, 2, blocksize); 636275970Scy 637330567Sgordon return blocksize; 638275970Scy} 639275970Scy 640275970Scystatic int 641275970Scytrrchan_trigger(kobj_t obj, void *data, int go) 642275970Scy{ 643275970Scy struct tr_rchinfo *ch = data; 644330567Sgordon struct tr_info *tr = ch->parent; 645275970Scy u_int32_t i; 646330567Sgordon 647275970Scy if (go == PCMTRIG_EMLDMAWR || go == PCMTRIG_EMLDMARD) 648275970Scy return 0; 649275970Scy 650275970Scy if (go == PCMTRIG_START) { 651275970Scy /* set up dma mode regs */ 652275970Scy tr_wr(tr, TR_REG_DMAR15, 0, 1); 653275970Scy i = tr_rd(tr, TR_REG_DMAR11, 1) & 0x03; 654275970Scy tr_wr(tr, TR_REG_DMAR11, i | 0x54, 1); 655330567Sgordon /* set up base address */ 656330567Sgordon tr_wr(tr, TR_REG_DMAR0, vtophys(sndbuf_getbuf(ch->buffer)), 4); 657275970Scy /* set up buffer size */ 658330567Sgordon i = tr_rd(tr, TR_REG_DMAR4, 4) & ~0x00ffffff; 659275970Scy tr_wr(tr, TR_REG_DMAR4, i | (sndbuf_runsz(ch->buffer) - 1), 4); 660275970Scy /* start */ 661275970Scy tr_wr(tr, TR_REG_SBCTRL, tr_rd(tr, TR_REG_SBCTRL, 1) | 1, 1); 662275970Scy ch->active = 1; 663275970Scy } else { 664275970Scy tr_wr(tr, TR_REG_SBCTRL, tr_rd(tr, TR_REG_SBCTRL, 1) & ~7, 1); 665275970Scy ch->active = 0; 666275970Scy } 667275970Scy 668275970Scy /* return 0 if ok */ 669275970Scy return 0; 670275970Scy} 671275970Scy 672275970Scystatic int 673275970Scytrrchan_getptr(kobj_t obj, void *data) 674275970Scy{ 675275970Scy struct tr_rchinfo *ch = data; 676275970Scy struct tr_info *tr = ch->parent; 677330567Sgordon 678275970Scy /* return current byte offset of channel */ 679275970Scy return tr_rd(tr, TR_REG_DMAR0, 4) - vtophys(sndbuf_getbuf(ch->buffer)); 680330567Sgordon} 681275970Scy 682275970Scystatic struct pcmchan_caps * 683275970Scytrrchan_getcaps(kobj_t obj, void *data) 684275970Scy{ 685275970Scy return &tr_reccaps; 686275970Scy} 687275970Scy 688275970Scystatic kobj_method_t trrchan_methods[] = { 689275970Scy KOBJMETHOD(channel_init, trrchan_init), 690275970Scy KOBJMETHOD(channel_setformat, trrchan_setformat), 691275970Scy KOBJMETHOD(channel_setspeed, trrchan_setspeed), 692330567Sgordon KOBJMETHOD(channel_setblocksize, trrchan_setblocksize), 693330567Sgordon KOBJMETHOD(channel_trigger, trrchan_trigger), 694330567Sgordon KOBJMETHOD(channel_getptr, trrchan_getptr), 695330567Sgordon KOBJMETHOD(channel_getcaps, trrchan_getcaps), 696330567Sgordon { 0, 0 } 697275970Scy}; 698275970ScyCHANNEL_DECLARE(trrchan); 699330567Sgordon 700330567Sgordon/* -------------------------------------------------------------------- */ 701275970Scy/* The interrupt handler */ 702275970Scy 703275970Scystatic void 704275970Scytr_intr(void *p) 705275970Scy{ 706275970Scy struct tr_info *tr = (struct tr_info *)p; 707275970Scy struct tr_chinfo *ch; 708275970Scy u_int32_t active, mask, bufhalf, chnum, intsrc; 709275970Scy int tmp; 710275970Scy 711275970Scy intsrc = tr_rd(tr, TR_REG_MISCINT, 4); 712275970Scy if (intsrc & TR_INT_ADDR) { 713275970Scy chnum = 0; 714275970Scy while (chnum < 64) { 715275970Scy mask = 0x00000001; 716275970Scy active = tr_rd(tr, (chnum < 32)? TR_REG_ADDRINTA : TR_REG_ADDRINTB, 4); 717275970Scy bufhalf = tr_rd(tr, (chnum < 32)? TR_REG_CSPF_A : TR_REG_CSPF_B, 4); 718275970Scy if (active) { 719275970Scy do { 720275970Scy if (active & mask) { 721275970Scy tmp = (bufhalf & mask)? 1 : 0; 722275970Scy if (chnum < tr->playchns) { 723275970Scy ch = &tr->chinfo[chnum]; 724275970Scy /* printf("%d @ %d, ", chnum, trpchan_getptr(NULL, ch)); */ 725275970Scy if (ch->bufhalf != tmp) { 726275970Scy chn_intr(ch->channel); 727275970Scy ch->bufhalf = tmp; 728275970Scy } 729275970Scy } 730275970Scy } 731330567Sgordon chnum++; 732275970Scy mask <<= 1; 733275970Scy } while (chnum & 31); 734275970Scy } else 735275970Scy chnum += 32; 736275970Scy 737330567Sgordon tr_wr(tr, (chnum <= 32)? TR_REG_ADDRINTA : TR_REG_ADDRINTB, active, 4); 738275970Scy } 739275970Scy } 740275970Scy if (intsrc & TR_INT_SB) { 741275970Scy chn_intr(tr->recchinfo.channel); 742275970Scy tr_rd(tr, TR_REG_SBR9, 1); 743275970Scy tr_rd(tr, TR_REG_SBR10, 1); 744275970Scy } 745275970Scy} 746275970Scy 747275970Scy/* -------------------------------------------------------------------- */ 748330567Sgordon 749275970Scy/* 750275970Scy * Probe and attach the card 751330567Sgordon */ 752275970Scy 753275970Scystatic int 754275970Scytr_init(struct tr_info *tr) 755275970Scy{ 756275970Scy switch (tr->type) { 757275970Scy case SPA_PCI_ID: 758275970Scy tr_wr(tr, SPA_REG_GPIO, 0, 4); 759275970Scy tr_wr(tr, SPA_REG_CODECST, SPA_RST_OFF, 4); 760275970Scy break; 761330567Sgordon case TDX_PCI_ID: 762275970Scy tr_wr(tr, TDX_REG_CODECST, TDX_CDC_ON, 4); 763275970Scy break; 764275970Scy case TNX_PCI_ID: 765275970Scy tr_wr(tr, TNX_REG_CODECST, TNX_CDC_ON, 4); 766275970Scy break; 767275970Scy } 768275970Scy 769275970Scy tr_wr(tr, TR_REG_CIR, TR_CIR_MIDENA | TR_CIR_ADDRENA, 4); 770275970Scy return 0; 771275970Scy} 772275970Scy 773275970Scystatic int 774330567Sgordontr_pci_probe(device_t dev) 775330567Sgordon{ 776275970Scy switch (pci_get_devid(dev)) { 777275970Scy case SPA_PCI_ID: 778275970Scy device_set_desc(dev, "SiS 7018"); 779275970Scy return 0; 780275970Scy case ALI_PCI_ID: 781275970Scy device_set_desc(dev, "Acer Labs M5451"); 782275970Scy return 0; 783275970Scy case TDX_PCI_ID: 784275970Scy device_set_desc(dev, "Trident 4DWave DX"); 785275970Scy return 0; 786275970Scy case TNX_PCI_ID: 787275970Scy device_set_desc(dev, "Trident 4DWave NX"); 788275970Scy return 0; 789275970Scy } 790330567Sgordon 791275970Scy return ENXIO; 792275970Scy} 793330567Sgordon 794275970Scystatic int 795275970Scytr_pci_attach(device_t dev) 796275970Scy{ 797275970Scy u_int32_t data; 798275970Scy struct tr_info *tr; 799330567Sgordon struct ac97_info *codec = 0; 800275970Scy int i; 801275970Scy char status[SND_STATUSLEN]; 802275970Scy 803275970Scy if ((tr = malloc(sizeof(*tr), M_DEVBUF, M_NOWAIT | M_ZERO)) == NULL) { 804330567Sgordon device_printf(dev, "cannot allocate softc\n"); 805275970Scy return ENXIO; 806275970Scy } 807275970Scy 808275970Scy tr->type = pci_get_devid(dev); 809275970Scy tr->rev = pci_get_revid(dev); 810275970Scy tr->lock = snd_mtxcreate(device_get_nameunit(dev), "sound softc"); 811275970Scy 812275970Scy data = pci_read_config(dev, PCIR_COMMAND, 2); 813275970Scy data |= (PCIM_CMD_PORTEN|PCIM_CMD_MEMEN|PCIM_CMD_BUSMASTEREN); 814275970Scy pci_write_config(dev, PCIR_COMMAND, data, 2); 815330567Sgordon data = pci_read_config(dev, PCIR_COMMAND, 2); 816275970Scy 817275970Scy tr->regid = PCIR_MAPS; 818275970Scy tr->regtype = SYS_RES_IOPORT; 819275970Scy tr->reg = bus_alloc_resource(dev, tr->regtype, &tr->regid, 0, ~0, 1, RF_ACTIVE); 820275970Scy if (tr->reg) { 821275970Scy tr->st = rman_get_bustag(tr->reg); 822275970Scy tr->sh = rman_get_bushandle(tr->reg); 823275970Scy } else { 824275970Scy device_printf(dev, "unable to map register space\n"); 825275970Scy goto bad; 826275970Scy } 827275970Scy 828275970Scy tr->bufsz = pcm_getbuffersize(dev, 4096, TR_DEFAULT_BUFSZ, 65536); 829275970Scy 830275970Scy if (tr_init(tr) == -1) { 831275970Scy device_printf(dev, "unable to initialize the card\n"); 832298770Sdelphij goto bad; 833298770Sdelphij } 834275970Scy tr->playchns = 0; 835275970Scy 836275970Scy codec = AC97_CREATE(dev, tr, tr_ac97); 837275970Scy if (codec == NULL) goto bad; 838275970Scy if (mixer_init(dev, ac97_getmixerclass(), codec) == -1) goto bad; 839275970Scy 840275970Scy tr->irqid = 0; 841275970Scy tr->irq = bus_alloc_resource(dev, SYS_RES_IRQ, &tr->irqid, 842275970Scy 0, ~0, 1, RF_ACTIVE | RF_SHAREABLE); 843275970Scy if (!tr->irq || snd_setup_intr(dev, tr->irq, INTR_MPSAFE, tr_intr, tr, &tr->ih)) { 844275970Scy device_printf(dev, "unable to map interrupt\n"); 845275970Scy goto bad; 846275970Scy } 847275970Scy 848275970Scy if (bus_dma_tag_create(/*parent*/NULL, /*alignment*/2, /*boundary*/0, 849275970Scy /*lowaddr*/BUS_SPACE_MAXADDR_32BIT, 850275970Scy /*highaddr*/BUS_SPACE_MAXADDR, 851275970Scy /*filter*/NULL, /*filterarg*/NULL, 852275970Scy /*maxsize*/tr->bufsz, /*nsegments*/1, /*maxsegz*/0x3ffff, 853275970Scy /*flags*/0, &tr->parent_dmat) != 0) { 854275970Scy device_printf(dev, "unable to create dma tag\n"); 855275970Scy goto bad; 856275970Scy } 857275970Scy 858330567Sgordon snprintf(status, 64, "at io 0x%lx irq %ld", 859275970Scy rman_get_start(tr->reg), rman_get_start(tr->irq)); 860275970Scy 861330567Sgordon if (pcm_register(dev, tr, TR_MAXPLAYCH, 1)) goto bad; 862275970Scy pcm_addchan(dev, PCMDIR_REC, &trrchan_class, tr); 863330567Sgordon for (i = 0; i < TR_MAXPLAYCH; i++) 864275970Scy pcm_addchan(dev, PCMDIR_PLAY, &trpchan_class, tr); 865275970Scy pcm_setstatus(dev, status); 866275970Scy 867275970Scy return 0; 868275970Scy 869275970Scybad: 870275970Scy if (codec) ac97_destroy(codec); 871275970Scy if (tr->reg) bus_release_resource(dev, tr->regtype, tr->regid, tr->reg); 872275970Scy if (tr->ih) bus_teardown_intr(dev, tr->irq, tr->ih); 873275970Scy if (tr->irq) bus_release_resource(dev, SYS_RES_IRQ, tr->irqid, tr->irq); 874275970Scy if (tr->parent_dmat) bus_dma_tag_destroy(tr->parent_dmat); 875275970Scy if (tr->lock) snd_mtxfree(tr->lock); 876275970Scy free(tr, M_DEVBUF); 877275970Scy return ENXIO; 878275970Scy} 879298770Sdelphij 880275970Scystatic int 881275970Scytr_pci_detach(device_t dev) 882275970Scy{ 883275970Scy int r; 884275970Scy struct tr_info *tr; 885275970Scy 886275970Scy r = pcm_unregister(dev); 887275970Scy if (r) 888275970Scy return r; 889275970Scy 890275970Scy tr = pcm_getdevinfo(dev); 891275970Scy bus_release_resource(dev, tr->regtype, tr->regid, tr->reg); 892275970Scy bus_teardown_intr(dev, tr->irq, tr->ih); 893275970Scy bus_release_resource(dev, SYS_RES_IRQ, tr->irqid, tr->irq); 894275970Scy bus_dma_tag_destroy(tr->parent_dmat); 895275970Scy snd_mtxfree(tr->lock); 896275970Scy free(tr, M_DEVBUF); 897298770Sdelphij 898298770Sdelphij return 0; 899298770Sdelphij} 900298770Sdelphij 901275970Scystatic int 902275970Scytr_pci_suspend(device_t dev) 903275970Scy{ 904275970Scy int i; 905275970Scy struct tr_info *tr; 906275970Scy 907275970Scy tr = pcm_getdevinfo(dev); 908275970Scy 909275970Scy for (i = 0; i < tr->playchns; i++) { 910275970Scy tr->chinfo[i].was_active = tr->chinfo[i].active; 911275970Scy if (tr->chinfo[i].active) { 912275970Scy trpchan_trigger(NULL, &tr->chinfo[i], PCMTRIG_STOP); 913275970Scy } 914330567Sgordon } 915275970Scy 916275970Scy tr->recchinfo.was_active = tr->recchinfo.active; 917275970Scy if (tr->recchinfo.active) { 918275970Scy trrchan_trigger(NULL, &tr->recchinfo, PCMTRIG_STOP); 919275970Scy } 920275970Scy 921275970Scy return 0; 922330567Sgordon} 923275970Scy 924275970Scystatic int 925275970Scytr_pci_resume(device_t dev) 926275970Scy{ 927275970Scy int i; 928275970Scy struct tr_info *tr; 929275970Scy 930275970Scy tr = pcm_getdevinfo(dev); 931275970Scy 932275970Scy if (tr_init(tr) == -1) { 933275970Scy device_printf(dev, "unable to initialize the card\n"); 934275970Scy return ENXIO; 935330567Sgordon } 936275970Scy 937275970Scy if (mixer_reinit(dev) == -1) { 938275970Scy device_printf(dev, "unable to initialize the mixer\n"); 939275970Scy return ENXIO; 940275970Scy } 941275970Scy 942275970Scy for (i = 0; i < tr->playchns; i++) { 943330567Sgordon if (tr->chinfo[i].was_active) { 944275970Scy trpchan_trigger(NULL, &tr->chinfo[i], PCMTRIG_START); 945275970Scy } 946275970Scy } 947275970Scy 948275970Scy if (tr->recchinfo.was_active) { 949275970Scy trrchan_trigger(NULL, &tr->recchinfo, PCMTRIG_START); 950275970Scy } 951275970Scy 952275970Scy return 0; 953275970Scy} 954275970Scy 955275970Scystatic device_method_t tr_methods[] = { 956275970Scy /* Device interface */ 957275970Scy DEVMETHOD(device_probe, tr_pci_probe), 958275970Scy DEVMETHOD(device_attach, tr_pci_attach), 959275970Scy DEVMETHOD(device_detach, tr_pci_detach), 960275970Scy DEVMETHOD(device_suspend, tr_pci_suspend), 961275970Scy DEVMETHOD(device_resume, tr_pci_resume), 962275970Scy { 0, 0 } 963275970Scy}; 964275970Scy 965275970Scystatic driver_t tr_driver = { 966275970Scy "pcm", 967275970Scy tr_methods, 968275970Scy PCM_SOFTC_SIZE, 969275970Scy}; 970275970Scy 971275970ScyDRIVER_MODULE(snd_t4dwave, pci, tr_driver, pcm_devclass, 0, 0); 972275970ScyMODULE_DEPEND(snd_t4dwave, snd_pcm, PCM_MINVER, PCM_PREFVER, PCM_MAXVER); 973275970ScyMODULE_VERSION(snd_t4dwave, 1); 974275970Scy