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