1139749Simp/*- 274429Sorion * Copyright (c) 2001 Orion Hodson <O.Hodson@cs.ucl.ac.uk> 374429Sorion * All rights reserved. 474429Sorion * 574429Sorion * Redistribution and use in source and binary forms, with or without 674429Sorion * modification, are permitted provided that the following conditions 774429Sorion * are met: 874429Sorion * 1. Redistributions of source code must retain the above copyright 974429Sorion * notice, this list of conditions and the following disclaimer. 1074429Sorion * 2. Redistributions in binary form must reproduce the above copyright 1174429Sorion * notice, this list of conditions and the following disclaimer in the 1274429Sorion * documentation and/or other materials provided with the distribution. 1374429Sorion * 1474429Sorion * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1574429Sorion * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1674429Sorion * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1774429Sorion * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1874429Sorion * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1974429Sorion * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2074429Sorion * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2174429Sorion * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2274429Sorion * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2374429Sorion * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2474429Sorion * SUCH DAMAGE. 25192919Sjoel */ 26192919Sjoel 27192919Sjoel/* 2874429Sorion * This card has the annoying habit of "clicking" when attached and 2974429Sorion * detached, haven't been able to remedy this with any combination of 3074429Sorion * muting. 3182180Scg */ 3274429Sorion 33193640Sariff#ifdef HAVE_KERNEL_OPTION_HEADERS 34193640Sariff#include "opt_snd.h" 35193640Sariff#endif 36193640Sariff 3774429Sorion#include <dev/sound/pcm/sound.h> 3874429Sorion#include <dev/sound/pci/vibes.h> 3974429Sorion 40119287Simp#include <dev/pci/pcireg.h> 41119287Simp#include <dev/pci/pcivar.h> 4274429Sorion 4374429Sorion#include "mixer_if.h" 4474429Sorion 4582180ScgSND_DECLARE_FILE("$FreeBSD$"); 4682180Scg 4774429Sorion/* ------------------------------------------------------------------------- */ 4874429Sorion/* Constants */ 4974429Sorion 5074429Sorion#define SV_PCI_ID 0xca005333 5184771Sorion#define SV_DEFAULT_BUFSZ 16384 5284771Sorion#define SV_MIN_BLKSZ 128 5374429Sorion#define SV_INTR_PER_BUFFER 2 5474429Sorion 5574429Sorion#ifndef DEB 5674429Sorion#define DEB(x) /* (x) */ 5774429Sorion#endif 5874429Sorion 5974429Sorion/* ------------------------------------------------------------------------- */ 6074429Sorion/* Structures */ 6174429Sorion 6274429Sorionstruct sc_info; 6374429Sorion 6474429Sorionstruct sc_chinfo { 6574429Sorion struct sc_info *parent; 6674763Scg struct pcm_channel *channel; 6774763Scg struct snd_dbuf *buffer; 6874571Sorion u_int32_t fmt, spd; 6974429Sorion int dir; 7074429Sorion int dma_active, dma_was_active; 7174429Sorion}; 7274429Sorion 7374429Sorionstruct sc_info { 7474429Sorion device_t dev; 7574429Sorion 7674429Sorion /* DMA buffer allocator */ 7774429Sorion bus_dma_tag_t parent_dmat; 7874429Sorion 7974429Sorion /* Enhanced register resources */ 8074429Sorion struct resource *enh_reg; 8174429Sorion bus_space_tag_t enh_st; 8274429Sorion bus_space_handle_t enh_sh; 8374429Sorion int enh_type; 8474429Sorion int enh_rid; 8574429Sorion 8674429Sorion /* DMA configuration */ 8774429Sorion struct resource *dmaa_reg, *dmac_reg; 8874429Sorion bus_space_tag_t dmaa_st, dmac_st; 8974429Sorion bus_space_handle_t dmaa_sh, dmac_sh; 9074429Sorion int dmaa_type, dmac_type; 9174429Sorion int dmaa_rid, dmac_rid; 9274429Sorion 9374429Sorion /* Interrupt resources */ 9474429Sorion struct resource *irq; 9574429Sorion int irqid; 9674429Sorion void *ih; 9774429Sorion 9884771Sorion /* User configurable buffer size */ 9984771Sorion unsigned int bufsz; 10084771Sorion 10174429Sorion struct sc_chinfo rch, pch; 10274429Sorion u_int8_t rev; 10374429Sorion}; 10474429Sorion 10574429Sorionstatic u_int32_t sc_fmt[] = { 106193640Sariff SND_FORMAT(AFMT_U8, 1, 0), 107193640Sariff SND_FORMAT(AFMT_U8, 2, 0), 108193640Sariff SND_FORMAT(AFMT_S16_LE, 1, 0), 109193640Sariff SND_FORMAT(AFMT_S16_LE, 2, 0), 11074429Sorion 0 11174429Sorion}; 11274429Sorion 11374763Scgstatic struct pcmchan_caps sc_caps = {8000, 48000, sc_fmt, 0}; 11474429Sorion 11574429Sorion/* ------------------------------------------------------------------------- */ 11674429Sorion/* Register Manipulations */ 11774429Sorion 11874429Sorion#define sv_direct_set(x, y, z) _sv_direct_set(x, y, z, __LINE__) 11974429Sorion 12074429Sorionstatic u_int8_t 12174797Scgsv_direct_get(struct sc_info *sc, u_int8_t reg) 12274429Sorion{ 12374429Sorion return bus_space_read_1(sc->enh_st, sc->enh_sh, reg); 12474429Sorion} 12574429Sorion 12674429Sorionstatic void 12774797Scg_sv_direct_set(struct sc_info *sc, u_int8_t reg, u_int8_t val, int line) 12874429Sorion{ 12974429Sorion u_int8_t n; 13074429Sorion bus_space_write_1(sc->enh_st, sc->enh_sh, reg, val); 13174429Sorion 13274429Sorion n = sv_direct_get(sc, reg); 13374429Sorion if (n != val) { 13474429Sorion device_printf(sc->dev, "sv_direct_set register 0x%02x %d != %d from line %d\n", reg, n, val, line); 13574429Sorion } 13674429Sorion} 13774429Sorion 13874429Sorionstatic u_int8_t 13974797Scgsv_indirect_get(struct sc_info *sc, u_int8_t reg) 14074429Sorion{ 14174797Scg if (reg == SV_REG_FORMAT || reg == SV_REG_ANALOG_PWR) 14274429Sorion reg |= SV_CM_INDEX_MCE; 14374429Sorion 14474429Sorion bus_space_write_1(sc->enh_st, sc->enh_sh, SV_CM_INDEX, reg); 14574429Sorion return bus_space_read_1(sc->enh_st, sc->enh_sh, SV_CM_DATA); 14674429Sorion} 14774429Sorion 14874429Sorion#define sv_indirect_set(x, y, z) _sv_indirect_set(x, y, z, __LINE__) 14974429Sorion 15074429Sorionstatic void 15174797Scg_sv_indirect_set(struct sc_info *sc, u_int8_t reg, u_int8_t val, int line) 15274429Sorion{ 15374797Scg if (reg == SV_REG_FORMAT || reg == SV_REG_ANALOG_PWR) 15474429Sorion reg |= SV_CM_INDEX_MCE; 15574429Sorion 15674429Sorion bus_space_write_1(sc->enh_st, sc->enh_sh, SV_CM_INDEX, reg); 15774429Sorion bus_space_write_1(sc->enh_st, sc->enh_sh, SV_CM_DATA, val); 15874429Sorion 15974429Sorion reg &= ~SV_CM_INDEX_MCE; 16074429Sorion if (reg != SV_REG_ADC_PLLM) { 16174429Sorion u_int8_t n; 16274429Sorion n = sv_indirect_get(sc, reg); 16374429Sorion if (n != val) { 16474429Sorion device_printf(sc->dev, "sv_indirect_set register 0x%02x %d != %d line %d\n", reg, n, val, line); 16574429Sorion } 16674429Sorion } 16774429Sorion} 16874429Sorion 16974797Scgstatic void 17074797Scgsv_dma_set_config(bus_space_tag_t st, bus_space_handle_t sh, 17174429Sorion u_int32_t base, u_int32_t count, u_int8_t mode) 17274429Sorion{ 17374429Sorion bus_space_write_4(st, sh, SV_DMA_ADDR, base); 17474429Sorion bus_space_write_4(st, sh, SV_DMA_COUNT, count & 0xffffff); 17574429Sorion bus_space_write_1(st, sh, SV_DMA_MODE, mode); 17674429Sorion 17774429Sorion DEB(printf("base 0x%08x count %5d mode 0x%02x\n", 17874429Sorion base, count, mode)); 17974429Sorion} 18074429Sorion 18174429Sorionstatic u_int32_t 18274429Sorionsv_dma_get_count(bus_space_tag_t st, bus_space_handle_t sh) 18374429Sorion{ 18474429Sorion return bus_space_read_4(st, sh, SV_DMA_COUNT) & 0xffffff; 18574429Sorion} 18674429Sorion 18774429Sorion/* ------------------------------------------------------------------------- */ 18874429Sorion/* Play / Record Common Interface */ 18974429Sorion 19074429Sorionstatic void * 19174763Scgsvchan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel *c, int dir) 19274429Sorion{ 19374429Sorion struct sc_info *sc = devinfo; 19474429Sorion struct sc_chinfo *ch; 19574797Scg ch = (dir == PCMDIR_PLAY) ? &sc->pch : &sc->rch; 19674429Sorion 19774429Sorion ch->parent = sc; 19874429Sorion ch->channel = c; 19974429Sorion ch->dir = dir; 20074429Sorion 201168847Sariff if (sndbuf_alloc(b, sc->parent_dmat, 0, sc->bufsz) != 0) { 20274429Sorion DEB(printf("svchan_init failed\n")); 20374429Sorion return NULL; 20474429Sorion } 20574429Sorion ch->buffer = b; 206193640Sariff ch->fmt = SND_FORMAT(AFMT_U8, 1, 0); 20774429Sorion ch->spd = DSP_DEFAULT_SPEED; 20874429Sorion ch->dma_active = ch->dma_was_active = 0; 20974429Sorion 21074429Sorion return ch; 21174429Sorion} 21274429Sorion 21374763Scgstatic struct pcmchan_caps * 21474429Sorionsvchan_getcaps(kobj_t obj, void *data) 21574429Sorion{ 21674429Sorion return &sc_caps; 21774429Sorion} 21874429Sorion 219193640Sariffstatic u_int32_t 22074429Sorionsvchan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize) 22174429Sorion{ 22274429Sorion struct sc_chinfo *ch = data; 22384771Sorion struct sc_info *sc = ch->parent; 22474429Sorion 22574429Sorion /* user has requested interrupts every blocksize bytes */ 22684771Sorion RANGE(blocksize, SV_MIN_BLKSZ, sc->bufsz / SV_INTR_PER_BUFFER); 22774429Sorion sndbuf_resize(ch->buffer, SV_INTR_PER_BUFFER, blocksize); 22874429Sorion DEB(printf("svchan_setblocksize: %d\n", blocksize)); 22982835Sorion return blocksize; 23074429Sorion} 23174429Sorion 23274429Sorionstatic int 23374429Sorionsvchan_setformat(kobj_t obj, void *data, u_int32_t format) 23474429Sorion{ 23574429Sorion struct sc_chinfo *ch = data; 23674429Sorion /* NB Just note format here as setting format register 23774429Sorion * generates noise if dma channel is inactive. */ 238193640Sariff ch->fmt = (AFMT_CHANNEL(format) > 1) ? SV_AFMT_STEREO : SV_AFMT_MONO; 23974429Sorion ch->fmt |= (format & AFMT_16BIT) ? SV_AFMT_S16 : SV_AFMT_U8; 24074429Sorion return 0; 24174429Sorion} 24274429Sorion 243193640Sariffstatic u_int32_t 24474429Sorionsvchan_setspeed(kobj_t obj, void *data, u_int32_t speed) 24574429Sorion{ 24674429Sorion struct sc_chinfo *ch = data; 24774429Sorion RANGE(speed, 8000, 48000); 24874429Sorion ch->spd = speed; 24974429Sorion return speed; 25074429Sorion} 25174429Sorion 25274429Sorion/* ------------------------------------------------------------------------- */ 25374429Sorion/* Recording interface */ 25474429Sorion 25574797Scgstatic int 25674429Sorionsv_set_recspeed(struct sc_info *sc, u_int32_t speed) 25774429Sorion{ 25874429Sorion u_int32_t f_out, f_actual; 25974429Sorion u_int32_t rs, re, r, best_r = 0, r2, t, n, best_n = 0; 26074429Sorion int32_t m, best_m = 0, ms, me, err, min_err; 26174429Sorion 26274429Sorion /* This algorithm is a variant described in sonicvibes.pdf 26374429Sorion * appendix A. This search is marginally more extensive and 26474429Sorion * results in (nominally) better sample rate matching. */ 26574429Sorion 26674429Sorion f_out = SV_F_SCALE * speed; 26774429Sorion min_err = 0x7fffffff; 26874429Sorion 26974429Sorion /* Find bounds of r to examine, rs <= r <= re */ 27074429Sorion t = 80000000 / f_out; 27174429Sorion for (rs = 1; (1 << rs) < t; rs++); 27274429Sorion 27374429Sorion t = 150000000 / f_out; 27474429Sorion for (re = 1; (2 << re) < t; re++); 27574429Sorion if (re > 7) re = 7; 27674429Sorion 27774429Sorion /* Search over r, n, m */ 27874429Sorion for (r = rs; r <= re; r++) { 27974429Sorion r2 = (1 << r); 28074797Scg for (n = 3; n < 34; n++) { 28174429Sorion m = f_out * n / (SV_F_REF / r2); 28274429Sorion ms = (m > 3) ? (m - 1) : 3; 28374429Sorion me = (m < 129) ? (m + 1) : 129; 28474429Sorion for (m = ms; m <= me; m++) { 28574797Scg f_actual = m * SV_F_REF / (n * r2); 28674429Sorion if (f_actual > f_out) { 28774429Sorion err = f_actual - f_out; 28874429Sorion } else { 28974429Sorion err = f_out - f_actual; 29074429Sorion } 29174429Sorion if (err < min_err) { 29274429Sorion best_r = r; 29374429Sorion best_m = m - 2; 29474429Sorion best_n = n - 2; 29574429Sorion min_err = err; 29674429Sorion if (err == 0) break; 29774429Sorion } 29874429Sorion } 29974429Sorion } 30074429Sorion } 30174429Sorion 30274429Sorion sv_indirect_set(sc, SV_REG_ADC_PLLM, best_m); 30374797Scg sv_indirect_set(sc, SV_REG_ADC_PLLN, 30474429Sorion SV_ADC_PLLN(best_n) | SV_ADC_PLLR(best_r)); 30574429Sorion DEB(printf("svrchan_setspeed: %d -> PLLM 0x%02x PLLNR 0x%08x\n", 30674429Sorion speed, 30774429Sorion sv_indirect_get(sc, SV_REG_ADC_PLLM), 30874429Sorion sv_indirect_get(sc, SV_REG_ADC_PLLN))); 30974429Sorion return 0; 31074429Sorion} 31174429Sorion 31274797Scgstatic int 31374429Sorionsvrchan_trigger(kobj_t obj, void *data, int go) 31474429Sorion{ 31574429Sorion struct sc_chinfo *ch = data; 31674429Sorion struct sc_info *sc = ch->parent; 31774429Sorion u_int32_t count, enable; 31874429Sorion u_int8_t v; 31974429Sorion 32074429Sorion switch(go) { 32174429Sorion case PCMTRIG_START: 32274429Sorion /* Set speed */ 32374429Sorion sv_set_recspeed(sc, ch->spd); 32474429Sorion 32574429Sorion /* Set format */ 32674429Sorion v = sv_indirect_get(sc, SV_REG_FORMAT) & ~SV_AFMT_DMAC_MSK; 32774429Sorion v |= SV_AFMT_DMAC(ch->fmt); 32874429Sorion sv_indirect_set(sc, SV_REG_FORMAT, v); 32974429Sorion 33074429Sorion /* Program DMA */ 33174429Sorion count = sndbuf_getsize(ch->buffer) / 2; /* DMAC uses words */ 33274429Sorion sv_dma_set_config(sc->dmac_st, sc->dmac_sh, 333111183Scognet sndbuf_getbufaddr(ch->buffer), 33474429Sorion count - 1, 33574429Sorion SV_DMA_MODE_AUTO | SV_DMA_MODE_RD); 33674429Sorion count = count / SV_INTR_PER_BUFFER - 1; 33774429Sorion sv_indirect_set(sc, SV_REG_DMAC_COUNT_HI, count >> 8); 33874429Sorion sv_indirect_set(sc, SV_REG_DMAC_COUNT_LO, count & 0xff); 33974429Sorion 34074429Sorion /* Enable DMA */ 34174429Sorion enable = sv_indirect_get(sc, SV_REG_ENABLE) | SV_RECORD_ENABLE; 34274429Sorion sv_indirect_set(sc, SV_REG_ENABLE, enable); 34374429Sorion ch->dma_active = 1; 34474429Sorion break; 345170521Sariff case PCMTRIG_STOP: 34674429Sorion case PCMTRIG_ABORT: 34774429Sorion enable = sv_indirect_get(sc, SV_REG_ENABLE) & ~SV_RECORD_ENABLE; 34874429Sorion sv_indirect_set(sc, SV_REG_ENABLE, enable); 34974429Sorion ch->dma_active = 0; 35074429Sorion break; 35174429Sorion } 35274429Sorion 35374429Sorion return 0; 35474429Sorion} 35574429Sorion 356193640Sariffstatic u_int32_t 35774429Sorionsvrchan_getptr(kobj_t obj, void *data) 35874429Sorion{ 35974429Sorion struct sc_chinfo *ch = data; 36074429Sorion struct sc_info *sc = ch->parent; 36174429Sorion u_int32_t sz, remain; 36274429Sorion 36374429Sorion sz = sndbuf_getsize(ch->buffer); 36474429Sorion /* DMAC uses words */ 36574797Scg remain = (sv_dma_get_count(sc->dmac_st, sc->dmac_sh) + 1) * 2; 36674429Sorion return sz - remain; 36774429Sorion} 36874429Sorion 36974429Sorionstatic kobj_method_t svrchan_methods[] = { 37074429Sorion KOBJMETHOD(channel_init, svchan_init), 37174429Sorion KOBJMETHOD(channel_setformat, svchan_setformat), 37274429Sorion KOBJMETHOD(channel_setspeed, svchan_setspeed), 37374429Sorion KOBJMETHOD(channel_setblocksize, svchan_setblocksize), 37474429Sorion KOBJMETHOD(channel_trigger, svrchan_trigger), 37574429Sorion KOBJMETHOD(channel_getptr, svrchan_getptr), 37674429Sorion KOBJMETHOD(channel_getcaps, svchan_getcaps), 377193640Sariff KOBJMETHOD_END 37874429Sorion}; 37974429SorionCHANNEL_DECLARE(svrchan); 38074429Sorion 38174429Sorion/* ------------------------------------------------------------------------- */ 38274429Sorion/* Playback interface */ 38374429Sorion 38474797Scgstatic int 38574429Sorionsvpchan_trigger(kobj_t obj, void *data, int go) 38674429Sorion{ 38774429Sorion struct sc_chinfo *ch = data; 38874429Sorion struct sc_info *sc = ch->parent; 38974429Sorion u_int32_t count, enable, speed; 39074429Sorion u_int8_t v; 39174429Sorion 39274429Sorion switch(go) { 39374429Sorion case PCMTRIG_START: 39474429Sorion /* Set speed */ 39574429Sorion speed = (ch->spd * 65536) / 48000; 39674797Scg if (speed > 65535) 39774429Sorion speed = 65535; 39874429Sorion sv_indirect_set(sc, SV_REG_PCM_SAMPLING_HI, speed >> 8); 39974429Sorion sv_indirect_set(sc, SV_REG_PCM_SAMPLING_LO, speed & 0xff); 40074429Sorion 40174429Sorion /* Set format */ 40274429Sorion v = sv_indirect_get(sc, SV_REG_FORMAT) & ~SV_AFMT_DMAA_MSK; 40374429Sorion v |= SV_AFMT_DMAA(ch->fmt); 40474429Sorion sv_indirect_set(sc, SV_REG_FORMAT, v); 40574429Sorion 40674429Sorion /* Program DMA */ 40774429Sorion count = sndbuf_getsize(ch->buffer); 40874429Sorion sv_dma_set_config(sc->dmaa_st, sc->dmaa_sh, 409111183Scognet sndbuf_getbufaddr(ch->buffer), 41074429Sorion count - 1, 41174429Sorion SV_DMA_MODE_AUTO | SV_DMA_MODE_WR); 41274429Sorion count = count / SV_INTR_PER_BUFFER - 1; 41374429Sorion sv_indirect_set(sc, SV_REG_DMAA_COUNT_HI, count >> 8); 41474429Sorion sv_indirect_set(sc, SV_REG_DMAA_COUNT_LO, count & 0xff); 41574429Sorion 41674429Sorion /* Enable DMA */ 41774429Sorion enable = sv_indirect_get(sc, SV_REG_ENABLE); 41874429Sorion enable = (enable | SV_PLAY_ENABLE) & ~SV_PLAYBACK_PAUSE; 41974429Sorion sv_indirect_set(sc, SV_REG_ENABLE, enable); 42074429Sorion ch->dma_active = 1; 42174429Sorion break; 422170521Sariff case PCMTRIG_STOP: 42374429Sorion case PCMTRIG_ABORT: 42474429Sorion enable = sv_indirect_get(sc, SV_REG_ENABLE) & ~SV_PLAY_ENABLE; 42574429Sorion sv_indirect_set(sc, SV_REG_ENABLE, enable); 42674429Sorion ch->dma_active = 0; 42774429Sorion break; 42874429Sorion } 42974429Sorion 43074429Sorion return 0; 43174429Sorion} 43274429Sorion 433193640Sariffstatic u_int32_t 43474429Sorionsvpchan_getptr(kobj_t obj, void *data) 43574429Sorion{ 43674429Sorion struct sc_chinfo *ch = data; 43774429Sorion struct sc_info *sc = ch->parent; 43874429Sorion u_int32_t sz, remain; 43974429Sorion 44074797Scg sz = sndbuf_getsize(ch->buffer); 44174429Sorion /* DMAA uses bytes */ 44274429Sorion remain = sv_dma_get_count(sc->dmaa_st, sc->dmaa_sh) + 1; 44374429Sorion return (sz - remain); 44474429Sorion} 44574429Sorion 44674429Sorionstatic kobj_method_t svpchan_methods[] = { 44774429Sorion KOBJMETHOD(channel_init, svchan_init), 44874429Sorion KOBJMETHOD(channel_setformat, svchan_setformat), 44974429Sorion KOBJMETHOD(channel_setspeed, svchan_setspeed), 45074429Sorion KOBJMETHOD(channel_setblocksize, svchan_setblocksize), 45174429Sorion KOBJMETHOD(channel_trigger, svpchan_trigger), 45274429Sorion KOBJMETHOD(channel_getptr, svpchan_getptr), 45374429Sorion KOBJMETHOD(channel_getcaps, svchan_getcaps), 454193640Sariff KOBJMETHOD_END 45574429Sorion}; 45674429SorionCHANNEL_DECLARE(svpchan); 45774429Sorion 45874429Sorion/* ------------------------------------------------------------------------- */ 45974429Sorion/* Mixer support */ 46074429Sorion 46174429Sorionstruct sv_mix_props { 46274429Sorion u_int8_t reg; /* Register */ 46374429Sorion u_int8_t stereo:1; /* Supports 2 channels */ 46474429Sorion u_int8_t mute:1; /* Supports muting */ 46574429Sorion u_int8_t neg:1; /* Negative gain */ 46674429Sorion u_int8_t max; /* Max gain */ 46774429Sorion u_int8_t iselect; /* Input selector */ 46874429Sorion} static const mt [SOUND_MIXER_NRDEVICES] = { 46974429Sorion [SOUND_MIXER_LINE1] = {SV_REG_AUX1, 1, 1, 1, SV_DEFAULT_MAX, SV_INPUT_AUX1}, 47074429Sorion [SOUND_MIXER_CD] = {SV_REG_CD, 1, 1, 1, SV_DEFAULT_MAX, SV_INPUT_CD}, 47174429Sorion [SOUND_MIXER_LINE] = {SV_REG_LINE, 1, 1, 1, SV_DEFAULT_MAX, SV_INPUT_LINE}, 47274429Sorion [SOUND_MIXER_MIC] = {SV_REG_MIC, 0, 1, 1, SV_MIC_MAX, SV_INPUT_MIC}, 47374429Sorion [SOUND_MIXER_SYNTH] = {SV_REG_SYNTH, 0, 1, 1, SV_DEFAULT_MAX, 0}, 47474429Sorion [SOUND_MIXER_LINE2] = {SV_REG_AUX2, 1, 1, 1, SV_DEFAULT_MAX, SV_INPUT_AUX2}, 47574429Sorion [SOUND_MIXER_VOLUME] = {SV_REG_MIX, 1, 1, 1, SV_DEFAULT_MAX, 0}, 47674429Sorion [SOUND_MIXER_PCM] = {SV_REG_PCM, 1, 1, 1, SV_PCM_MAX, 0}, 47774429Sorion [SOUND_MIXER_RECLEV] = {SV_REG_ADC_INPUT, 1, 0, 0, SV_ADC_MAX, 0}, 47874429Sorion}; 47974429Sorion 48074429Sorionstatic void 48174429Sorionsv_channel_gain(struct sc_info *sc, u_int32_t dev, u_int32_t gain, u_int32_t channel) 48274429Sorion{ 48374429Sorion u_int8_t v; 48474429Sorion int32_t g; 48574797Scg 48674429Sorion g = mt[dev].max * gain / 100; 48774429Sorion if (mt[dev].neg) 48874429Sorion g = mt[dev].max - g; 48974429Sorion v = sv_indirect_get(sc, mt[dev].reg + channel) & ~mt[dev].max; 49074429Sorion v |= g; 49174429Sorion 49274429Sorion if (mt[dev].mute) { 49374429Sorion if (gain == 0) { 49474429Sorion v |= SV_MUTE; 49574429Sorion } else { 49674429Sorion v &= ~SV_MUTE; 49774429Sorion } 49874429Sorion } 49974429Sorion sv_indirect_set(sc, mt[dev].reg + channel, v); 50074797Scg} 50174429Sorion 50274429Sorionstatic int 50374429Sorionsv_gain(struct sc_info *sc, u_int32_t dev, u_int32_t left, u_int32_t right) 50474429Sorion{ 50574429Sorion sv_channel_gain(sc, dev, left, 0); 50674797Scg if (mt[dev].stereo) 50774429Sorion sv_channel_gain(sc, dev, right, 1); 50874429Sorion return 0; 50974797Scg} 51074429Sorion 51174429Sorionstatic void 51274797Scgsv_mix_mute_all(struct sc_info *sc) 51374429Sorion{ 51474429Sorion int32_t i; 51574429Sorion for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { 51674429Sorion if (mt[i].reg) sv_gain(sc, i, 0, 0); 51774429Sorion } 51874797Scg} 51974429Sorion 52074429Sorionstatic int 52174763Scgsv_mix_init(struct snd_mixer *m) 52274429Sorion{ 52374429Sorion u_int32_t i, v; 52474429Sorion 52574429Sorion for(i = v = 0; i < SOUND_MIXER_NRDEVICES; i++) { 52674429Sorion if (mt[i].max) v |= (1 << i); 52774429Sorion } 52874429Sorion mix_setdevs(m, v); 52974429Sorion 53074429Sorion for(i = v = 0; i < SOUND_MIXER_NRDEVICES; i++) { 53174429Sorion if (mt[i].iselect) v |= (1 << i); 53274429Sorion } 53374429Sorion mix_setrecdevs(m, v); 53474429Sorion 53574429Sorion return 0; 53674429Sorion} 53774429Sorion 53874429Sorionstatic int 53974763Scgsv_mix_set(struct snd_mixer *m, u_int32_t dev, u_int32_t left, u_int32_t right) 54074429Sorion{ 54174429Sorion struct sc_info *sc = mix_getdevinfo(m); 54274429Sorion return sv_gain(sc, dev, left, right); 54374429Sorion} 54474429Sorion 545193640Sariffstatic u_int32_t 54674763Scgsv_mix_setrecsrc(struct snd_mixer *m, u_int32_t mask) 54774429Sorion{ 54874429Sorion struct sc_info *sc = mix_getdevinfo(m); 54974429Sorion u_int32_t i, v; 55074429Sorion 55174429Sorion v = sv_indirect_get(sc, SV_REG_ADC_INPUT) & SV_INPUT_GAIN_MASK; 55274429Sorion for(i = 0; i < SOUND_MIXER_NRDEVICES; i++) { 55374429Sorion if ((1 << i) & mask) { 55474429Sorion v |= mt[i].iselect; 55574429Sorion } 55674429Sorion } 55774429Sorion DEB(printf("sv_mix_setrecsrc: mask 0x%08x adc_input 0x%02x\n", mask, v)); 558127218Smarcel sv_indirect_set(sc, SV_REG_ADC_INPUT, v); 55974429Sorion return mask; 56074429Sorion} 56174429Sorion 56274429Sorionstatic kobj_method_t sv_mixer_methods[] = { 56374429Sorion KOBJMETHOD(mixer_init, sv_mix_init), 56474429Sorion KOBJMETHOD(mixer_set, sv_mix_set), 56574429Sorion KOBJMETHOD(mixer_setrecsrc, sv_mix_setrecsrc), 566193640Sariff KOBJMETHOD_END 56774429Sorion}; 56874429SorionMIXER_DECLARE(sv_mixer); 56974429Sorion 57074429Sorion/* ------------------------------------------------------------------------- */ 57174429Sorion/* Power management and reset */ 57274429Sorion 57374429Sorionstatic void 57474429Sorionsv_power(struct sc_info *sc, int state) 57574429Sorion{ 57674429Sorion u_int8_t v; 57774797Scg 57874429Sorion switch (state) { 57974797Scg case 0: 58074429Sorion /* power on */ 58174429Sorion v = sv_indirect_get(sc, SV_REG_ANALOG_PWR) &~ SV_ANALOG_OFF; 58274429Sorion v |= SV_ANALOG_OFF_SRS | SV_ANALOG_OFF_SPLL; 58374429Sorion sv_indirect_set(sc, SV_REG_ANALOG_PWR, v); 58474429Sorion v = sv_indirect_get(sc, SV_REG_DIGITAL_PWR) &~ SV_DIGITAL_OFF; 58574429Sorion v |= SV_DIGITAL_OFF_SYN | SV_DIGITAL_OFF_MU | SV_DIGITAL_OFF_GP; 58674429Sorion sv_indirect_set(sc, SV_REG_DIGITAL_PWR, v); 58774429Sorion break; 58874797Scg default: 58974429Sorion /* power off */ 59074429Sorion v = sv_indirect_get(sc, SV_REG_ANALOG_PWR) | SV_ANALOG_OFF; 59174429Sorion sv_indirect_set(sc, SV_REG_ANALOG_PWR, v); 59274429Sorion v = sv_indirect_get(sc, SV_REG_DIGITAL_PWR) | SV_DIGITAL_OFF; 59374429Sorion sv_indirect_set(sc, SV_REG_DIGITAL_PWR, SV_DIGITAL_OFF); 59474429Sorion break; 59574429Sorion } 59674797Scg DEB(printf("Power state %d\n", state)); 59774429Sorion} 59874429Sorion 59974429Sorionstatic int 60074429Sorionsv_init(struct sc_info *sc) 60174429Sorion{ 60274429Sorion u_int8_t v; 60374429Sorion 60474429Sorion /* Effect reset */ 60574429Sorion v = sv_direct_get(sc, SV_CM_CONTROL) & ~SV_CM_CONTROL_ENHANCED; 60674429Sorion v |= SV_CM_CONTROL_RESET; 60774429Sorion sv_direct_set(sc, SV_CM_CONTROL, v); 60874429Sorion DELAY(50); 60974429Sorion 61074429Sorion v = sv_direct_get(sc, SV_CM_CONTROL) & ~SV_CM_CONTROL_RESET; 61174429Sorion sv_direct_set(sc, SV_CM_CONTROL, v); 61274429Sorion DELAY(50); 61374797Scg 61474429Sorion /* Set in enhanced mode */ 61574429Sorion v = sv_direct_get(sc, SV_CM_CONTROL); 61674429Sorion v |= SV_CM_CONTROL_ENHANCED; 61774429Sorion sv_direct_set(sc, SV_CM_CONTROL, v); 61874429Sorion 61974429Sorion /* Enable interrupts (UDM and MIDM are superfluous) */ 62074429Sorion v = sv_direct_get(sc, SV_CM_IMR); 62174429Sorion v &= ~(SV_CM_IMR_AMSK | SV_CM_IMR_CMSK | SV_CM_IMR_SMSK); 62274429Sorion sv_direct_set(sc, SV_CM_IMR, v); 62374429Sorion 62474429Sorion /* Select ADC PLL for ADC clock */ 62574429Sorion v = sv_indirect_get(sc, SV_REG_CLOCK_SOURCE) & ~SV_CLOCK_ALTERNATE; 62674429Sorion sv_indirect_set(sc, SV_REG_CLOCK_SOURCE, v); 62774429Sorion 62874429Sorion /* Disable loopback - binds ADC and DAC rates */ 62974429Sorion v = sv_indirect_get(sc, SV_REG_LOOPBACK) & ~SV_LOOPBACK_ENABLE; 63074797Scg sv_indirect_set(sc, SV_REG_LOOPBACK, v); 63174429Sorion 63274429Sorion /* Disable SRS */ 63374429Sorion v = sv_indirect_get(sc, SV_REG_SRS_SPACE) | SV_SRS_DISABLED; 63474797Scg sv_indirect_set(sc, SV_REG_SRS_SPACE, v); 63574429Sorion 63674429Sorion /* Get revision */ 63774429Sorion sc->rev = sv_indirect_get(sc, SV_REG_REVISION); 63874429Sorion 63974429Sorion return 0; 64074429Sorion} 64174429Sorion 64274429Sorionstatic int 64374429Sorionsv_suspend(device_t dev) 64474429Sorion{ 64574429Sorion struct sc_info *sc = pcm_getdevinfo(dev); 64674429Sorion 64774429Sorion sc->rch.dma_was_active = sc->rch.dma_active; 64874429Sorion svrchan_trigger(NULL, &sc->rch, PCMTRIG_ABORT); 64974429Sorion 65074429Sorion sc->pch.dma_was_active = sc->pch.dma_active; 65174429Sorion svrchan_trigger(NULL, &sc->pch, PCMTRIG_ABORT); 65274429Sorion 65374429Sorion sv_mix_mute_all(sc); 65474429Sorion sv_power(sc, 3); 65574429Sorion 65674429Sorion return 0; 65774429Sorion} 65874429Sorion 65974429Sorionstatic int 66074429Sorionsv_resume(device_t dev) 66174429Sorion{ 66274429Sorion struct sc_info *sc = pcm_getdevinfo(dev); 66374429Sorion 66474429Sorion sv_mix_mute_all(sc); 66574429Sorion sv_power(sc, 0); 66674429Sorion if (sv_init(sc) == -1) { 66774429Sorion device_printf(dev, "unable to reinitialize the card\n"); 66874429Sorion return ENXIO; 66974429Sorion } 67074429Sorion 67174429Sorion if (mixer_reinit(dev) == -1) { 67274429Sorion device_printf(dev, "unable to reinitialize the mixer\n"); 67374429Sorion return ENXIO; 67474429Sorion } 67574429Sorion 67674429Sorion if (sc->rch.dma_was_active) { 67774797Scg svrchan_trigger(0, &sc->rch, PCMTRIG_START); 67874429Sorion } 67974429Sorion 68074429Sorion if (sc->pch.dma_was_active) { 68174797Scg svpchan_trigger(0, &sc->pch, PCMTRIG_START); 68274429Sorion } 68374429Sorion 68474429Sorion return 0; 68574429Sorion} 68674429Sorion 68774429Sorion/* ------------------------------------------------------------------------- */ 68874429Sorion/* Resource related */ 68974429Sorion 69074429Sorionstatic void 69174429Sorionsv_intr(void *data) 69274429Sorion{ 69374429Sorion struct sc_info *sc = data; 69474429Sorion u_int8_t status; 69574429Sorion 69674429Sorion status = sv_direct_get(sc, SV_CM_STATUS); 69774797Scg if (status & SV_CM_STATUS_AINT) 69874429Sorion chn_intr(sc->pch.channel); 69974429Sorion 70074797Scg if (status & SV_CM_STATUS_CINT) 70174429Sorion chn_intr(sc->rch.channel); 70274429Sorion 70374797Scg status &= ~(SV_CM_STATUS_AINT|SV_CM_STATUS_CINT); 70474429Sorion DEB(if (status) printf("intr 0x%02x ?\n", status)); 70574429Sorion 70674429Sorion return; 70774429Sorion} 70874429Sorion 70974429Sorionstatic int 71074429Sorionsv_probe(device_t dev) 71174429Sorion{ 71274429Sorion switch(pci_get_devid(dev)) { 71374429Sorion case SV_PCI_ID: 71474429Sorion device_set_desc(dev, "S3 Sonicvibes"); 715142890Simp return BUS_PROBE_DEFAULT; 71674429Sorion default: 71774429Sorion return ENXIO; 71874429Sorion } 71974429Sorion} 72074429Sorion 72174429Sorionstatic int 72274429Sorionsv_attach(device_t dev) { 72374429Sorion struct sc_info *sc; 72474429Sorion u_int32_t data; 72574429Sorion char status[SND_STATUSLEN]; 72682835Sorion u_long midi_start, games_start, count, sdmaa, sdmac, ml, mu; 72774429Sorion 728170873Sariff sc = malloc(sizeof(*sc), M_DEVBUF, M_WAITOK | M_ZERO); 72974429Sorion sc->dev = dev; 73074429Sorion 731254263Sscottl pci_enable_busmaster(dev); 73274429Sorion 73374429Sorion#if __FreeBSD_version > 500000 73474429Sorion if (pci_get_powerstate(dev) != PCI_POWERSTATE_D0) { 73574429Sorion device_printf(dev, "chip is in D%d power mode " 73674429Sorion "-- setting to D0\n", pci_get_powerstate(dev)); 73774429Sorion pci_set_powerstate(dev, PCI_POWERSTATE_D0); 73874429Sorion } 73974797Scg#endif 74074429Sorion sc->enh_rid = SV_PCI_ENHANCED; 74174429Sorion sc->enh_type = SYS_RES_IOPORT; 74274797Scg sc->enh_reg = bus_alloc_resource(dev, sc->enh_type, 74374429Sorion &sc->enh_rid, 0, ~0, 74474429Sorion SV_PCI_ENHANCED_SIZE, RF_ACTIVE); 74574429Sorion if (sc->enh_reg == NULL) { 74674429Sorion device_printf(dev, "sv_attach: cannot allocate enh\n"); 74774429Sorion return ENXIO; 74874429Sorion } 74974429Sorion sc->enh_st = rman_get_bustag(sc->enh_reg); 75074429Sorion sc->enh_sh = rman_get_bushandle(sc->enh_reg); 75174429Sorion 75274429Sorion data = pci_read_config(dev, SV_PCI_DMAA, 4); 75374429Sorion DEB(printf("sv_attach: initial dmaa 0x%08x\n", data)); 75474429Sorion data = pci_read_config(dev, SV_PCI_DMAC, 4); 75574429Sorion DEB(printf("sv_attach: initial dmac 0x%08x\n", data)); 75674429Sorion 75774429Sorion /* Initialize DMA_A and DMA_C */ 75874429Sorion pci_write_config(dev, SV_PCI_DMAA, SV_PCI_DMA_EXTENDED, 4); 75974429Sorion pci_write_config(dev, SV_PCI_DMAC, 0, 4); 76074429Sorion 76174429Sorion /* Register IRQ handler */ 76274429Sorion sc->irqid = 0; 76374429Sorion sc->irq = bus_alloc_resource(dev, SYS_RES_IRQ, &sc->irqid, 76474429Sorion 0, ~0, 1, RF_ACTIVE | RF_SHAREABLE); 76574797Scg if (!sc->irq || 766166917Sariff snd_setup_intr(dev, sc->irq, 0, sv_intr, sc, &sc->ih)) { 76774429Sorion device_printf(dev, "sv_attach: Unable to map interrupt\n"); 76874429Sorion goto fail; 76974429Sorion } 77074429Sorion 77184771Sorion sc->bufsz = pcm_getbuffersize(dev, 4096, SV_DEFAULT_BUFSZ, 65536); 772166904Snetchild if (bus_dma_tag_create(/*parent*/bus_get_dma_tag(dev), /*alignment*/2, 773166904Snetchild /*boundary*/0, 77474429Sorion /*lowaddr*/BUS_SPACE_MAXADDR_24BIT, 77574429Sorion /*highaddr*/BUS_SPACE_MAXADDR, 77674429Sorion /*filter*/NULL, /*filterarg*/NULL, 77784771Sorion /*maxsize*/sc->bufsz, /*nsegments*/1, 77874797Scg /*maxsegz*/0x3ffff, /*flags*/0, 779117126Sscottl /*lockfunc*/busdma_lock_mutex, 780117126Sscottl /*lockarg*/&Giant, &sc->parent_dmat) != 0) { 78174429Sorion device_printf(dev, "sv_attach: Unable to create dma tag\n"); 78274429Sorion goto fail; 78374429Sorion } 78474429Sorion 78574429Sorion /* Power up and initialize */ 78674429Sorion sv_mix_mute_all(sc); 78774429Sorion sv_power(sc, 0); 78874429Sorion sv_init(sc); 78974429Sorion 79074429Sorion if (mixer_init(dev, &sv_mixer_class, sc) != 0) { 79174429Sorion device_printf(dev, "sv_attach: Mixer failed to initialize\n"); 79274429Sorion goto fail; 79374797Scg } 79474429Sorion 79574429Sorion /* XXX This is a hack, and it's ugly. Okay, the deal is this 79674429Sorion * card has two more io regions that available for automatic 79774429Sorion * configuration by the pci code. These need to be allocated 79874429Sorion * to used as control registers for the DMA engines. 79974429Sorion * Unfortunately FBSD has no bus_space_foo() functions so we 80074429Sorion * have to grab port space in region of existing resources. Go 80174797Scg * for space between midi and game ports. 80274429Sorion */ 80374429Sorion bus_get_resource(dev, SYS_RES_IOPORT, SV_PCI_MIDI, &midi_start, &count); 80474429Sorion bus_get_resource(dev, SYS_RES_IOPORT, SV_PCI_GAMES, &games_start, &count); 80574429Sorion 80682835Sorion if (games_start < midi_start) { 80782835Sorion ml = games_start; 80882835Sorion mu = midi_start; 80982835Sorion } else { 81082835Sorion ml = midi_start; 81182835Sorion mu = games_start; 81282835Sorion } 81382835Sorion /* Check assumptions about space availability and 81482835Sorion alignment. How driver loaded can determine whether 81582835Sorion games_start > midi_start or vice versa */ 81682835Sorion if ((mu - ml >= 0x800) || 81782835Sorion ((mu - ml) % 0x200)) { 81882835Sorion device_printf(dev, "sv_attach: resource assumptions not met " 81982835Sorion "(midi 0x%08lx, games 0x%08lx)\n", 82082835Sorion midi_start, games_start); 82174429Sorion goto fail; 82274429Sorion } 82374429Sorion 82482835Sorion sdmaa = ml + 0x40; 82574429Sorion sdmac = sdmaa + 0x40; 82674797Scg 82774429Sorion /* Add resources to list of pci resources for this device - from here on 82874429Sorion * they look like normal pci resources. */ 82974429Sorion bus_set_resource(dev, SYS_RES_IOPORT, SV_PCI_DMAA, sdmaa, SV_PCI_DMAA_SIZE); 83074429Sorion bus_set_resource(dev, SYS_RES_IOPORT, SV_PCI_DMAC, sdmac, SV_PCI_DMAC_SIZE); 83174429Sorion 83274429Sorion /* Cache resource short-cuts for dma_a */ 83374429Sorion sc->dmaa_rid = SV_PCI_DMAA; 83474429Sorion sc->dmaa_type = SYS_RES_IOPORT; 83574797Scg sc->dmaa_reg = bus_alloc_resource(dev, sc->dmaa_type, 83674429Sorion &sc->dmaa_rid, 0, ~0, 83774429Sorion SV_PCI_ENHANCED_SIZE, RF_ACTIVE); 83874429Sorion if (sc->dmaa_reg == NULL) { 83974429Sorion device_printf(dev, "sv_attach: cannot allocate dmaa\n"); 84074429Sorion goto fail; 84174429Sorion } 84274429Sorion sc->dmaa_st = rman_get_bustag(sc->dmaa_reg); 84374429Sorion sc->dmaa_sh = rman_get_bushandle(sc->dmaa_reg); 84474429Sorion 84574429Sorion /* Poke port into dma_a configuration, nb bit flags to enable dma */ 84674429Sorion data = pci_read_config(dev, SV_PCI_DMAA, 4) | SV_PCI_DMA_ENABLE | SV_PCI_DMA_EXTENDED; 84774429Sorion data = ((u_int32_t)sdmaa & 0xfffffff0) | (data & 0x0f); 84874429Sorion pci_write_config(dev, SV_PCI_DMAA, data, 4); 84974429Sorion DEB(printf("dmaa: 0x%x 0x%x\n", data, pci_read_config(dev, SV_PCI_DMAA, 4))); 85074429Sorion 85174797Scg /* Cache resource short-cuts for dma_c */ 85274429Sorion sc->dmac_rid = SV_PCI_DMAC; 85374429Sorion sc->dmac_type = SYS_RES_IOPORT; 85474797Scg sc->dmac_reg = bus_alloc_resource(dev, sc->dmac_type, 85574429Sorion &sc->dmac_rid, 0, ~0, 85674429Sorion SV_PCI_ENHANCED_SIZE, RF_ACTIVE); 85774429Sorion if (sc->dmac_reg == NULL) { 85874429Sorion device_printf(dev, "sv_attach: cannot allocate dmac\n"); 85974429Sorion goto fail; 86074429Sorion } 86174429Sorion sc->dmac_st = rman_get_bustag(sc->dmac_reg); 86274429Sorion sc->dmac_sh = rman_get_bushandle(sc->dmac_reg); 86374429Sorion 86474429Sorion /* Poke port into dma_c configuration, nb bit flags to enable dma */ 86574429Sorion data = pci_read_config(dev, SV_PCI_DMAC, 4) | SV_PCI_DMA_ENABLE | SV_PCI_DMA_EXTENDED; 86674429Sorion data = ((u_int32_t)sdmac & 0xfffffff0) | (data & 0x0f); 86774429Sorion pci_write_config(dev, SV_PCI_DMAC, data, 4); 86874429Sorion DEB(printf("dmac: 0x%x 0x%x\n", data, pci_read_config(dev, SV_PCI_DMAC, 4))); 86974429Sorion 87074429Sorion if (bootverbose) 87174429Sorion printf("Sonicvibes: revision %d.\n", sc->rev); 87274429Sorion 87374429Sorion if (pcm_register(dev, sc, 1, 1)) { 87474429Sorion device_printf(dev, "sv_attach: pcm_register fail\n"); 87574429Sorion goto fail; 87674429Sorion } 87774429Sorion 87874429Sorion pcm_addchan(dev, PCMDIR_PLAY, &svpchan_class, sc); 87974429Sorion pcm_addchan(dev, PCMDIR_REC, &svrchan_class, sc); 88074429Sorion 881126695Smatk snprintf(status, SND_STATUSLEN, "at io 0x%lx irq %ld %s", 882126695Smatk rman_get_start(sc->enh_reg), rman_get_start(sc->irq),PCM_KLDSTRING(snd_vibes)); 88374429Sorion pcm_setstatus(dev, status); 88474429Sorion 88574429Sorion DEB(printf("sv_attach: succeeded\n")); 88674429Sorion 88774429Sorion return 0; 88874429Sorion 88974797Scg fail: 89074797Scg if (sc->parent_dmat) 89174429Sorion bus_dma_tag_destroy(sc->parent_dmat); 89274797Scg if (sc->ih) 89374429Sorion bus_teardown_intr(dev, sc->irq, sc->ih); 89474797Scg if (sc->irq) 89574429Sorion bus_release_resource(dev, SYS_RES_IRQ, sc->irqid, sc->irq); 89674429Sorion if (sc->enh_reg) 89774429Sorion bus_release_resource(dev, sc->enh_type, sc->enh_rid, sc->enh_reg); 89874429Sorion if (sc->dmaa_reg) 89974429Sorion bus_release_resource(dev, sc->dmaa_type, sc->dmaa_rid, sc->dmaa_reg); 90074429Sorion if (sc->dmac_reg) 90174429Sorion bus_release_resource(dev, sc->dmac_type, sc->dmac_rid, sc->dmac_reg); 90274429Sorion return ENXIO; 90374429Sorion} 90474429Sorion 90574429Sorionstatic int 90674429Sorionsv_detach(device_t dev) { 90774429Sorion struct sc_info *sc; 90874429Sorion int r; 90974429Sorion 91074429Sorion r = pcm_unregister(dev); 91174429Sorion if (r) return r; 91274429Sorion 91374429Sorion sc = pcm_getdevinfo(dev); 91474429Sorion sv_mix_mute_all(sc); 91574429Sorion sv_power(sc, 3); 91674429Sorion 91774429Sorion bus_dma_tag_destroy(sc->parent_dmat); 91874429Sorion bus_teardown_intr(dev, sc->irq, sc->ih); 91974429Sorion bus_release_resource(dev, SYS_RES_IRQ, sc->irqid, sc->irq); 92074429Sorion bus_release_resource(dev, sc->enh_type, sc->enh_rid, sc->enh_reg); 92174429Sorion bus_release_resource(dev, sc->dmaa_type, sc->dmaa_rid, sc->dmaa_reg); 92274429Sorion bus_release_resource(dev, sc->dmac_type, sc->dmac_rid, sc->dmac_reg); 92374429Sorion 92474429Sorion free(sc, M_DEVBUF); 92574429Sorion 92674429Sorion return 0; 92774429Sorion} 92874429Sorion 92974429Sorionstatic device_method_t sc_methods[] = { 93074429Sorion DEVMETHOD(device_probe, sv_probe), 93174429Sorion DEVMETHOD(device_attach, sv_attach), 93274429Sorion DEVMETHOD(device_detach, sv_detach), 93374429Sorion DEVMETHOD(device_resume, sv_resume), 93474429Sorion DEVMETHOD(device_suspend, sv_suspend), 93574429Sorion { 0, 0 } 93674429Sorion}; 93774429Sorion 93874429Sorionstatic driver_t sonicvibes_driver = { 93974429Sorion "pcm", 94074429Sorion sc_methods, 94182180Scg PCM_SOFTC_SIZE 94274429Sorion}; 94374429Sorion 94485443SjhbDRIVER_MODULE(snd_vibes, pci, sonicvibes_driver, pcm_devclass, 0, 0); 945132236StanimuraMODULE_DEPEND(snd_vibes, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER); 94685443SjhbMODULE_VERSION(snd_vibes, 1); 947