t4dwave.c revision 74763
150724Scg/* 250724Scg * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk> 350724Scg * All rights reserved. 450724Scg * 550724Scg * Redistribution and use in source and binary forms, with or without 650724Scg * modification, are permitted provided that the following conditions 750724Scg * are met: 850724Scg * 1. Redistributions of source code must retain the above copyright 950724Scg * notice, this list of conditions and the following disclaimer. 1050724Scg * 2. Redistributions in binary form must reproduce the above copyright 1150724Scg * notice, this list of conditions and the following disclaimer in the 1250724Scg * documentation and/or other materials provided with the distribution. 1350724Scg * 1450724Scg * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1550724Scg * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1650724Scg * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1750724Scg * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1850724Scg * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1950724Scg * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2050724Scg * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2150724Scg * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHERIN CONTRACT, STRICT 2250724Scg * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2350724Scg * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THEPOSSIBILITY OF 2450724Scg * SUCH DAMAGE. 2550724Scg * 2650733Speter * $FreeBSD: head/sys/dev/sound/pci/t4dwave.c 74763 2001-03-24 23:10:29Z cg $ 2750724Scg */ 2850724Scg 2953465Scg#include <dev/sound/pcm/sound.h> 3053465Scg#include <dev/sound/pcm/ac97.h> 3153465Scg#include <dev/sound/pci/t4dwave.h> 3250724Scg 3350724Scg#include <pci/pcireg.h> 3450724Scg#include <pci/pcivar.h> 3550724Scg 3650724Scg/* -------------------------------------------------------------------- */ 3750724Scg 3854459Scg#define TDX_PCI_ID 0x20001023 3954459Scg#define TNX_PCI_ID 0x20011023 4054459Scg 4171503Scg#define TR_BUFFSIZE 0x1000 4254459Scg#define TR_TIMEOUT_CDC 0xffff 4354459Scg#define TR_MAXPLAYCH 4 4454459Scg 4550724Scgstruct tr_info; 4650724Scg 4750724Scg/* channel registers */ 4850724Scgstruct tr_chinfo { 4950724Scg u_int32_t cso, alpha, fms, fmc, ec; 5050724Scg u_int32_t lba; 5150724Scg u_int32_t eso, delta; 5250724Scg u_int32_t rvol, cvol; 5350724Scg u_int32_t gvsel, pan, vol, ctrl; 5471503Scg int index, bufhalf; 5574763Scg struct snd_dbuf *buffer; 5674763Scg struct pcm_channel *channel; 5750724Scg struct tr_info *parent; 5850724Scg}; 5950724Scg 6070325Scgstruct tr_rchinfo { 6170325Scg u_int32_t delta; 6274763Scg struct snd_dbuf *buffer; 6374763Scg struct pcm_channel *channel; 6470325Scg struct tr_info *parent; 6570325Scg}; 6670325Scg 6750724Scg/* device private data */ 6850724Scgstruct tr_info { 6950724Scg u_int32_t type; 7050724Scg 7150724Scg bus_space_tag_t st; 7250724Scg bus_space_handle_t sh; 7350724Scg bus_dma_tag_t parent_dmat; 7450724Scg 7550724Scg struct resource *reg, *irq; 7650724Scg int regtype, regid, irqid; 7750724Scg void *ih; 7850724Scg 7974763Scg void *lock; 8074763Scg 8150724Scg u_int32_t playchns; 8250724Scg struct tr_chinfo chinfo[TR_MAXPLAYCH]; 8370325Scg struct tr_rchinfo recchinfo; 8450724Scg}; 8550724Scg 8650724Scg/* -------------------------------------------------------------------- */ 8750724Scg 8864881Scgstatic u_int32_t tr_recfmt[] = { 8964881Scg AFMT_U8, 9064881Scg AFMT_STEREO | AFMT_U8, 9164881Scg AFMT_S8, 9264881Scg AFMT_STEREO | AFMT_S8, 9364881Scg AFMT_S16_LE, 9464881Scg AFMT_STEREO | AFMT_S16_LE, 9564881Scg AFMT_U16_LE, 9664881Scg AFMT_STEREO | AFMT_U16_LE, 9764881Scg 0 9850724Scg}; 9974763Scgstatic struct pcmchan_caps tr_reccaps = {4000, 48000, tr_recfmt, 0}; 10050724Scg 10164881Scgstatic u_int32_t tr_playfmt[] = { 10264881Scg AFMT_U8, 10364881Scg AFMT_STEREO | AFMT_U8, 10464881Scg AFMT_S8, 10564881Scg AFMT_STEREO | AFMT_S8, 10664881Scg AFMT_S16_LE, 10764881Scg AFMT_STEREO | AFMT_S16_LE, 10864881Scg AFMT_U16_LE, 10964881Scg AFMT_STEREO | AFMT_U16_LE, 11064881Scg 0 11150724Scg}; 11274763Scgstatic struct pcmchan_caps tr_playcaps = {4000, 48000, tr_playfmt, 0}; 11350724Scg 11450724Scg/* -------------------------------------------------------------------- */ 11550724Scg 11650724Scg/* Hardware */ 11750724Scg 11850724Scgstatic u_int32_t 11950724Scgtr_rd(struct tr_info *tr, int regno, int size) 12050724Scg{ 12150724Scg switch(size) { 12250724Scg case 1: 12350724Scg return bus_space_read_1(tr->st, tr->sh, regno); 12450724Scg case 2: 12550724Scg return bus_space_read_2(tr->st, tr->sh, regno); 12650724Scg case 4: 12750724Scg return bus_space_read_4(tr->st, tr->sh, regno); 12850724Scg default: 12950724Scg return 0xffffffff; 13050724Scg } 13150724Scg} 13250724Scg 13350724Scgstatic void 13450724Scgtr_wr(struct tr_info *tr, int regno, u_int32_t data, int size) 13550724Scg{ 13650724Scg switch(size) { 13750724Scg case 1: 13850724Scg bus_space_write_1(tr->st, tr->sh, regno, data); 13950724Scg break; 14050724Scg case 2: 14150724Scg bus_space_write_2(tr->st, tr->sh, regno, data); 14250724Scg break; 14350724Scg case 4: 14450724Scg bus_space_write_4(tr->st, tr->sh, regno, data); 14550724Scg break; 14650724Scg } 14750724Scg} 14850724Scg 14970134Scg/* -------------------------------------------------------------------- */ 15050724Scg/* ac97 codec */ 15150724Scg 15270134Scgstatic int 15370134Scgtr_rdcd(kobj_t obj, void *devinfo, int regno) 15450724Scg{ 15550724Scg struct tr_info *tr = (struct tr_info *)devinfo; 15650724Scg int i, j, treg, trw; 15750724Scg 15850724Scg switch (tr->type) { 15950724Scg case TDX_PCI_ID: 16050724Scg treg=TDX_REG_CODECRD; 16150724Scg trw=TDX_CDC_RWSTAT; 16250724Scg break; 16350724Scg case TNX_PCI_ID: 16450724Scg treg=(regno & 0x100)? TNX_REG_CODEC2RD : TNX_REG_CODEC1RD; 16550724Scg trw=TNX_CDC_RWSTAT; 16650724Scg break; 16750724Scg default: 16850724Scg printf("!!! tr_rdcd defaulted !!!\n"); 16970134Scg return -1; 17050724Scg } 17150724Scg 17250724Scg regno &= 0x7f; 17374763Scg snd_mtxlock(tr->lock); 17450724Scg tr_wr(tr, treg, regno | trw, 4); 17550724Scg j=trw; 17650724Scg for (i=TR_TIMEOUT_CDC; (i > 0) && (j & trw); i--) j=tr_rd(tr, treg, 4); 17774763Scg snd_mtxunlock(tr->lock); 17850724Scg if (i == 0) printf("codec timeout during read of register %x\n", regno); 17950724Scg return (j >> TR_CDC_DATA) & 0xffff; 18050724Scg} 18150724Scg 18270134Scgstatic int 18370134Scgtr_wrcd(kobj_t obj, void *devinfo, int regno, u_int32_t data) 18450724Scg{ 18550724Scg struct tr_info *tr = (struct tr_info *)devinfo; 18650724Scg int i, j, treg, trw; 18750724Scg 18850724Scg switch (tr->type) { 18950724Scg case TDX_PCI_ID: 19050724Scg treg=TDX_REG_CODECWR; 19150724Scg trw=TDX_CDC_RWSTAT; 19250724Scg break; 19350724Scg case TNX_PCI_ID: 19450724Scg treg=TNX_REG_CODECWR; 19550724Scg trw=TNX_CDC_RWSTAT | ((regno & 0x100)? TNX_CDC_SEC : 0); 19650724Scg break; 19750724Scg default: 19850724Scg printf("!!! tr_wrcd defaulted !!!"); 19970134Scg return -1; 20050724Scg } 20150724Scg 20250724Scg regno &= 0x7f; 20350724Scg#if 0 20450724Scg printf("tr_wrcd: reg %x was %x", regno, tr_rdcd(devinfo, regno)); 20550724Scg#endif 20650724Scg j=trw; 20774763Scg snd_mtxlock(tr->lock); 20850724Scg for (i=TR_TIMEOUT_CDC; (i>0) && (j & trw); i--) j=tr_rd(tr, treg, 4); 20950724Scg tr_wr(tr, treg, (data << TR_CDC_DATA) | regno | trw, 4); 21050724Scg#if 0 21150724Scg printf(" - wrote %x, now %x\n", data, tr_rdcd(devinfo, regno)); 21250724Scg#endif 21374763Scg snd_mtxunlock(tr->lock); 21450724Scg if (i==0) printf("codec timeout writing %x, data %x\n", regno, data); 21570134Scg return (i > 0)? 0 : -1; 21650724Scg} 21750724Scg 21870134Scgstatic kobj_method_t tr_ac97_methods[] = { 21970134Scg KOBJMETHOD(ac97_read, tr_rdcd), 22070134Scg KOBJMETHOD(ac97_write, tr_wrcd), 22170134Scg { 0, 0 } 22270134Scg}; 22370134ScgAC97_DECLARE(tr_ac97); 22470134Scg 22570134Scg/* -------------------------------------------------------------------- */ 22650724Scg/* playback channel interrupts */ 22750724Scg 22871503Scg#if 0 22950724Scgstatic u_int32_t 23070325Scgtr_testint(struct tr_chinfo *ch) 23150724Scg{ 23270325Scg struct tr_info *tr = ch->parent; 23370325Scg int bank, chan; 23470325Scg 23570325Scg bank = (ch->index & 0x20) ? 1 : 0; 23670325Scg chan = ch->index & 0x1f; 23770325Scg return tr_rd(tr, bank? TR_REG_ADDRINTB : TR_REG_ADDRINTA, 4) & (1 << chan); 23850724Scg} 23971503Scg#endif 24050724Scg 24150724Scgstatic void 24270325Scgtr_clrint(struct tr_chinfo *ch) 24350724Scg{ 24470325Scg struct tr_info *tr = ch->parent; 24570325Scg int bank, chan; 24670325Scg 24770325Scg bank = (ch->index & 0x20) ? 1 : 0; 24870325Scg chan = ch->index & 0x1f; 24970325Scg tr_wr(tr, bank? TR_REG_ADDRINTB : TR_REG_ADDRINTA, 1 << chan, 4); 25050724Scg} 25150724Scg 25250724Scgstatic void 25370325Scgtr_enaint(struct tr_chinfo *ch, int enable) 25450724Scg{ 25570325Scg struct tr_info *tr = ch->parent; 25670325Scg u_int32_t i, reg; 25770325Scg int bank, chan; 25870325Scg 25974763Scg snd_mtxlock(tr->lock); 26070325Scg bank = (ch->index & 0x20) ? 1 : 0; 26170325Scg chan = ch->index & 0x1f; 26270325Scg reg = bank? TR_REG_INTENB : TR_REG_INTENA; 26370325Scg 26470325Scg i = tr_rd(tr, reg, 4); 26570325Scg i &= ~(1 << chan); 26670325Scg i |= (enable? 1 : 0) << chan; 26770325Scg 26870325Scg tr_clrint(ch); 26950724Scg tr_wr(tr, reg, i, 4); 27074763Scg snd_mtxunlock(tr->lock); 27150724Scg} 27250724Scg 27350724Scg/* playback channels */ 27450724Scg 27550724Scgstatic void 27670325Scgtr_selch(struct tr_chinfo *ch) 27750724Scg{ 27870325Scg struct tr_info *tr = ch->parent; 27970325Scg int i; 28070325Scg 28170325Scg i = tr_rd(tr, TR_REG_CIR, 4); 28250724Scg i &= ~TR_CIR_MASK; 28370325Scg i |= ch->index & 0x3f; 28450724Scg tr_wr(tr, TR_REG_CIR, i, 4); 28550724Scg} 28650724Scg 28750724Scgstatic void 28870325Scgtr_startch(struct tr_chinfo *ch) 28950724Scg{ 29070325Scg struct tr_info *tr = ch->parent; 29170325Scg int bank, chan; 29270325Scg 29370325Scg bank = (ch->index & 0x20) ? 1 : 0; 29470325Scg chan = ch->index & 0x1f; 29570325Scg tr_wr(tr, bank? TR_REG_STARTB : TR_REG_STARTA, 1 << chan, 4); 29650724Scg} 29750724Scg 29850724Scgstatic void 29970325Scgtr_stopch(struct tr_chinfo *ch) 30050724Scg{ 30170325Scg struct tr_info *tr = ch->parent; 30270325Scg int bank, chan; 30370325Scg 30470325Scg bank = (ch->index & 0x20) ? 1 : 0; 30570325Scg chan = ch->index & 0x1f; 30670325Scg tr_wr(tr, bank? TR_REG_STOPB : TR_REG_STOPA, 1 << chan, 4); 30750724Scg} 30850724Scg 30950724Scgstatic void 31070325Scgtr_wrch(struct tr_chinfo *ch) 31150724Scg{ 31270325Scg struct tr_info *tr = ch->parent; 31350724Scg u_int32_t cr[TR_CHN_REGS], i; 31450724Scg 31550724Scg ch->gvsel &= 0x00000001; 31650724Scg ch->fmc &= 0x00000003; 31750724Scg ch->fms &= 0x0000000f; 31850724Scg ch->ctrl &= 0x0000000f; 31950724Scg ch->pan &= 0x0000007f; 32050724Scg ch->rvol &= 0x0000007f; 32150724Scg ch->cvol &= 0x0000007f; 32250724Scg ch->vol &= 0x000000ff; 32350724Scg ch->ec &= 0x00000fff; 32450724Scg ch->alpha &= 0x00000fff; 32550724Scg ch->delta &= 0x0000ffff; 32650724Scg ch->lba &= 0x3fffffff; 32750724Scg 32850724Scg cr[1]=ch->lba; 32971503Scg cr[3]=(ch->fmc<<14) | (ch->rvol<<7) | (ch->cvol); 33071503Scg cr[4]=(ch->gvsel<<31) | (ch->pan<<24) | (ch->vol<<16) | (ch->ctrl<<12) | (ch->ec); 33150724Scg 33250724Scg switch (tr->type) { 33350724Scg case TDX_PCI_ID: 33450724Scg ch->cso &= 0x0000ffff; 33550724Scg ch->eso &= 0x0000ffff; 33650724Scg cr[0]=(ch->cso<<16) | (ch->alpha<<4) | (ch->fms); 33750724Scg cr[2]=(ch->eso<<16) | (ch->delta); 33850724Scg break; 33950724Scg case TNX_PCI_ID: 34050724Scg ch->cso &= 0x00ffffff; 34150724Scg ch->eso &= 0x00ffffff; 34250724Scg cr[0]=((ch->delta & 0xff)<<24) | (ch->cso); 34350724Scg cr[2]=((ch->delta>>16)<<24) | (ch->eso); 34450724Scg cr[3]|=(ch->alpha<<20) | (ch->fms<<16) | (ch->fmc<<14); 34550724Scg break; 34650724Scg } 34774763Scg snd_mtxlock(tr->lock); 34870325Scg tr_selch(ch); 34950724Scg for (i=0; i<TR_CHN_REGS; i++) 35050724Scg tr_wr(tr, TR_REG_CHNBASE+(i<<2), cr[i], 4); 35174763Scg snd_mtxunlock(tr->lock); 35250724Scg} 35350724Scg 35450724Scgstatic void 35570325Scgtr_rdch(struct tr_chinfo *ch) 35650724Scg{ 35770325Scg struct tr_info *tr = ch->parent; 35850724Scg u_int32_t cr[5], i; 35970325Scg 36074763Scg snd_mtxlock(tr->lock); 36170325Scg tr_selch(ch); 36270325Scg for (i=0; i<5; i++) 36370325Scg cr[i]=tr_rd(tr, TR_REG_CHNBASE+(i<<2), 4); 36474763Scg snd_mtxunlock(tr->lock); 36570325Scg 36670325Scg 36750724Scg ch->lba= (cr[1] & 0x3fffffff); 36850724Scg ch->fmc= (cr[3] & 0x0000c000) >> 14; 36950724Scg ch->rvol= (cr[3] & 0x00003f80) >> 7; 37050724Scg ch->cvol= (cr[3] & 0x0000007f); 37150724Scg ch->gvsel= (cr[4] & 0x80000000) >> 31; 37250724Scg ch->pan= (cr[4] & 0x7f000000) >> 24; 37350724Scg ch->vol= (cr[4] & 0x00ff0000) >> 16; 37450724Scg ch->ctrl= (cr[4] & 0x0000f000) >> 12; 37550724Scg ch->ec= (cr[4] & 0x00000fff); 37650724Scg switch(tr->type) { 37750724Scg case TDX_PCI_ID: 37850724Scg ch->cso= (cr[0] & 0xffff0000) >> 16; 37950724Scg ch->alpha= (cr[0] & 0x0000fff0) >> 4; 38050724Scg ch->fms= (cr[0] & 0x0000000f); 38150724Scg ch->eso= (cr[2] & 0xffff0000) >> 16; 38250724Scg ch->delta= (cr[2] & 0x0000ffff); 38350724Scg break; 38450724Scg case TNX_PCI_ID: 38550724Scg ch->cso= (cr[0] & 0x00ffffff); 38650724Scg ch->eso= (cr[2] & 0x00ffffff); 38770325Scg ch->delta= ((cr[2] & 0xff000000) >> 16) | ((cr[0] & 0xff000000) >> 24); 38850724Scg ch->alpha= (cr[3] & 0xfff00000) >> 20; 38950724Scg ch->fms= (cr[3] & 0x000f0000) >> 16; 39050724Scg break; 39150724Scg } 39250724Scg} 39350724Scg 39471503Scgstatic u_int32_t 39571503Scgtr_fmttobits(u_int32_t fmt) 39671503Scg{ 39771503Scg u_int32_t bits; 39871503Scg 39971503Scg bits = 0; 40071503Scg bits |= (fmt & AFMT_SIGNED)? 0x2 : 0; 40171503Scg bits |= (fmt & AFMT_STEREO)? 0x4 : 0; 40271503Scg bits |= (fmt & AFMT_16BIT)? 0x8 : 0; 40371503Scg 40471503Scg return bits; 40571503Scg} 40671503Scg 40770134Scg/* -------------------------------------------------------------------- */ 40850724Scg/* channel interface */ 40950724Scg 41070134Scgstatic void * 41174763Scgtrpchan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel *c, int dir) 41250724Scg{ 41350724Scg struct tr_info *tr = devinfo; 41450724Scg struct tr_chinfo *ch; 41570325Scg 41670325Scg KASSERT(dir == PCMDIR_PLAY, ("trpchan_init: bad direction")); 41770325Scg ch = &tr->chinfo[tr->playchns]; 41870325Scg ch->index = tr->playchns++; 41950724Scg ch->buffer = b; 42050724Scg ch->parent = tr; 42150724Scg ch->channel = c; 42270325Scg if (sndbuf_alloc(ch->buffer, tr->parent_dmat, TR_BUFFSIZE) == -1) 42370325Scg return NULL; 42470325Scg 42570325Scg return ch; 42650724Scg} 42750724Scg 42850724Scgstatic int 42970325Scgtrpchan_setformat(kobj_t obj, void *data, u_int32_t format) 43050724Scg{ 43150724Scg struct tr_chinfo *ch = data; 43270325Scg 43370325Scg ch->ctrl = tr_fmttobits(format) | 0x01; 43470325Scg 43570325Scg return 0; 43670325Scg} 43770325Scg 43870325Scgstatic int 43970325Scgtrpchan_setspeed(kobj_t obj, void *data, u_int32_t speed) 44070325Scg{ 44170325Scg struct tr_chinfo *ch = data; 44270325Scg 44370325Scg ch->delta = (speed << 12) / 48000; 44470325Scg return (ch->delta * 48000) >> 12; 44570325Scg} 44670325Scg 44770325Scgstatic int 44870325Scgtrpchan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize) 44970325Scg{ 45070325Scg struct tr_chinfo *ch = data; 45170325Scg 45270325Scg sndbuf_resize(ch->buffer, 2, blocksize); 45370325Scg return blocksize; 45470325Scg} 45570325Scg 45670325Scgstatic int 45770325Scgtrpchan_trigger(kobj_t obj, void *data, int go) 45870325Scg{ 45970325Scg struct tr_chinfo *ch = data; 46070325Scg 46170325Scg if (go == PCMTRIG_EMLDMAWR || go == PCMTRIG_EMLDMARD) 46270325Scg return 0; 46370325Scg 46470325Scg if (go == PCMTRIG_START) { 46571503Scg ch->fmc = 3; 46671503Scg ch->fms = 0; 46771503Scg ch->ec = 0; 46871503Scg ch->alpha = 0; 46970291Scg ch->lba = vtophys(sndbuf_getbuf(ch->buffer)); 47050724Scg ch->cso = 0; 47170325Scg ch->eso = (sndbuf_getsize(ch->buffer) / sndbuf_getbps(ch->buffer)) - 1; 47271503Scg ch->rvol = ch->cvol = 0x7f; 47350724Scg ch->gvsel = 0; 47450724Scg ch->pan = 0; 47550724Scg ch->vol = 0; 47671503Scg ch->bufhalf = 0; 47770325Scg tr_wrch(ch); 47870325Scg tr_enaint(ch, 1); 47970325Scg tr_startch(ch); 48070325Scg } else 48170325Scg tr_stopch(ch); 48270325Scg 48350724Scg return 0; 48450724Scg} 48550724Scg 48650724Scgstatic int 48770325Scgtrpchan_getptr(kobj_t obj, void *data) 48850724Scg{ 48950724Scg struct tr_chinfo *ch = data; 49070325Scg 49170325Scg tr_rdch(ch); 49270325Scg return ch->cso * sndbuf_getbps(ch->buffer); 49370325Scg} 49470325Scg 49574763Scgstatic struct pcmchan_caps * 49670325Scgtrpchan_getcaps(kobj_t obj, void *data) 49770325Scg{ 49870325Scg return &tr_playcaps; 49970325Scg} 50070325Scg 50170325Scgstatic kobj_method_t trpchan_methods[] = { 50270325Scg KOBJMETHOD(channel_init, trpchan_init), 50370325Scg KOBJMETHOD(channel_setformat, trpchan_setformat), 50470325Scg KOBJMETHOD(channel_setspeed, trpchan_setspeed), 50570325Scg KOBJMETHOD(channel_setblocksize, trpchan_setblocksize), 50670325Scg KOBJMETHOD(channel_trigger, trpchan_trigger), 50770325Scg KOBJMETHOD(channel_getptr, trpchan_getptr), 50870325Scg KOBJMETHOD(channel_getcaps, trpchan_getcaps), 50970325Scg { 0, 0 } 51070325Scg}; 51170325ScgCHANNEL_DECLARE(trpchan); 51270325Scg 51370325Scg/* -------------------------------------------------------------------- */ 51470325Scg/* rec channel interface */ 51570325Scg 51670325Scgstatic void * 51774763Scgtrrchan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel *c, int dir) 51870325Scg{ 51970325Scg struct tr_info *tr = devinfo; 52070325Scg struct tr_rchinfo *ch; 52170325Scg 52270325Scg KASSERT(dir == PCMDIR_REC, ("trrchan_init: bad direction")); 52370325Scg ch = &tr->recchinfo; 52470325Scg ch->buffer = b; 52570325Scg ch->parent = tr; 52670325Scg ch->channel = c; 52770325Scg if (sndbuf_alloc(ch->buffer, tr->parent_dmat, TR_BUFFSIZE) == -1) 52870325Scg return NULL; 52970325Scg 53070325Scg return ch; 53170325Scg} 53270325Scg 53370325Scgstatic int 53470325Scgtrrchan_setformat(kobj_t obj, void *data, u_int32_t format) 53570325Scg{ 53670325Scg struct tr_rchinfo *ch = data; 53750724Scg struct tr_info *tr = ch->parent; 53870325Scg u_int32_t i, bits; 53950724Scg 54070325Scg bits = tr_fmttobits(format); 54170325Scg /* set # of samples between interrupts */ 54271503Scg i = (sndbuf_runsz(ch->buffer) >> ((bits & 0x08)? 1 : 0)) - 1; 54370325Scg tr_wr(tr, TR_REG_SBBL, i | (i << 16), 4); 54470325Scg /* set sample format */ 54570325Scg i = 0x18 | (bits << 4); 54670325Scg tr_wr(tr, TR_REG_SBCTRL, i, 1); 54770325Scg 54850724Scg return 0; 54970325Scg 55050724Scg} 55150724Scg 55250724Scgstatic int 55370325Scgtrrchan_setspeed(kobj_t obj, void *data, u_int32_t speed) 55450724Scg{ 55570325Scg struct tr_rchinfo *ch = data; 55650724Scg struct tr_info *tr = ch->parent; 55750724Scg 55870325Scg /* setup speed */ 55970325Scg ch->delta = (48000 << 12) / speed; 56070325Scg tr_wr(tr, TR_REG_SBDELTA, ch->delta, 2); 56170325Scg 56270325Scg /* return closest possible speed */ 56370325Scg return (48000 << 12) / ch->delta; 56450724Scg} 56550724Scg 56650724Scgstatic int 56770325Scgtrrchan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize) 56850724Scg{ 56970325Scg struct tr_rchinfo *ch = data; 57070325Scg 57170325Scg sndbuf_resize(ch->buffer, 2, blocksize); 57270325Scg 57370325Scg return blocksize; 57450724Scg} 57550724Scg 57650724Scgstatic int 57770325Scgtrrchan_trigger(kobj_t obj, void *data, int go) 57850724Scg{ 57970325Scg struct tr_rchinfo *ch = data; 58050724Scg struct tr_info *tr = ch->parent; 58170325Scg u_int32_t i; 58259323Scg 58360958Scg if (go == PCMTRIG_EMLDMAWR || go == PCMTRIG_EMLDMARD) 58460958Scg return 0; 58560958Scg 58670325Scg if (go == PCMTRIG_START) { 58770325Scg /* set up dma mode regs */ 58870325Scg tr_wr(tr, TR_REG_DMAR15, 0, 1); 58970325Scg i = tr_rd(tr, TR_REG_DMAR11, 1) & 0x03; 59070325Scg tr_wr(tr, TR_REG_DMAR11, i | 0x54, 1); 59170325Scg /* set up base address */ 59270325Scg tr_wr(tr, TR_REG_DMAR0, vtophys(sndbuf_getbuf(ch->buffer)), 4); 59370325Scg /* set up buffer size */ 59470325Scg i = tr_rd(tr, TR_REG_DMAR4, 4) & ~0x00ffffff; 59570325Scg tr_wr(tr, TR_REG_DMAR4, i | (sndbuf_runsz(ch->buffer) - 1), 4); 59670325Scg /* start */ 59770325Scg tr_wr(tr, TR_REG_SBCTRL, tr_rd(tr, TR_REG_SBCTRL, 1) | 1, 1); 59870325Scg } else 59970325Scg tr_wr(tr, TR_REG_SBCTRL, tr_rd(tr, TR_REG_SBCTRL, 1) & ~7, 1); 60070325Scg 60170325Scg /* return 0 if ok */ 60250724Scg return 0; 60350724Scg} 60450724Scg 60550724Scgstatic int 60670325Scgtrrchan_getptr(kobj_t obj, void *data) 60750724Scg{ 60870325Scg struct tr_rchinfo *ch = data; 60950724Scg struct tr_info *tr = ch->parent; 61060958Scg 61170325Scg /* return current byte offset of channel */ 61270325Scg return tr_rd(tr, TR_REG_DMAR0, 4) - vtophys(sndbuf_getbuf(ch->buffer)); 61350724Scg} 61450724Scg 61574763Scgstatic struct pcmchan_caps * 61670325Scgtrrchan_getcaps(kobj_t obj, void *data) 61750724Scg{ 61870325Scg return &tr_reccaps; 61950724Scg} 62050724Scg 62170325Scgstatic kobj_method_t trrchan_methods[] = { 62270325Scg KOBJMETHOD(channel_init, trrchan_init), 62370325Scg KOBJMETHOD(channel_setformat, trrchan_setformat), 62470325Scg KOBJMETHOD(channel_setspeed, trrchan_setspeed), 62570325Scg KOBJMETHOD(channel_setblocksize, trrchan_setblocksize), 62670325Scg KOBJMETHOD(channel_trigger, trrchan_trigger), 62770325Scg KOBJMETHOD(channel_getptr, trrchan_getptr), 62870325Scg KOBJMETHOD(channel_getcaps, trrchan_getcaps), 62970134Scg { 0, 0 } 63070134Scg}; 63170325ScgCHANNEL_DECLARE(trrchan); 63270134Scg 63370134Scg/* -------------------------------------------------------------------- */ 63450724Scg/* The interrupt handler */ 63550724Scg 63650724Scgstatic void 63750724Scgtr_intr(void *p) 63850724Scg{ 63950724Scg struct tr_info *tr = (struct tr_info *)p; 64070325Scg struct tr_chinfo *ch; 64171503Scg u_int32_t active, mask, bufhalf, chnum, intsrc; 64271503Scg int tmp; 64350724Scg 64470325Scg intsrc = tr_rd(tr, TR_REG_MISCINT, 4); 64550724Scg if (intsrc & TR_INT_ADDR) { 64671503Scg chnum = 0; 64771503Scg while (chnum < 64) { 64871503Scg mask = 0x00000001; 64971503Scg active = tr_rd(tr, (chnum < 32)? TR_REG_ADDRINTA : TR_REG_ADDRINTB, 4); 65071503Scg bufhalf = tr_rd(tr, (chnum < 32)? TR_REG_CSPF_A : TR_REG_CSPF_B, 4); 65171503Scg if (active) { 65271503Scg do { 65371503Scg if (active & mask) { 65471503Scg tmp = (bufhalf & mask)? 1 : 0; 65571503Scg if (chnum < tr->playchns) { 65671503Scg ch = &tr->chinfo[chnum]; 65771503Scg /* printf("%d @ %d, ", chnum, trpchan_getptr(NULL, ch)); */ 65871503Scg if (ch->bufhalf != tmp) { 65971503Scg chn_intr(ch->channel); 66071503Scg ch->bufhalf = tmp; 66174763Scg } 66271503Scg } 66371503Scg } 66471503Scg chnum++; 66571503Scg mask <<= 1; 66671503Scg } while (chnum & 31); 66771503Scg } else 66871503Scg chnum += 32; 66971503Scg 67071503Scg tr_wr(tr, (chnum <= 32)? TR_REG_ADDRINTA : TR_REG_ADDRINTB, active, 4); 67150724Scg } 67250724Scg } 67350724Scg if (intsrc & TR_INT_SB) { 67450724Scg chn_intr(tr->recchinfo.channel); 67550724Scg tr_rd(tr, TR_REG_SBR9, 1); 67650724Scg tr_rd(tr, TR_REG_SBR10, 1); 67750724Scg } 67850724Scg} 67950724Scg 68050724Scg/* -------------------------------------------------------------------- */ 68150724Scg 68250724Scg/* 68350724Scg * Probe and attach the card 68450724Scg */ 68550724Scg 68650724Scgstatic int 68750724Scgtr_init(struct tr_info *tr) 68850724Scg{ 68950724Scg if (tr->type == TDX_PCI_ID) { 69050724Scg tr_wr(tr, TDX_REG_CODECST, TDX_CDC_ON, 4); 69150724Scg } else tr_wr(tr, TNX_REG_CODECST, TNX_CDC_ON, 4); 69250724Scg 69350724Scg tr_wr(tr, TR_REG_CIR, TR_CIR_MIDENA | TR_CIR_ADDRENA, 4); 69450724Scg tr->playchns = 0; 69550724Scg return 0; 69650724Scg} 69750724Scg 69850724Scgstatic int 69950724Scgtr_pci_probe(device_t dev) 70050724Scg{ 70150724Scg if (pci_get_devid(dev) == TDX_PCI_ID) { 70250724Scg device_set_desc(dev, "Trident 4DWave DX"); 70350724Scg return 0; 70450724Scg } 70550724Scg if (pci_get_devid(dev) == TNX_PCI_ID) { 70650724Scg device_set_desc(dev, "Trident 4DWave NX"); 70750724Scg return 0; 70850724Scg } 70950724Scg 71050724Scg return ENXIO; 71150724Scg} 71250724Scg 71350724Scgstatic int 71450724Scgtr_pci_attach(device_t dev) 71550724Scg{ 71650724Scg u_int32_t data; 71750724Scg struct tr_info *tr; 71866012Scg struct ac97_info *codec = 0; 71950724Scg int i; 72050724Scg char status[SND_STATUSLEN]; 72150724Scg 72250724Scg if ((tr = malloc(sizeof(*tr), M_DEVBUF, M_NOWAIT)) == NULL) { 72350724Scg device_printf(dev, "cannot allocate softc\n"); 72450724Scg return ENXIO; 72550724Scg } 72650724Scg 72750724Scg bzero(tr, sizeof(*tr)); 72850724Scg tr->type = pci_get_devid(dev); 72974763Scg tr->lock = snd_mtxcreate(device_get_nameunit(dev)); 73050724Scg 73150724Scg data = pci_read_config(dev, PCIR_COMMAND, 2); 73250724Scg data |= (PCIM_CMD_PORTEN|PCIM_CMD_MEMEN|PCIM_CMD_BUSMASTEREN); 73350724Scg pci_write_config(dev, PCIR_COMMAND, data, 2); 73450724Scg data = pci_read_config(dev, PCIR_COMMAND, 2); 73550724Scg 73670325Scg tr->regid = PCIR_MAPS; 73770325Scg tr->regtype = SYS_RES_IOPORT; 73870325Scg tr->reg = bus_alloc_resource(dev, tr->regtype, &tr->regid, 0, ~0, 1, RF_ACTIVE); 73970325Scg if (tr->reg) { 74070325Scg tr->st = rman_get_bustag(tr->reg); 74170325Scg tr->sh = rman_get_bushandle(tr->reg); 74270325Scg } else { 74350724Scg device_printf(dev, "unable to map register space\n"); 74450724Scg goto bad; 74550724Scg } 74650724Scg 74750724Scg if (tr_init(tr) == -1) { 74850724Scg device_printf(dev, "unable to initialize the card\n"); 74950724Scg goto bad; 75050724Scg } 75150724Scg 75270134Scg codec = AC97_CREATE(dev, tr, tr_ac97); 75350724Scg if (codec == NULL) goto bad; 75470134Scg if (mixer_init(dev, ac97_getmixerclass(), codec) == -1) goto bad; 75550724Scg 75650724Scg tr->irqid = 0; 75750724Scg tr->irq = bus_alloc_resource(dev, SYS_RES_IRQ, &tr->irqid, 75850724Scg 0, ~0, 1, RF_ACTIVE | RF_SHAREABLE); 75974763Scg if (!tr->irq || snd_setup_intr(dev, tr->irq, INTR_MPSAFE, tr_intr, tr, &tr->ih)) { 76050724Scg device_printf(dev, "unable to map interrupt\n"); 76150724Scg goto bad; 76250724Scg } 76350724Scg 76450724Scg if (bus_dma_tag_create(/*parent*/NULL, /*alignment*/2, /*boundary*/0, 76550724Scg /*lowaddr*/BUS_SPACE_MAXADDR_32BIT, 76650724Scg /*highaddr*/BUS_SPACE_MAXADDR, 76750724Scg /*filter*/NULL, /*filterarg*/NULL, 76850724Scg /*maxsize*/TR_BUFFSIZE, /*nsegments*/1, /*maxsegz*/0x3ffff, 76950724Scg /*flags*/0, &tr->parent_dmat) != 0) { 77050724Scg device_printf(dev, "unable to create dma tag\n"); 77150724Scg goto bad; 77250724Scg } 77350724Scg 77470325Scg snprintf(status, 64, "at io 0x%lx irq %ld", 77550724Scg rman_get_start(tr->reg), rman_get_start(tr->irq)); 77650724Scg 77750724Scg if (pcm_register(dev, tr, TR_MAXPLAYCH, 1)) goto bad; 77870325Scg pcm_addchan(dev, PCMDIR_REC, &trrchan_class, tr); 77950724Scg for (i = 0; i < TR_MAXPLAYCH; i++) 78070325Scg pcm_addchan(dev, PCMDIR_PLAY, &trpchan_class, tr); 78150724Scg pcm_setstatus(dev, status); 78250724Scg 78350724Scg return 0; 78450724Scg 78550724Scgbad: 78665644Scg if (codec) ac97_destroy(codec); 78750724Scg if (tr->reg) bus_release_resource(dev, tr->regtype, tr->regid, tr->reg); 78850724Scg if (tr->ih) bus_teardown_intr(dev, tr->irq, tr->ih); 78950724Scg if (tr->irq) bus_release_resource(dev, SYS_RES_IRQ, tr->irqid, tr->irq); 79065644Scg if (tr->parent_dmat) bus_dma_tag_destroy(tr->parent_dmat); 79174763Scg if (tr->lock) snd_mtxfree(tr->lock); 79250724Scg free(tr, M_DEVBUF); 79350724Scg return ENXIO; 79450724Scg} 79550724Scg 79665644Scgstatic int 79765644Scgtr_pci_detach(device_t dev) 79865644Scg{ 79965644Scg int r; 80065644Scg struct tr_info *tr; 80165644Scg 80265644Scg r = pcm_unregister(dev); 80365644Scg if (r) 80465644Scg return r; 80565644Scg 80665644Scg tr = pcm_getdevinfo(dev); 80765644Scg bus_release_resource(dev, tr->regtype, tr->regid, tr->reg); 80865644Scg bus_teardown_intr(dev, tr->irq, tr->ih); 80965644Scg bus_release_resource(dev, SYS_RES_IRQ, tr->irqid, tr->irq); 81065644Scg bus_dma_tag_destroy(tr->parent_dmat); 81174763Scg snd_mtxfree(tr->lock); 81265644Scg free(tr, M_DEVBUF); 81365644Scg 81465644Scg return 0; 81565644Scg} 81665644Scg 81750724Scgstatic device_method_t tr_methods[] = { 81850724Scg /* Device interface */ 81950724Scg DEVMETHOD(device_probe, tr_pci_probe), 82050724Scg DEVMETHOD(device_attach, tr_pci_attach), 82165644Scg DEVMETHOD(device_detach, tr_pci_detach), 82250724Scg 82350724Scg { 0, 0 } 82450724Scg}; 82550724Scg 82650724Scgstatic driver_t tr_driver = { 82750724Scg "pcm", 82850724Scg tr_methods, 82974763Scg sizeof(struct snddev_info), 83050724Scg}; 83150724Scg 83250724Scgstatic devclass_t pcm_devclass; 83350724Scg 83462483ScgDRIVER_MODULE(snd_t4dwave, pci, tr_driver, pcm_devclass, 0, 0); 83562483ScgMODULE_DEPEND(snd_t4dwave, snd_pcm, PCM_MINVER, PCM_PREFVER, PCM_MAXVER); 83662483ScgMODULE_VERSION(snd_t4dwave, 1); 837