cmi.c revision 122461
172016Scg/*
272016Scg * Copyright (c) 2000 Orion Hodson <O.Hodson@cs.ucl.ac.uk>
372016Scg * All rights reserved.
472016Scg *
572016Scg * Redistribution and use in source and binary forms, with or without
672016Scg * modification, are permitted provided that the following conditions
772016Scg * are met:
872016Scg * 1. Redistributions of source code must retain the above copyright
972016Scg *    notice, this list of conditions and the following disclaimer.
1072016Scg * 2. Redistributions in binary form must reproduce the above copyright
1172016Scg *    notice, this list of conditions and the following disclaimer in the
1272016Scg *    documentation and/or other materials provided with the distribution.
1372016Scg *
1472016Scg * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1572016Scg * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1672016Scg * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1772016Scg * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1872016Scg * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1972016Scg * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2072016Scg * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2172016Scg * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHERIN CONTRACT, STRICT
2272016Scg * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2372016Scg * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THEPOSSIBILITY OF
2472016Scg * SUCH DAMAGE.
2572016Scg *
2672016Scg * This driver exists largely as a result of other people's efforts.
2772016Scg * Much of register handling is based on NetBSD CMI8x38 audio driver
2872016Scg * by Takuya Shiozaki <AoiMoe@imou.to>.  Chen-Li Tien
2972016Scg * <cltien@cmedia.com.tw> clarified points regarding the DMA related
30108533Sschweikh * registers and the 8738 mixer devices.  His Linux driver was also a
3172016Scg * useful reference point.
3273772Scg *
3378362Scg * TODO: MIDI
3472016Scg *
3572016Scg * SPDIF contributed by Gerhard Gonter <gonter@whisky.wu-wien.ac.at>.
3672016Scg *
3774994Sorion * This card/code does not always manage to sample at 44100 - actual
3874994Sorion * rate drifts slightly between recordings (usually 0-3%).  No
3974994Sorion * differences visible in register dumps between times that work and
4074994Sorion * those that don't.
4172016Scg */
4272016Scg
4372016Scg#include <dev/sound/pcm/sound.h>
4472016Scg#include <dev/sound/pci/cmireg.h>
4572016Scg#include <dev/sound/isa/sb.h>
4672016Scg
47119287Simp#include <dev/pci/pcireg.h>
48119287Simp#include <dev/pci/pcivar.h>
4972016Scg
5074994Sorion#include <sys/sysctl.h>
5174994Sorion
5272016Scg#include "mixer_if.h"
5372016Scg
5482180ScgSND_DECLARE_FILE("$FreeBSD: head/sys/dev/sound/pci/cmi.c 122461 2003-11-11 05:38:28Z scottl $");
5582180Scg
5672016Scg/* Supported chip ID's */
5772016Scg#define CMI8338A_PCI_ID   0x010013f6
5872016Scg#define CMI8338B_PCI_ID   0x010113f6
5972016Scg#define CMI8738_PCI_ID    0x011113f6
6072016Scg#define CMI8738B_PCI_ID   0x011213f6
6172016Scg
6272016Scg/* Buffer size max is 64k for permitted DMA boundaries */
6384771Sorion#define CMI_DEFAULT_BUFSZ      16384
6472016Scg
6572016Scg/* Interrupts per length of buffer */
6672016Scg#define CMI_INTR_PER_BUFFER      2
6772016Scg
6872016Scg/* Clarify meaning of named defines in cmireg.h */
6972016Scg#define CMPCI_REG_DMA0_MAX_SAMPLES  CMPCI_REG_DMA0_BYTES
7072016Scg#define CMPCI_REG_DMA0_INTR_SAMPLES CMPCI_REG_DMA0_SAMPLES
7172016Scg#define CMPCI_REG_DMA1_MAX_SAMPLES  CMPCI_REG_DMA1_BYTES
7272016Scg#define CMPCI_REG_DMA1_INTR_SAMPLES CMPCI_REG_DMA1_SAMPLES
7372016Scg
7472016Scg/* Our indication of custom mixer control */
7572016Scg#define CMPCI_NON_SB16_CONTROL		0xff
7672016Scg
7772016Scg/* Debugging macro's */
7874994Sorion#undef DEB
7972016Scg#ifndef DEB
8072016Scg#define DEB(x) /* x */
8172016Scg#endif /* DEB */
8272016Scg
8372016Scg#ifndef DEBMIX
8472016Scg#define DEBMIX(x) /* x */
8572016Scg#endif  /* DEBMIX */
8672016Scg
8772016Scg/* ------------------------------------------------------------------------- */
8872016Scg/* Structures */
8972016Scg
9074994Sorionstruct sc_info;
9172016Scg
9274994Sorionstruct sc_chinfo {
9374994Sorion	struct sc_info		*parent;
9474994Sorion	struct pcm_channel	*channel;
9574994Sorion	struct snd_dbuf		*buffer;
9674994Sorion	u_int32_t		fmt, spd, phys_buf, bps;
9774994Sorion	u_int32_t		dma_active:1, dma_was_active:1;
9874994Sorion	int			dir;
9972016Scg};
10072016Scg
10174994Sorionstruct sc_info {
10274994Sorion	device_t		dev;
10373772Scg
10474994Sorion	bus_space_tag_t		st;
10574994Sorion	bus_space_handle_t	sh;
10674994Sorion	bus_dma_tag_t		parent_dmat;
10775174Sorion	struct resource		*reg, *irq;
10874994Sorion	int			regid, irqid;
10974994Sorion	void 			*ih;
110107285Scg	struct mtx		*lock;
11172016Scg
11288032Sorion	int			spdif_enabled;
11384771Sorion	unsigned int		bufsz;
11474994Sorion	struct sc_chinfo 	pch, rch;
11572016Scg};
11672016Scg
11772016Scg/* Channel caps */
11872016Scg
11972016Scgstatic u_int32_t cmi_fmt[] = {
12072016Scg	AFMT_U8,
12172016Scg	AFMT_STEREO | AFMT_U8,
12272016Scg	AFMT_S16_LE,
12372016Scg	AFMT_STEREO | AFMT_S16_LE,
12472016Scg	0
12572016Scg};
12672016Scg
12774763Scgstatic struct pcmchan_caps cmi_caps = {5512, 48000, cmi_fmt, 0};
12872016Scg
12972016Scg/* ------------------------------------------------------------------------- */
13072016Scg/* Register Utilities */
13172016Scg
13272016Scgstatic u_int32_t
13374994Sorioncmi_rd(struct sc_info *sc, int regno, int size)
13472016Scg{
13572016Scg	switch (size) {
13672016Scg	case 1:
13774994Sorion		return bus_space_read_1(sc->st, sc->sh, regno);
13872016Scg	case 2:
13974994Sorion		return bus_space_read_2(sc->st, sc->sh, regno);
14072016Scg	case 4:
14174994Sorion		return bus_space_read_4(sc->st, sc->sh, regno);
14272016Scg	default:
14372016Scg		DEB(printf("cmi_rd: failed 0x%04x %d\n", regno, size));
14472016Scg		return 0xFFFFFFFF;
14572016Scg	}
14672016Scg}
14772016Scg
14872016Scgstatic void
14974994Sorioncmi_wr(struct sc_info *sc, int regno, u_int32_t data, int size)
15072016Scg{
15172016Scg	switch (size) {
15272016Scg	case 1:
15374994Sorion		bus_space_write_1(sc->st, sc->sh, regno, data);
15472016Scg		break;
15572016Scg	case 2:
15674994Sorion		bus_space_write_2(sc->st, sc->sh, regno, data);
15772016Scg		break;
15872016Scg	case 4:
15974994Sorion		bus_space_write_4(sc->st, sc->sh, regno, data);
16072016Scg		break;
16172016Scg	}
16272016Scg}
16372016Scg
16472016Scgstatic void
16578362Scgcmi_partial_wr4(struct sc_info *sc,
16672016Scg		int reg, int shift, u_int32_t mask, u_int32_t val)
16772016Scg{
16872016Scg	u_int32_t r;
16972016Scg
17074994Sorion	r = cmi_rd(sc, reg, 4);
17172016Scg	r &= ~(mask << shift);
17272016Scg	r |= val << shift;
17374994Sorion	cmi_wr(sc, reg, r, 4);
17472016Scg}
17572016Scg
17672016Scgstatic void
17774994Sorioncmi_clr4(struct sc_info *sc, int reg, u_int32_t mask)
17872016Scg{
17972016Scg	u_int32_t r;
18073772Scg
18174994Sorion	r = cmi_rd(sc, reg, 4);
18272016Scg	r &= ~mask;
18374994Sorion	cmi_wr(sc, reg, r, 4);
18472016Scg}
18572016Scg
18672016Scgstatic void
18774994Sorioncmi_set4(struct sc_info *sc, int reg, u_int32_t mask)
18872016Scg{
18972016Scg	u_int32_t r;
19072016Scg
19174994Sorion	r = cmi_rd(sc, reg, 4);
19272016Scg	r |= mask;
19374994Sorion	cmi_wr(sc, reg, r, 4);
19472016Scg}
19572016Scg
19672016Scg/* ------------------------------------------------------------------------- */
19772016Scg/* Rate Mapping */
19872016Scg
19973772Scgstatic int cmi_rates[] = {5512, 8000, 11025, 16000,
20072016Scg			  22050, 32000, 44100, 48000};
20172016Scg#define NUM_CMI_RATES (sizeof(cmi_rates)/sizeof(cmi_rates[0]))
20272016Scg
20372016Scg/* cmpci_rate_to_regvalue returns sampling freq selector for FCR1
20472016Scg * register - reg order is 5k,11k,22k,44k,8k,16k,32k,48k */
20572016Scg
20673772Scgstatic u_int32_t
20772016Scgcmpci_rate_to_regvalue(int rate)
20872016Scg{
20972016Scg	int i, r;
21073772Scg
21172016Scg	for(i = 0; i < NUM_CMI_RATES - 1; i++) {
21272016Scg		if (rate < ((cmi_rates[i] + cmi_rates[i + 1]) / 2)) {
21372016Scg			break;
21472016Scg		}
21572016Scg	}
21672016Scg
21772016Scg	DEB(printf("cmpci_rate_to_regvalue: %d -> %d\n", rate, cmi_rates[i]));
21872016Scg
21972016Scg	r = ((i >> 1) | (i << 2)) & 0x07;
22072016Scg	return r;
22172016Scg}
22272016Scg
22373772Scgstatic int
22473772Scgcmpci_regvalue_to_rate(u_int32_t r)
22572016Scg{
22672016Scg	int i;
22772016Scg
22872016Scg	i = ((r << 1) | (r >> 2)) & 0x07;
22972016Scg	DEB(printf("cmpci_regvalue_to_rate: %d -> %d\n", r, i));
23072016Scg	return cmi_rates[i];
23172016Scg}
23272016Scg
23372016Scg/* ------------------------------------------------------------------------- */
23474994Sorion/* ADC/DAC control - there are 2 dma channels on 8738, either can be
23574994Sorion * playback or capture.  We use ch0 for playback and ch1 for capture. */
23672016Scg
23772016Scgstatic void
23878362Scgcmi_dma_prog(struct sc_info *sc, struct sc_chinfo *ch, u_int32_t base)
23972016Scg{
240102328Sorion	u_int32_t s, i, sz;
24174994Sorion
242111183Scognet	ch->phys_buf = sndbuf_getbufaddr(ch->buffer);
24374994Sorion
244102328Sorion	cmi_wr(sc, base, ch->phys_buf, 4);
24574994Sorion	sz = (u_int32_t)sndbuf_getsize(ch->buffer);
24674994Sorion
24774994Sorion	s = sz / ch->bps - 1;
24875174Sorion	cmi_wr(sc, base + 4, s, 2);
24974994Sorion
25074994Sorion	i = sz / (ch->bps * CMI_INTR_PER_BUFFER) - 1;
25175174Sorion	cmi_wr(sc, base + 6, i, 2);
25278362Scg}
25374994Sorion
25475174Sorion
25575174Sorionstatic void
25675174Sorioncmi_ch0_start(struct sc_info *sc, struct sc_chinfo *ch)
25775174Sorion{
25875174Sorion	cmi_dma_prog(sc, ch, CMPCI_REG_DMA0_BASE);
25975174Sorion
26078362Scg	cmi_set4(sc, CMPCI_REG_FUNC_0, CMPCI_REG_CH0_ENABLE);
26178362Scg	cmi_set4(sc, CMPCI_REG_INTR_CTRL,
26275174Sorion		 CMPCI_REG_CH0_INTR_ENABLE);
26375174Sorion
26474994Sorion	ch->dma_active = 1;
26572016Scg}
26672016Scg
26774994Sorionstatic u_int32_t
26874994Sorioncmi_ch0_stop(struct sc_info *sc, struct sc_chinfo *ch)
26972016Scg{
27074994Sorion	u_int32_t r = ch->dma_active;
27172016Scg
27275174Sorion	cmi_clr4(sc, CMPCI_REG_INTR_CTRL, CMPCI_REG_CH0_INTR_ENABLE);
27378362Scg	cmi_clr4(sc, CMPCI_REG_FUNC_0, CMPCI_REG_CH0_ENABLE);
27478362Scg        cmi_set4(sc, CMPCI_REG_FUNC_0, CMPCI_REG_CH0_RESET);
27575290Sorion        cmi_clr4(sc, CMPCI_REG_FUNC_0, CMPCI_REG_CH0_RESET);
27674994Sorion	ch->dma_active = 0;
27774994Sorion	return r;
27872016Scg}
27972016Scg
28072016Scgstatic void
28174994Sorioncmi_ch1_start(struct sc_info *sc, struct sc_chinfo *ch)
28272016Scg{
28375174Sorion	cmi_dma_prog(sc, ch, CMPCI_REG_DMA1_BASE);
28478362Scg	cmi_set4(sc, CMPCI_REG_FUNC_0, CMPCI_REG_CH1_ENABLE);
28575174Sorion	/* Enable Interrupts */
28678362Scg	cmi_set4(sc, CMPCI_REG_INTR_CTRL,
28775174Sorion		 CMPCI_REG_CH1_INTR_ENABLE);
28874994Sorion	DEB(printf("cmi_ch1_start: dma prog\n"));
28974994Sorion	ch->dma_active = 1;
29072016Scg}
29172016Scg
29274994Sorionstatic u_int32_t
29374994Sorioncmi_ch1_stop(struct sc_info *sc, struct sc_chinfo *ch)
29472016Scg{
29574994Sorion	u_int32_t r = ch->dma_active;
29672016Scg
29775174Sorion	cmi_clr4(sc, CMPCI_REG_INTR_CTRL, CMPCI_REG_CH1_INTR_ENABLE);
29878362Scg	cmi_clr4(sc, CMPCI_REG_FUNC_0, CMPCI_REG_CH1_ENABLE);
29978362Scg        cmi_set4(sc, CMPCI_REG_FUNC_0, CMPCI_REG_CH1_RESET);
30075290Sorion        cmi_clr4(sc, CMPCI_REG_FUNC_0, CMPCI_REG_CH1_RESET);
30174994Sorion	ch->dma_active = 0;
30274994Sorion	return r;
30372016Scg}
30472016Scg
30572016Scgstatic void
30674994Sorioncmi_spdif_speed(struct sc_info *sc, int speed) {
30772016Scg	u_int32_t fcr1, lcr, mcr;
30872016Scg
30972016Scg	if (speed >= 44100) {
31072016Scg		fcr1 = CMPCI_REG_SPDIF0_ENABLE;
31172016Scg		lcr  = CMPCI_REG_XSPDIF_ENABLE;
31273772Scg		mcr  = (speed == 48000) ?
31372016Scg			CMPCI_REG_W_SPDIF_48L | CMPCI_REG_SPDIF_48K : 0;
31472016Scg	} else {
31572016Scg		fcr1 = mcr = lcr = 0;
31672016Scg	}
31772016Scg
31874994Sorion	cmi_partial_wr4(sc, CMPCI_REG_MISC, 0,
31972016Scg			CMPCI_REG_W_SPDIF_48L | CMPCI_REG_SPDIF_48K, mcr);
32074994Sorion	cmi_partial_wr4(sc, CMPCI_REG_FUNC_1, 0,
32174994Sorion			CMPCI_REG_SPDIF0_ENABLE, fcr1);
32274994Sorion	cmi_partial_wr4(sc, CMPCI_REG_LEGACY_CTRL, 0,
32372016Scg			CMPCI_REG_XSPDIF_ENABLE, lcr);
32472016Scg}
32572016Scg
32672016Scg/* ------------------------------------------------------------------------- */
32772016Scg/* Channel Interface implementation */
32872016Scg
32972016Scgstatic void *
33078362Scgcmichan_init(kobj_t obj, void *devinfo,
33174994Sorion	     struct snd_dbuf *b, struct pcm_channel *c, int dir)
33272016Scg{
33374994Sorion	struct sc_info   *sc = devinfo;
33474994Sorion	struct sc_chinfo *ch = (dir == PCMDIR_PLAY) ? &sc->pch : &sc->rch;
33572016Scg
33674994Sorion	ch->parent     = sc;
33774994Sorion	ch->channel    = c;
33874994Sorion	ch->bps        = 1;
33974994Sorion	ch->fmt        = AFMT_U8;
34074994Sorion	ch->spd        = DSP_DEFAULT_SPEED;
34174994Sorion	ch->buffer     = b;
34274994Sorion	ch->dma_active = 0;
34384771Sorion	if (sndbuf_alloc(ch->buffer, sc->parent_dmat, sc->bufsz) != 0) {
34472016Scg		DEB(printf("cmichan_init failed\n"));
34572016Scg		return NULL;
34672016Scg	}
34772016Scg
34872016Scg	ch->dir = dir;
34983214Sgreen	snd_mtxlock(sc->lock);
35075174Sorion	if (ch->dir == PCMDIR_PLAY) {
35175174Sorion		cmi_dma_prog(sc, ch, CMPCI_REG_DMA0_BASE);
35272016Scg	} else {
35375174Sorion		cmi_dma_prog(sc, ch, CMPCI_REG_DMA1_BASE);
35472016Scg	}
35583214Sgreen	snd_mtxunlock(sc->lock);
35672016Scg
35772016Scg	return ch;
35872016Scg}
35972016Scg
36073772Scgstatic int
36173772Scgcmichan_setformat(kobj_t obj, void *data, u_int32_t format)
36272016Scg{
36374994Sorion	struct sc_chinfo *ch = data;
36483214Sgreen	struct sc_info	*sc = ch->parent;
36572016Scg	u_int32_t f;
36672016Scg
36772016Scg	if (format & AFMT_S16_LE) {
36872016Scg		f = CMPCI_REG_FORMAT_16BIT;
36972016Scg		ch->bps = 2;
37072016Scg	} else {
37172016Scg		f = CMPCI_REG_FORMAT_8BIT;
37272016Scg		ch->bps = 1;
37372016Scg	}
37472016Scg
37572016Scg	if (format & AFMT_STEREO) {
37672016Scg		f |= CMPCI_REG_FORMAT_STEREO;
37772016Scg		ch->bps *= 2;
37872016Scg	} else {
37972016Scg		f |= CMPCI_REG_FORMAT_MONO;
38072016Scg	}
38172016Scg
38283214Sgreen	snd_mtxlock(sc->lock);
38372016Scg	if (ch->dir == PCMDIR_PLAY) {
38472016Scg		cmi_partial_wr4(ch->parent,
38572016Scg				CMPCI_REG_CHANNEL_FORMAT,
38672016Scg				CMPCI_REG_CH0_FORMAT_SHIFT,
38772016Scg				CMPCI_REG_CH0_FORMAT_MASK,
38872016Scg				f);
38972016Scg	} else {
39072016Scg		cmi_partial_wr4(ch->parent,
39172016Scg				CMPCI_REG_CHANNEL_FORMAT,
39272016Scg				CMPCI_REG_CH1_FORMAT_SHIFT,
39372016Scg				CMPCI_REG_CH1_FORMAT_MASK,
39472016Scg				f);
39572016Scg	}
39683214Sgreen	snd_mtxunlock(sc->lock);
39773772Scg	ch->fmt = format;
39872016Scg
39972016Scg	return 0;
40072016Scg}
40172016Scg
40273772Scgstatic int
40372016Scgcmichan_setspeed(kobj_t obj, void *data, u_int32_t speed)
40473772Scg{
40574994Sorion	struct sc_chinfo *ch = data;
40683214Sgreen	struct sc_info	*sc = ch->parent;
40772016Scg	u_int32_t r, rsp;
40872016Scg
40972016Scg	r = cmpci_rate_to_regvalue(speed);
41083214Sgreen	snd_mtxlock(sc->lock);
41172016Scg	if (ch->dir == PCMDIR_PLAY) {
41288032Sorion		if (speed < 44100) {
41388032Sorion			/* disable if req before rate change */
41472016Scg			cmi_spdif_speed(ch->parent, speed);
41588032Sorion		}
41672016Scg		cmi_partial_wr4(ch->parent,
41772016Scg				CMPCI_REG_FUNC_1,
41872016Scg				CMPCI_REG_DAC_FS_SHIFT,
41972016Scg				CMPCI_REG_DAC_FS_MASK,
42072016Scg				r);
42188032Sorion		if (speed >= 44100 && ch->parent->spdif_enabled) {
42288032Sorion			/* enable if req after rate change */
42372016Scg			cmi_spdif_speed(ch->parent, speed);
42488032Sorion		}
42572016Scg		rsp = cmi_rd(ch->parent, CMPCI_REG_FUNC_1, 4);
42672016Scg		rsp >>= CMPCI_REG_DAC_FS_SHIFT;
42772016Scg		rsp &= 	CMPCI_REG_DAC_FS_MASK;
42872016Scg	} else {
42972016Scg		cmi_partial_wr4(ch->parent,
43072016Scg				CMPCI_REG_FUNC_1,
43172016Scg				CMPCI_REG_ADC_FS_SHIFT,
43272016Scg				CMPCI_REG_ADC_FS_MASK,
43372016Scg				r);
43472016Scg		rsp = cmi_rd(ch->parent, CMPCI_REG_FUNC_1, 4);
43572016Scg		rsp >>= CMPCI_REG_ADC_FS_SHIFT;
43672016Scg		rsp &= 	CMPCI_REG_ADC_FS_MASK;
43772016Scg	}
43883214Sgreen	snd_mtxunlock(sc->lock);
43972016Scg	ch->spd = cmpci_regvalue_to_rate(r);
44072016Scg
44173772Scg	DEB(printf("cmichan_setspeed (%s) %d -> %d (%d)\n",
44272016Scg		   (ch->dir == PCMDIR_PLAY) ? "play" : "rec",
44372016Scg		   speed, ch->spd, cmpci_regvalue_to_rate(rsp)));
44472016Scg
44572016Scg	return ch->spd;
44672016Scg}
44772016Scg
44872016Scgstatic int
44972016Scgcmichan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize)
45072016Scg{
45174994Sorion	struct sc_chinfo *ch = data;
45284771Sorion	struct sc_info	 *sc = ch->parent;
45372016Scg
45472016Scg	/* user has requested interrupts every blocksize bytes */
45584771Sorion	if (blocksize > sc->bufsz / CMI_INTR_PER_BUFFER) {
45684771Sorion		blocksize = sc->bufsz / CMI_INTR_PER_BUFFER;
45772016Scg	}
45872016Scg	sndbuf_resize(ch->buffer, CMI_INTR_PER_BUFFER, blocksize);
45972016Scg
46082834Sorion	return blocksize;
46172016Scg}
46272016Scg
46372016Scgstatic int
46472016Scgcmichan_trigger(kobj_t obj, void *data, int go)
46572016Scg{
46674994Sorion	struct sc_chinfo	*ch = data;
46774994Sorion	struct sc_info		*sc = ch->parent;
46872016Scg
46983214Sgreen	snd_mtxlock(sc->lock);
47072016Scg	if (ch->dir == PCMDIR_PLAY) {
47172016Scg		switch(go) {
47272016Scg		case PCMTRIG_START:
47374994Sorion			cmi_ch0_start(sc, ch);
47472016Scg			break;
47572016Scg		case PCMTRIG_ABORT:
47674994Sorion			cmi_ch0_stop(sc, ch);
47772016Scg			break;
47872016Scg		}
47973772Scg	} else {
48072016Scg		switch(go) {
48172016Scg		case PCMTRIG_START:
48274994Sorion			cmi_ch1_start(sc, ch);
48372016Scg			break;
48472016Scg		case PCMTRIG_ABORT:
48574994Sorion			cmi_ch1_stop(sc, ch);
48672016Scg			break;
48772016Scg		}
48872016Scg	}
48983214Sgreen	snd_mtxunlock(sc->lock);
49072016Scg	return 0;
49172016Scg}
49272016Scg
49372016Scgstatic int
49472016Scgcmichan_getptr(kobj_t obj, void *data)
49572016Scg{
49674994Sorion	struct sc_chinfo	*ch = data;
49774994Sorion	struct sc_info		*sc = ch->parent;
49872016Scg	u_int32_t physptr, bufptr, sz;
49972016Scg
50083214Sgreen	snd_mtxlock(sc->lock);
50172016Scg	if (ch->dir == PCMDIR_PLAY) {
50274994Sorion		physptr = cmi_rd(sc, CMPCI_REG_DMA0_BASE, 4);
50372016Scg	} else {
50474994Sorion		physptr = cmi_rd(sc, CMPCI_REG_DMA1_BASE, 4);
50572016Scg	}
50683214Sgreen	snd_mtxunlock(sc->lock);
50773772Scg
50872016Scg	sz = sndbuf_getsize(ch->buffer);
50974994Sorion	bufptr = (physptr - ch->phys_buf + sz - ch->bps) % sz;
51072016Scg
51172016Scg	return bufptr;
51272016Scg}
51372016Scg
51473772Scgstatic void
51573772Scgcmi_intr(void *data)
51672016Scg{
51774994Sorion	struct sc_info *sc = data;
51872016Scg	u_int32_t intrstat;
519122461Sscottl	u_int32_t toclear;
52072016Scg
52183214Sgreen	snd_mtxlock(sc->lock);
52274994Sorion	intrstat = cmi_rd(sc, CMPCI_REG_INTR_STATUS, 4);
523122461Sscottl	if ((intrstat & CMPCI_REG_ANY_INTR) != 0) {
52472016Scg
525122461Sscottl		toclear = 0;
526122461Sscottl		if (intrstat & CMPCI_REG_CH0_INTR) {
527122461Sscottl			toclear |= CMPCI_REG_CH0_INTR_ENABLE;
528122461Sscottl			//cmi_clr4(sc, CMPCI_REG_INTR_CTRL, CMPCI_REG_CH0_INTR_ENABLE);
529122461Sscottl		}
53072016Scg
531122461Sscottl		if (intrstat & CMPCI_REG_CH1_INTR) {
532122461Sscottl			toclear |= CMPCI_REG_CH1_INTR_ENABLE;
533122461Sscottl			//cmi_clr4(sc, CMPCI_REG_INTR_CTRL, CMPCI_REG_CH1_INTR_ENABLE);
534122461Sscottl		}
53572016Scg
536122461Sscottl		if (toclear) {
537122461Sscottl			cmi_clr4(sc, CMPCI_REG_INTR_CTRL, toclear);
538122461Sscottl			snd_mtxunlock(sc->lock);
53972016Scg
540122461Sscottl			/* Signal interrupts to channel */
541122461Sscottl			if (intrstat & CMPCI_REG_CH0_INTR) {
542122461Sscottl				chn_intr(sc->pch.channel);
543122461Sscottl			}
54473772Scg
545122461Sscottl			if (intrstat & CMPCI_REG_CH1_INTR) {
546122461Sscottl				chn_intr(sc->rch.channel);
547122461Sscottl			}
54872016Scg
549122461Sscottl			snd_mtxlock(sc->lock);
550122461Sscottl			cmi_set4(sc, CMPCI_REG_INTR_CTRL, toclear);
551122461Sscottl
552122461Sscottl		}
55372016Scg	}
55483214Sgreen	snd_mtxunlock(sc->lock);
55572016Scg	return;
55672016Scg}
55772016Scg
55874763Scgstatic struct pcmchan_caps *
55972016Scgcmichan_getcaps(kobj_t obj, void *data)
56072016Scg{
56172016Scg	return &cmi_caps;
56272016Scg}
56372016Scg
56472016Scgstatic kobj_method_t cmichan_methods[] = {
56572016Scg    	KOBJMETHOD(channel_init,		cmichan_init),
56672016Scg    	KOBJMETHOD(channel_setformat,		cmichan_setformat),
56772016Scg    	KOBJMETHOD(channel_setspeed,		cmichan_setspeed),
56872016Scg    	KOBJMETHOD(channel_setblocksize,	cmichan_setblocksize),
56972016Scg    	KOBJMETHOD(channel_trigger,		cmichan_trigger),
57072016Scg    	KOBJMETHOD(channel_getptr,		cmichan_getptr),
57172016Scg    	KOBJMETHOD(channel_getcaps,		cmichan_getcaps),
57272016Scg	{ 0, 0 }
57372016Scg};
57472016ScgCHANNEL_DECLARE(cmichan);
57572016Scg
57672016Scg/* ------------------------------------------------------------------------- */
57772016Scg/* Mixer - sb16 with kinks */
57872016Scg
57973772Scgstatic void
58074994Sorioncmimix_wr(struct sc_info *sc, u_int8_t port, u_int8_t val)
58172016Scg{
58274994Sorion	cmi_wr(sc, CMPCI_REG_SBADDR, port, 1);
58374994Sorion	cmi_wr(sc, CMPCI_REG_SBDATA, val, 1);
58472016Scg}
58572016Scg
58673772Scgstatic u_int8_t
58774994Sorioncmimix_rd(struct sc_info *sc, u_int8_t port)
58872016Scg{
58974994Sorion	cmi_wr(sc, CMPCI_REG_SBADDR, port, 1);
59074994Sorion	return (u_int8_t)cmi_rd(sc, CMPCI_REG_SBDATA, 1);
59172016Scg}
59272016Scg
59372016Scgstruct sb16props {
59472016Scg	u_int8_t  rreg;     /* right reg chan register */
59572016Scg	u_int8_t  stereo:1; /* (no explanation needed, honest) */
59672016Scg	u_int8_t  rec:1;    /* recording source */
59772016Scg	u_int8_t  bits:3;   /* num bits to represent maximum gain rep */
59872016Scg	u_int8_t  oselect;  /* output select mask */
59972016Scg	u_int8_t  iselect;  /* right input select mask */
60072016Scg} static const cmt[SOUND_MIXER_NRDEVICES] = {
60173772Scg	[SOUND_MIXER_SYNTH]   = {CMPCI_SB16_MIXER_FM_R,      1, 1, 5,
60272016Scg				 CMPCI_SB16_SW_FM,   CMPCI_SB16_MIXER_FM_SRC_R},
60372016Scg	[SOUND_MIXER_CD]      = {CMPCI_SB16_MIXER_CDDA_R,    1, 1, 5,
60472016Scg				 CMPCI_SB16_SW_CD,   CMPCI_SB16_MIXER_CD_SRC_R},
60572016Scg	[SOUND_MIXER_LINE]    = {CMPCI_SB16_MIXER_LINE_R,    1, 1, 5,
60672016Scg				 CMPCI_SB16_SW_LINE, CMPCI_SB16_MIXER_LINE_SRC_R},
60773772Scg	[SOUND_MIXER_MIC]     = {CMPCI_SB16_MIXER_MIC,       0, 1, 5,
60872016Scg				 CMPCI_SB16_SW_MIC,  CMPCI_SB16_MIXER_MIC_SRC},
60972016Scg	[SOUND_MIXER_SPEAKER] = {CMPCI_SB16_MIXER_SPEAKER,  0, 0, 2, 0, 0},
61072016Scg	[SOUND_MIXER_PCM]     = {CMPCI_SB16_MIXER_VOICE_R,  1, 0, 5, 0, 0},
61172016Scg	[SOUND_MIXER_VOLUME]  = {CMPCI_SB16_MIXER_MASTER_R, 1, 0, 5, 0, 0},
61272016Scg	/* These controls are not implemented in CMI8738, but maybe at a
61372016Scg	   future date.  They are not documented in C-Media documentation,
61472016Scg	   though appear in other drivers for future h/w (ALSA, Linux, NetBSD).
61572016Scg	*/
61673772Scg	[SOUND_MIXER_IGAIN]   = {CMPCI_SB16_MIXER_INGAIN_R,  1, 0, 2, 0, 0},
61773772Scg	[SOUND_MIXER_OGAIN]   = {CMPCI_SB16_MIXER_OUTGAIN_R, 1, 0, 2, 0, 0},
61873772Scg	[SOUND_MIXER_BASS]    = {CMPCI_SB16_MIXER_BASS_R,    1, 0, 4, 0, 0},
61973772Scg	[SOUND_MIXER_TREBLE]  = {CMPCI_SB16_MIXER_TREBLE_R,  1, 0, 4, 0, 0},
62074994Sorion	/* The mic pre-amp is implemented with non-SB16 compatible
62174994Sorion	   registers. */
62272016Scg	[SOUND_MIXER_MONITOR]  = {CMPCI_NON_SB16_CONTROL,     0, 1, 4, 0},
62372016Scg};
62472016Scg
62572016Scg#define MIXER_GAIN_REG_RTOL(r) (r - 1)
62672016Scg
62772016Scgstatic int
62874763Scgcmimix_init(struct snd_mixer *m)
62972016Scg{
63074994Sorion	struct sc_info	*sc = mix_getdevinfo(m);
63174994Sorion	u_int32_t	i,v;
63272016Scg
63374994Sorion	for(i = v = 0; i < SOUND_MIXER_NRDEVICES; i++) {
63472016Scg		if (cmt[i].bits) v |= 1 << i;
63572016Scg	}
63672016Scg	mix_setdevs(m, v);
63774994Sorion
63874994Sorion	for(i = v = 0; i < SOUND_MIXER_NRDEVICES; i++) {
63974994Sorion		if (cmt[i].rec) v |= 1 << i;
64072016Scg	}
64172016Scg	mix_setrecdevs(m, v);
64272016Scg
64374994Sorion	cmimix_wr(sc, CMPCI_SB16_MIXER_RESET, 0);
64474994Sorion	cmimix_wr(sc, CMPCI_SB16_MIXER_ADCMIX_L, 0);
64574994Sorion	cmimix_wr(sc, CMPCI_SB16_MIXER_ADCMIX_R, 0);
64674994Sorion	cmimix_wr(sc, CMPCI_SB16_MIXER_OUTMIX,
64772016Scg		  CMPCI_SB16_SW_CD | CMPCI_SB16_SW_MIC | CMPCI_SB16_SW_LINE);
64872016Scg	return 0;
64972016Scg}
65072016Scg
65172016Scgstatic int
65274763Scgcmimix_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right)
65372016Scg{
65474994Sorion	struct sc_info *sc = mix_getdevinfo(m);
65572016Scg	u_int32_t r, l, max;
65672016Scg	u_int8_t  v;
65772016Scg
65872016Scg	max = (1 << cmt[dev].bits) - 1;
65972016Scg
66072016Scg	if (cmt[dev].rreg == CMPCI_NON_SB16_CONTROL) {
66174994Sorion		/* For time being this can only be one thing (mic in
66274994Sorion		 * mic/aux reg) */
66374994Sorion		v = cmi_rd(sc, CMPCI_REG_AUX_MIC, 1) & 0xf0;
66472016Scg		l = left * max / 100;
66574994Sorion		/* 3 bit gain with LSB MICGAIN off(1),on(1) -> 4 bit value */
66673772Scg		v |= ((l << 1) | (~l >> 3)) & 0x0f;
66774994Sorion		cmi_wr(sc, CMPCI_REG_AUX_MIC, v, 1);
66872016Scg		return 0;
66972016Scg	}
67072016Scg
67172016Scg	l  = (left * max / 100) << (8 - cmt[dev].bits);
67272016Scg	if (cmt[dev].stereo) {
67372016Scg		r = (right * max / 100) << (8 - cmt[dev].bits);
67474994Sorion		cmimix_wr(sc, MIXER_GAIN_REG_RTOL(cmt[dev].rreg), l);
67574994Sorion		cmimix_wr(sc, cmt[dev].rreg, r);
67672016Scg		DEBMIX(printf("Mixer stereo write dev %d reg 0x%02x "\
67772016Scg			      "value 0x%02x:0x%02x\n",
67872016Scg			      dev, MIXER_GAIN_REG_RTOL(cmt[dev].rreg), l, r));
67972016Scg	} else {
68072016Scg		r = l;
68174994Sorion		cmimix_wr(sc, cmt[dev].rreg, l);
68272016Scg		DEBMIX(printf("Mixer mono write dev %d reg 0x%02x " \
68372016Scg			      "value 0x%02x:0x%02x\n",
68472016Scg			      dev, cmt[dev].rreg, l, l));
68572016Scg	}
68672016Scg
68772016Scg	/* Zero gain does not mute channel from output, but this does... */
68874994Sorion	v = cmimix_rd(sc, CMPCI_SB16_MIXER_OUTMIX);
68972016Scg	if (l == 0 && r == 0) {
69072016Scg		v &= ~cmt[dev].oselect;
69172016Scg	} else {
69272016Scg		v |= cmt[dev].oselect;
69372016Scg	}
69474994Sorion	cmimix_wr(sc,  CMPCI_SB16_MIXER_OUTMIX, v);
69572016Scg
69672016Scg	return 0;
69772016Scg}
69872016Scg
69972016Scgstatic int
70074763Scgcmimix_setrecsrc(struct snd_mixer *m, u_int32_t src)
70172016Scg{
70274994Sorion	struct sc_info *sc = mix_getdevinfo(m);
70372016Scg	u_int32_t i, ml, sl;
70472016Scg
70572016Scg	ml = sl = 0;
70672016Scg	for(i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
70772016Scg		if ((1<<i) & src) {
70872016Scg			if (cmt[i].stereo) {
70972016Scg				sl |= cmt[i].iselect;
71072016Scg			} else {
71172016Scg				ml |= cmt[i].iselect;
71272016Scg			}
71372016Scg		}
71472016Scg	}
71574994Sorion	cmimix_wr(sc, CMPCI_SB16_MIXER_ADCMIX_R, sl|ml);
71672016Scg	DEBMIX(printf("cmimix_setrecsrc: reg 0x%02x val 0x%02x\n",
71773772Scg		      CMPCI_SB16_MIXER_ADCMIX_R, sl|ml));
71872016Scg	ml = CMPCI_SB16_MIXER_SRC_R_TO_L(ml);
71974994Sorion	cmimix_wr(sc, CMPCI_SB16_MIXER_ADCMIX_L, sl|ml);
72073772Scg	DEBMIX(printf("cmimix_setrecsrc: reg 0x%02x val 0x%02x\n",
72173772Scg		      CMPCI_SB16_MIXER_ADCMIX_L, sl|ml));
72272016Scg
72372016Scg	return src;
72472016Scg}
72572016Scg
72688032Sorion/* Optional SPDIF support. */
72788032Sorion
72888032Sorionstatic int
72988032Sorioncmi_initsys(struct sc_info* sc)
73088032Sorion{
73188032Sorion#ifdef SND_DYNSYSCTL
73288032Sorion	SYSCTL_ADD_INT(snd_sysctl_tree(sc->dev),
73388032Sorion		       SYSCTL_CHILDREN(snd_sysctl_tree_top(sc->dev)),
73488032Sorion		       OID_AUTO, "spdif_enabled", CTLFLAG_RW,
73588032Sorion		       &sc->spdif_enabled, 0,
73688032Sorion		       "enable SPDIF output at 44.1 kHz and above");
73788032Sorion#endif /* SND_DYNSYSCTL */
73888032Sorion	return 0;
73988032Sorion}
74088032Sorion
74188032Sorion/* ------------------------------------------------------------------------- */
74272016Scgstatic kobj_method_t cmi_mixer_methods[] = {
74372016Scg	KOBJMETHOD(mixer_init,	cmimix_init),
74472016Scg	KOBJMETHOD(mixer_set,	cmimix_set),
74572016Scg	KOBJMETHOD(mixer_setrecsrc,	cmimix_setrecsrc),
74672016Scg	{ 0, 0 }
74772016Scg};
74872016ScgMIXER_DECLARE(cmi_mixer);
74972016Scg
75072016Scg/* ------------------------------------------------------------------------- */
75172016Scg/* Power and reset */
75272016Scg
75372016Scgstatic void
75474994Sorioncmi_power(struct sc_info *sc, int state)
75572016Scg{
75672016Scg	switch (state) {
75772016Scg	case 0: /* full power */
75874994Sorion		cmi_clr4(sc, CMPCI_REG_MISC, CMPCI_REG_POWER_DOWN);
75972016Scg		break;
76072016Scg	default:
76172016Scg		/* power off */
76274994Sorion		cmi_set4(sc, CMPCI_REG_MISC, CMPCI_REG_POWER_DOWN);
76372016Scg		break;
76472016Scg	}
76572016Scg}
76672016Scg
76774994Sorionstatic int
76874994Sorioncmi_init(struct sc_info *sc)
76974994Sorion{
77074994Sorion	/* Effect reset */
77174994Sorion	cmi_set4(sc, CMPCI_REG_MISC, CMPCI_REG_BUS_AND_DSP_RESET);
77274994Sorion	DELAY(100);
77374994Sorion	cmi_clr4(sc, CMPCI_REG_MISC, CMPCI_REG_BUS_AND_DSP_RESET);
77474994Sorion
77574994Sorion	/* Disable interrupts and channels */
77674994Sorion	cmi_clr4(sc, CMPCI_REG_FUNC_0,
77774994Sorion		 CMPCI_REG_CH0_ENABLE | CMPCI_REG_CH1_ENABLE);
77875174Sorion	cmi_clr4(sc, CMPCI_REG_INTR_CTRL,
77975174Sorion		 CMPCI_REG_CH0_INTR_ENABLE | CMPCI_REG_CH1_INTR_ENABLE);
78074994Sorion
78175174Sorion	/* Configure DMA channels, ch0 = play, ch1 = capture */
78278362Scg	cmi_clr4(sc, CMPCI_REG_FUNC_0, CMPCI_REG_CH0_DIR);
78378362Scg	cmi_set4(sc, CMPCI_REG_FUNC_0, CMPCI_REG_CH1_DIR);
78475174Sorion
78574994Sorion	/* Attempt to enable 4 Channel output */
78678362Scg	cmi_set4(sc, CMPCI_REG_MISC, CMPCI_REG_N4SPK3D);
78774994Sorion
78874994Sorion	/* Disable SPDIF1 - not compatible with config */
78974994Sorion	cmi_clr4(sc, CMPCI_REG_FUNC_1, CMPCI_REG_SPDIF1_ENABLE);
79074994Sorion	cmi_clr4(sc, CMPCI_REG_FUNC_1, CMPCI_REG_SPDIF_LOOP);
79174994Sorion
79274994Sorion	return 0;
79378362Scg}
79474994Sorion
79574994Sorionstatic void
79674994Sorioncmi_uninit(struct sc_info *sc)
79774994Sorion{
79874994Sorion	/* Disable interrupts and channels */
79974994Sorion	cmi_clr4(sc, CMPCI_REG_INTR_CTRL,
80074994Sorion		 CMPCI_REG_CH0_INTR_ENABLE |
80174994Sorion		 CMPCI_REG_CH1_INTR_ENABLE |
80274994Sorion		 CMPCI_REG_TDMA_INTR_ENABLE);
80374994Sorion	cmi_clr4(sc, CMPCI_REG_FUNC_0,
80474994Sorion		 CMPCI_REG_CH0_ENABLE | CMPCI_REG_CH1_ENABLE);
80574994Sorion}
80674994Sorion
80772016Scg/* ------------------------------------------------------------------------- */
80872016Scg/* Bus and device registration */
80972016Scgstatic int
81072016Scgcmi_probe(device_t dev)
81172016Scg{
81272016Scg	switch(pci_get_devid(dev)) {
81372016Scg	case CMI8338A_PCI_ID:
81472016Scg		device_set_desc(dev, "CMedia CMI8338A");
81572016Scg		return 0;
81672016Scg	case CMI8338B_PCI_ID:
81772016Scg		device_set_desc(dev, "CMedia CMI8338B");
81872016Scg		return 0;
81972016Scg	case CMI8738_PCI_ID:
82072016Scg		device_set_desc(dev, "CMedia CMI8738");
82172016Scg		return 0;
82272016Scg	case CMI8738B_PCI_ID:
82372016Scg		device_set_desc(dev, "CMedia CMI8738B");
82472016Scg		return 0;
82572016Scg	default:
82672016Scg		return ENXIO;
82772016Scg	}
82872016Scg}
82972016Scg
83073772Scgstatic int
83172016Scgcmi_attach(device_t dev)
83272016Scg{
83374994Sorion	struct snddev_info	*d;
83474994Sorion	struct sc_info		*sc;
83574994Sorion	u_int32_t		data;
83674994Sorion	char			status[SND_STATUSLEN];
83772016Scg
83872016Scg	d = device_get_softc(dev);
83978564Sgreid	sc = malloc(sizeof(struct sc_info), M_DEVBUF, M_NOWAIT | M_ZERO);
84074994Sorion	if (sc == NULL) {
84172016Scg		device_printf(dev, "cannot allocate softc\n");
84272016Scg		return ENXIO;
84372016Scg	}
84473772Scg
84593816Sjhb	sc->lock = snd_mtxcreate(device_get_nameunit(dev), "sound softc");
84672016Scg	data = pci_read_config(dev, PCIR_COMMAND, 2);
84772016Scg	data |= (PCIM_CMD_PORTEN|PCIM_CMD_BUSMASTEREN);
84872016Scg	pci_write_config(dev, PCIR_COMMAND, data, 2);
84972016Scg	data = pci_read_config(dev, PCIR_COMMAND, 2);
85072016Scg
85188032Sorion	sc->dev = dev;
852119690Sjhb	sc->regid = PCIR_BAR(0);
85374994Sorion	sc->reg = bus_alloc_resource(dev, SYS_RES_IOPORT, &sc->regid,
85472016Scg				      0, BUS_SPACE_UNRESTRICTED, 1, RF_ACTIVE);
85574994Sorion	if (!sc->reg) {
85672016Scg		device_printf(dev, "cmi_attach: Cannot allocate bus resource\n");
85772016Scg		goto bad;
85872016Scg	}
85974994Sorion	sc->st = rman_get_bustag(sc->reg);
86074994Sorion	sc->sh = rman_get_bushandle(sc->reg);
86172016Scg
86274994Sorion	sc->irqid = 0;
86374994Sorion	sc->irq   = bus_alloc_resource(dev, SYS_RES_IRQ, &sc->irqid,
86472016Scg					0, ~0, 1, RF_ACTIVE | RF_SHAREABLE);
86574994Sorion	if (!sc->irq ||
86683214Sgreen	    snd_setup_intr(dev, sc->irq, INTR_MPSAFE, cmi_intr, sc, &sc->ih)) {
86772016Scg		device_printf(dev, "cmi_attach: Unable to map interrupt\n");
86872016Scg		goto bad;
86972016Scg	}
87073772Scg
87184771Sorion	sc->bufsz = pcm_getbuffersize(dev, 4096, CMI_DEFAULT_BUFSZ, 65536);
87284771Sorion
87372016Scg	if (bus_dma_tag_create(/*parent*/NULL, /*alignment*/2, /*boundary*/0,
87472016Scg			       /*lowaddr*/BUS_SPACE_MAXADDR_32BIT,
87572016Scg			       /*highaddr*/BUS_SPACE_MAXADDR,
87672016Scg			       /*filter*/NULL, /*filterarg*/NULL,
87784771Sorion			       /*maxsize*/sc->bufsz, /*nsegments*/1,
87873772Scg			       /*maxsegz*/0x3ffff, /*flags*/0,
879117126Sscottl			       /*lockfunc*/busdma_lock_mutex,
880117126Sscottl			       /*lockfunc*/&Giant,
88174994Sorion			       &sc->parent_dmat) != 0) {
88272016Scg		device_printf(dev, "cmi_attach: Unable to create dma tag\n");
88372016Scg		goto bad;
88472016Scg	}
88572016Scg
88674994Sorion	cmi_power(sc, 0);
88775174Sorion	if (cmi_init(sc))
88875174Sorion		goto bad;
88972016Scg
89074994Sorion	if (mixer_init(dev, &cmi_mixer_class, sc))
89172016Scg		goto bad;
89272016Scg
89374994Sorion	if (pcm_register(dev, sc, 1, 1))
89473772Scg		goto bad;
89573772Scg
89688032Sorion	cmi_initsys(sc);
89788032Sorion
89874994Sorion	pcm_addchan(dev, PCMDIR_PLAY, &cmichan_class, sc);
89974994Sorion	pcm_addchan(dev, PCMDIR_REC, &cmichan_class, sc);
90072016Scg
90172016Scg	snprintf(status, SND_STATUSLEN, "at io 0x%lx irq %ld",
90274994Sorion		 rman_get_start(sc->reg), rman_get_start(sc->irq));
90372016Scg	pcm_setstatus(dev, status);
90472016Scg
90572016Scg	DEB(printf("cmi_attach: succeeded\n"));
90672016Scg	return 0;
90773772Scg
90872016Scg bad:
90978362Scg	if (sc->parent_dmat)
91074994Sorion		bus_dma_tag_destroy(sc->parent_dmat);
91178362Scg	if (sc->ih)
91274994Sorion		bus_teardown_intr(dev, sc->irq, sc->ih);
91378362Scg	if (sc->irq)
91474994Sorion		bus_release_resource(dev, SYS_RES_IRQ, sc->irqid, sc->irq);
91578362Scg	if (sc->reg)
91674994Sorion		bus_release_resource(dev, SYS_RES_IOPORT, sc->regid, sc->reg);
91783214Sgreen	if (sc->lock)
91883214Sgreen		snd_mtxfree(sc->lock);
91978362Scg	if (sc)
92074994Sorion		free(sc, M_DEVBUF);
92172016Scg
92272016Scg	return ENXIO;
92372016Scg}
92472016Scg
92572016Scgstatic int
92672016Scgcmi_detach(device_t dev)
92772016Scg{
92874994Sorion	struct sc_info *sc;
92972016Scg	int r;
93072016Scg
93172016Scg	r = pcm_unregister(dev);
93272016Scg	if (r) return r;
93372016Scg
93474994Sorion	sc = pcm_getdevinfo(dev);
93574994Sorion	cmi_uninit(sc);
93674994Sorion	cmi_power(sc, 3);
93772016Scg
93874994Sorion	bus_dma_tag_destroy(sc->parent_dmat);
93974994Sorion	bus_teardown_intr(dev, sc->irq, sc->ih);
94074994Sorion	bus_release_resource(dev, SYS_RES_IRQ, sc->irqid, sc->irq);
94174994Sorion	bus_release_resource(dev, SYS_RES_IOPORT, sc->regid, sc->reg);
94283214Sgreen	snd_mtxfree(sc->lock);
94374994Sorion	free(sc, M_DEVBUF);
94474994Sorion
94572016Scg	return 0;
94672016Scg}
94772016Scg
94874994Sorionstatic int
94974994Sorioncmi_suspend(device_t dev)
95074994Sorion{
95174994Sorion	struct sc_info *sc = pcm_getdevinfo(dev);
95274994Sorion
95383214Sgreen	snd_mtxlock(sc->lock);
95474994Sorion	sc->pch.dma_was_active = cmi_ch0_stop(sc, &sc->pch);
95574994Sorion	sc->rch.dma_was_active = cmi_ch1_stop(sc, &sc->rch);
95674994Sorion	cmi_power(sc, 3);
95783214Sgreen	snd_mtxunlock(sc->lock);
95874994Sorion	return 0;
95974994Sorion}
96074994Sorion
96174994Sorionstatic int
96274994Sorioncmi_resume(device_t dev)
96374994Sorion{
96474994Sorion	struct sc_info *sc = pcm_getdevinfo(dev);
96574994Sorion
96683214Sgreen	snd_mtxlock(sc->lock);
96774994Sorion	cmi_power(sc, 0);
96874994Sorion	if (cmi_init(sc) != 0) {
96974994Sorion		device_printf(dev, "unable to reinitialize the card\n");
97083214Sgreen		snd_mtxunlock(sc->lock);
97174994Sorion		return ENXIO;
97274994Sorion	}
97374994Sorion
97474994Sorion	if (mixer_reinit(dev) == -1) {
97574994Sorion		device_printf(dev, "unable to reinitialize the mixer\n");
97683214Sgreen		snd_mtxunlock(sc->lock);
97774994Sorion                return ENXIO;
97878362Scg        }
97974994Sorion
98074994Sorion	if (sc->pch.dma_was_active) {
98174994Sorion		cmichan_setspeed(NULL, &sc->pch, sc->pch.spd);
98274994Sorion		cmichan_setformat(NULL, &sc->pch, sc->pch.fmt);
98374994Sorion		cmi_ch0_start(sc, &sc->pch);
98474994Sorion	}
98574994Sorion
98674994Sorion	if (sc->rch.dma_was_active) {
98774994Sorion		cmichan_setspeed(NULL, &sc->rch, sc->rch.spd);
98874994Sorion		cmichan_setformat(NULL, &sc->rch, sc->rch.fmt);
98974994Sorion		cmi_ch1_start(sc, &sc->rch);
99074994Sorion	}
99183214Sgreen	snd_mtxunlock(sc->lock);
99274994Sorion	return 0;
99374994Sorion}
99474994Sorion
99572016Scgstatic device_method_t cmi_methods[] = {
99672016Scg	DEVMETHOD(device_probe,         cmi_probe),
99772016Scg	DEVMETHOD(device_attach,        cmi_attach),
99872016Scg	DEVMETHOD(device_detach,        cmi_detach),
99974994Sorion	DEVMETHOD(device_resume,        cmi_resume),
100074994Sorion	DEVMETHOD(device_suspend,       cmi_suspend),
100172016Scg	{ 0, 0 }
100272016Scg};
100372016Scg
100472016Scgstatic driver_t cmi_driver = {
100572016Scg	"pcm",
100672016Scg	cmi_methods,
100782180Scg	PCM_SOFTC_SIZE
100872016Scg};
100972016Scg
101085440SjhbDRIVER_MODULE(snd_cmi, pci, cmi_driver, pcm_devclass, 0, 0);
101185440SjhbMODULE_DEPEND(snd_cmi, snd_pcm, PCM_MINVER, PCM_PREFVER, PCM_MAXVER);
101285440SjhbMODULE_VERSION(snd_cmi, 1);
1013