cmi.c revision 75174
1175282Sobrien/* 2175282Sobrien * Copyright (c) 2000 Orion Hodson <O.Hodson@cs.ucl.ac.uk> 3175282Sobrien * All rights reserved. 4175282Sobrien * 5175282Sobrien * Redistribution and use in source and binary forms, with or without 6175282Sobrien * modification, are permitted provided that the following conditions 7175282Sobrien * are met: 8175282Sobrien * 1. Redistributions of source code must retain the above copyright 9175282Sobrien * notice, this list of conditions and the following disclaimer. 10175282Sobrien * 2. Redistributions in binary form must reproduce the above copyright 11175282Sobrien * notice, this list of conditions and the following disclaimer in the 12175282Sobrien * documentation and/or other materials provided with the distribution. 13175282Sobrien * 14175282Sobrien * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1525839Speter * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1625839Speter * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17102852Speter * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18102852Speter * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1925839Speter * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2025839Speter * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2125839Speter * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHERIN CONTRACT, STRICT 2225839Speter * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2325839Speter * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THEPOSSIBILITY OF 2425839Speter * SUCH DAMAGE. 25102840Speter * 26102840Speter * This driver exists largely as a result of other people's efforts. 27102840Speter * Much of register handling is based on NetBSD CMI8x38 audio driver 28102840Speter * by Takuya Shiozaki <AoiMoe@imou.to>. Chen-Li Tien 29102840Speter * <cltien@cmedia.com.tw> clarified points regarding the DMA related 30102840Speter * registers and the 8738 mixer devices. His Linux was driver a also 3125839Speter * useful reference point. 3225839Speter * 3325839Speter * TODO: MIDI 3425839Speter * 3525839Speter * SPDIF contributed by Gerhard Gonter <gonter@whisky.wu-wien.ac.at>. 3625839Speter * 3725839Speter * This card/code does not always manage to sample at 44100 - actual 3825839Speter * rate drifts slightly between recordings (usually 0-3%). No 3925839Speter * differences visible in register dumps between times that work and 4025839Speter * those that don't. 4134461Speter * 4225839Speter * $FreeBSD: head/sys/dev/sound/pci/cmi.c 75174 2001-04-04 13:48:33Z orion $ 4325839Speter */ 4425839Speter 4525839Speter#include <dev/sound/pcm/sound.h> 4625839Speter#include <dev/sound/pci/cmireg.h> 4725839Speter#include <dev/sound/isa/sb.h> 4825839Speter 4925839Speter#include <pci/pcireg.h> 5025839Speter#include <pci/pcivar.h> 5125839Speter 5225839Speter#include <sys/sysctl.h> 53102840Speter 5425839Speter#include "mixer_if.h" 5525839Speter 5625839Speter/* Supported chip ID's */ 5725839Speter#define CMI8338A_PCI_ID 0x010013f6 5825839Speter#define CMI8338B_PCI_ID 0x010113f6 5925839Speter#define CMI8738_PCI_ID 0x011113f6 6025839Speter#define CMI8738B_PCI_ID 0x011213f6 6125839Speter 6225839Speter/* Buffer size max is 64k for permitted DMA boundaries */ 6325839Speter#define CMI_BUFFER_SIZE 16384 6425839Speter 6525839Speter/* Interrupts per length of buffer */ 6625839Speter#define CMI_INTR_PER_BUFFER 2 6725839Speter 6834461Speter/* Clarify meaning of named defines in cmireg.h */ 6925839Speter#define CMPCI_REG_DMA0_MAX_SAMPLES CMPCI_REG_DMA0_BYTES 7025839Speter#define CMPCI_REG_DMA0_INTR_SAMPLES CMPCI_REG_DMA0_SAMPLES 7125839Speter#define CMPCI_REG_DMA1_MAX_SAMPLES CMPCI_REG_DMA1_BYTES 7225839Speter#define CMPCI_REG_DMA1_INTR_SAMPLES CMPCI_REG_DMA1_SAMPLES 7334461Speter 7434461Speter/* Our indication of custom mixer control */ 7534461Speter#define CMPCI_NON_SB16_CONTROL 0xff 7634461Speter 7734461Speter/* Debugging macro's */ 7834461Speter#undef DEB 79128269Speter#ifndef DEB 80128269Speter#define DEB(x) /* x */ 81128269Speter#endif /* DEB */ 82128269Speter 83128269Speter#ifndef DEBMIX 8434461Speter#define DEBMIX(x) /* x */ 8534461Speter#endif /* DEBMIX */ 8634461Speter 8734461Speter/* ------------------------------------------------------------------------- */ 8834461Speter/* Structures */ 8934461Speter 9034461Speterstruct sc_info; 9134461Speter 9225839Speterstruct sc_chinfo { 9325839Speter struct sc_info *parent; 9425839Speter struct pcm_channel *channel; 9525839Speter struct snd_dbuf *buffer; 9625839Speter u_int32_t fmt, spd, phys_buf, bps; 9725839Speter u_int32_t dma_active:1, dma_was_active:1; 9825839Speter int dir; 9925839Speter}; 10025839Speter 10125839Speterstruct sc_info { 10225839Speter device_t dev; 103102840Speter 10425839Speter bus_space_tag_t st; 10525839Speter bus_space_handle_t sh; 10625839Speter bus_dma_tag_t parent_dmat; 10725839Speter struct resource *reg, *irq; 10834461Speter int regid, irqid; 10934461Speter void *ih; 11034461Speter 11134461Speter struct sc_chinfo pch, rch; 11234461Speter}; 11334461Speter 11434461Speter/* Channel caps */ 11534461Speter 11634461Speterstatic u_int32_t cmi_fmt[] = { 11725839Speter AFMT_U8, 11825839Speter AFMT_STEREO | AFMT_U8, 11925839Speter AFMT_S16_LE, 12025839Speter AFMT_STEREO | AFMT_S16_LE, 12125839Speter 0 12225839Speter}; 12325839Speter 12425839Speterstatic struct pcmchan_caps cmi_caps = {5512, 48000, cmi_fmt, 0}; 12525839Speter 12625839Speter/* ------------------------------------------------------------------------- */ 12725839Speter/* Register Utilities */ 12825839Speter 129175282Sobrienstatic u_int32_t 13025839Spetercmi_rd(struct sc_info *sc, int regno, int size) 131175282Sobrien{ 132175282Sobrien switch (size) { 133175282Sobrien case 1: 13425839Speter return bus_space_read_1(sc->st, sc->sh, regno); 135175282Sobrien case 2: 13625839Speter return bus_space_read_2(sc->st, sc->sh, regno); 13725839Speter case 4: 13825839Speter return bus_space_read_4(sc->st, sc->sh, regno); 13925839Speter default: 14025839Speter DEB(printf("cmi_rd: failed 0x%04x %d\n", regno, size)); 14125839Speter return 0xFFFFFFFF; 14225839Speter } 14325839Speter} 14425839Speter 14525839Speterstatic void 14625839Spetercmi_wr(struct sc_info *sc, int regno, u_int32_t data, int size) 14725839Speter{ 14825839Speter switch (size) { 14925839Speter case 1: 15025839Speter bus_space_write_1(sc->st, sc->sh, regno, data); 15125839Speter break; 15225839Speter case 2: 15325839Speter bus_space_write_2(sc->st, sc->sh, regno, data); 15425839Speter break; 15525839Speter case 4: 15625839Speter bus_space_write_4(sc->st, sc->sh, regno, data); 15725839Speter break; 15825839Speter } 15925839Speter} 16025839Speter 16125839Speterstatic void 16225839Spetercmi_partial_wr4(struct sc_info *sc, 16325839Speter int reg, int shift, u_int32_t mask, u_int32_t val) 164128269Speter{ 165128269Speter u_int32_t r; 16625839Speter 167128269Speter r = cmi_rd(sc, reg, 4); 168128269Speter r &= ~(mask << shift); 169128269Speter r |= val << shift; 170128269Speter cmi_wr(sc, reg, r, 4); 171128269Speter} 172128269Speter 173128269Speterstatic void 174128269Spetercmi_clr4(struct sc_info *sc, int reg, u_int32_t mask) 175128269Speter{ 176128269Speter u_int32_t r; 177128269Speter 178128269Speter r = cmi_rd(sc, reg, 4); 179128269Speter r &= ~mask; 180128269Speter cmi_wr(sc, reg, r, 4); 18125839Speter} 18225839Speter 18325839Speterstatic void 18425839Spetercmi_set4(struct sc_info *sc, int reg, u_int32_t mask) 18525839Speter{ 18625839Speter u_int32_t r; 18725839Speter 18825839Speter r = cmi_rd(sc, reg, 4); 18925839Speter r |= mask; 19025839Speter cmi_wr(sc, reg, r, 4); 19125839Speter} 192128269Speter 193128269Speter/* ------------------------------------------------------------------------- */ 19425839Speter/* Rate Mapping */ 19525839Speter 19625839Speterstatic int cmi_rates[] = {5512, 8000, 11025, 16000, 197109660Speter 22050, 32000, 44100, 48000}; 19825839Speter#define NUM_CMI_RATES (sizeof(cmi_rates)/sizeof(cmi_rates[0])) 19925839Speter 20025839Speter/* cmpci_rate_to_regvalue returns sampling freq selector for FCR1 20125839Speter * register - reg order is 5k,11k,22k,44k,8k,16k,32k,48k */ 20225839Speter 20325839Speterstatic u_int32_t 20425839Spetercmpci_rate_to_regvalue(int rate) 20525839Speter{ 20625839Speter int i, r; 20725839Speter 20825839Speter for(i = 0; i < NUM_CMI_RATES - 1; i++) { 20925839Speter if (rate < ((cmi_rates[i] + cmi_rates[i + 1]) / 2)) { 21025839Speter break; 21125839Speter } 21225839Speter } 21325839Speter 21425839Speter DEB(printf("cmpci_rate_to_regvalue: %d -> %d\n", rate, cmi_rates[i])); 21525839Speter 21625839Speter r = ((i >> 1) | (i << 2)) & 0x07; 21725839Speter return r; 21825839Speter} 21925839Speter 22025839Speterstatic int 22125839Spetercmpci_regvalue_to_rate(u_int32_t r) 22225839Speter{ 22325839Speter int i; 22425839Speter 22525839Speter i = ((r << 1) | (r >> 2)) & 0x07; 22625839Speter DEB(printf("cmpci_regvalue_to_rate: %d -> %d\n", r, i)); 22725839Speter return cmi_rates[i]; 22825839Speter} 22925839Speter 23025839Speter/* ------------------------------------------------------------------------- */ 23125839Speter/* ADC/DAC control - there are 2 dma channels on 8738, either can be 23225839Speter * playback or capture. We use ch0 for playback and ch1 for capture. */ 23325839Speter 23425839Speterstatic void 23525839Spetercmi_dma_prog(struct sc_info *sc, struct sc_chinfo *ch, u_int32_t base) 23625839Speter{ 23725839Speter u_int32_t s, i, sz, physbuf; 23825839Speter 23925839Speter physbuf = vtophys(sndbuf_getbuf(ch->buffer)); 24025839Speter 24125839Speter cmi_wr(sc, base, physbuf, 4); 24225839Speter sz = (u_int32_t)sndbuf_getsize(ch->buffer); 24325839Speter 24425839Speter s = sz / ch->bps - 1; 24525839Speter cmi_wr(sc, base + 4, s, 2); 24625839Speter 24725839Speter i = sz / (ch->bps * CMI_INTR_PER_BUFFER) - 1; 24825839Speter cmi_wr(sc, base + 6, i, 2); 24925839Speter} 25025839Speter 25125839Speter 25225839Speterstatic void 25325839Spetercmi_ch0_start(struct sc_info *sc, struct sc_chinfo *ch) 25425839Speter{ 25525839Speter cmi_dma_prog(sc, ch, CMPCI_REG_DMA0_BASE); 25625839Speter 25725839Speter cmi_set4(sc, CMPCI_REG_FUNC_0, CMPCI_REG_CH0_ENABLE); 25825839Speter cmi_set4(sc, CMPCI_REG_INTR_CTRL, 25925839Speter CMPCI_REG_CH0_INTR_ENABLE); 26025839Speter 26125839Speter ch->dma_active = 1; 26225839Speter} 26325839Speter 26425839Speterstatic u_int32_t 26525839Spetercmi_ch0_stop(struct sc_info *sc, struct sc_chinfo *ch) 26625839Speter{ 26725839Speter u_int32_t r = ch->dma_active; 26825839Speter 26925839Speter cmi_clr4(sc, CMPCI_REG_INTR_CTRL, CMPCI_REG_CH0_INTR_ENABLE); 27025839Speter cmi_clr4(sc, CMPCI_REG_FUNC_0, CMPCI_REG_CH0_ENABLE); 27125839Speter ch->dma_active = 0; 27225839Speter return r; 27325839Speter} 27425839Speter 27525839Speterstatic void 27625839Spetercmi_ch1_start(struct sc_info *sc, struct sc_chinfo *ch) 27725839Speter{ 27825839Speter cmi_dma_prog(sc, ch, CMPCI_REG_DMA1_BASE); 27925839Speter cmi_set4(sc, CMPCI_REG_FUNC_0, CMPCI_REG_CH1_ENABLE); 28025839Speter /* Enable Interrupts */ 28125839Speter cmi_set4(sc, CMPCI_REG_INTR_CTRL, 28225839Speter CMPCI_REG_CH1_INTR_ENABLE); 28325839Speter DEB(printf("cmi_ch1_start: dma prog\n")); 28425839Speter ch->dma_active = 1; 28525839Speter} 28625839Speter 28725839Speterstatic u_int32_t 28825839Spetercmi_ch1_stop(struct sc_info *sc, struct sc_chinfo *ch) 28925839Speter{ 29025839Speter u_int32_t r = ch->dma_active; 29125839Speter 29225839Speter cmi_clr4(sc, CMPCI_REG_INTR_CTRL, CMPCI_REG_CH1_INTR_ENABLE); 29325839Speter cmi_clr4(sc, CMPCI_REG_FUNC_0, CMPCI_REG_CH1_ENABLE); 29425839Speter ch->dma_active = 0; 29525839Speter return r; 29625839Speter} 29725839Speter 29825839Speterstatic void 29925839Spetercmi_spdif_speed(struct sc_info *sc, int speed) { 30025839Speter u_int32_t fcr1, lcr, mcr; 30125839Speter 30225839Speter if (speed >= 44100) { 30325839Speter fcr1 = CMPCI_REG_SPDIF0_ENABLE; 30425839Speter lcr = CMPCI_REG_XSPDIF_ENABLE; 30525839Speter mcr = (speed == 48000) ? 30625839Speter CMPCI_REG_W_SPDIF_48L | CMPCI_REG_SPDIF_48K : 0; 30725839Speter } else { 30825839Speter fcr1 = mcr = lcr = 0; 30925839Speter } 31025839Speter 31125839Speter cmi_partial_wr4(sc, CMPCI_REG_MISC, 0, 31225839Speter CMPCI_REG_W_SPDIF_48L | CMPCI_REG_SPDIF_48K, mcr); 31325839Speter cmi_partial_wr4(sc, CMPCI_REG_FUNC_1, 0, 31425839Speter CMPCI_REG_SPDIF0_ENABLE, fcr1); 31525839Speter cmi_partial_wr4(sc, CMPCI_REG_LEGACY_CTRL, 0, 31625839Speter CMPCI_REG_XSPDIF_ENABLE, lcr); 31725839Speter} 31825839Speter 31925839Speter/* ------------------------------------------------------------------------- */ 32025839Speter/* Channel Interface implementation */ 32125839Speter 32225839Speterstatic void * 32325839Spetercmichan_init(kobj_t obj, void *devinfo, 32425839Speter struct snd_dbuf *b, struct pcm_channel *c, int dir) 32525839Speter{ 32625839Speter struct sc_info *sc = devinfo; 32725839Speter struct sc_chinfo *ch = (dir == PCMDIR_PLAY) ? &sc->pch : &sc->rch; 32825839Speter 32925839Speter ch->parent = sc; 33025839Speter ch->channel = c; 33125839Speter ch->bps = 1; 33225839Speter ch->fmt = AFMT_U8; 33325839Speter ch->spd = DSP_DEFAULT_SPEED; 33425839Speter ch->buffer = b; 33525839Speter ch->dma_active = 0; 33625839Speter if (sndbuf_alloc(ch->buffer, sc->parent_dmat, CMI_BUFFER_SIZE) != 0) { 33725839Speter DEB(printf("cmichan_init failed\n")); 33825839Speter return NULL; 33925839Speter } 34025839Speter 34125839Speter ch->dir = dir; 34225839Speter if (ch->dir == PCMDIR_PLAY) { 34325839Speter cmi_dma_prog(sc, ch, CMPCI_REG_DMA0_BASE); 34425839Speter } else { 34525839Speter cmi_dma_prog(sc, ch, CMPCI_REG_DMA1_BASE); 34625839Speter } 34725839Speter 34825839Speter return ch; 34925839Speter} 35025839Speter 35125839Speterstatic int 35225839Spetercmichan_setformat(kobj_t obj, void *data, u_int32_t format) 35325839Speter{ 35425839Speter struct sc_chinfo *ch = data; 35525839Speter u_int32_t f; 35625839Speter 35725839Speter if (format & AFMT_S16_LE) { 35825839Speter f = CMPCI_REG_FORMAT_16BIT; 35925839Speter ch->bps = 2; 36025839Speter } else { 36125839Speter f = CMPCI_REG_FORMAT_8BIT; 36225839Speter ch->bps = 1; 36325839Speter } 36425839Speter 36525839Speter if (format & AFMT_STEREO) { 36625839Speter f |= CMPCI_REG_FORMAT_STEREO; 36725839Speter ch->bps *= 2; 36825839Speter } else { 36925839Speter f |= CMPCI_REG_FORMAT_MONO; 37025839Speter } 37125839Speter 37225839Speter if (ch->dir == PCMDIR_PLAY) { 37325839Speter cmi_partial_wr4(ch->parent, 37425839Speter CMPCI_REG_CHANNEL_FORMAT, 37525839Speter CMPCI_REG_CH0_FORMAT_SHIFT, 37625839Speter CMPCI_REG_CH0_FORMAT_MASK, 37725839Speter f); 37825839Speter } else { 37925839Speter cmi_partial_wr4(ch->parent, 38025839Speter CMPCI_REG_CHANNEL_FORMAT, 38125839Speter CMPCI_REG_CH1_FORMAT_SHIFT, 38225839Speter CMPCI_REG_CH1_FORMAT_MASK, 38325839Speter f); 38425839Speter } 38525839Speter ch->fmt = format; 38625839Speter 38725839Speter return 0; 38825839Speter} 38925839Speter 39025839Speterstatic int 39125839Spetercmichan_setspeed(kobj_t obj, void *data, u_int32_t speed) 39225839Speter{ 39325839Speter struct sc_chinfo *ch = data; 39425839Speter u_int32_t r, rsp; 39525839Speter 39625839Speter r = cmpci_rate_to_regvalue(speed); 39725839Speter if (ch->dir == PCMDIR_PLAY) { 39825839Speter if (speed < 44100) /* disable if req before rate change */ 39925839Speter cmi_spdif_speed(ch->parent, speed); 40025839Speter cmi_partial_wr4(ch->parent, 40125839Speter CMPCI_REG_FUNC_1, 40225839Speter CMPCI_REG_DAC_FS_SHIFT, 40325839Speter CMPCI_REG_DAC_FS_MASK, 40425839Speter r); 40525839Speter if (speed >= 44100) /* enable if req after rate change */ 40625839Speter cmi_spdif_speed(ch->parent, speed); 40725839Speter rsp = cmi_rd(ch->parent, CMPCI_REG_FUNC_1, 4); 40825839Speter rsp >>= CMPCI_REG_DAC_FS_SHIFT; 40925839Speter rsp &= CMPCI_REG_DAC_FS_MASK; 41025839Speter } else { 41125839Speter cmi_partial_wr4(ch->parent, 41225839Speter CMPCI_REG_FUNC_1, 41325839Speter CMPCI_REG_ADC_FS_SHIFT, 41425839Speter CMPCI_REG_ADC_FS_MASK, 41525839Speter r); 41625839Speter rsp = cmi_rd(ch->parent, CMPCI_REG_FUNC_1, 4); 41725839Speter rsp >>= CMPCI_REG_ADC_FS_SHIFT; 41825839Speter rsp &= CMPCI_REG_ADC_FS_MASK; 41925839Speter } 42025839Speter ch->spd = cmpci_regvalue_to_rate(r); 42125839Speter 42225839Speter DEB(printf("cmichan_setspeed (%s) %d -> %d (%d)\n", 42325839Speter (ch->dir == PCMDIR_PLAY) ? "play" : "rec", 42425839Speter speed, ch->spd, cmpci_regvalue_to_rate(rsp))); 42525839Speter 42625839Speter return ch->spd; 42725839Speter} 42825839Speter 42925839Speterstatic int 43025839Spetercmichan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize) 43125839Speter{ 43225839Speter struct sc_chinfo *ch = data; 43325839Speter 43425839Speter /* user has requested interrupts every blocksize bytes */ 43525839Speter if (blocksize > CMI_BUFFER_SIZE / CMI_INTR_PER_BUFFER) { 43625839Speter blocksize = CMI_BUFFER_SIZE / CMI_INTR_PER_BUFFER; 43725839Speter } 43825839Speter sndbuf_resize(ch->buffer, CMI_INTR_PER_BUFFER, blocksize); 43925839Speter 44025839Speter return sndbuf_getsize(ch->buffer); 44125839Speter} 44225839Speter 44325839Speterstatic int 44425839Spetercmichan_trigger(kobj_t obj, void *data, int go) 44525839Speter{ 44625839Speter struct sc_chinfo *ch = data; 44725839Speter struct sc_info *sc = ch->parent; 44825839Speter 44925839Speter if (ch->dir == PCMDIR_PLAY) { 45025839Speter switch(go) { 45125839Speter case PCMTRIG_START: 45225839Speter cmi_ch0_start(sc, ch); 45325839Speter break; 45425839Speter case PCMTRIG_ABORT: 45525839Speter cmi_ch0_stop(sc, ch); 45625839Speter break; 45725839Speter } 45825839Speter } else { 45925839Speter switch(go) { 46025839Speter case PCMTRIG_START: 46125839Speter cmi_ch1_start(sc, ch); 46225839Speter break; 46325839Speter case PCMTRIG_ABORT: 46425839Speter cmi_ch1_stop(sc, ch); 46525839Speter break; 46625839Speter } 46725839Speter } 46825839Speter return 0; 46925839Speter} 47025839Speter 47125839Speterstatic int 47225839Spetercmichan_getptr(kobj_t obj, void *data) 47325839Speter{ 47425839Speter struct sc_chinfo *ch = data; 47525839Speter struct sc_info *sc = ch->parent; 47625839Speter u_int32_t physptr, bufptr, sz; 47725839Speter 47825839Speter if (ch->dir == PCMDIR_PLAY) { 47925839Speter physptr = cmi_rd(sc, CMPCI_REG_DMA0_BASE, 4); 48025839Speter } else { 48125839Speter physptr = cmi_rd(sc, CMPCI_REG_DMA1_BASE, 4); 48225839Speter } 48325839Speter 48425839Speter sz = sndbuf_getsize(ch->buffer); 48525839Speter bufptr = (physptr - ch->phys_buf + sz - ch->bps) % sz; 48625839Speter 48725839Speter return bufptr; 48825839Speter} 48925839Speter 49025839Speterstatic void 49125839Spetercmi_intr(void *data) 49225839Speter{ 49325839Speter struct sc_info *sc = data; 49425839Speter u_int32_t intrstat; 49525839Speter 49625839Speter intrstat = cmi_rd(sc, CMPCI_REG_INTR_STATUS, 4); 49725839Speter if ((intrstat & CMPCI_REG_ANY_INTR) == 0) { 49825839Speter return; 49925839Speter } 50025839Speter 50125839Speter /* Disable interrupts */ 50225839Speter if (intrstat & CMPCI_REG_CH0_INTR) { 50325839Speter cmi_clr4(sc, CMPCI_REG_INTR_CTRL, CMPCI_REG_CH0_INTR_ENABLE); 50425839Speter } 50525839Speter 50625839Speter if (intrstat & CMPCI_REG_CH1_INTR) { 50725839Speter cmi_clr4(sc, CMPCI_REG_INTR_CTRL, CMPCI_REG_CH1_INTR_ENABLE); 50825839Speter } 50925839Speter 51025839Speter /* Signal interrupts to channel */ 51125839Speter if (intrstat & CMPCI_REG_CH0_INTR) { 51225839Speter chn_intr(sc->pch.channel); 51325839Speter } 51425839Speter 51525839Speter if (intrstat & CMPCI_REG_CH1_INTR) { 51625839Speter chn_intr(sc->rch.channel); 51725839Speter } 51825839Speter 51925839Speter /* Enable interrupts */ 52025839Speter if (intrstat & CMPCI_REG_CH0_INTR) { 52125839Speter cmi_set4(sc, CMPCI_REG_INTR_CTRL, CMPCI_REG_CH0_INTR_ENABLE); 52225839Speter } 52325839Speter 52425839Speter if (intrstat & CMPCI_REG_CH1_INTR) { 52525839Speter cmi_set4(sc, CMPCI_REG_INTR_CTRL, CMPCI_REG_CH1_INTR_ENABLE); 52625839Speter } 52725839Speter 52825839Speter return; 52925839Speter} 53025839Speter 53125839Speterstatic struct pcmchan_caps * 53225839Spetercmichan_getcaps(kobj_t obj, void *data) 53325839Speter{ 53425839Speter return &cmi_caps; 53525839Speter} 53625839Speter 53725839Speterstatic kobj_method_t cmichan_methods[] = { 53825839Speter KOBJMETHOD(channel_init, cmichan_init), 53925839Speter KOBJMETHOD(channel_setformat, cmichan_setformat), 54025839Speter KOBJMETHOD(channel_setspeed, cmichan_setspeed), 54125839Speter KOBJMETHOD(channel_setblocksize, cmichan_setblocksize), 54225839Speter KOBJMETHOD(channel_trigger, cmichan_trigger), 54334461Speter KOBJMETHOD(channel_getptr, cmichan_getptr), 54434461Speter KOBJMETHOD(channel_getcaps, cmichan_getcaps), 54534461Speter { 0, 0 } 54634461Speter}; 54734461SpeterCHANNEL_DECLARE(cmichan); 54834461Speter 54934461Speter/* ------------------------------------------------------------------------- */ 55034461Speter/* Mixer - sb16 with kinks */ 55134461Speter 55234461Speterstatic void 55334461Spetercmimix_wr(struct sc_info *sc, u_int8_t port, u_int8_t val) 55434461Speter{ 55534461Speter cmi_wr(sc, CMPCI_REG_SBADDR, port, 1); 55625839Speter cmi_wr(sc, CMPCI_REG_SBDATA, val, 1); 55725839Speter} 55825839Speter 55925839Speterstatic u_int8_t 56025839Spetercmimix_rd(struct sc_info *sc, u_int8_t port) 56125839Speter{ 56225839Speter cmi_wr(sc, CMPCI_REG_SBADDR, port, 1); 56325839Speter return (u_int8_t)cmi_rd(sc, CMPCI_REG_SBDATA, 1); 56425839Speter} 56525839Speter 56625839Speterstruct sb16props { 56725839Speter u_int8_t rreg; /* right reg chan register */ 56825839Speter u_int8_t stereo:1; /* (no explanation needed, honest) */ 56925839Speter u_int8_t rec:1; /* recording source */ 57025839Speter u_int8_t bits:3; /* num bits to represent maximum gain rep */ 57125839Speter u_int8_t oselect; /* output select mask */ 57225839Speter u_int8_t iselect; /* right input select mask */ 57325839Speter} static const cmt[SOUND_MIXER_NRDEVICES] = { 57425839Speter [SOUND_MIXER_SYNTH] = {CMPCI_SB16_MIXER_FM_R, 1, 1, 5, 57525839Speter CMPCI_SB16_SW_FM, CMPCI_SB16_MIXER_FM_SRC_R}, 57625839Speter [SOUND_MIXER_CD] = {CMPCI_SB16_MIXER_CDDA_R, 1, 1, 5, 57725839Speter CMPCI_SB16_SW_CD, CMPCI_SB16_MIXER_CD_SRC_R}, 57825839Speter [SOUND_MIXER_LINE] = {CMPCI_SB16_MIXER_LINE_R, 1, 1, 5, 57925839Speter CMPCI_SB16_SW_LINE, CMPCI_SB16_MIXER_LINE_SRC_R}, 58025839Speter [SOUND_MIXER_MIC] = {CMPCI_SB16_MIXER_MIC, 0, 1, 5, 58125839Speter CMPCI_SB16_SW_MIC, CMPCI_SB16_MIXER_MIC_SRC}, 58225839Speter [SOUND_MIXER_SPEAKER] = {CMPCI_SB16_MIXER_SPEAKER, 0, 0, 2, 0, 0}, 58325839Speter [SOUND_MIXER_PCM] = {CMPCI_SB16_MIXER_VOICE_R, 1, 0, 5, 0, 0}, 58425839Speter [SOUND_MIXER_VOLUME] = {CMPCI_SB16_MIXER_MASTER_R, 1, 0, 5, 0, 0}, 58525839Speter /* These controls are not implemented in CMI8738, but maybe at a 58625839Speter future date. They are not documented in C-Media documentation, 58725839Speter though appear in other drivers for future h/w (ALSA, Linux, NetBSD). 58825839Speter */ 58925839Speter [SOUND_MIXER_IGAIN] = {CMPCI_SB16_MIXER_INGAIN_R, 1, 0, 2, 0, 0}, 59025839Speter [SOUND_MIXER_OGAIN] = {CMPCI_SB16_MIXER_OUTGAIN_R, 1, 0, 2, 0, 0}, 59125839Speter [SOUND_MIXER_BASS] = {CMPCI_SB16_MIXER_BASS_R, 1, 0, 4, 0, 0}, 59225839Speter [SOUND_MIXER_TREBLE] = {CMPCI_SB16_MIXER_TREBLE_R, 1, 0, 4, 0, 0}, 59325839Speter /* The mic pre-amp is implemented with non-SB16 compatible 59425839Speter registers. */ 59525839Speter [SOUND_MIXER_MONITOR] = {CMPCI_NON_SB16_CONTROL, 0, 1, 4, 0}, 59625839Speter}; 59725839Speter 59825839Speter#define MIXER_GAIN_REG_RTOL(r) (r - 1) 59925839Speter 60025839Speterstatic int 60125839Spetercmimix_init(struct snd_mixer *m) 60225839Speter{ 60325839Speter struct sc_info *sc = mix_getdevinfo(m); 60425839Speter u_int32_t i,v; 60525839Speter 60625839Speter for(i = v = 0; i < SOUND_MIXER_NRDEVICES; i++) { 60725839Speter if (cmt[i].bits) v |= 1 << i; 60825839Speter } 60925839Speter mix_setdevs(m, v); 61025839Speter 61125839Speter for(i = v = 0; i < SOUND_MIXER_NRDEVICES; i++) { 61225839Speter if (cmt[i].rec) v |= 1 << i; 61325839Speter } 61425839Speter mix_setrecdevs(m, v); 61525839Speter 61625839Speter cmimix_wr(sc, CMPCI_SB16_MIXER_RESET, 0); 61725839Speter cmimix_wr(sc, CMPCI_SB16_MIXER_ADCMIX_L, 0); 61825839Speter cmimix_wr(sc, CMPCI_SB16_MIXER_ADCMIX_R, 0); 61925839Speter cmimix_wr(sc, CMPCI_SB16_MIXER_OUTMIX, 62025839Speter CMPCI_SB16_SW_CD | CMPCI_SB16_SW_MIC | CMPCI_SB16_SW_LINE); 62125839Speter return 0; 62225839Speter} 62325839Speter 62425839Speterstatic int 62525839Spetercmimix_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right) 62625839Speter{ 62725839Speter struct sc_info *sc = mix_getdevinfo(m); 62825839Speter u_int32_t r, l, max; 62925839Speter u_int8_t v; 63025839Speter 63125839Speter max = (1 << cmt[dev].bits) - 1; 63225839Speter 63325839Speter if (cmt[dev].rreg == CMPCI_NON_SB16_CONTROL) { 63425839Speter /* For time being this can only be one thing (mic in 63525839Speter * mic/aux reg) */ 63625839Speter v = cmi_rd(sc, CMPCI_REG_AUX_MIC, 1) & 0xf0; 63725839Speter l = left * max / 100; 63825839Speter /* 3 bit gain with LSB MICGAIN off(1),on(1) -> 4 bit value */ 63925839Speter v |= ((l << 1) | (~l >> 3)) & 0x0f; 64025839Speter cmi_wr(sc, CMPCI_REG_AUX_MIC, v, 1); 64125839Speter return 0; 64225839Speter } 64325839Speter 64425839Speter l = (left * max / 100) << (8 - cmt[dev].bits); 64525839Speter if (cmt[dev].stereo) { 64625839Speter r = (right * max / 100) << (8 - cmt[dev].bits); 64725839Speter cmimix_wr(sc, MIXER_GAIN_REG_RTOL(cmt[dev].rreg), l); 64825839Speter cmimix_wr(sc, cmt[dev].rreg, r); 64925839Speter DEBMIX(printf("Mixer stereo write dev %d reg 0x%02x "\ 65025839Speter "value 0x%02x:0x%02x\n", 65125839Speter dev, MIXER_GAIN_REG_RTOL(cmt[dev].rreg), l, r)); 65225839Speter } else { 65325839Speter r = l; 65425839Speter cmimix_wr(sc, cmt[dev].rreg, l); 65525839Speter DEBMIX(printf("Mixer mono write dev %d reg 0x%02x " \ 65625839Speter "value 0x%02x:0x%02x\n", 65725839Speter dev, cmt[dev].rreg, l, l)); 65825839Speter } 65925839Speter 66025839Speter /* Zero gain does not mute channel from output, but this does... */ 66125839Speter v = cmimix_rd(sc, CMPCI_SB16_MIXER_OUTMIX); 66225839Speter if (l == 0 && r == 0) { 66325839Speter v &= ~cmt[dev].oselect; 66425839Speter } else { 66525839Speter v |= cmt[dev].oselect; 66625839Speter } 66725839Speter cmimix_wr(sc, CMPCI_SB16_MIXER_OUTMIX, v); 66825839Speter 66925839Speter return 0; 67025839Speter} 67125839Speter 67225839Speterstatic int 67325839Spetercmimix_setrecsrc(struct snd_mixer *m, u_int32_t src) 67425839Speter{ 67525839Speter struct sc_info *sc = mix_getdevinfo(m); 67625839Speter u_int32_t i, ml, sl; 67725839Speter 67825839Speter ml = sl = 0; 67925839Speter for(i = 0; i < SOUND_MIXER_NRDEVICES; i++) { 68025839Speter if ((1<<i) & src) { 68125839Speter if (cmt[i].stereo) { 68225839Speter sl |= cmt[i].iselect; 68325839Speter } else { 68425839Speter ml |= cmt[i].iselect; 68525839Speter } 68625839Speter } 68725839Speter } 68825839Speter cmimix_wr(sc, CMPCI_SB16_MIXER_ADCMIX_R, sl|ml); 68925839Speter DEBMIX(printf("cmimix_setrecsrc: reg 0x%02x val 0x%02x\n", 69025839Speter CMPCI_SB16_MIXER_ADCMIX_R, sl|ml)); 69125839Speter ml = CMPCI_SB16_MIXER_SRC_R_TO_L(ml); 69225839Speter cmimix_wr(sc, CMPCI_SB16_MIXER_ADCMIX_L, sl|ml); 69325839Speter DEBMIX(printf("cmimix_setrecsrc: reg 0x%02x val 0x%02x\n", 69425839Speter CMPCI_SB16_MIXER_ADCMIX_L, sl|ml)); 69525839Speter 69625839Speter return src; 69725839Speter} 69825839Speter 69925839Speterstatic kobj_method_t cmi_mixer_methods[] = { 70025839Speter KOBJMETHOD(mixer_init, cmimix_init), 70125839Speter KOBJMETHOD(mixer_set, cmimix_set), 70225839Speter KOBJMETHOD(mixer_setrecsrc, cmimix_setrecsrc), 70325839Speter { 0, 0 } 70425839Speter}; 70525839SpeterMIXER_DECLARE(cmi_mixer); 70625839Speter 70734461Speter/* ------------------------------------------------------------------------- */ 70834461Speter/* Power and reset */ 70934461Speter 71034461Speterstatic void 71134461Spetercmi_power(struct sc_info *sc, int state) 71234461Speter{ 71334461Speter switch (state) { 71434461Speter case 0: /* full power */ 71534461Speter cmi_clr4(sc, CMPCI_REG_MISC, CMPCI_REG_POWER_DOWN); 71625839Speter break; 71725839Speter default: 71825839Speter /* power off */ 71925839Speter cmi_set4(sc, CMPCI_REG_MISC, CMPCI_REG_POWER_DOWN); 72025839Speter break; 72125839Speter } 72225839Speter} 72325839Speter 72425839Speterstatic int 72525839Spetercmi_init(struct sc_info *sc) 72625839Speter{ 72725839Speter /* Effect reset */ 72825839Speter cmi_set4(sc, CMPCI_REG_MISC, CMPCI_REG_BUS_AND_DSP_RESET); 72925839Speter DELAY(100); 73025839Speter cmi_clr4(sc, CMPCI_REG_MISC, CMPCI_REG_BUS_AND_DSP_RESET); 73125839Speter 73225839Speter /* Disable interrupts and channels */ 73325839Speter cmi_clr4(sc, CMPCI_REG_FUNC_0, 73425839Speter CMPCI_REG_CH0_ENABLE | CMPCI_REG_CH1_ENABLE); 73525839Speter cmi_clr4(sc, CMPCI_REG_INTR_CTRL, 73625839Speter CMPCI_REG_CH0_INTR_ENABLE | CMPCI_REG_CH1_INTR_ENABLE); 73725839Speter 73825839Speter /* Configure DMA channels, ch0 = play, ch1 = capture */ 73925839Speter cmi_clr4(sc, CMPCI_REG_FUNC_0, CMPCI_REG_CH0_DIR); 74025839Speter cmi_set4(sc, CMPCI_REG_FUNC_0, CMPCI_REG_CH1_DIR); 74125839Speter 74225839Speter /* Attempt to enable 4 Channel output */ 74325839Speter cmi_set4(sc, CMPCI_REG_MISC, CMPCI_REG_N4SPK3D); 74425839Speter 74525839Speter /* Disable SPDIF1 - not compatible with config */ 74625839Speter cmi_clr4(sc, CMPCI_REG_FUNC_1, CMPCI_REG_SPDIF1_ENABLE); 74725839Speter cmi_clr4(sc, CMPCI_REG_FUNC_1, CMPCI_REG_SPDIF_LOOP); 74825839Speter 74925839Speter return 0; 75025839Speter} 75125839Speter 75225839Speterstatic void 75325839Spetercmi_uninit(struct sc_info *sc) 75425839Speter{ 75525839Speter /* Disable interrupts and channels */ 75625839Speter cmi_clr4(sc, CMPCI_REG_INTR_CTRL, 75725839Speter CMPCI_REG_CH0_INTR_ENABLE | 75825839Speter CMPCI_REG_CH1_INTR_ENABLE | 75925839Speter CMPCI_REG_TDMA_INTR_ENABLE); 76025839Speter cmi_clr4(sc, CMPCI_REG_FUNC_0, 76125839Speter CMPCI_REG_CH0_ENABLE | CMPCI_REG_CH1_ENABLE); 76225839Speter} 76325839Speter 76425839Speter/* ------------------------------------------------------------------------- */ 76525839Speter/* Bus and device registration */ 76625839Speterstatic int 76725839Spetercmi_probe(device_t dev) 76825839Speter{ 76925839Speter switch(pci_get_devid(dev)) { 77025839Speter case CMI8338A_PCI_ID: 77125839Speter device_set_desc(dev, "CMedia CMI8338A"); 77225839Speter return 0; 77325839Speter case CMI8338B_PCI_ID: 77425839Speter device_set_desc(dev, "CMedia CMI8338B"); 77525839Speter return 0; 77625839Speter case CMI8738_PCI_ID: 77725839Speter device_set_desc(dev, "CMedia CMI8738"); 77825839Speter return 0; 77925839Speter case CMI8738B_PCI_ID: 78025839Speter device_set_desc(dev, "CMedia CMI8738B"); 78125839Speter return 0; 78225839Speter default: 78325839Speter return ENXIO; 78425839Speter } 78525839Speter} 78625839Speter 78725839Speterstatic int 78825839Spetercmi_attach(device_t dev) 78925839Speter{ 79025839Speter struct snddev_info *d; 79125839Speter struct sc_info *sc; 79225839Speter u_int32_t data; 79325839Speter char status[SND_STATUSLEN]; 79425839Speter 79525839Speter d = device_get_softc(dev); 79625839Speter sc = malloc(sizeof(struct sc_info), M_DEVBUF, M_NOWAIT); 79725839Speter if (sc == NULL) { 79825839Speter device_printf(dev, "cannot allocate softc\n"); 79925839Speter return ENXIO; 80025839Speter } 80125839Speter bzero(sc, sizeof(*sc)); 80225839Speter 80325839Speter data = pci_read_config(dev, PCIR_COMMAND, 2); 80425839Speter data |= (PCIM_CMD_PORTEN|PCIM_CMD_BUSMASTEREN); 80525839Speter pci_write_config(dev, PCIR_COMMAND, data, 2); 80625839Speter data = pci_read_config(dev, PCIR_COMMAND, 2); 80725839Speter 80825839Speter sc->regid = PCIR_MAPS; 80925839Speter sc->reg = bus_alloc_resource(dev, SYS_RES_IOPORT, &sc->regid, 81025839Speter 0, BUS_SPACE_UNRESTRICTED, 1, RF_ACTIVE); 81125839Speter if (!sc->reg) { 81225839Speter device_printf(dev, "cmi_attach: Cannot allocate bus resource\n"); 81325839Speter goto bad; 81425839Speter } 81525839Speter sc->st = rman_get_bustag(sc->reg); 81625839Speter sc->sh = rman_get_bushandle(sc->reg); 81725839Speter 81825839Speter sc->irqid = 0; 81925839Speter sc->irq = bus_alloc_resource(dev, SYS_RES_IRQ, &sc->irqid, 82025839Speter 0, ~0, 1, RF_ACTIVE | RF_SHAREABLE); 82125839Speter if (!sc->irq || 82225839Speter snd_setup_intr(dev, sc->irq, 0, cmi_intr, sc, &sc->ih)){ 82325839Speter device_printf(dev, "cmi_attach: Unable to map interrupt\n"); 82425839Speter goto bad; 82525839Speter } 82625839Speter 82725839Speter if (bus_dma_tag_create(/*parent*/NULL, /*alignment*/2, /*boundary*/0, 82825839Speter /*lowaddr*/BUS_SPACE_MAXADDR_32BIT, 82925839Speter /*highaddr*/BUS_SPACE_MAXADDR, 83025839Speter /*filter*/NULL, /*filterarg*/NULL, 83125839Speter /*maxsize*/CMI_BUFFER_SIZE, /*nsegments*/1, 83225839Speter /*maxsegz*/0x3ffff, /*flags*/0, 83325839Speter &sc->parent_dmat) != 0) { 83425839Speter device_printf(dev, "cmi_attach: Unable to create dma tag\n"); 83525839Speter goto bad; 83625839Speter } 837109660Speter 83825839Speter cmi_power(sc, 0); 83925839Speter if (cmi_init(sc)) 84025839Speter goto bad; 84125839Speter 84225839Speter if (mixer_init(dev, &cmi_mixer_class, sc)) 84325839Speter goto bad; 84425839Speter 84525839Speter if (pcm_register(dev, sc, 1, 1)) 84625839Speter goto bad; 84725839Speter 84825839Speter pcm_addchan(dev, PCMDIR_PLAY, &cmichan_class, sc); 84925839Speter pcm_addchan(dev, PCMDIR_REC, &cmichan_class, sc); 85025839Speter 85125839Speter snprintf(status, SND_STATUSLEN, "at io 0x%lx irq %ld", 85225839Speter rman_get_start(sc->reg), rman_get_start(sc->irq)); 85325839Speter pcm_setstatus(dev, status); 85425839Speter 85525839Speter DEB(printf("cmi_attach: succeeded\n")); 85625839Speter return 0; 85725839Speter 85825839Speter bad: 85925839Speter if (sc->parent_dmat) 86025839Speter bus_dma_tag_destroy(sc->parent_dmat); 86125839Speter if (sc->ih) 86225839Speter bus_teardown_intr(dev, sc->irq, sc->ih); 86325839Speter if (sc->irq) 86425839Speter bus_release_resource(dev, SYS_RES_IRQ, sc->irqid, sc->irq); 86525839Speter if (sc->reg) 86625839Speter bus_release_resource(dev, SYS_RES_IOPORT, sc->regid, sc->reg); 86725839Speter if (sc) 86825839Speter free(sc, M_DEVBUF); 86925839Speter 87025839Speter return ENXIO; 87125839Speter} 87225839Speter 87325839Speterstatic int 87425839Spetercmi_detach(device_t dev) 87525839Speter{ 87625839Speter struct sc_info *sc; 87725839Speter int r; 87825839Speter 87925839Speter r = pcm_unregister(dev); 88025839Speter if (r) return r; 88125839Speter 88225839Speter sc = pcm_getdevinfo(dev); 88325839Speter cmi_uninit(sc); 88425839Speter cmi_power(sc, 3); 88525839Speter 88625839Speter bus_dma_tag_destroy(sc->parent_dmat); 88725839Speter bus_teardown_intr(dev, sc->irq, sc->ih); 88825839Speter bus_release_resource(dev, SYS_RES_IRQ, sc->irqid, sc->irq); 88925839Speter bus_release_resource(dev, SYS_RES_IOPORT, sc->regid, sc->reg); 89025839Speter free(sc, M_DEVBUF); 89125839Speter 89225839Speter return 0; 89325839Speter} 89425839Speter 89525839Speterstatic int 89625839Spetercmi_suspend(device_t dev) 89725839Speter{ 89825839Speter struct sc_info *sc = pcm_getdevinfo(dev); 89925839Speter 90025839Speter sc->pch.dma_was_active = cmi_ch0_stop(sc, &sc->pch); 90125839Speter sc->rch.dma_was_active = cmi_ch1_stop(sc, &sc->rch); 90225839Speter cmi_power(sc, 3); 90325839Speter return 0; 90425839Speter} 90525839Speter 90625839Speterstatic int 90725839Spetercmi_resume(device_t dev) 90825839Speter{ 90925839Speter struct sc_info *sc = pcm_getdevinfo(dev); 91025839Speter 91125839Speter cmi_power(sc, 0); 91225839Speter if (cmi_init(sc) != 0) { 91325839Speter device_printf(dev, "unable to reinitialize the card\n"); 91425839Speter return ENXIO; 91525839Speter } 91625839Speter 91725839Speter if (mixer_reinit(dev) == -1) { 91825839Speter device_printf(dev, "unable to reinitialize the mixer\n"); 91925839Speter return ENXIO; 92025839Speter } 92125839Speter 92225839Speter if (sc->pch.dma_was_active) { 92325839Speter cmichan_setspeed(NULL, &sc->pch, sc->pch.spd); 92425839Speter cmichan_setformat(NULL, &sc->pch, sc->pch.fmt); 92525839Speter cmi_ch0_start(sc, &sc->pch); 92625839Speter } 92725839Speter 92825839Speter if (sc->rch.dma_was_active) { 92925839Speter cmichan_setspeed(NULL, &sc->rch, sc->rch.spd); 93025839Speter cmichan_setformat(NULL, &sc->rch, sc->rch.fmt); 93125839Speter cmi_ch1_start(sc, &sc->rch); 93225839Speter } 93325839Speter return 0; 93425839Speter} 93525839Speter 93625839Speterstatic device_method_t cmi_methods[] = { 93725839Speter DEVMETHOD(device_probe, cmi_probe), 93825839Speter DEVMETHOD(device_attach, cmi_attach), 93925839Speter DEVMETHOD(device_detach, cmi_detach), 94025839Speter DEVMETHOD(device_resume, cmi_resume), 94125839Speter DEVMETHOD(device_suspend, cmi_suspend), 94225839Speter { 0, 0 } 94325839Speter}; 94425839Speter 94525839Speterstatic driver_t cmi_driver = { 94625839Speter "pcm", 94725839Speter cmi_methods, 94825839Speter sizeof(struct snddev_info) 94925839Speter}; 95025839Speter 95125839Speterstatic devclass_t pcm_devclass; 95225839SpeterDRIVER_MODULE(snd_cmipci, pci, cmi_driver, pcm_devclass, 0, 0); 95325839SpeterMODULE_DEPEND(snd_cmipci, snd_pcm, PCM_MINVER, PCM_PREFVER, PCM_MAXVER); 95425839SpeterMODULE_VERSION(snd_cmipci, 1); 95525839Speter