cs4281.c revision 82837
172017Scg/* 272017Scg * Copyright (c) 2000 Orion Hodson <O.Hodson@cs.ucl.ac.uk> 372017Scg * All rights reserved. 472017Scg * 572017Scg * Redistribution and use in source and binary forms, with or without 672017Scg * modification, are permitted provided that the following conditions 772017Scg * are met: 872017Scg * 1. Redistributions of source code must retain the above copyright 972017Scg * notice, this list of conditions and the following disclaimer. 1072017Scg * 2. Redistributions in binary form must reproduce the above copyright 1172017Scg * notice, this list of conditions and the following disclaimer in the 1272017Scg * documentation and/or other materials provided with the distribution. 1372017Scg * 1472017Scg * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1572017Scg * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1672017Scg * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1772017Scg * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1872017Scg * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1972017Scg * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2072017Scg * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2172017Scg * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHERIN CONTRACT, STRICT 2272017Scg * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2372017Scg * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THEPOSSIBILITY OF 2472017Scg * SUCH DAMAGE. 2572017Scg * 2672017Scg * The order of pokes in the initiation sequence is based on Linux 2772017Scg * driver by Thomas Sailer, gw boynton (wesb@crystal.cirrus.com), tom 2872455Scg * woller (twoller@crystal.cirrus.com). Shingo Watanabe (nabe@nabechan.org) 2972455Scg * contributed towards power management. 3072455Scg */ 3172017Scg 3272017Scg#include <dev/sound/pcm/sound.h> 3372017Scg#include <dev/sound/pcm/ac97.h> 3472017Scg 3572017Scg#include <pci/pcireg.h> 3672017Scg#include <pci/pcivar.h> 3772017Scg 3872017Scg#include <dev/sound/pci/cs4281.h> 3972017Scg 4082180ScgSND_DECLARE_FILE("$FreeBSD: head/sys/dev/sound/pci/cs4281.c 82837 2001-09-03 02:14:55Z orion $"); 4182180Scg 4272017Scg#define CS4281_BUFFER_SIZE 16384 4372017Scg 4472017Scg/* Max fifo size for full duplex is 64 */ 4572017Scg#define CS4281_FIFO_SIZE 15 4672017Scg 4772017Scg/* DMA Engine Indices */ 4872017Scg#define CS4281_DMA_PLAY 0 4972017Scg#define CS4281_DMA_REC 1 5072017Scg 5172017Scg/* Misc */ 5272017Scg 5372017Scg#define MIN(x,y) (x) < (y) ? (x) : (y) 5472017Scg#define MAX(x,y) (x) > (y) ? (x) : (y) 5572017Scg 5672017Scg#define inline __inline 5772017Scg 5872017Scg#ifndef DEB 5972017Scg#define DEB(x) /* x */ 6072017Scg#endif /* DEB */ 6172017Scg 6272017Scg/* ------------------------------------------------------------------------- */ 6372017Scg/* Structures */ 6472017Scg 6572017Scgstruct sc_info; 6672017Scg 6772017Scg/* channel registers */ 6872017Scgstruct sc_chinfo { 6972017Scg struct sc_info *parent; 7072017Scg 7174763Scg struct snd_dbuf *buffer; 7274763Scg struct pcm_channel *channel; 7372017Scg 7472455Scg u_int32_t spd, fmt, bps, blksz; 7572455Scg 7672455Scg int dma_setup, dma_active, dma_chan; 7772017Scg}; 7872017Scg 7972017Scg/* device private data */ 8072017Scgstruct sc_info { 8172017Scg device_t dev; 8272017Scg u_int32_t type; 8372017Scg 8472017Scg bus_space_tag_t st; 8572017Scg bus_space_handle_t sh; 8672017Scg bus_dma_tag_t parent_dmat; 8772017Scg 8872017Scg struct resource *reg, *irq, *mem; 8972017Scg int regtype, regid, irqid, memid; 9072017Scg void *ih; 9172017Scg 9272017Scg int power; 9372017Scg struct sc_chinfo pch; 9472017Scg struct sc_chinfo rch; 9572017Scg}; 9672017Scg 9772017Scg/* -------------------------------------------------------------------- */ 9872017Scg/* prototypes */ 9972017Scg 10072017Scg/* ADC/DAC control */ 10172017Scgstatic u_int32_t adcdac_go(struct sc_chinfo *ch, u_int32_t go); 10272017Scgstatic void adcdac_prog(struct sc_chinfo *ch); 10372017Scg 10472455Scg/* power management and interrupt control */ 10572017Scgstatic void cs4281_intr(void *); 10672017Scgstatic int cs4281_power(struct sc_info *, int); 10772017Scgstatic int cs4281_init(struct sc_info *); 10872017Scg 10972017Scg/* talk to the card */ 11072017Scgstatic u_int32_t cs4281_rd(struct sc_info *, int); 11172017Scgstatic void cs4281_wr(struct sc_info *, int, u_int32_t); 11272017Scg 11372017Scg/* misc */ 11472017Scgstatic u_int8_t cs4281_rate_to_rv(u_int32_t); 11572017Scgstatic u_int32_t cs4281_format_to_dmr(u_int32_t); 11672017Scgstatic u_int32_t cs4281_format_to_bps(u_int32_t); 11772017Scg 11872017Scg/* -------------------------------------------------------------------- */ 11972017Scg/* formats (do not add formats without editing cs_fmt_tab) */ 12072017Scg 12172017Scgstatic u_int32_t cs4281_fmts[] = { 12272017Scg AFMT_U8, 12372017Scg AFMT_U8 | AFMT_STEREO, 12472017Scg AFMT_S8, 12572017Scg AFMT_S8 | AFMT_STEREO, 12672017Scg AFMT_S16_LE, 12772017Scg AFMT_S16_LE | AFMT_STEREO, 12872017Scg AFMT_U16_LE, 12972017Scg AFMT_U16_LE | AFMT_STEREO, 13072017Scg AFMT_S16_BE, 13172017Scg AFMT_S16_BE | AFMT_STEREO, 13272017Scg AFMT_U16_BE, 13372017Scg AFMT_U16_BE | AFMT_STEREO, 13472017Scg 0 13572017Scg}; 13672017Scg 13774763Scgstatic struct pcmchan_caps cs4281_caps = {6024, 48000, cs4281_fmts, 0}; 13872017Scg 13972017Scg/* -------------------------------------------------------------------- */ 14072017Scg/* Hardware */ 14172017Scg 14272017Scgstatic inline u_int32_t 14372017Scgcs4281_rd(struct sc_info *sc, int regno) 14472017Scg{ 14572017Scg return bus_space_read_4(sc->st, sc->sh, regno); 14672017Scg} 14772017Scg 14872017Scgstatic inline void 14972017Scgcs4281_wr(struct sc_info *sc, int regno, u_int32_t data) 15072017Scg{ 15172017Scg bus_space_write_4(sc->st, sc->sh, regno, data); 15272017Scg DELAY(100); 15372017Scg} 15472017Scg 15572017Scgstatic inline void 15672017Scgcs4281_clr4(struct sc_info *sc, int regno, u_int32_t mask) 15772017Scg{ 15872017Scg u_int32_t r; 15972017Scg r = cs4281_rd(sc, regno); 16072017Scg cs4281_wr(sc, regno, r & ~mask); 16172017Scg} 16272017Scg 16372017Scgstatic inline void 16472017Scgcs4281_set4(struct sc_info *sc, int regno, u_int32_t mask) 16572017Scg{ 16672017Scg u_int32_t v; 16772017Scg v = cs4281_rd(sc, regno); 16872017Scg cs4281_wr(sc, regno, v | mask); 16972017Scg} 17072017Scg 17172017Scgstatic int 17272017Scgcs4281_waitset(struct sc_info *sc, int regno, u_int32_t mask, int tries) 17372017Scg{ 17472017Scg u_int32_t v; 17572017Scg 17672017Scg while(tries > 0) { 17772017Scg DELAY(100); 17872017Scg v = cs4281_rd(sc, regno); 17972017Scg if ((v & mask) == mask) break; 18072017Scg tries --; 18172017Scg } 18272017Scg return tries; 18372017Scg} 18472017Scg 18572017Scgstatic int 18672017Scgcs4281_waitclr(struct sc_info *sc, int regno, u_int32_t mask, int tries) 18772017Scg{ 18872017Scg u_int32_t v; 18972017Scg 19072017Scg while(tries > 0) { 19172017Scg DELAY(100); 19272017Scg v = ~ cs4281_rd(sc, regno); 19372017Scg if (v & mask) break; 19472017Scg tries --; 19572017Scg } 19672017Scg return tries; 19772017Scg} 19872017Scg 19972017Scg/* ------------------------------------------------------------------------- */ 20072017Scg/* Register value mapping functions */ 20172017Scg 20272017Scgstatic u_int32_t cs4281_rates[] = {48000, 44100, 22050, 16000, 11025, 8000}; 20372017Scg#define CS4281_NUM_RATES sizeof(cs4281_rates)/sizeof(cs4281_rates[0]) 20472017Scg 20572455Scgstatic u_int8_t 20672017Scgcs4281_rate_to_rv(u_int32_t rate) 20772017Scg{ 20872017Scg u_int32_t v; 20972017Scg 21072017Scg for (v = 0; v < CS4281_NUM_RATES; v++) { 21172017Scg if (rate == cs4281_rates[v]) return v; 21272017Scg } 21372017Scg 21472017Scg v = 1536000 / rate; 21572017Scg if (v > 255 || v < 32) v = 5; /* default to 8k */ 21672017Scg return v; 21772017Scg} 21872017Scg 21972017Scgstatic u_int32_t 22072017Scgcs4281_rv_to_rate(u_int8_t rv) 22172017Scg{ 22272017Scg u_int32_t r; 22372017Scg 22472017Scg if (rv < CS4281_NUM_RATES) return cs4281_rates[rv]; 22572017Scg r = 1536000 / rv; 22672017Scg return r; 22772017Scg} 22872017Scg 22972017Scgstatic inline u_int32_t 23072455Scgcs4281_format_to_dmr(u_int32_t format) 23172017Scg{ 23272017Scg u_int32_t dmr = 0; 23372017Scg if (AFMT_8BIT & format) dmr |= CS4281PCI_DMR_SIZE8; 23472017Scg if (!(AFMT_STEREO & format)) dmr |= CS4281PCI_DMR_MONO; 23572017Scg if (AFMT_BIGENDIAN & format) dmr |= CS4281PCI_DMR_BEND; 23672017Scg if (!(AFMT_SIGNED & format)) dmr |= CS4281PCI_DMR_USIGN; 23772017Scg return dmr; 23872455Scg} 23972017Scg 24072017Scgstatic inline u_int32_t 24172455Scgcs4281_format_to_bps(u_int32_t format) 24272017Scg{ 24372017Scg return ((AFMT_8BIT & format) ? 1 : 2) * ((AFMT_STEREO & format) ? 2 : 1); 24472017Scg} 24572017Scg 24672017Scg/* -------------------------------------------------------------------- */ 24772017Scg/* ac97 codec */ 24872017Scg 24972017Scgstatic u_int32_t 25072017Scgcs4281_rdcd(kobj_t obj, void *devinfo, int regno) 25172017Scg{ 25272017Scg struct sc_info *sc = (struct sc_info *)devinfo; 25372017Scg int codecno; 25472455Scg 25572017Scg codecno = regno >> 8; 25672017Scg regno &= 0xff; 25772017Scg 25872017Scg /* Remove old state */ 25972455Scg cs4281_rd(sc, CS4281PCI_ACSDA); 26072017Scg 26172017Scg /* Fill in AC97 register value request form */ 26272017Scg cs4281_wr(sc, CS4281PCI_ACCAD, regno); 26372017Scg cs4281_wr(sc, CS4281PCI_ACCDA, 0); 26472455Scg cs4281_wr(sc, CS4281PCI_ACCTL, CS4281PCI_ACCTL_ESYN | 26572455Scg CS4281PCI_ACCTL_VFRM | CS4281PCI_ACCTL_DCV | 26672017Scg CS4281PCI_ACCTL_CRW); 26772017Scg 26872017Scg /* Wait for read to complete */ 26972017Scg if (cs4281_waitclr(sc, CS4281PCI_ACCTL, CS4281PCI_ACCTL_DCV, 250) == 0) { 27072017Scg device_printf(sc->dev, "cs4281_rdcd: DCV did not go\n"); 27172017Scg return 0xffffffff; 27272017Scg } 27372017Scg 27472017Scg /* Wait for valid status */ 27572017Scg if (cs4281_waitset(sc, CS4281PCI_ACSTS, CS4281PCI_ACSTS_VSTS, 250) == 0) { 27672017Scg device_printf(sc->dev,"cs4281_rdcd: VSTS did not come\n"); 27772017Scg return 0xffffffff; 27872017Scg } 27972455Scg 28072455Scg return cs4281_rd(sc, CS4281PCI_ACSDA); 28172017Scg} 28272017Scg 28372017Scgstatic void 28472017Scgcs4281_wrcd(kobj_t obj, void *devinfo, int regno, u_int32_t data) 28572017Scg{ 28672017Scg struct sc_info *sc = (struct sc_info *)devinfo; 28772017Scg int codecno; 28872455Scg 28972017Scg codecno = regno >> 8; 29072017Scg regno &= 0xff; 29172017Scg 29272017Scg cs4281_wr(sc, CS4281PCI_ACCAD, regno); 29372017Scg cs4281_wr(sc, CS4281PCI_ACCDA, data); 29472455Scg cs4281_wr(sc, CS4281PCI_ACCTL, CS4281PCI_ACCTL_ESYN | 29572017Scg CS4281PCI_ACCTL_VFRM | CS4281PCI_ACCTL_DCV); 29672455Scg 29772017Scg if (cs4281_waitclr(sc, CS4281PCI_ACCTL, CS4281PCI_ACCTL_DCV, 250) == 0) { 29872017Scg device_printf(sc->dev,"cs4281_wrcd: DCV did not go\n"); 29972017Scg } 30072017Scg} 30172017Scg 30272017Scgstatic kobj_method_t cs4281_ac97_methods[] = { 30372017Scg KOBJMETHOD(ac97_read, cs4281_rdcd), 30472017Scg KOBJMETHOD(ac97_write, cs4281_wrcd), 30572017Scg { 0, 0 } 30672017Scg}; 30772017ScgAC97_DECLARE(cs4281_ac97); 30872017Scg 30972017Scg/* ------------------------------------------------------------------------- */ 31072017Scg/* shared rec/play channel interface */ 31172017Scg 31272017Scgstatic void * 31374763Scgcs4281chan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel *c, int dir) 31472017Scg{ 31572017Scg struct sc_info *sc = devinfo; 31672017Scg struct sc_chinfo *ch = (dir == PCMDIR_PLAY) ? &sc->pch : &sc->rch; 31772017Scg 31872017Scg ch->buffer = b; 31972017Scg if (sndbuf_alloc(ch->buffer, sc->parent_dmat, CS4281_BUFFER_SIZE) != 0) { 32072017Scg return NULL; 32172017Scg } 32272017Scg ch->parent = sc; 32372017Scg ch->channel = c; 32472017Scg 32572017Scg ch->fmt = AFMT_U8; 32672017Scg ch->spd = DSP_DEFAULT_SPEED; 32772017Scg ch->bps = 1; 32872455Scg ch->blksz = sndbuf_getsize(ch->buffer); 32972017Scg 33072017Scg ch->dma_chan = (dir == PCMDIR_PLAY) ? CS4281_DMA_PLAY : CS4281_DMA_REC; 33172017Scg ch->dma_setup = 0; 33272017Scg 33372017Scg adcdac_go(ch, 0); 33472017Scg adcdac_prog(ch); 33572017Scg 33672017Scg return ch; 33772017Scg} 33872017Scg 33972017Scgstatic int 34072017Scgcs4281chan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize) 34172017Scg{ 34272017Scg struct sc_chinfo *ch = data; 34372455Scg u_int32_t go; 34472017Scg 34572017Scg go = adcdac_go(ch, 0); 34672017Scg 34772017Scg /* 2 interrupts are possible and used in buffer (half-empty,empty), 34872017Scg * hence factor of 2. */ 34972455Scg ch->blksz = MIN(blocksize, CS4281_BUFFER_SIZE / 2); 35072455Scg sndbuf_resize(ch->buffer, 2, ch->blksz); 35172017Scg ch->dma_setup = 0; 35272017Scg adcdac_prog(ch); 35372017Scg adcdac_go(ch, go); 35472017Scg 35572455Scg DEB(printf("cs4281chan_setblocksize: bufsz %d Setting %d\n", blocksize, ch->blksz)); 35672017Scg 35782837Sorion return ch->blksz; 35872017Scg} 35972017Scg 36072017Scgstatic int 36172017Scgcs4281chan_setspeed(kobj_t obj, void *data, u_int32_t speed) 36272017Scg{ 36372017Scg struct sc_chinfo *ch = data; 36472017Scg struct sc_info *sc = ch->parent; 36572017Scg u_int32_t go, v, r; 36672017Scg 36772017Scg go = adcdac_go(ch, 0); /* pause */ 36872017Scg r = (ch->dma_chan == CS4281_DMA_PLAY) ? CS4281PCI_DACSR : CS4281PCI_ADCSR; 36972017Scg v = cs4281_rate_to_rv(speed); 37072017Scg cs4281_wr(sc, r, v); 37172017Scg adcdac_go(ch, go); /* unpause */ 37272017Scg 37372017Scg ch->spd = cs4281_rv_to_rate(v); 37472017Scg return ch->spd; 37572017Scg} 37672017Scg 37772017Scgstatic int 37872017Scgcs4281chan_setformat(kobj_t obj, void *data, u_int32_t format) 37972017Scg{ 38072017Scg struct sc_chinfo *ch = data; 38172017Scg struct sc_info *sc = ch->parent; 38272017Scg u_int32_t v, go; 38372017Scg 38472017Scg go = adcdac_go(ch, 0); /* pause */ 38572017Scg 38672017Scg if (ch->dma_chan == CS4281_DMA_PLAY) 38772017Scg v = CS4281PCI_DMR_TR_PLAY; 38872455Scg else 38972017Scg v = CS4281PCI_DMR_TR_REC; 39072017Scg v |= CS4281PCI_DMR_DMA | CS4281PCI_DMR_AUTO; 39172017Scg v |= cs4281_format_to_dmr(format); 39272017Scg cs4281_wr(sc, CS4281PCI_DMR(ch->dma_chan), v); 39372017Scg 39472017Scg adcdac_go(ch, go); /* unpause */ 39572017Scg 39672017Scg ch->fmt = format; 39772017Scg ch->bps = cs4281_format_to_bps(format); 39872017Scg ch->dma_setup = 0; 39972017Scg 40072017Scg return 0; 40172017Scg} 40272017Scg 40372017Scgstatic int 40472017Scgcs4281chan_getptr(kobj_t obj, void *data) 40572017Scg{ 40672017Scg struct sc_chinfo *ch = data; 40772017Scg struct sc_info *sc = ch->parent; 40872017Scg u_int32_t dba, dca, ptr; 40972017Scg int sz; 41072017Scg 41172017Scg sz = sndbuf_getsize(ch->buffer); 41272017Scg dba = cs4281_rd(sc, CS4281PCI_DBA(ch->dma_chan)); 41372017Scg dca = cs4281_rd(sc, CS4281PCI_DCA(ch->dma_chan)); 41472017Scg ptr = (dca - dba + sz) % sz; 41572017Scg 41672017Scg return ptr; 41772017Scg} 41872017Scg 41972017Scgstatic int 42072017Scgcs4281chan_trigger(kobj_t obj, void *data, int go) 42172017Scg{ 42272017Scg struct sc_chinfo *ch = data; 42372017Scg 42472017Scg switch(go) { 42572017Scg case PCMTRIG_START: 42672017Scg adcdac_prog(ch); 42772017Scg adcdac_go(ch, 1); 42872017Scg break; 42972017Scg case PCMTRIG_ABORT: 43072017Scg adcdac_go(ch, 0); 43172017Scg break; 43272017Scg default: 43372017Scg break; 43472017Scg } 43572017Scg 43672017Scg /* return 0 if ok */ 43772017Scg return 0; 43872017Scg} 43972017Scg 44074763Scgstatic struct pcmchan_caps * 44172017Scgcs4281chan_getcaps(kobj_t obj, void *data) 44272017Scg{ 44372017Scg return &cs4281_caps; 44472017Scg} 44572017Scg 44672017Scgstatic kobj_method_t cs4281chan_methods[] = { 44772017Scg KOBJMETHOD(channel_init, cs4281chan_init), 44872017Scg KOBJMETHOD(channel_setformat, cs4281chan_setformat), 44972017Scg KOBJMETHOD(channel_setspeed, cs4281chan_setspeed), 45072017Scg KOBJMETHOD(channel_setblocksize, cs4281chan_setblocksize), 45172017Scg KOBJMETHOD(channel_trigger, cs4281chan_trigger), 45272017Scg KOBJMETHOD(channel_getptr, cs4281chan_getptr), 45372017Scg KOBJMETHOD(channel_getcaps, cs4281chan_getcaps), 45472017Scg { 0, 0 } 45572017Scg}; 45672017ScgCHANNEL_DECLARE(cs4281chan); 45772017Scg 45872017Scg/* -------------------------------------------------------------------- */ 45972017Scg/* ADC/DAC control */ 46072017Scg 46172017Scg/* adcdac_go enables/disable DMA channel, returns non-zero if DMA was 46272017Scg * active before call */ 46372017Scg 46472017Scgstatic u_int32_t 46572017Scgadcdac_go(struct sc_chinfo *ch, u_int32_t go) 46672017Scg{ 46772017Scg struct sc_info *sc = ch->parent; 46872017Scg u_int32_t going; 46972455Scg 47072017Scg going = !(cs4281_rd(sc, CS4281PCI_DCR(ch->dma_chan)) & CS4281PCI_DCR_MSK); 47172017Scg 47272455Scg if (go) 47372017Scg cs4281_clr4(sc, CS4281PCI_DCR(ch->dma_chan), CS4281PCI_DCR_MSK); 47472455Scg else 47572017Scg cs4281_set4(sc, CS4281PCI_DCR(ch->dma_chan), CS4281PCI_DCR_MSK); 47672017Scg 47772017Scg cs4281_wr(sc, CS4281PCI_HICR, CS4281PCI_HICR_EOI); 47872017Scg 47972017Scg return going; 48072017Scg} 48172017Scg 48272017Scgstatic void 48372455Scgadcdac_prog(struct sc_chinfo *ch) 48472017Scg{ 48572017Scg struct sc_info *sc = ch->parent; 48672017Scg u_int32_t go; 48772017Scg 48872017Scg if (!ch->dma_setup) { 48972017Scg go = adcdac_go(ch, 0); 49072017Scg cs4281_wr(sc, CS4281PCI_DBA(ch->dma_chan), 49172017Scg vtophys(sndbuf_getbuf(ch->buffer))); 49272455Scg cs4281_wr(sc, CS4281PCI_DBC(ch->dma_chan), 49372017Scg sndbuf_getsize(ch->buffer) / ch->bps - 1); 49472017Scg ch->dma_setup = 1; 49572017Scg adcdac_go(ch, go); 49672017Scg } 49772017Scg} 49872017Scg 49972017Scg/* -------------------------------------------------------------------- */ 50072017Scg/* The interrupt handler */ 50172017Scg 50272017Scgstatic void 50372017Scgcs4281_intr(void *p) 50472017Scg{ 50572017Scg struct sc_info *sc = (struct sc_info *)p; 50672017Scg u_int32_t hisr; 50772017Scg 50872017Scg hisr = cs4281_rd(sc, CS4281PCI_HISR); 50972455Scg 51072017Scg if (hisr == 0) return; 51172017Scg 51272017Scg if (hisr & CS4281PCI_HISR_DMA(CS4281_DMA_PLAY)) { 51372017Scg chn_intr(sc->pch.channel); 51472017Scg cs4281_rd(sc, CS4281PCI_HDSR(CS4281_DMA_PLAY)); /* Clear interrupt */ 51572017Scg } 51672017Scg 51772017Scg if (hisr & CS4281PCI_HISR_DMA(CS4281_DMA_REC)) { 51872017Scg chn_intr(sc->rch.channel); 51972017Scg cs4281_rd(sc, CS4281PCI_HDSR(CS4281_DMA_REC)); /* Clear interrupt */ 52072017Scg } 52172017Scg 52272017Scg /* Signal End-of-Interrupt */ 52372455Scg cs4281_wr(sc, CS4281PCI_HICR, CS4281PCI_HICR_EOI); 52472017Scg} 52572017Scg 52672017Scg/* -------------------------------------------------------------------- */ 52772455Scg/* power management related */ 52872017Scg 52972017Scgstatic int 53072017Scgcs4281_power(struct sc_info *sc, int state) 53172017Scg{ 53272455Scg 53372017Scg switch (state) { 53472455Scg case 0: 53572455Scg /* Permit r/w access to all BA0 registers */ 53672455Scg cs4281_wr(sc, CS4281PCI_CWPR, CS4281PCI_CWPR_MAGIC); 53772455Scg /* Power on */ 53872455Scg cs4281_clr4(sc, CS4281PCI_EPPMC, CS4281PCI_EPPMC_FPDN); 53972017Scg break; 54072455Scg case 3: 54172455Scg /* Power off card and codec */ 54272455Scg cs4281_set4(sc, CS4281PCI_EPPMC, CS4281PCI_EPPMC_FPDN); 54372455Scg cs4281_clr4(sc, CS4281PCI_SPMC, CS4281PCI_SPMC_RSTN); 54472017Scg break; 54572017Scg } 54672455Scg 54772455Scg DEB(printf("cs4281_power %d -> %d\n", sc->power, state)); 54872017Scg sc->power = state; 54972017Scg 55072017Scg return 0; 55172017Scg} 55272017Scg 55372017Scgstatic int 55472017Scgcs4281_init(struct sc_info *sc) 55572017Scg{ 55672017Scg u_int32_t i, v; 55772017Scg 55872017Scg /* (0) Blast clock register and serial port */ 55972017Scg cs4281_wr(sc, CS4281PCI_CLKCR1, 0); 56072017Scg cs4281_wr(sc, CS4281PCI_SERMC, 0); 56172455Scg 56272017Scg /* (1) Make ESYN 0 to turn sync pulse on AC97 link */ 56372017Scg cs4281_wr(sc, CS4281PCI_ACCTL, 0); 56472017Scg DELAY(50); 56572017Scg 56672017Scg /* (2) Effect Reset */ 56772017Scg cs4281_wr(sc, CS4281PCI_SPMC, 0); 56872017Scg DELAY(100); 56972017Scg cs4281_wr(sc, CS4281PCI_SPMC, CS4281PCI_SPMC_RSTN); 57072017Scg /* Wait 50ms for ABITCLK to become stable */ 57172455Scg DELAY(50000); 57272017Scg 57372017Scg /* (3) Enable Sound System Clocks */ 57472455Scg cs4281_wr(sc, CS4281PCI_CLKCR1, CS4281PCI_CLKCR1_DLLP); 57572017Scg DELAY(50000); /* Wait for PLL to stabilize */ 57672455Scg cs4281_wr(sc, CS4281PCI_CLKCR1, 57772455Scg CS4281PCI_CLKCR1_DLLP | CS4281PCI_CLKCR1_SWCE); 57872017Scg 57972017Scg /* (4) Power Up - this combination is essential. */ 58072455Scg cs4281_set4(sc, CS4281PCI_SSPM, 58172017Scg CS4281PCI_SSPM_ACLEN | CS4281PCI_SSPM_PSRCEN | 58272017Scg CS4281PCI_SSPM_CSRCEN | CS4281PCI_SSPM_MIXEN); 58372017Scg 58472017Scg /* (5) Wait for clock stabilization */ 58572455Scg if (cs4281_waitset(sc, 58672455Scg CS4281PCI_CLKCR1, 58772455Scg CS4281PCI_CLKCR1_DLLRDY, 58872017Scg 250) == 0) { 58972017Scg device_printf(sc->dev, "Clock stabilization failed\n"); 59072017Scg return -1; 59172017Scg } 59272017Scg 59372017Scg /* (6) Enable ASYNC generation. */ 59472455Scg cs4281_wr(sc, CS4281PCI_ACCTL,CS4281PCI_ACCTL_ESYN); 59572017Scg 59672017Scg /* Wait to allow AC97 to start generating clock bit */ 59772017Scg DELAY(50000); 59872017Scg 59972017Scg /* Set AC97 timing */ 60072017Scg cs4281_wr(sc, CS4281PCI_SERMC, CS4281PCI_SERMC_PTC_AC97); 60172017Scg 60272017Scg /* (7) Wait for AC97 ready signal */ 60372017Scg if (cs4281_waitset(sc, CS4281PCI_ACSTS, CS4281PCI_ACSTS_CRDY, 250) == 0) { 60472017Scg device_printf(sc->dev, "codec did not avail\n"); 60572017Scg return -1; 60672455Scg } 60772017Scg 60872017Scg /* (8) Assert valid frame signal to begin sending commands to 60972017Scg * AC97 codec */ 61072455Scg cs4281_wr(sc, 61172455Scg CS4281PCI_ACCTL, 61272017Scg CS4281PCI_ACCTL_VFRM | CS4281PCI_ACCTL_ESYN); 61372017Scg 61472017Scg /* (9) Wait for codec calibration */ 61572017Scg for(i = 0 ; i < 1000; i++) { 61672017Scg DELAY(10000); 61772017Scg v = cs4281_rdcd(0, sc, AC97_REG_POWER); 61872017Scg if ((v & 0x0f) == 0x0f) { 61972017Scg break; 62072017Scg } 62172017Scg } 62272017Scg if (i == 1000) { 62372017Scg device_printf(sc->dev, "codec failed to calibrate\n"); 62472017Scg return -1; 62572017Scg } 62672017Scg 62772017Scg /* (10) Set AC97 timing */ 62872017Scg cs4281_wr(sc, CS4281PCI_SERMC, CS4281PCI_SERMC_PTC_AC97); 62972017Scg 63072017Scg /* (11) Wait for valid data to arrive */ 63172455Scg if (cs4281_waitset(sc, 63272455Scg CS4281PCI_ACISV, 63372455Scg CS4281PCI_ACISV_ISV(3) | CS4281PCI_ACISV_ISV(4), 63472017Scg 10000) == 0) { 63572017Scg device_printf(sc->dev, "cs4281 never got valid data\n"); 63672017Scg return -1; 63772455Scg } 63872017Scg 63972017Scg /* (12) Start digital data transfer of audio data to codec */ 64072455Scg cs4281_wr(sc, 64172455Scg CS4281PCI_ACOSV, 64272017Scg CS4281PCI_ACOSV_SLV(3) | CS4281PCI_ACOSV_SLV(4)); 64372017Scg 64472017Scg /* Set Master and headphone to max */ 64572017Scg cs4281_wrcd(0, sc, AC97_MIX_PHONES, 0); 64672017Scg cs4281_wrcd(0, sc, AC97_MIX_MASTER, 0); 64772017Scg 64872017Scg /* Power on the DAC */ 64972017Scg v = cs4281_rdcd(0, sc, AC97_REG_POWER) & 0xfdff; 65072455Scg cs4281_wrcd(0, sc, AC97_REG_POWER, v); 65172017Scg 65272017Scg /* Wait until DAC state ready */ 65372017Scg for(i = 0; i < 320; i++) { 65472017Scg DELAY(100); 65572017Scg v = cs4281_rdcd(0, sc, AC97_REG_POWER); 65672017Scg if (v & 0x02) break; 65772017Scg } 65872017Scg 65972017Scg /* Power on the ADC */ 66072017Scg v = cs4281_rdcd(0, sc, AC97_REG_POWER) & 0xfeff; 66172455Scg cs4281_wrcd(0, sc, AC97_REG_POWER, v); 66272017Scg 66372017Scg /* Wait until ADC state ready */ 66472017Scg for(i = 0; i < 320; i++) { 66572017Scg DELAY(100); 66672017Scg v = cs4281_rdcd(0, sc, AC97_REG_POWER); 66772017Scg if (v & 0x01) break; 66872017Scg } 66972017Scg 67072017Scg /* FIFO configuration (driver is DMA orientated, implicit FIFO) */ 67172017Scg /* Play FIFO */ 67272017Scg 67372017Scg v = CS4281PCI_FCR_RS(CS4281PCI_RPCM_PLAY_SLOT) | 67472017Scg CS4281PCI_FCR_LS(CS4281PCI_LPCM_PLAY_SLOT) | 67572017Scg CS4281PCI_FCR_SZ(CS4281_FIFO_SIZE)| 67672017Scg CS4281PCI_FCR_OF(0); 67772017Scg cs4281_wr(sc, CS4281PCI_FCR(CS4281_DMA_PLAY), v); 67872017Scg 67972017Scg cs4281_wr(sc, CS4281PCI_FCR(CS4281_DMA_PLAY), v | CS4281PCI_FCR_FEN); 68072017Scg 68172017Scg /* Record FIFO */ 68272017Scg v = CS4281PCI_FCR_RS(CS4281PCI_RPCM_REC_SLOT) | 68372017Scg CS4281PCI_FCR_LS(CS4281PCI_LPCM_REC_SLOT) | 68472017Scg CS4281PCI_FCR_SZ(CS4281_FIFO_SIZE)| 68572017Scg CS4281PCI_FCR_OF(CS4281_FIFO_SIZE + 1); 68672017Scg cs4281_wr(sc, CS4281PCI_FCR(CS4281_DMA_REC), v | CS4281PCI_FCR_PSH); 68772017Scg cs4281_wr(sc, CS4281PCI_FCR(CS4281_DMA_REC), v | CS4281PCI_FCR_FEN); 68872017Scg 68972017Scg /* Match AC97 slots to FIFOs */ 69072017Scg v = CS4281PCI_SRCSA_PLSS(CS4281PCI_LPCM_PLAY_SLOT) | 69172017Scg CS4281PCI_SRCSA_PRSS(CS4281PCI_RPCM_PLAY_SLOT) | 69272017Scg CS4281PCI_SRCSA_CLSS(CS4281PCI_LPCM_REC_SLOT) | 69372017Scg CS4281PCI_SRCSA_CRSS(CS4281PCI_RPCM_REC_SLOT); 69472017Scg cs4281_wr(sc, CS4281PCI_SRCSA, v); 69572017Scg 69672017Scg /* Set Auto-Initialize and set directions */ 69772017Scg cs4281_wr(sc, 69872017Scg CS4281PCI_DMR(CS4281_DMA_PLAY), 69972017Scg CS4281PCI_DMR_DMA | 70072017Scg CS4281PCI_DMR_AUTO | 70172017Scg CS4281PCI_DMR_TR_PLAY); 70272017Scg cs4281_wr(sc, 70372017Scg CS4281PCI_DMR(CS4281_DMA_REC), 70472017Scg CS4281PCI_DMR_DMA | 70572017Scg CS4281PCI_DMR_AUTO | 70672017Scg CS4281PCI_DMR_TR_REC); 70772017Scg 70872017Scg /* Enable half and empty buffer interrupts keeping DMA paused */ 70972017Scg cs4281_wr(sc, 71072017Scg CS4281PCI_DCR(CS4281_DMA_PLAY), 71172017Scg CS4281PCI_DCR_TCIE | CS4281PCI_DCR_HTCIE | CS4281PCI_DCR_MSK); 71272017Scg cs4281_wr(sc, 71372017Scg CS4281PCI_DCR(CS4281_DMA_REC), 71472017Scg CS4281PCI_DCR_TCIE | CS4281PCI_DCR_HTCIE | CS4281PCI_DCR_MSK); 71572455Scg 71672017Scg /* Enable Interrupts */ 71772455Scg cs4281_clr4(sc, 71872455Scg CS4281PCI_HIMR, 71972017Scg CS4281PCI_HIMR_DMAI | 72072017Scg CS4281PCI_HIMR_DMA(CS4281_DMA_PLAY) | 72172017Scg CS4281PCI_HIMR_DMA(CS4281_DMA_REC)); 72272017Scg 72372017Scg /* Set playback volume */ 72472017Scg cs4281_wr(sc, CS4281PCI_PPLVC, 7); 72572017Scg cs4281_wr(sc, CS4281PCI_PPRVC, 7); 72672017Scg 72772017Scg return 0; 72872017Scg} 72972017Scg 73072017Scg/* -------------------------------------------------------------------- */ 73172017Scg/* Probe and attach the card */ 73272017Scg 73372017Scgstatic int 73472017Scgcs4281_pci_probe(device_t dev) 73572017Scg{ 73672017Scg char *s = NULL; 73772017Scg 73872017Scg switch (pci_get_devid(dev)) { 73972017Scg case CS4281_PCI_ID: 74072017Scg s = "Crystal Semiconductor CS4281"; 74172017Scg break; 74272017Scg } 74372017Scg 74472017Scg if (s) 74572017Scg device_set_desc(dev, s); 74672455Scg return s ? 0 : ENXIO; 74772017Scg} 74872017Scg 74972017Scgstatic int 75072017Scgcs4281_pci_attach(device_t dev) 75172017Scg{ 75272017Scg struct sc_info *sc; 75372017Scg struct ac97_info *codec = NULL; 75472017Scg u_int32_t data; 75572017Scg char status[SND_STATUSLEN]; 75672017Scg 75778564Sgreid if ((sc = malloc(sizeof(*sc), M_DEVBUF, M_NOWAIT | M_ZERO)) == NULL) { 75872017Scg device_printf(dev, "cannot allocate softc\n"); 75972017Scg return ENXIO; 76072017Scg } 76172017Scg 76272017Scg sc->dev = dev; 76372017Scg sc->type = pci_get_devid(dev); 76472017Scg 76572017Scg data = pci_read_config(dev, PCIR_COMMAND, 2); 76672017Scg data |= (PCIM_CMD_PORTEN | PCIM_CMD_MEMEN | PCIM_CMD_BUSMASTEREN); 76772017Scg pci_write_config(dev, PCIR_COMMAND, data, 2); 76872017Scg 76973770Scg#if __FreeBSD_version > 500000 77072455Scg if (pci_get_powerstate(dev) != PCI_POWERSTATE_D0) { 77172455Scg /* Reset the power state. */ 77272455Scg device_printf(dev, "chip is in D%d power mode " 77372455Scg "-- setting to D0\n", pci_get_powerstate(dev)); 77472455Scg 77572455Scg pci_set_powerstate(dev, PCI_POWERSTATE_D0); 77672455Scg } 77775702Sorion#else 77875702Sorion data = pci_read_config(dev, CS4281PCI_PMCS_OFFSET, 4); 77975702Sorion if (data & CS4281PCI_PMCS_PS_MASK) { 78075702Sorion /* Reset the power state. */ 78175702Sorion device_printf(dev, "chip is in D%d power mode " 78278362Scg "-- setting to D0\n", 78375702Sorion data & CS4281PCI_PMCS_PS_MASK); 78475702Sorion pci_write_config(dev, CS4281PCI_PMCS_OFFSET, 78575702Sorion data & ~CS4281PCI_PMCS_PS_MASK, 4); 78675702Sorion } 78773770Scg#endif 78875702Sorion 78972017Scg sc->regid = PCIR_MAPS; 79072017Scg sc->regtype = SYS_RES_MEMORY; 79172017Scg sc->reg = bus_alloc_resource(dev, sc->regtype, &sc->regid, 79272017Scg 0, ~0, CS4281PCI_BA0_SIZE, RF_ACTIVE); 79372017Scg if (!sc->reg) { 79472017Scg sc->regtype = SYS_RES_IOPORT; 79572017Scg sc->reg = bus_alloc_resource(dev, sc->regtype, &sc->regid, 79672017Scg 0, ~0, CS4281PCI_BA0_SIZE, RF_ACTIVE); 79772017Scg if (!sc->reg) { 79872017Scg device_printf(dev, "unable to allocate register space\n"); 79972017Scg goto bad; 80072017Scg } 80172017Scg } 80272017Scg sc->st = rman_get_bustag(sc->reg); 80372017Scg sc->sh = rman_get_bushandle(sc->reg); 80472017Scg 80572017Scg sc->memid = PCIR_MAPS + 4; 80672455Scg sc->mem = bus_alloc_resource(dev, SYS_RES_MEMORY, &sc->memid, 0, 80772017Scg ~0, CS4281PCI_BA1_SIZE, RF_ACTIVE); 80872017Scg if (sc->mem == NULL) { 80972017Scg device_printf(dev, "unable to allocate fifo space\n"); 81072017Scg goto bad; 81172017Scg } 81272017Scg 81372017Scg sc->irqid = 0; 81472017Scg sc->irq = bus_alloc_resource(dev, SYS_RES_IRQ, &sc->irqid, 81572017Scg 0, ~0, 1, RF_ACTIVE | RF_SHAREABLE); 81672017Scg if (!sc->irq) { 81772017Scg device_printf(dev, "unable to allocate interrupt\n"); 81872017Scg goto bad; 81972017Scg } 82072017Scg 82174763Scg if (snd_setup_intr(dev, sc->irq, 0, cs4281_intr, sc, &sc->ih)) { 82272017Scg device_printf(dev, "unable to setup interrupt\n"); 82372017Scg goto bad; 82472017Scg } 82572017Scg 82672017Scg if (bus_dma_tag_create(/*parent*/NULL, /*alignment*/2, /*boundary*/0, 82772017Scg /*lowaddr*/BUS_SPACE_MAXADDR_32BIT, 82872017Scg /*highaddr*/BUS_SPACE_MAXADDR, 82972017Scg /*filter*/NULL, /*filterarg*/NULL, 83072455Scg /*maxsize*/CS4281_BUFFER_SIZE, /*nsegments*/1, 83172017Scg /*maxsegz*/0x3ffff, 83272017Scg /*flags*/0, &sc->parent_dmat) != 0) { 83372017Scg device_printf(dev, "unable to create dma tag\n"); 83472017Scg goto bad; 83572017Scg } 83672017Scg 83772017Scg /* power up */ 83872017Scg cs4281_power(sc, 0); 83972017Scg 84072017Scg /* init chip */ 84172017Scg if (cs4281_init(sc) == -1) { 84272017Scg device_printf(dev, "unable to initialize the card\n"); 84372017Scg goto bad; 84472017Scg } 84572017Scg 84672017Scg /* create/init mixer */ 84772017Scg codec = AC97_CREATE(dev, sc, cs4281_ac97); 84872017Scg if (codec == NULL) 84972017Scg goto bad; 85072017Scg 85172017Scg mixer_init(dev, ac97_getmixerclass(), codec); 85272017Scg 85372017Scg if (pcm_register(dev, sc, 1, 1)) 85472017Scg goto bad; 85572017Scg 85672017Scg pcm_addchan(dev, PCMDIR_PLAY, &cs4281chan_class, sc); 85772017Scg pcm_addchan(dev, PCMDIR_REC, &cs4281chan_class, sc); 85872017Scg 85972017Scg snprintf(status, SND_STATUSLEN, "at %s 0x%lx irq %ld", 86072017Scg (sc->regtype == SYS_RES_IOPORT)? "io" : "memory", 86172017Scg rman_get_start(sc->reg), rman_get_start(sc->irq)); 86272017Scg pcm_setstatus(dev, status); 86372017Scg 86472017Scg return 0; 86572017Scg 86672017Scg bad: 86772017Scg if (codec) 86872017Scg ac97_destroy(codec); 86972017Scg if (sc->reg) 87072017Scg bus_release_resource(dev, sc->regtype, sc->regid, sc->reg); 87172017Scg if (sc->mem) 87272017Scg bus_release_resource(dev, SYS_RES_MEMORY, sc->memid, sc->mem); 87372017Scg if (sc->ih) 87472017Scg bus_teardown_intr(dev, sc->irq, sc->ih); 87572017Scg if (sc->irq) 87672017Scg bus_release_resource(dev, SYS_RES_IRQ, sc->irqid, sc->irq); 87772017Scg if (sc->parent_dmat) 87872017Scg bus_dma_tag_destroy(sc->parent_dmat); 87972017Scg free(sc, M_DEVBUF); 88072017Scg 88172017Scg return ENXIO; 88272017Scg} 88372017Scg 88472017Scgstatic int 88572017Scgcs4281_pci_detach(device_t dev) 88672017Scg{ 88772017Scg int r; 88872017Scg struct sc_info *sc; 88972017Scg 89072017Scg r = pcm_unregister(dev); 89172017Scg if (r) 89272017Scg return r; 89372017Scg 89472017Scg sc = pcm_getdevinfo(dev); 89572017Scg 89672017Scg /* power off */ 89772017Scg cs4281_power(sc, 3); 89872017Scg 89972017Scg bus_release_resource(dev, sc->regtype, sc->regid, sc->reg); 90072017Scg bus_release_resource(dev, SYS_RES_MEMORY, sc->memid, sc->mem); 90172017Scg bus_teardown_intr(dev, sc->irq, sc->ih); 90272017Scg bus_release_resource(dev, SYS_RES_IRQ, sc->irqid, sc->irq); 90372017Scg bus_dma_tag_destroy(sc->parent_dmat); 90472017Scg free(sc, M_DEVBUF); 90572017Scg 90672017Scg return 0; 90772017Scg} 90872017Scg 90972017Scgstatic int 91072017Scgcs4281_pci_suspend(device_t dev) 91172017Scg{ 91272017Scg struct sc_info *sc; 91372017Scg 91472017Scg sc = pcm_getdevinfo(dev); 91572017Scg 91672455Scg sc->rch.dma_active = adcdac_go(&sc->rch, 0); 91772455Scg sc->pch.dma_active = adcdac_go(&sc->pch, 0); 91872017Scg 91972455Scg cs4281_power(sc, 3); 92072455Scg 92172017Scg return 0; 92272017Scg} 92372017Scg 92472017Scgstatic int 92572017Scgcs4281_pci_resume(device_t dev) 92672017Scg{ 92772017Scg struct sc_info *sc; 92872017Scg 92972017Scg sc = pcm_getdevinfo(dev); 93072017Scg 93172017Scg /* power up */ 93272455Scg cs4281_power(sc, 0); 93372017Scg 93472455Scg /* initialize chip */ 93572455Scg if (cs4281_init(sc) == -1) { 93672455Scg device_printf(dev, "unable to reinitialize the card\n"); 93772455Scg return ENXIO; 93872017Scg } 93972017Scg 94072017Scg /* restore mixer state */ 94172017Scg if (mixer_reinit(dev) == -1) { 94272017Scg device_printf(dev, "unable to reinitialize the mixer\n"); 94372017Scg return ENXIO; 94472017Scg } 94572017Scg 94672455Scg /* restore chip state */ 94772455Scg cs4281chan_setspeed(NULL, &sc->rch, sc->rch.spd); 94872455Scg cs4281chan_setblocksize(NULL, &sc->rch, sc->rch.blksz); 94972455Scg cs4281chan_setformat(NULL, &sc->rch, sc->rch.fmt); 95072455Scg adcdac_go(&sc->rch, sc->rch.dma_active); 95172455Scg 95272455Scg cs4281chan_setspeed(NULL, &sc->pch, sc->pch.spd); 95372455Scg cs4281chan_setblocksize(NULL, &sc->pch, sc->pch.blksz); 95472455Scg cs4281chan_setformat(NULL, &sc->pch, sc->pch.fmt); 95572455Scg adcdac_go(&sc->pch, sc->pch.dma_active); 95672455Scg 95772017Scg return 0; 95872017Scg} 95972017Scg 96072017Scgstatic device_method_t cs4281_methods[] = { 96172017Scg /* Device interface */ 96272017Scg DEVMETHOD(device_probe, cs4281_pci_probe), 96372017Scg DEVMETHOD(device_attach, cs4281_pci_attach), 96472017Scg DEVMETHOD(device_detach, cs4281_pci_detach), 96572017Scg DEVMETHOD(device_suspend, cs4281_pci_suspend), 96672017Scg DEVMETHOD(device_resume, cs4281_pci_resume), 96772017Scg { 0, 0 } 96872017Scg}; 96972017Scg 97072017Scgstatic driver_t cs4281_driver = { 97172017Scg "pcm", 97272017Scg cs4281_methods, 97382180Scg PCM_SOFTC_SIZE, 97472017Scg}; 97572017Scg 97672017ScgDRIVER_MODULE(snd_cs4281, pci, cs4281_driver, pcm_devclass, 0, 0); 97772017ScgMODULE_DEPEND(snd_cs4281, snd_pcm, PCM_MINVER, PCM_PREFVER, PCM_MAXVER); 97872017ScgMODULE_VERSION(snd_cs4281, 1); 979