1139749Simp/*-
2119853Scg * Copyright (c) 1999 Cameron Grant <cg@freebsd.org>
364064Scg *
464064Scg * Redistribution and use in source and binary forms, with or without
564064Scg * modification, are permitted provided that the following conditions
664064Scg * are met:
764064Scg * 1. Redistributions of source code must retain the above copyright
864064Scg *    notice, this list of conditions and the following disclaimer.
964064Scg * 2. Redistributions in binary form must reproduce the above copyright
1064064Scg *    notice, this list of conditions and the following disclaimer in the
1164064Scg *    documentation and/or other materials provided with the distribution.
1264064Scg *
1364064Scg * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1464064Scg * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1564064Scg * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1664064Scg * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1764064Scg * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1864064Scg * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
1964064Scg * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2064064Scg * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2164064Scg * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2264064Scg * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2364064Scg * SUCH DAMAGE.
2464064Scg */
2564064Scg
26193640Sariff#ifdef HAVE_KERNEL_OPTION_HEADERS
27193640Sariff#include "opt_snd.h"
28193640Sariff#endif
29193640Sariff
3064064Scg#include <dev/sound/pcm/sound.h>
3164064Scg
32119287Simp#include <dev/pci/pcireg.h>
33119287Simp#include <dev/pci/pcivar.h>
3464064Scg
3564064Scg#include  <dev/sound/isa/sb.h>
3664064Scg#include  <dev/sound/chip.h>
3764064Scg
3870134Scg#include "mixer_if.h"
3970134Scg
4082180ScgSND_DECLARE_FILE("$FreeBSD$");
4182180Scg
4284658Scg#define SOLO_DEFAULT_BUFSZ 16384
4364064Scg#define ABS(x) (((x) < 0)? -(x) : (x))
4464064Scg
4564163Snsayer/* if defined, playback always uses the 2nd channel and full duplex works */
46154881Sariff#define ESS18XX_DUPLEX	1
4764064Scg
4864064Scg/* more accurate clocks and split audio1/audio2 rates */
4964064Scg#define ESS18XX_NEWSPEED
5064064Scg
51154066Sariff/* 1 = INTR_MPSAFE, 0 = GIANT */
52154066Sariff#define ESS18XX_MPSAFE	1
53154066Sariff
5464881Scgstatic u_int32_t ess_playfmt[] = {
55193640Sariff	SND_FORMAT(AFMT_U8, 1, 0),
56193640Sariff	SND_FORMAT(AFMT_U8, 2, 0),
57193640Sariff	SND_FORMAT(AFMT_S8, 1, 0),
58193640Sariff	SND_FORMAT(AFMT_S8, 2, 0),
59193640Sariff	SND_FORMAT(AFMT_S16_LE, 1, 0),
60193640Sariff	SND_FORMAT(AFMT_S16_LE, 2, 0),
61193640Sariff	SND_FORMAT(AFMT_U16_LE, 1, 0),
62193640Sariff	SND_FORMAT(AFMT_U16_LE, 2, 0),
6364881Scg	0
6464064Scg};
65154024Sariffstatic struct pcmchan_caps ess_playcaps = {6000, 48000, ess_playfmt, 0};
6664064Scg
6764163Snsayer/*
6864163Snsayer * Recording output is byte-swapped
6964163Snsayer */
7064881Scgstatic u_int32_t ess_recfmt[] = {
71193640Sariff	SND_FORMAT(AFMT_U8, 1, 0),
72193640Sariff	SND_FORMAT(AFMT_U8, 2, 0),
73193640Sariff	SND_FORMAT(AFMT_S8, 1, 0),
74193640Sariff	SND_FORMAT(AFMT_S8, 2, 0),
75193640Sariff	SND_FORMAT(AFMT_S16_BE, 1, 0),
76193640Sariff	SND_FORMAT(AFMT_S16_BE, 2, 0),
77193640Sariff	SND_FORMAT(AFMT_U16_BE, 1, 0),
78193640Sariff	SND_FORMAT(AFMT_U16_BE, 2, 0),
7964881Scg	0
8064064Scg};
81154024Sariffstatic struct pcmchan_caps ess_reccaps = {6000, 48000, ess_recfmt, 0};
8264064Scg
8364064Scgstruct ess_info;
8464064Scg
8564064Scgstruct ess_chinfo {
8664064Scg	struct ess_info *parent;
8774763Scg	struct pcm_channel *channel;
8874763Scg	struct snd_dbuf *buffer;
8964064Scg	int dir, hwch, stopping;
9070291Scg	u_int32_t fmt, spd, blksz;
9164064Scg};
9264064Scg
9364064Scgstruct ess_info {
9464064Scg    	struct resource *io, *sb, *vc, *mpu, *gp;	/* I/O address for the board */
9564064Scg    	struct resource *irq;
9665644Scg	void		*ih;
9764064Scg    	bus_dma_tag_t parent_dmat;
9864064Scg
99170032Sariff    	int simplex_dir, type, dmasz[2];
100170032Sariff	unsigned int duplex:1, newspeed:1;
10184658Scg	unsigned int bufsz;
10284658Scg
10364064Scg    	struct ess_chinfo pch, rch;
104154066Sariff#if ESS18XX_MPSAFE == 1
105154066Sariff	struct mtx *lock;
106154066Sariff#endif
10764064Scg};
10864064Scg
109154066Sariff#if ESS18XX_MPSAFE == 1
110154066Sariff#define ess_lock(_ess) snd_mtxlock((_ess)->lock)
111154066Sariff#define ess_unlock(_ess) snd_mtxunlock((_ess)->lock)
112154066Sariff#define ess_lock_assert(_ess) snd_mtxassert((_ess)->lock)
113154066Sariff#else
114154066Sariff#define ess_lock(_ess)
115154066Sariff#define ess_unlock(_ess)
116154066Sariff#define ess_lock_assert(_ess)
117154066Sariff#endif
118154066Sariff
11964064Scgstatic int ess_rd(struct ess_info *sc, int reg);
12064064Scgstatic void ess_wr(struct ess_info *sc, int reg, u_int8_t val);
12164064Scgstatic int ess_dspready(struct ess_info *sc);
12264064Scgstatic int ess_cmd(struct ess_info *sc, u_char val);
12364064Scgstatic int ess_cmd1(struct ess_info *sc, u_char cmd, int val);
12464064Scgstatic int ess_get_byte(struct ess_info *sc);
12564064Scgstatic void ess_setmixer(struct ess_info *sc, u_int port, u_int value);
12664064Scgstatic int ess_getmixer(struct ess_info *sc, u_int port);
12764064Scgstatic int ess_reset_dsp(struct ess_info *sc);
12864064Scg
12964064Scgstatic int ess_write(struct ess_info *sc, u_char reg, int val);
13064064Scgstatic int ess_read(struct ess_info *sc, u_char reg);
13164064Scg
13264064Scgstatic void ess_intr(void *arg);
13364064Scgstatic int ess_setupch(struct ess_info *sc, int ch, int dir, int spd, u_int32_t fmt, int len);
13464064Scgstatic int ess_start(struct ess_chinfo *ch);
13564064Scgstatic int ess_stop(struct ess_chinfo *ch);
13664064Scg
13764064Scgstatic int ess_dmasetup(struct ess_info *sc, int ch, u_int32_t base, u_int16_t cnt, int dir);
13864064Scgstatic int ess_dmapos(struct ess_info *sc, int ch);
13964064Scgstatic int ess_dmatrigger(struct ess_info *sc, int ch, int go);
14064064Scg
14164064Scg/*
14264064Scg * Common code for the midi and pcm functions
14364064Scg *
14464064Scg * ess_cmd write a single byte to the CMD port.
14564064Scg * ess_cmd1 write a CMD + 1 byte arg
14664064Scg * ess_cmd2 write a CMD + 2 byte arg
14764064Scg * ess_get_byte returns a single byte from the DSP data port
14864064Scg *
14964064Scg * ess_write is actually ess_cmd1
15064064Scg * ess_read access ext. regs via ess_cmd(0xc0, reg) followed by ess_get_byte
15164064Scg */
15264064Scg
15364064Scgstatic int
15464064Scgport_rd(struct resource *port, int regno, int size)
15564064Scg{
15664064Scg	bus_space_tag_t st = rman_get_bustag(port);
15764064Scg	bus_space_handle_t sh = rman_get_bushandle(port);
15864064Scg
15964064Scg	switch (size) {
16064064Scg	case 1:
16164064Scg		return bus_space_read_1(st, sh, regno);
16264064Scg	case 2:
16364064Scg		return bus_space_read_2(st, sh, regno);
16464064Scg	case 4:
16564064Scg		return bus_space_read_4(st, sh, regno);
16664064Scg	default:
16764064Scg		return 0xffffffff;
16864064Scg	}
16964064Scg}
17064064Scg
17164064Scgstatic void
17264064Scgport_wr(struct resource *port, int regno, u_int32_t data, int size)
17364064Scg{
17464064Scg	bus_space_tag_t st = rman_get_bustag(port);
17564064Scg	bus_space_handle_t sh = rman_get_bushandle(port);
17664064Scg
17764064Scg	switch (size) {
17864064Scg	case 1:
17964064Scg		bus_space_write_1(st, sh, regno, data);
18064064Scg		break;
18164064Scg	case 2:
18264064Scg		bus_space_write_2(st, sh, regno, data);
18364064Scg		break;
18464064Scg	case 4:
18564064Scg		bus_space_write_4(st, sh, regno, data);
18664064Scg		break;
18764064Scg	}
18864064Scg}
18964064Scg
19064064Scgstatic int
19164064Scgess_rd(struct ess_info *sc, int reg)
19264064Scg{
19364064Scg	return port_rd(sc->sb, reg, 1);
19464064Scg}
19564064Scg
19664064Scgstatic void
19764064Scgess_wr(struct ess_info *sc, int reg, u_int8_t val)
19864064Scg{
19964064Scg	port_wr(sc->sb, reg, val, 1);
20064064Scg}
20164064Scg
20264064Scgstatic int
20364064Scgess_dspready(struct ess_info *sc)
20464064Scg{
20564064Scg	return ((ess_rd(sc, SBDSP_STATUS) & 0x80) == 0);
20664064Scg}
20764064Scg
20864064Scgstatic int
20964064Scgess_dspwr(struct ess_info *sc, u_char val)
21064064Scg{
21164064Scg    	int  i;
21264064Scg
21364064Scg    	for (i = 0; i < 1000; i++) {
21464064Scg		if (ess_dspready(sc)) {
21564064Scg	    		ess_wr(sc, SBDSP_CMD, val);
21664064Scg	    		return 1;
21764064Scg		}
21864064Scg		if (i > 10) DELAY((i > 100)? 1000 : 10);
21964064Scg    	}
22064064Scg    	printf("ess_dspwr(0x%02x) timed out.\n", val);
22164064Scg    	return 0;
22264064Scg}
22364064Scg
22464064Scgstatic int
22564064Scgess_cmd(struct ess_info *sc, u_char val)
22664064Scg{
22764140Snsayer	DEB(printf("ess_cmd: %x\n", val));
22864064Scg    	return ess_dspwr(sc, val);
22964064Scg}
23064064Scg
23164064Scgstatic int
23264064Scgess_cmd1(struct ess_info *sc, u_char cmd, int val)
23364064Scg{
23464140Snsayer    	DEB(printf("ess_cmd1: %x, %x\n", cmd, val));
23564064Scg    	if (ess_dspwr(sc, cmd)) {
23664064Scg		return ess_dspwr(sc, val & 0xff);
23764064Scg    	} else return 0;
23864064Scg}
23964064Scg
24064064Scgstatic void
24164064Scgess_setmixer(struct ess_info *sc, u_int port, u_int value)
24264064Scg{
24364064Scg	DEB(printf("ess_setmixer: reg=%x, val=%x\n", port, value);)
24464064Scg    	ess_wr(sc, SB_MIX_ADDR, (u_char) (port & 0xff)); /* Select register */
24564064Scg    	DELAY(10);
24664064Scg    	ess_wr(sc, SB_MIX_DATA, (u_char) (value & 0xff));
24764064Scg    	DELAY(10);
24864064Scg}
24964064Scg
25064064Scgstatic int
25164064Scgess_getmixer(struct ess_info *sc, u_int port)
25264064Scg{
25364064Scg    	int val;
25464064Scg
25564064Scg    	ess_wr(sc, SB_MIX_ADDR, (u_char) (port & 0xff)); /* Select register */
25664064Scg    	DELAY(10);
25764064Scg    	val = ess_rd(sc, SB_MIX_DATA);
25864064Scg    	DELAY(10);
25964064Scg
26064064Scg    	return val;
26164064Scg}
26264064Scg
26364064Scgstatic int
26464064Scgess_get_byte(struct ess_info *sc)
26564064Scg{
26664064Scg    	int i;
26764064Scg
26864064Scg    	for (i = 1000; i > 0; i--) {
26964447Snsayer		if (ess_rd(sc, 0xc) & 0x40)
27064064Scg			return ess_rd(sc, DSP_READ);
27164064Scg		else
27264064Scg			DELAY(20);
27364064Scg    	}
27464064Scg    	return -1;
27564064Scg}
27664064Scg
27764064Scgstatic int
27864064Scgess_write(struct ess_info *sc, u_char reg, int val)
27964064Scg{
28064064Scg    	return ess_cmd1(sc, reg, val);
28164064Scg}
28264064Scg
28364064Scgstatic int
28464064Scgess_read(struct ess_info *sc, u_char reg)
28564064Scg{
28664064Scg    	return (ess_cmd(sc, 0xc0) && ess_cmd(sc, reg))? ess_get_byte(sc) : -1;
28764064Scg}
28864064Scg
28964064Scgstatic int
29064064Scgess_reset_dsp(struct ess_info *sc)
29164064Scg{
29264140Snsayer	DEB(printf("ess_reset_dsp\n"));
29364064Scg    	ess_wr(sc, SBDSP_RST, 3);
29464064Scg    	DELAY(100);
29564064Scg    	ess_wr(sc, SBDSP_RST, 0);
29664064Scg    	if (ess_get_byte(sc) != 0xAA) {
29764140Snsayer        	DEB(printf("ess_reset_dsp failed\n"));
29864140Snsayer/*
29964064Scg			   rman_get_start(d->io_base)));
30064140Snsayer*/
30164064Scg		return ENXIO;	/* Sorry */
30264064Scg    	}
30364064Scg    	ess_cmd(sc, 0xc6);
30464064Scg    	return 0;
30564064Scg}
30664064Scg
30764064Scgstatic void
30864064Scgess_intr(void *arg)
30964064Scg{
31064064Scg    	struct ess_info *sc = (struct ess_info *)arg;
31164163Snsayer	int src, pirq = 0, rirq = 0;
31264064Scg
313154066Sariff	ess_lock(sc);
31464064Scg	src = 0;
31564064Scg	if (ess_getmixer(sc, 0x7a) & 0x80)
31664064Scg		src |= 2;
31764064Scg	if (ess_rd(sc, 0x0c) & 0x01)
31864064Scg		src |= 1;
31964064Scg
320160127Syongari	if (src == 0) {
321160127Syongari		ess_unlock(sc);
32264846Scg		return;
323160127Syongari	}
32464846Scg
32564163Snsayer	if (sc->duplex) {
32664163Snsayer		pirq = (src & sc->pch.hwch)? 1 : 0;
32764163Snsayer		rirq = (src & sc->rch.hwch)? 1 : 0;
32864163Snsayer	} else {
32964163Snsayer		if (sc->simplex_dir == PCMDIR_PLAY)
33064163Snsayer			pirq = 1;
33164163Snsayer		if (sc->simplex_dir == PCMDIR_REC)
33264163Snsayer			rirq = 1;
33364163Snsayer		if (!pirq && !rirq)
33464163Snsayer			printf("solo: IRQ neither playback nor rec!\n");
33564163Snsayer	}
33664064Scg
33764140Snsayer	DEB(printf("ess_intr: pirq:%d rirq:%d\n",pirq,rirq));
33864140Snsayer
33964064Scg	if (pirq) {
34064064Scg		if (sc->pch.stopping) {
34164064Scg			ess_dmatrigger(sc, sc->pch.hwch, 0);
34264064Scg			sc->pch.stopping = 0;
34364064Scg			if (sc->pch.hwch == 1)
34464064Scg				ess_write(sc, 0xb8, ess_read(sc, 0xb8) & ~0x01);
34564064Scg			else
34664064Scg				ess_setmixer(sc, 0x78, ess_getmixer(sc, 0x78) & ~0x03);
34764064Scg		}
348154066Sariff		ess_unlock(sc);
34964064Scg		chn_intr(sc->pch.channel);
350154066Sariff		ess_lock(sc);
35164064Scg	}
35264064Scg
35364064Scg	if (rirq) {
35464064Scg		if (sc->rch.stopping) {
35564064Scg			ess_dmatrigger(sc, sc->rch.hwch, 0);
35664064Scg			sc->rch.stopping = 0;
35764064Scg			/* XXX: will this stop audio2? */
35864064Scg			ess_write(sc, 0xb8, ess_read(sc, 0xb8) & ~0x01);
35964064Scg		}
360154066Sariff		ess_unlock(sc);
36164064Scg		chn_intr(sc->rch.channel);
362154066Sariff		ess_lock(sc);
36364064Scg	}
36464064Scg
36564064Scg	if (src & 2)
36664064Scg		ess_setmixer(sc, 0x7a, ess_getmixer(sc, 0x7a) & ~0x80);
36764064Scg	if (src & 1)
36864064Scg    		ess_rd(sc, DSP_DATA_AVAIL);
369154066Sariff
370154066Sariff	ess_unlock(sc);
37164064Scg}
37264064Scg
37364064Scg/* utility functions for ESS */
37464064Scgstatic u_int8_t
37564064Scgess_calcspeed8(int *spd)
37664064Scg{
37764064Scg	int speed = *spd;
37864064Scg	u_int32_t t;
37964064Scg
38064064Scg	if (speed > 22000) {
38164064Scg		t = (795500 + speed / 2) / speed;
38264064Scg		speed = (795500 + t / 2) / t;
38364064Scg		t = (256 - t) | 0x80;
38464064Scg	} else {
38564064Scg		t = (397700 + speed / 2) / speed;
38664064Scg		speed = (397700 + t / 2) / t;
38764064Scg		t = 128 - t;
38864064Scg	}
38964064Scg	*spd = speed;
39064064Scg	return t & 0x000000ff;
39164064Scg}
39264064Scg
39364064Scgstatic u_int8_t
39464064Scgess_calcspeed9(int *spd)
39564064Scg{
39664064Scg	int speed, s0, s1, use0;
39764064Scg	u_int8_t t0, t1;
39864064Scg
39964064Scg	/* rate = source / (256 - divisor) */
40064064Scg	/* divisor = 256 - (source / rate) */
40164064Scg	speed = *spd;
40264064Scg	t0 = 128 - (793800 / speed);
40364064Scg	s0 = 793800 / (128 - t0);
40464064Scg
40564064Scg	t1 = 128 - (768000 / speed);
40664064Scg	s1 = 768000 / (128 - t1);
40764064Scg	t1 |= 0x80;
40864064Scg
40964064Scg	use0 = (ABS(speed - s0) < ABS(speed - s1))? 1 : 0;
41064064Scg
41164064Scg	*spd = use0? s0 : s1;
41264064Scg	return use0? t0 : t1;
41364064Scg}
41464064Scg
41564064Scgstatic u_int8_t
41664064Scgess_calcfilter(int spd)
41764064Scg{
41864064Scg	int cutoff;
41964064Scg
42064064Scg	/* cutoff = 7160000 / (256 - divisor) */
42164064Scg	/* divisor = 256 - (7160000 / cutoff) */
42264064Scg	cutoff = (spd * 9 * 82) / 20;
42364064Scg	return (256 - (7160000 / cutoff));
42464064Scg}
42564064Scg
42664064Scgstatic int
42764064Scgess_setupch(struct ess_info *sc, int ch, int dir, int spd, u_int32_t fmt, int len)
42864064Scg{
42964064Scg	int play = (dir == PCMDIR_PLAY)? 1 : 0;
43064064Scg	int b16 = (fmt & AFMT_16BIT)? 1 : 0;
431193640Sariff	int stereo = (AFMT_CHANNEL(fmt) > 1)? 1 : 0;
432193640Sariff	int unsign = (!(fmt & AFMT_SIGNED))? 1 : 0;
43364064Scg	u_int8_t spdval, fmtval;
43464064Scg
43564140Snsayer	DEB(printf("ess_setupch\n"));
43664064Scg	spdval = (sc->newspeed)? ess_calcspeed9(&spd) : ess_calcspeed8(&spd);
43764064Scg
43864163Snsayer	sc->simplex_dir = play ? PCMDIR_PLAY : PCMDIR_REC ;
43964163Snsayer
44064064Scg	if (ch == 1) {
44164064Scg		KASSERT((dir == PCMDIR_PLAY) || (dir == PCMDIR_REC), ("ess_setupch: dir1 bad"));
44264064Scg		len = -len;
44364064Scg		/* transfer length low */
44464064Scg		ess_write(sc, 0xa4, len & 0x00ff);
44564064Scg		/* transfer length high */
44664064Scg		ess_write(sc, 0xa5, (len & 0xff00) >> 8);
44764064Scg		/* autoinit, dma dir */
44864140Snsayer		ess_write(sc, 0xb8, 0x04 | (play? 0x00 : 0x0a));
44964064Scg		/* mono/stereo */
45064064Scg		ess_write(sc, 0xa8, (ess_read(sc, 0xa8) & ~0x03) | (stereo? 0x01 : 0x02));
45164064Scg		/* demand mode, 4 bytes/xfer */
45264064Scg		ess_write(sc, 0xb9, 0x02);
45364064Scg		/* sample rate */
45464064Scg        	ess_write(sc, 0xa1, spdval);
45564064Scg		/* filter cutoff */
45664064Scg		ess_write(sc, 0xa2, ess_calcfilter(spd));
45764064Scg		/* setup dac/adc */
45864064Scg		/*
45964064Scg		if (play)
46064064Scg			ess_write(sc, 0xb6, unsign? 0x80 : 0x00);
46164064Scg		*/
46264064Scg		/* mono, b16: signed, load signal */
46364064Scg		/*
46464064Scg		ess_write(sc, 0xb7, 0x51 | (unsign? 0x00 : 0x20));
46564064Scg		*/
46664064Scg		/* setup fifo */
46764140Snsayer		ess_write(sc, 0xb7, 0x91 | (unsign? 0x00 : 0x20) |
46864064Scg					   (b16? 0x04 : 0x00) |
46964064Scg					   (stereo? 0x08 : 0x40));
47064064Scg		/* irq control */
47164064Scg		ess_write(sc, 0xb1, (ess_read(sc, 0xb1) & 0x0f) | 0x50);
47264064Scg		/* drq control */
47364064Scg		ess_write(sc, 0xb2, (ess_read(sc, 0xb2) & 0x0f) | 0x50);
47464064Scg	} else if (ch == 2) {
47564064Scg		KASSERT(dir == PCMDIR_PLAY, ("ess_setupch: dir2 bad"));
47664064Scg		len >>= 1;
47764064Scg		len = -len;
47864064Scg		/* transfer length low */
47964064Scg		ess_setmixer(sc, 0x74, len & 0x00ff);
48064064Scg		/* transfer length high */
48164064Scg		ess_setmixer(sc, 0x76, (len & 0xff00) >> 8);
48264064Scg		/* autoinit, 4 bytes/req */
48364064Scg		ess_setmixer(sc, 0x78, 0x10);
48464083Snsayer		fmtval = b16 | (stereo << 1) | ((!unsign) << 2);
48564064Scg		/* enable irq, set format */
48664064Scg		ess_setmixer(sc, 0x7a, 0x40 | fmtval);
48764064Scg		if (sc->newspeed) {
48864064Scg			/* sample rate */
48964064Scg			ess_setmixer(sc, 0x70, spdval);
49064064Scg			/* filter cutoff */
49164064Scg			ess_setmixer(sc, 0x72, ess_calcfilter(spd));
49264064Scg		}
49364064Scg
49464064Scg	}
49564064Scg	return 0;
49664064Scg}
49764064Scgstatic int
49864064Scgess_start(struct ess_chinfo *ch)
49964064Scg{
50064064Scg	struct ess_info *sc = ch->parent;
50164064Scg
50264140Snsayer	DEB(printf("ess_start\n"););
50370291Scg	ess_setupch(sc, ch->hwch, ch->dir, ch->spd, ch->fmt, ch->blksz);
50464064Scg	ch->stopping = 0;
50564122Snsayer	if (ch->hwch == 1) {
50664064Scg		ess_write(sc, 0xb8, ess_read(sc, 0xb8) | 0x01);
50764122Snsayer		if (ch->dir == PCMDIR_PLAY) {
50864447Snsayer#if 0
50964122Snsayer			DELAY(100000); /* 100 ms */
51064447Snsayer#endif
51164122Snsayer			ess_cmd(sc, 0xd1);
51264122Snsayer		}
51364122Snsayer	} else
51464064Scg		ess_setmixer(sc, 0x78, ess_getmixer(sc, 0x78) | 0x03);
51564064Scg	return 0;
51664064Scg}
51764064Scg
51864064Scgstatic int
51964064Scgess_stop(struct ess_chinfo *ch)
52064064Scg{
52164064Scg	struct ess_info *sc = ch->parent;
52264064Scg
52364140Snsayer	DEB(printf("ess_stop\n"));
52464064Scg	ch->stopping = 1;
52564064Scg	if (ch->hwch == 1)
52664064Scg		ess_write(sc, 0xb8, ess_read(sc, 0xb8) & ~0x04);
52764064Scg	else
52864064Scg		ess_setmixer(sc, 0x78, ess_getmixer(sc, 0x78) & ~0x10);
52964140Snsayer	DEB(printf("done with stop\n"));
53064064Scg	return 0;
53164064Scg}
53264064Scg
53370134Scg/* -------------------------------------------------------------------- */
53464064Scg/* channel interface for ESS18xx */
53564064Scgstatic void *
53674763Scgesschan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel *c, int dir)
53764064Scg{
53864064Scg	struct ess_info *sc = devinfo;
53964064Scg	struct ess_chinfo *ch = (dir == PCMDIR_PLAY)? &sc->pch : &sc->rch;
54064064Scg
54164140Snsayer	DEB(printf("esschan_init\n"));
54264064Scg	ch->parent = sc;
54364064Scg	ch->channel = c;
54464064Scg	ch->buffer = b;
54565644Scg	ch->dir = dir;
546168847Sariff	if (sndbuf_alloc(ch->buffer, sc->parent_dmat, 0, sc->bufsz) != 0)
54764064Scg		return NULL;
54864064Scg	ch->hwch = 1;
54964064Scg	if ((dir == PCMDIR_PLAY) && (sc->duplex))
55064064Scg		ch->hwch = 2;
55164064Scg	return ch;
55264064Scg}
55364064Scg
55464064Scgstatic int
55570134Scgesschan_setformat(kobj_t obj, void *data, u_int32_t format)
55664064Scg{
55764064Scg	struct ess_chinfo *ch = data;
55864064Scg
55964064Scg	ch->fmt = format;
56064064Scg	return 0;
56164064Scg}
56264064Scg
563193640Sariffstatic u_int32_t
56470134Scgesschan_setspeed(kobj_t obj, void *data, u_int32_t speed)
56564064Scg{
56664064Scg	struct ess_chinfo *ch = data;
56764064Scg	struct ess_info *sc = ch->parent;
56864064Scg
56964064Scg	ch->spd = speed;
57064064Scg	if (sc->newspeed)
57164064Scg		ess_calcspeed9(&ch->spd);
57264064Scg	else
57364064Scg		ess_calcspeed8(&ch->spd);
57464064Scg	return ch->spd;
57564064Scg}
57664064Scg
577193640Sariffstatic u_int32_t
57870134Scgesschan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize)
57964064Scg{
58070291Scg	struct ess_chinfo *ch = data;
58170291Scg
58270291Scg	ch->blksz = blocksize;
58370291Scg	return ch->blksz;
58464064Scg}
58564064Scg
58664064Scgstatic int
58770134Scgesschan_trigger(kobj_t obj, void *data, int go)
58864064Scg{
58964064Scg	struct ess_chinfo *ch = data;
59064064Scg	struct ess_info *sc = ch->parent;
59164064Scg
592170521Sariff	if (!PCMTRIG_COMMON(go))
59364064Scg		return 0;
59464064Scg
595170521Sariff	DEB(printf("esschan_trigger: %d\n",go));
596170521Sariff
597154066Sariff	ess_lock(sc);
59864064Scg	switch (go) {
59964064Scg	case PCMTRIG_START:
600111183Scognet		ess_dmasetup(sc, ch->hwch, sndbuf_getbufaddr(ch->buffer), sndbuf_getsize(ch->buffer), ch->dir);
60164064Scg		ess_dmatrigger(sc, ch->hwch, 1);
60264064Scg		ess_start(ch);
60364064Scg		break;
60464064Scg
60564064Scg	case PCMTRIG_STOP:
60664064Scg	case PCMTRIG_ABORT:
60764064Scg	default:
60864064Scg		ess_stop(ch);
60964064Scg		break;
61064064Scg	}
611154066Sariff	ess_unlock(sc);
61264064Scg	return 0;
61364064Scg}
61464064Scg
615193640Sariffstatic u_int32_t
61670134Scgesschan_getptr(kobj_t obj, void *data)
61764064Scg{
61864064Scg	struct ess_chinfo *ch = data;
61964064Scg	struct ess_info *sc = ch->parent;
620193640Sariff	u_int32_t ret;
62164064Scg
622154066Sariff	ess_lock(sc);
623154066Sariff	ret = ess_dmapos(sc, ch->hwch);
624154066Sariff	ess_unlock(sc);
625154066Sariff	return ret;
62664064Scg}
62764064Scg
62874763Scgstatic struct pcmchan_caps *
62970134Scgesschan_getcaps(kobj_t obj, void *data)
63064064Scg{
63164064Scg	struct ess_chinfo *ch = data;
63264064Scg
63364064Scg	return (ch->dir == PCMDIR_PLAY)? &ess_playcaps : &ess_reccaps;
63464064Scg}
63564064Scg
63670134Scgstatic kobj_method_t esschan_methods[] = {
63770134Scg    	KOBJMETHOD(channel_init,		esschan_init),
63870134Scg    	KOBJMETHOD(channel_setformat,		esschan_setformat),
63970134Scg    	KOBJMETHOD(channel_setspeed,		esschan_setspeed),
64070134Scg    	KOBJMETHOD(channel_setblocksize,	esschan_setblocksize),
64170134Scg    	KOBJMETHOD(channel_trigger,		esschan_trigger),
64270134Scg    	KOBJMETHOD(channel_getptr,		esschan_getptr),
64370134Scg    	KOBJMETHOD(channel_getcaps,		esschan_getcaps),
644193640Sariff	KOBJMETHOD_END
64570134Scg};
64670134ScgCHANNEL_DECLARE(esschan);
64770134Scg
64864064Scg/************************************************************/
64964064Scg
65064064Scgstatic int
65174763Scgessmix_init(struct snd_mixer *m)
65264064Scg{
65364064Scg    	struct ess_info *sc = mix_getdevinfo(m);
65464064Scg
65564064Scg	mix_setrecdevs(m, SOUND_MASK_CD | SOUND_MASK_MIC | SOUND_MASK_LINE |
65664064Scg			  SOUND_MASK_IMIX);
65764064Scg
65864064Scg	mix_setdevs(m, SOUND_MASK_SYNTH | SOUND_MASK_PCM | SOUND_MASK_LINE |
65964064Scg		       SOUND_MASK_MIC | SOUND_MASK_CD | SOUND_MASK_VOLUME |
66064064Scg		       SOUND_MASK_LINE1);
66164064Scg
66264064Scg	ess_setmixer(sc, 0, 0); /* reset */
66364064Scg
66464064Scg	return 0;
66564064Scg}
66664064Scg
66764064Scgstatic int
66874763Scgessmix_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right)
66964064Scg{
67064064Scg    	struct ess_info *sc = mix_getdevinfo(m);
67164064Scg    	int preg = 0, rreg = 0, l, r;
67264064Scg
67364064Scg	l = (left * 15) / 100;
67464064Scg	r = (right * 15) / 100;
67564064Scg	switch (dev) {
67664064Scg	case SOUND_MIXER_SYNTH:
67764064Scg		preg = 0x36;
67864064Scg		rreg = 0x6b;
67964064Scg		break;
68064064Scg
68164064Scg	case SOUND_MIXER_PCM:
68264064Scg		preg = 0x14;
68364064Scg		rreg = 0x7c;
68464064Scg		break;
68564064Scg
68664064Scg	case SOUND_MIXER_LINE:
68764064Scg		preg = 0x3e;
68864064Scg		rreg = 0x6e;
68964064Scg		break;
69064064Scg
69164064Scg	case SOUND_MIXER_MIC:
69264064Scg		preg = 0x1a;
69364064Scg		rreg = 0x68;
69464064Scg		break;
69564064Scg
69664064Scg	case SOUND_MIXER_LINE1:
69764064Scg		preg = 0x3a;
69864064Scg		rreg = 0x6c;
69964064Scg		break;
70064064Scg
70164064Scg	case SOUND_MIXER_CD:
70264064Scg		preg = 0x38;
70364064Scg		rreg = 0x6a;
70464064Scg		break;
70564064Scg
70664064Scg	case SOUND_MIXER_VOLUME:
70764064Scg		l = left? (left * 63) / 100 : 64;
70864064Scg		r = right? (right * 63) / 100 : 64;
70964064Scg		ess_setmixer(sc, 0x60, l);
71064064Scg		ess_setmixer(sc, 0x62, r);
71164064Scg		left = (l == 64)? 0 : (l * 100) / 63;
71264064Scg		right = (r == 64)? 0 : (r * 100) / 63;
71364064Scg    		return left | (right << 8);
71464064Scg	}
71564064Scg
71664064Scg	if (preg)
71764064Scg		ess_setmixer(sc, preg, (l << 4) | r);
71864064Scg	if (rreg)
71964064Scg		ess_setmixer(sc, rreg, (l << 4) | r);
72064064Scg
72164064Scg	left = (l * 100) / 15;
72264064Scg	right = (r * 100) / 15;
72364064Scg
72464064Scg    	return left | (right << 8);
72564064Scg}
72664064Scg
727193640Sariffstatic u_int32_t
72874763Scgessmix_setrecsrc(struct snd_mixer *m, u_int32_t src)
72964064Scg{
73064064Scg    	struct ess_info *sc = mix_getdevinfo(m);
73164064Scg    	u_char recdev;
73264064Scg
73364064Scg    	switch (src) {
73464064Scg	case SOUND_MASK_CD:
73564064Scg		recdev = 0x02;
73664064Scg		break;
73764064Scg
73864064Scg	case SOUND_MASK_LINE:
73964064Scg		recdev = 0x06;
74064064Scg		break;
74164064Scg
74264064Scg	case SOUND_MASK_IMIX:
74364064Scg		recdev = 0x05;
74464064Scg		break;
74564064Scg
74664064Scg	case SOUND_MASK_MIC:
74764064Scg	default:
74864064Scg		recdev = 0x00;
74964064Scg		src = SOUND_MASK_MIC;
75064064Scg		break;
75164064Scg	}
75264064Scg
75364064Scg	ess_setmixer(sc, 0x1c, recdev);
75464064Scg
75564064Scg	return src;
75664064Scg}
75764064Scg
75870134Scgstatic kobj_method_t solomixer_methods[] = {
75970134Scg    	KOBJMETHOD(mixer_init,		essmix_init),
76070134Scg    	KOBJMETHOD(mixer_set,		essmix_set),
76170134Scg    	KOBJMETHOD(mixer_setrecsrc,	essmix_setrecsrc),
762193640Sariff	KOBJMETHOD_END
76370134Scg};
76470134ScgMIXER_DECLARE(solomixer);
76570134Scg
76664064Scg/************************************************************/
76764064Scg
76864064Scgstatic int
76964064Scgess_dmasetup(struct ess_info *sc, int ch, u_int32_t base, u_int16_t cnt, int dir)
77064064Scg{
77164064Scg	KASSERT(ch == 1 || ch == 2, ("bad ch"));
77264064Scg	sc->dmasz[ch - 1] = cnt;
77364064Scg	if (ch == 1) {
77464122Snsayer		port_wr(sc->vc, 0x8, 0xc4, 1); /* command */
77564064Scg		port_wr(sc->vc, 0xd, 0xff, 1); /* reset */
77664064Scg		port_wr(sc->vc, 0xf, 0x01, 1); /* mask */
77764122Snsayer		port_wr(sc->vc, 0xb, dir == PCMDIR_PLAY? 0x58 : 0x54, 1); /* mode */
77864064Scg		port_wr(sc->vc, 0x0, base, 4);
77964447Snsayer		port_wr(sc->vc, 0x4, cnt - 1, 2);
78064064Scg
78164064Scg	} else if (ch == 2) {
78264064Scg		port_wr(sc->io, 0x6, 0x08, 1); /* autoinit */
78364064Scg		port_wr(sc->io, 0x0, base, 4);
78464064Scg		port_wr(sc->io, 0x4, cnt, 2);
78564064Scg	}
78664064Scg	return 0;
78764064Scg}
78864064Scg
78964064Scgstatic int
79064064Scgess_dmapos(struct ess_info *sc, int ch)
79164064Scg{
79264623Snsayer	int p = 0, i = 0, j = 0;
79364064Scg
79464064Scg	KASSERT(ch == 1 || ch == 2, ("bad ch"));
79564447Snsayer	if (ch == 1) {
79664447Snsayer
79764447Snsayer/*
79864447Snsayer * During recording, this register is known to give back
79964447Snsayer * garbage if it's not quiescent while being read. That's
80064623Snsayer * why we spl, stop the DMA, and try over and over until
80164623Snsayer * adjacent reads are "close", in the right order and not
80264623Snsayer * bigger than is otherwise possible.
80364447Snsayer */
80464447Snsayer		ess_dmatrigger(sc, ch, 0);
80564623Snsayer		DELAY(20);
80664623Snsayer		do {
80764623Snsayer			DELAY(10);
80864623Snsayer			if (j > 1)
80964623Snsayer				printf("DMA count reg bogus: %04x & %04x\n",
81064623Snsayer					i, p);
81164623Snsayer			i = port_rd(sc->vc, 0x4, 2) + 1;
81264623Snsayer			p = port_rd(sc->vc, 0x4, 2) + 1;
81370291Scg		} while ((p > sc->dmasz[ch - 1] || i < p || (p - i) > 0x8) && j++ < 1000);
81464447Snsayer		ess_dmatrigger(sc, ch, 1);
81564447Snsayer	}
81664064Scg	else if (ch == 2)
81764064Scg		p = port_rd(sc->io, 0x4, 2);
81864064Scg	return sc->dmasz[ch - 1] - p;
81964064Scg}
82064064Scg
82164064Scgstatic int
82264064Scgess_dmatrigger(struct ess_info *sc, int ch, int go)
82364064Scg{
82464064Scg	KASSERT(ch == 1 || ch == 2, ("bad ch"));
82564064Scg	if (ch == 1)
82664119Snsayer		port_wr(sc->vc, 0xf, go? 0x00 : 0x01, 1); /* mask */
82764064Scg	else if (ch == 2)
82864064Scg		port_wr(sc->io, 0x6, 0x08 | (go? 0x02 : 0x00), 1); /* autoinit */
82964064Scg	return 0;
83064064Scg}
83164064Scg
83264064Scgstatic void
83364064Scgess_release_resources(struct ess_info *sc, device_t dev)
83464064Scg{
83564064Scg    	if (sc->irq) {
83665644Scg		if (sc->ih)
83765644Scg			bus_teardown_intr(dev, sc->irq, sc->ih);
83864064Scg		bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq);
83964064Scg		sc->irq = 0;
84064064Scg    	}
84164064Scg    	if (sc->io) {
842119690Sjhb		bus_release_resource(dev, SYS_RES_IOPORT, PCIR_BAR(0), sc->io);
84364064Scg		sc->io = 0;
84464064Scg    	}
84564064Scg
84664064Scg    	if (sc->sb) {
847119690Sjhb		bus_release_resource(dev, SYS_RES_IOPORT, PCIR_BAR(1), sc->sb);
84864064Scg		sc->sb = 0;
84964064Scg    	}
85064064Scg
85164064Scg    	if (sc->vc) {
852119690Sjhb		bus_release_resource(dev, SYS_RES_IOPORT, PCIR_BAR(2), sc->vc);
85364064Scg		sc->vc = 0;
85464064Scg    	}
85564064Scg
85664064Scg    	if (sc->mpu) {
857119690Sjhb		bus_release_resource(dev, SYS_RES_IOPORT, PCIR_BAR(3), sc->mpu);
85864064Scg		sc->mpu = 0;
85964064Scg    	}
86064064Scg
86164064Scg    	if (sc->gp) {
862119690Sjhb		bus_release_resource(dev, SYS_RES_IOPORT, PCIR_BAR(4), sc->gp);
86364064Scg		sc->gp = 0;
86464064Scg    	}
86564064Scg
86665644Scg	if (sc->parent_dmat) {
86765644Scg		bus_dma_tag_destroy(sc->parent_dmat);
86865644Scg		sc->parent_dmat = 0;
86965644Scg    	}
87065644Scg
871154066Sariff#if ESS18XX_MPSAFE == 1
872154066Sariff	if (sc->lock) {
873154066Sariff		snd_mtxfree(sc->lock);
874154066Sariff		sc->lock = NULL;
875154066Sariff	}
876154066Sariff#endif
877154066Sariff
87864064Scg    	free(sc, M_DEVBUF);
87964064Scg}
88064064Scg
88164064Scgstatic int
88264064Scgess_alloc_resources(struct ess_info *sc, device_t dev)
88364064Scg{
88464064Scg	int rid;
88564064Scg
886119690Sjhb	rid = PCIR_BAR(0);
887127135Snjl    	sc->io = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &rid, RF_ACTIVE);
88864064Scg
889119690Sjhb	rid = PCIR_BAR(1);
890127135Snjl    	sc->sb = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &rid, RF_ACTIVE);
89164064Scg
892119690Sjhb	rid = PCIR_BAR(2);
893127135Snjl    	sc->vc = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &rid, RF_ACTIVE);
89464064Scg
895119690Sjhb	rid = PCIR_BAR(3);
896127135Snjl    	sc->mpu = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &rid, RF_ACTIVE);
89764064Scg
898119690Sjhb	rid = PCIR_BAR(4);
899127135Snjl    	sc->gp = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &rid, RF_ACTIVE);
90064064Scg
90164064Scg	rid = 0;
902127135Snjl	sc->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
903127135Snjl		RF_ACTIVE | RF_SHAREABLE);
90464064Scg
905154066Sariff#if ESS18XX_MPSAFE == 1
906167608Sariff	sc->lock = snd_mtxcreate(device_get_nameunit(dev), "snd_solo softc");
907154066Sariff
908154066Sariff	return (sc->irq && sc->io && sc->sb && sc->vc &&
909154066Sariff				sc->mpu && sc->gp && sc->lock)? 0 : ENXIO;
910154066Sariff#else
91164064Scg	return (sc->irq && sc->io && sc->sb && sc->vc && sc->mpu && sc->gp)? 0 : ENXIO;
912154066Sariff#endif
91364064Scg}
91464064Scg
91564064Scgstatic int
91664064Scgess_probe(device_t dev)
91764064Scg{
91864064Scg	char *s = NULL;
91964064Scg	u_int32_t subdev;
92064064Scg
92164064Scg	subdev = (pci_get_subdevice(dev) << 16) | pci_get_subvendor(dev);
92264064Scg	switch (pci_get_devid(dev)) {
92364064Scg	case 0x1969125d:
92464064Scg		if (subdev == 0x8888125d)
92564064Scg			s = "ESS Solo-1E";
92664064Scg		else if (subdev == 0x1818125d)
92764064Scg			s = "ESS Solo-1";
92864064Scg		else
92964064Scg			s = "ESS Solo-1 (unknown vendor)";
93064064Scg		break;
93164064Scg	}
93264064Scg
93364064Scg	if (s)
93464064Scg		device_set_desc(dev, s);
935142890Simp	return s ? BUS_PROBE_DEFAULT : ENXIO;
93664064Scg}
93764064Scg
938102390Sorion#define ESS_PCI_LEGACYCONTROL       0x40
939102390Sorion#define ESS_PCI_CONFIG              0x50
940102390Sorion#define ESS_PCI_DDMACONTROL      	0x60
94164064Scg
942102390Sorionstatic int
943102390Sorioness_suspend(device_t dev)
944102390Sorion{
945102390Sorion  return 0;
946102390Sorion}
947102390Sorion
948102390Sorionstatic int
949102390Sorioness_resume(device_t dev)
950102390Sorion{
951102390Sorion	uint16_t ddma;
952102390Sorion	struct ess_info *sc = pcm_getdevinfo(dev);
953102390Sorion
954154066Sariff	ess_lock(sc);
955102390Sorion	ddma = rman_get_start(sc->vc) | 1;
956102390Sorion	pci_write_config(dev, ESS_PCI_LEGACYCONTROL, 0x805f, 2);
957102390Sorion	pci_write_config(dev, ESS_PCI_DDMACONTROL, ddma, 2);
958102390Sorion	pci_write_config(dev, ESS_PCI_CONFIG, 0, 2);
959102390Sorion
960154066Sariff    	if (ess_reset_dsp(sc)) {
961154066Sariff		ess_unlock(sc);
962102390Sorion		goto no;
963154066Sariff	}
964154066Sariff	ess_unlock(sc);
965102390Sorion    	if (mixer_reinit(dev))
966102390Sorion		goto no;
967154066Sariff	ess_lock(sc);
968102390Sorion	if (sc->newspeed)
969102390Sorion		ess_setmixer(sc, 0x71, 0x2a);
970102390Sorion
971102390Sorion	port_wr(sc->io, 0x7, 0xb0, 1); /* enable irqs */
972154066Sariff	ess_unlock(sc);
973102390Sorion
974102390Sorion	return 0;
975102390Sorion no:
976102390Sorion	return EIO;
977102390Sorion}
978102390Sorion
97964064Scgstatic int
98064064Scgess_attach(device_t dev)
98164064Scg{
98264064Scg    	struct ess_info *sc;
98364064Scg    	char status[SND_STATUSLEN];
98464064Scg	u_int16_t ddma;
98564064Scg
986170873Sariff	sc = malloc(sizeof(*sc), M_DEVBUF, M_WAITOK | M_ZERO);
987254306Sscottl	pci_enable_busmaster(dev);
98864064Scg
98964064Scg    	if (ess_alloc_resources(sc, dev))
99064064Scg		goto no;
99164064Scg
99284658Scg	sc->bufsz = pcm_getbuffersize(dev, 4096, SOLO_DEFAULT_BUFSZ, 65536);
99384658Scg
99464064Scg	ddma = rman_get_start(sc->vc) | 1;
995102390Sorion	pci_write_config(dev, ESS_PCI_LEGACYCONTROL, 0x805f, 2);
996102390Sorion	pci_write_config(dev, ESS_PCI_DDMACONTROL, ddma, 2);
997102390Sorion	pci_write_config(dev, ESS_PCI_CONFIG, 0, 2);
99864064Scg
99964064Scg	port_wr(sc->io, 0x7, 0xb0, 1); /* enable irqs */
100064163Snsayer#ifdef ESS18XX_DUPLEX
100164064Scg	sc->duplex = 1;
100264163Snsayer#else
100364163Snsayer	sc->duplex = 0;
100464163Snsayer#endif
100564163Snsayer
100664163Snsayer#ifdef ESS18XX_NEWSPEED
100764064Scg	sc->newspeed = 1;
100864163Snsayer#else
100964163Snsayer	sc->newspeed = 0;
101064163Snsayer#endif
1011154066Sariff	if (snd_setup_intr(dev, sc->irq,
1012154066Sariff#if ESS18XX_MPSAFE == 1
1013154066Sariff			INTR_MPSAFE
1014154066Sariff#else
1015154066Sariff			0
1016154066Sariff#endif
1017154066Sariff			, ess_intr, sc, &sc->ih)) {
1018154066Sariff		device_printf(dev, "unable to map interrupt\n");
1019154066Sariff		goto no;
1020154066Sariff	}
102164064Scg
102264064Scg    	if (!sc->duplex)
102364064Scg		pcm_setflags(dev, pcm_getflags(dev) | SD_F_SIMPLEX);
102464064Scg
1025154066Sariff#if 0
1026166904Snetchild    	if (bus_dma_tag_create(/*parent*/bus_get_dma_tag(dev), /*alignment*/65536, /*boundary*/0,
1027154066Sariff#endif
1028166904Snetchild    	if (bus_dma_tag_create(/*parent*/bus_get_dma_tag(dev), /*alignment*/2, /*boundary*/0,
102964064Scg			/*lowaddr*/BUS_SPACE_MAXADDR_24BIT,
103064064Scg			/*highaddr*/BUS_SPACE_MAXADDR,
103164064Scg			/*filter*/NULL, /*filterarg*/NULL,
103284658Scg			/*maxsize*/sc->bufsz, /*nsegments*/1,
103364064Scg			/*maxsegz*/0x3ffff,
1034154066Sariff			/*flags*/0,
1035154066Sariff#if ESS18XX_MPSAFE == 1
1036154066Sariff			/*lockfunc*/NULL, /*lockarg*/NULL,
1037154066Sariff#else
1038154066Sariff			/*lockfunc*/busdma_lock_mutex, /*lockarg*/&Giant,
1039154066Sariff#endif
1040154066Sariff			&sc->parent_dmat) != 0) {
104164064Scg		device_printf(dev, "unable to create dma tag\n");
104264064Scg		goto no;
104364064Scg    	}
104464064Scg
1045154066Sariff    	if (ess_reset_dsp(sc))
1046154066Sariff		goto no;
1047154066Sariff
1048154066Sariff	if (sc->newspeed)
1049154066Sariff		ess_setmixer(sc, 0x71, 0x2a);
1050154066Sariff
1051154066Sariff    	if (mixer_init(dev, &solomixer_class, sc))
1052154066Sariff		goto no;
1053154066Sariff
1054126695Smatk    	snprintf(status, SND_STATUSLEN, "at io 0x%lx,0x%lx,0x%lx irq %ld %s",
105564064Scg    	     	rman_get_start(sc->io), rman_get_start(sc->sb), rman_get_start(sc->vc),
1056126695Smatk		rman_get_start(sc->irq),PCM_KLDSTRING(snd_solo));
105764064Scg
105864064Scg    	if (pcm_register(dev, sc, 1, 1))
105964064Scg		goto no;
106070134Scg      	pcm_addchan(dev, PCMDIR_REC, &esschan_class, sc);
106170134Scg	pcm_addchan(dev, PCMDIR_PLAY, &esschan_class, sc);
106264064Scg	pcm_setstatus(dev, status);
106364064Scg
106464064Scg    	return 0;
106564064Scg
106664064Scgno:
106764064Scg    	ess_release_resources(sc, dev);
106864064Scg    	return ENXIO;
106964064Scg}
107064064Scg
107165644Scgstatic int
107265644Scgess_detach(device_t dev)
107365644Scg{
107465644Scg	int r;
107566012Scg	struct ess_info *sc;
107665644Scg
107765644Scg	r = pcm_unregister(dev);
107865644Scg	if (r)
107965644Scg		return r;
108065644Scg
108165644Scg	sc = pcm_getdevinfo(dev);
108265644Scg    	ess_release_resources(sc, dev);
108365644Scg	return 0;
108465644Scg}
108565644Scg
108664064Scgstatic device_method_t ess_methods[] = {
108764064Scg	/* Device interface */
108864064Scg	DEVMETHOD(device_probe,		ess_probe),
108964064Scg	DEVMETHOD(device_attach,	ess_attach),
109065644Scg	DEVMETHOD(device_detach,	ess_detach),
1091102390Sorion	DEVMETHOD(device_resume,	ess_resume),
1092102390Sorion	DEVMETHOD(device_suspend,	ess_suspend),
109364064Scg
109464064Scg	{ 0, 0 }
109564064Scg};
109664064Scg
109764064Scgstatic driver_t ess_driver = {
109864064Scg	"pcm",
109964064Scg	ess_methods,
110082180Scg	PCM_SOFTC_SIZE,
110164064Scg};
110264064Scg
110364064ScgDRIVER_MODULE(snd_solo, pci, ess_driver, pcm_devclass, 0, 0);
1104132236StanimuraMODULE_DEPEND(snd_solo, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER);
110564064ScgMODULE_VERSION(snd_solo, 1);
110664064Scg
110764064Scg
110864064Scg
1109