xref: /freebsd/sys/dev/sound/pci/emu10k1.c (revision 3ac1248373900d6bd6b3a4452404aee819d2bab8)
1098ca2bdSWarner Losh /*-
22511c244SDavid E. O'Brien  * Copyright (c) 2004 David O'Brien <obrien@FreeBSD.org>
321f1e37cSDavid E. O'Brien  * Copyright (c) 2003 Orlando Bassotto <orlando.bassotto@ieo-research.it>
43f225978SCameron Grant  * Copyright (c) 1999 Cameron Grant <cg@freebsd.org>
5c067afaaSCameron Grant  * All rights reserved.
6c067afaaSCameron Grant  *
7c067afaaSCameron Grant  * Redistribution and use in source and binary forms, with or without
8c067afaaSCameron Grant  * modification, are permitted provided that the following conditions
9c067afaaSCameron Grant  * are met:
10c067afaaSCameron Grant  * 1. Redistributions of source code must retain the above copyright
11c067afaaSCameron Grant  *    notice, this list of conditions and the following disclaimer.
12c067afaaSCameron Grant  * 2. Redistributions in binary form must reproduce the above copyright
13c067afaaSCameron Grant  *    notice, this list of conditions and the following disclaimer in the
14c067afaaSCameron Grant  *    documentation and/or other materials provided with the distribution.
15c067afaaSCameron Grant  *
16c067afaaSCameron Grant  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17c067afaaSCameron Grant  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18c067afaaSCameron Grant  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19c067afaaSCameron Grant  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20c067afaaSCameron Grant  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21c067afaaSCameron Grant  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22c067afaaSCameron Grant  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23c067afaaSCameron Grant  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHERIN CONTRACT, STRICT
24c067afaaSCameron Grant  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25c067afaaSCameron Grant  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26c067afaaSCameron Grant  * SUCH DAMAGE.
27c067afaaSCameron Grant  */
28c067afaaSCameron Grant 
2990da2b28SAriff Abdullah #ifdef HAVE_KERNEL_OPTION_HEADERS
3090da2b28SAriff Abdullah #include "opt_snd.h"
3190da2b28SAriff Abdullah #endif
3290da2b28SAriff Abdullah 
33c067afaaSCameron Grant #include <dev/sound/pcm/sound.h>
34c067afaaSCameron Grant #include <dev/sound/pcm/ac97.h>
35c7e0c9dbSPedro F. Giffuni #include <dev/sound/pci/emuxkireg.h>
36c067afaaSCameron Grant 
3790cf0136SWarner Losh #include <dev/pci/pcireg.h>
3890cf0136SWarner Losh #include <dev/pci/pcivar.h>
39c067afaaSCameron Grant #include <sys/queue.h>
40c067afaaSCameron Grant 
41f510d240SAlexander Leidinger #include <dev/sound/midi/mpu401.h>
42f510d240SAlexander Leidinger #include "mpufoi_if.h"
43f510d240SAlexander Leidinger 
4467b1dce3SCameron Grant SND_DECLARE_FILE("$FreeBSD$");
4567b1dce3SCameron Grant 
46c067afaaSCameron Grant /* -------------------------------------------------------------------- */
47c067afaaSCameron Grant 
482511c244SDavid E. O'Brien #define	NUM_G		64	/* use all channels */
492511c244SDavid E. O'Brien #define	WAVEOUT_MAXBUFSIZE 32768
502511c244SDavid E. O'Brien #define	EMUPAGESIZE	4096	/* don't change */
5121f1e37cSDavid E. O'Brien #define	EMUMAXPAGES	(WAVEOUT_MAXBUFSIZE * NUM_G / EMUPAGESIZE)
5221f1e37cSDavid E. O'Brien #define	EMU10K1_PCI_ID	0x00021102	/* 1102 => Creative Labs Vendor ID */
53a791cfeeSCameron Grant #define	EMU10K2_PCI_ID	0x00041102
54d2b677bbSWarner Losh #define	EMU10K3_PCI_ID	0x00081102
55a791cfeeSCameron Grant #define	EMU_DEFAULT_BUFSZ	4096
5621f1e37cSDavid E. O'Brien #define EMU_MAX_CHANS	8
5770776a9cSCameron Grant #define	EMU_CHANS	4
5821f1e37cSDavid E. O'Brien 
5921f1e37cSDavid E. O'Brien #define	MAXREQVOICES	8
6021f1e37cSDavid E. O'Brien #define	RESERVED	0
6121f1e37cSDavid E. O'Brien #define	NUM_MIDI	16
6221f1e37cSDavid E. O'Brien #define	NUM_FXSENDS	4
6321f1e37cSDavid E. O'Brien 
6421f1e37cSDavid E. O'Brien #define	TMEMSIZE	256*1024
6521f1e37cSDavid E. O'Brien #define	TMEMSIZEREG	4
6621f1e37cSDavid E. O'Brien 
6721f1e37cSDavid E. O'Brien #define	ENABLE		0xffffffff
6821f1e37cSDavid E. O'Brien #define	DISABLE		0x00000000
69c7e0c9dbSPedro F. Giffuni #define	ENV_ON		EMU_CHAN_DCYSUSV_CHANNELENABLE_MASK
702511c244SDavid E. O'Brien #define	ENV_OFF		0x00	/* XXX: should this be 1? */
71c067afaaSCameron Grant 
72c7e0c9dbSPedro F. Giffuni #define	EMU_A_IOCFG_GPOUT_A	0x40
73c7e0c9dbSPedro F. Giffuni #define	EMU_A_IOCFG_GPOUT_D	0x04
74c7e0c9dbSPedro F. Giffuni #define	EMU_A_IOCFG_GPOUT_AD (EMU_A_IOCFG_GPOUT_A|EMU_A_IOCFG_GPOUT_D)  /* EMU_A_IOCFG_GPOUT0 */
75c7e0c9dbSPedro F. Giffuni 
76c7e0c9dbSPedro F. Giffuni #define	EMU_HCFG_GPOUT1		0x00000800
77c7e0c9dbSPedro F. Giffuni 
78c7e0c9dbSPedro F. Giffuni /* instruction set */
79c7e0c9dbSPedro F. Giffuni #define iACC3	 0x06
80c7e0c9dbSPedro F. Giffuni #define iMACINT0 0x04
81c7e0c9dbSPedro F. Giffuni #define iINTERP  0x0e
82c7e0c9dbSPedro F. Giffuni 
83c7e0c9dbSPedro F. Giffuni #define C_00000000	0x40
84c7e0c9dbSPedro F. Giffuni #define C_00000001	0x41
85c7e0c9dbSPedro F. Giffuni #define C_00000004	0x44
86c7e0c9dbSPedro F. Giffuni #define C_40000000	0x4d
87c7e0c9dbSPedro F. Giffuni /* Audigy constants */
88c7e0c9dbSPedro F. Giffuni #define A_C_00000000	0xc0
89c7e0c9dbSPedro F. Giffuni #define A_C_40000000	0xcd
90c7e0c9dbSPedro F. Giffuni 
91c7e0c9dbSPedro F. Giffuni /* GPRs */
92c7e0c9dbSPedro F. Giffuni #define FXBUS(x)	(0x00 + (x))
93c7e0c9dbSPedro F. Giffuni #define EXTIN(x)	(0x10 + (x))
94c7e0c9dbSPedro F. Giffuni #define EXTOUT(x)	(0x20 + (x))
95c7e0c9dbSPedro F. Giffuni 
96c7e0c9dbSPedro F. Giffuni #define GPR(x)		(EMU_FXGPREGBASE + (x))
97c7e0c9dbSPedro F. Giffuni #define A_EXTIN(x)	(0x40 + (x))
98c7e0c9dbSPedro F. Giffuni #define A_FXBUS(x)	(0x00 + (x))
99c7e0c9dbSPedro F. Giffuni #define A_EXTOUT(x)	(0x60 + (x))
100c7e0c9dbSPedro F. Giffuni #define A_GPR(x)	(EMU_A_FXGPREGBASE + (x))
101c7e0c9dbSPedro F. Giffuni 
102c7e0c9dbSPedro F. Giffuni /* FX buses */
103c7e0c9dbSPedro F. Giffuni #define FXBUS_PCM_LEFT		0x00
104c7e0c9dbSPedro F. Giffuni #define FXBUS_PCM_RIGHT		0x01
105c7e0c9dbSPedro F. Giffuni #define FXBUS_MIDI_LEFT		0x04
106c7e0c9dbSPedro F. Giffuni #define FXBUS_MIDI_RIGHT	0x05
107c7e0c9dbSPedro F. Giffuni #define FXBUS_MIDI_REVERB	0x0c
108c7e0c9dbSPedro F. Giffuni #define FXBUS_MIDI_CHORUS	0x0d
109c7e0c9dbSPedro F. Giffuni 
110c7e0c9dbSPedro F. Giffuni /* Inputs */
111c7e0c9dbSPedro F. Giffuni #define EXTIN_AC97_L		0x00
112c7e0c9dbSPedro F. Giffuni #define EXTIN_AC97_R		0x01
113c7e0c9dbSPedro F. Giffuni #define EXTIN_SPDIF_CD_L	0x02
114c7e0c9dbSPedro F. Giffuni #define EXTIN_SPDIF_CD_R	0x03
115c7e0c9dbSPedro F. Giffuni #define EXTIN_TOSLINK_L		0x06
116c7e0c9dbSPedro F. Giffuni #define EXTIN_TOSLINK_R		0x07
117c7e0c9dbSPedro F. Giffuni #define EXTIN_COAX_SPDIF_L	0x0a
118c7e0c9dbSPedro F. Giffuni #define EXTIN_COAX_SPDIF_R	0x0b
119c7e0c9dbSPedro F. Giffuni /* Audigy Inputs */
120c7e0c9dbSPedro F. Giffuni #define A_EXTIN_AC97_L		0x00
121c7e0c9dbSPedro F. Giffuni #define A_EXTIN_AC97_R		0x01
122c7e0c9dbSPedro F. Giffuni 
123c7e0c9dbSPedro F. Giffuni /* Outputs */
124c7e0c9dbSPedro F. Giffuni #define EXTOUT_AC97_L	   0x00
125c7e0c9dbSPedro F. Giffuni #define EXTOUT_AC97_R	   0x01
126c7e0c9dbSPedro F. Giffuni #define EXTOUT_TOSLINK_L   0x02
127c7e0c9dbSPedro F. Giffuni #define EXTOUT_TOSLINK_R   0x03
128c7e0c9dbSPedro F. Giffuni #define EXTOUT_AC97_CENTER 0x04
129c7e0c9dbSPedro F. Giffuni #define EXTOUT_AC97_LFE	   0x05
130c7e0c9dbSPedro F. Giffuni #define EXTOUT_HEADPHONE_L 0x06
131c7e0c9dbSPedro F. Giffuni #define EXTOUT_HEADPHONE_R 0x07
132c7e0c9dbSPedro F. Giffuni #define EXTOUT_REAR_L	   0x08
133c7e0c9dbSPedro F. Giffuni #define EXTOUT_REAR_R	   0x09
134c7e0c9dbSPedro F. Giffuni #define EXTOUT_ADC_CAP_L   0x0a
135c7e0c9dbSPedro F. Giffuni #define EXTOUT_ADC_CAP_R   0x0b
136c7e0c9dbSPedro F. Giffuni #define EXTOUT_ACENTER	   0x11
137c7e0c9dbSPedro F. Giffuni #define EXTOUT_ALFE	   0x12
138c7e0c9dbSPedro F. Giffuni /* Audigy Outputs */
139c7e0c9dbSPedro F. Giffuni #define A_EXTOUT_FRONT_L	0x00
140c7e0c9dbSPedro F. Giffuni #define A_EXTOUT_FRONT_R	0x01
141c7e0c9dbSPedro F. Giffuni #define A_EXTOUT_CENTER		0x02
142c7e0c9dbSPedro F. Giffuni #define A_EXTOUT_LFE		0x03
143c7e0c9dbSPedro F. Giffuni #define A_EXTOUT_HEADPHONE_L	0x04
144c7e0c9dbSPedro F. Giffuni #define A_EXTOUT_HEADPHONE_R	0x05
145c7e0c9dbSPedro F. Giffuni #define A_EXTOUT_REAR_L		0x06
146c7e0c9dbSPedro F. Giffuni #define A_EXTOUT_REAR_R		0x07
147c7e0c9dbSPedro F. Giffuni #define A_EXTOUT_AFRONT_L	0x08
148c7e0c9dbSPedro F. Giffuni #define A_EXTOUT_AFRONT_R	0x09
149c7e0c9dbSPedro F. Giffuni #define A_EXTOUT_ACENTER	0x0a
150c7e0c9dbSPedro F. Giffuni #define A_EXTOUT_ALFE		0x0b
151c7e0c9dbSPedro F. Giffuni #define A_EXTOUT_AREAR_L	0x0e
152c7e0c9dbSPedro F. Giffuni #define A_EXTOUT_AREAR_R	0x0f
153c7e0c9dbSPedro F. Giffuni #define A_EXTOUT_AC97_L		0x10
154c7e0c9dbSPedro F. Giffuni #define A_EXTOUT_AC97_R		0x11
155c7e0c9dbSPedro F. Giffuni #define A_EXTOUT_ADC_CAP_L	0x16
156c7e0c9dbSPedro F. Giffuni #define A_EXTOUT_ADC_CAP_R	0x17
15721f1e37cSDavid E. O'Brien 
158c067afaaSCameron Grant struct emu_memblk {
159e3975643SJake Burkholder 	SLIST_ENTRY(emu_memblk) link;
160c067afaaSCameron Grant 	void *buf;
16138cc9942SOlivier Houchard 	bus_addr_t buf_addr;
162c067afaaSCameron Grant 	u_int32_t pte_start, pte_size;
163c067afaaSCameron Grant };
164c067afaaSCameron Grant 
165c067afaaSCameron Grant struct emu_mem {
16621f1e37cSDavid E. O'Brien 	u_int8_t bmap[EMUMAXPAGES / 8];
16797b3c9d8SCameron Grant 	u_int32_t *ptb_pages;
168c067afaaSCameron Grant 	void *silent_page;
16938cc9942SOlivier Houchard 	bus_addr_t silent_page_addr;
17038cc9942SOlivier Houchard 	bus_addr_t ptb_pages_addr;
171e3975643SJake Burkholder 	SLIST_HEAD(, emu_memblk) blocks;
172c067afaaSCameron Grant };
173c067afaaSCameron Grant 
174c067afaaSCameron Grant struct emu_voice {
175c067afaaSCameron Grant 	int vnum;
17679462204SAriff Abdullah 	unsigned int b16:1, stereo:1, busy:1, running:1, ismaster:1;
177c067afaaSCameron Grant 	int speed;
1782d3ce9d5SCameron Grant 	int start, end, vol;
17921f1e37cSDavid E. O'Brien 	int fxrt1;	/* FX routing */
18021f1e37cSDavid E. O'Brien 	int fxrt2;	/* FX routing (only for audigy) */
18197b3c9d8SCameron Grant 	u_int32_t buf;
1826c1146c0SCameron Grant 	struct emu_voice *slave;
18366ef8af5SCameron Grant 	struct pcm_channel *channel;
184c067afaaSCameron Grant };
185c067afaaSCameron Grant 
186c067afaaSCameron Grant struct sc_info;
187c067afaaSCameron Grant 
188c067afaaSCameron Grant /* channel registers */
18970776a9cSCameron Grant struct sc_pchinfo {
190350a5fafSCameron Grant 	int spd, fmt, blksz, run;
1916c1146c0SCameron Grant 	struct emu_voice *master, *slave;
19266ef8af5SCameron Grant 	struct snd_dbuf *buffer;
19366ef8af5SCameron Grant 	struct pcm_channel *channel;
194c067afaaSCameron Grant 	struct sc_info *parent;
195c067afaaSCameron Grant };
196c067afaaSCameron Grant 
19770776a9cSCameron Grant struct sc_rchinfo {
198350a5fafSCameron Grant 	int spd, fmt, run, blksz, num;
19970776a9cSCameron Grant 	u_int32_t idxreg, basereg, sizereg, setupreg, irqmask;
20066ef8af5SCameron Grant 	struct snd_dbuf *buffer;
20166ef8af5SCameron Grant 	struct pcm_channel *channel;
20270776a9cSCameron Grant 	struct sc_info *parent;
20370776a9cSCameron Grant };
20470776a9cSCameron Grant 
205c067afaaSCameron Grant /* device private data */
206c067afaaSCameron Grant struct sc_info {
207c067afaaSCameron Grant 	device_t	dev;
208c067afaaSCameron Grant 	u_int32_t	type, rev;
20921f1e37cSDavid E. O'Brien 	u_int32_t	tos_link:1, APS:1, audigy:1, audigy2:1;
21021f1e37cSDavid E. O'Brien 	u_int32_t	addrmask;	/* wider if audigy */
211c067afaaSCameron Grant 
212c067afaaSCameron Grant 	bus_space_tag_t st;
213c067afaaSCameron Grant 	bus_space_handle_t sh;
214c067afaaSCameron Grant 	bus_dma_tag_t parent_dmat;
215c067afaaSCameron Grant 
216c067afaaSCameron Grant 	struct resource *reg, *irq;
217c067afaaSCameron Grant 	void		*ih;
21800acb133SCameron Grant 	struct mtx	*lock;
219c067afaaSCameron Grant 
220a791cfeeSCameron Grant 	unsigned int bufsz;
221350a5fafSCameron Grant 	int timer, timerinterval;
22270776a9cSCameron Grant 	int pnum, rnum;
22321f1e37cSDavid E. O'Brien 	int nchans;
224c067afaaSCameron Grant 	struct emu_mem mem;
225c067afaaSCameron Grant 	struct emu_voice voice[64];
22621f1e37cSDavid E. O'Brien 	struct sc_pchinfo pch[EMU_MAX_CHANS];
2276c1146c0SCameron Grant 	struct sc_rchinfo rch[3];
228f510d240SAlexander Leidinger 	struct mpu401   *mpu;
229f510d240SAlexander Leidinger 	mpu401_intr_t           *mpu_intr;
230f510d240SAlexander Leidinger 	int mputx;
231c067afaaSCameron Grant };
232c067afaaSCameron Grant 
233c067afaaSCameron Grant /* -------------------------------------------------------------------- */
234c067afaaSCameron Grant 
235c067afaaSCameron Grant /*
236c067afaaSCameron Grant  * prototypes
237c067afaaSCameron Grant  */
238c067afaaSCameron Grant 
239c067afaaSCameron Grant /* stuff */
240c067afaaSCameron Grant static int emu_init(struct sc_info *);
241c067afaaSCameron Grant static void emu_intr(void *);
24238cc9942SOlivier Houchard static void *emu_malloc(struct sc_info *sc, u_int32_t sz, bus_addr_t *addr);
24338cc9942SOlivier Houchard static void *emu_memalloc(struct sc_info *sc, u_int32_t sz, bus_addr_t *addr);
244c067afaaSCameron Grant static int emu_memfree(struct sc_info *sc, void *buf);
245c067afaaSCameron Grant static int emu_memstart(struct sc_info *sc, void *buf);
2468046c763SCameron Grant #ifdef EMUDEBUG
2478046c763SCameron Grant static void emu_vdump(struct sc_info *sc, struct emu_voice *v);
2488046c763SCameron Grant #endif
249c067afaaSCameron Grant 
250c067afaaSCameron Grant /* talk to the card */
251c067afaaSCameron Grant static u_int32_t emu_rd(struct sc_info *, int, int);
252c067afaaSCameron Grant static void emu_wr(struct sc_info *, int, u_int32_t, int);
253c067afaaSCameron Grant 
254c067afaaSCameron Grant /* -------------------------------------------------------------------- */
255c067afaaSCameron Grant 
256513693beSCameron Grant static u_int32_t emu_rfmt_ac97[] = {
25790da2b28SAriff Abdullah 	SND_FORMAT(AFMT_S16_LE, 1, 0),
25890da2b28SAriff Abdullah 	SND_FORMAT(AFMT_S16_LE, 2, 0),
259513693beSCameron Grant 	0
260c067afaaSCameron Grant };
261c067afaaSCameron Grant 
262513693beSCameron Grant static u_int32_t emu_rfmt_mic[] = {
26390da2b28SAriff Abdullah 	SND_FORMAT(AFMT_U8, 1, 0),
264513693beSCameron Grant 	0
265c067afaaSCameron Grant };
266c067afaaSCameron Grant 
267513693beSCameron Grant static u_int32_t emu_rfmt_efx[] = {
26890da2b28SAriff Abdullah 	SND_FORMAT(AFMT_S16_LE, 2, 0),
269513693beSCameron Grant 	0
270513693beSCameron Grant };
271513693beSCameron Grant 
27266ef8af5SCameron Grant static struct pcmchan_caps emu_reccaps[3] = {
273513693beSCameron Grant 	{8000, 48000, emu_rfmt_ac97, 0},
274513693beSCameron Grant 	{8000, 8000, emu_rfmt_mic, 0},
275513693beSCameron Grant 	{48000, 48000, emu_rfmt_efx, 0},
276513693beSCameron Grant };
277513693beSCameron Grant 
278513693beSCameron Grant static u_int32_t emu_pfmt[] = {
27990da2b28SAriff Abdullah 	SND_FORMAT(AFMT_U8, 1, 0),
28090da2b28SAriff Abdullah 	SND_FORMAT(AFMT_U8, 2, 0),
28190da2b28SAriff Abdullah 	SND_FORMAT(AFMT_S16_LE, 1, 0),
28290da2b28SAriff Abdullah 	SND_FORMAT(AFMT_S16_LE, 2, 0),
283513693beSCameron Grant 	0
284513693beSCameron Grant };
285513693beSCameron Grant 
28666ef8af5SCameron Grant static struct pcmchan_caps emu_playcaps = {4000, 48000, emu_pfmt, 0};
28770776a9cSCameron Grant 
28870776a9cSCameron Grant static int adcspeed[8] = {48000, 44100, 32000, 24000, 22050, 16000, 11025, 8000};
28921f1e37cSDavid E. O'Brien /* audigy supports 12kHz. */
29021f1e37cSDavid E. O'Brien static int audigy_adcspeed[9] = {
29121f1e37cSDavid E. O'Brien 	48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000
29221f1e37cSDavid E. O'Brien };
29370776a9cSCameron Grant 
294c067afaaSCameron Grant /* -------------------------------------------------------------------- */
295c067afaaSCameron Grant /* Hardware */
296c067afaaSCameron Grant static u_int32_t
297c067afaaSCameron Grant emu_rd(struct sc_info *sc, int regno, int size)
298c067afaaSCameron Grant {
299c067afaaSCameron Grant 	switch (size) {
300c067afaaSCameron Grant 	case 1:
301c067afaaSCameron Grant 		return bus_space_read_1(sc->st, sc->sh, regno);
302c067afaaSCameron Grant 	case 2:
303c067afaaSCameron Grant 		return bus_space_read_2(sc->st, sc->sh, regno);
304c067afaaSCameron Grant 	case 4:
305c067afaaSCameron Grant 		return bus_space_read_4(sc->st, sc->sh, regno);
306c067afaaSCameron Grant 	default:
307c067afaaSCameron Grant 		return 0xffffffff;
308c067afaaSCameron Grant 	}
309c067afaaSCameron Grant }
310c067afaaSCameron Grant 
311c067afaaSCameron Grant static void
312c067afaaSCameron Grant emu_wr(struct sc_info *sc, int regno, u_int32_t data, int size)
313c067afaaSCameron Grant {
314c067afaaSCameron Grant 	switch (size) {
315c067afaaSCameron Grant 	case 1:
316c067afaaSCameron Grant 		bus_space_write_1(sc->st, sc->sh, regno, data);
317c067afaaSCameron Grant 		break;
318c067afaaSCameron Grant 	case 2:
319c067afaaSCameron Grant 		bus_space_write_2(sc->st, sc->sh, regno, data);
320c067afaaSCameron Grant 		break;
321c067afaaSCameron Grant 	case 4:
322c067afaaSCameron Grant 		bus_space_write_4(sc->st, sc->sh, regno, data);
323c067afaaSCameron Grant 		break;
324c067afaaSCameron Grant 	}
325c067afaaSCameron Grant }
326c067afaaSCameron Grant 
327c067afaaSCameron Grant static u_int32_t
328c067afaaSCameron Grant emu_rdptr(struct sc_info *sc, int chn, int reg)
329c067afaaSCameron Grant {
330c067afaaSCameron Grant 	u_int32_t ptr, val, mask, size, offset;
331c067afaaSCameron Grant 
332c7e0c9dbSPedro F. Giffuni 	ptr = ((reg << 16) & sc->addrmask) | (chn & EMU_PTR_CHNO_MASK);
333c7e0c9dbSPedro F. Giffuni 	emu_wr(sc, EMU_PTR, ptr, 4);
334c7e0c9dbSPedro F. Giffuni 	val = emu_rd(sc, EMU_DATA, 4);
335c067afaaSCameron Grant 	if (reg & 0xff000000) {
336c067afaaSCameron Grant 		size = (reg >> 24) & 0x3f;
337c067afaaSCameron Grant 		offset = (reg >> 16) & 0x1f;
338c067afaaSCameron Grant 		mask = ((1 << size) - 1) << offset;
339c067afaaSCameron Grant 		val &= mask;
340c067afaaSCameron Grant 		val >>= offset;
341c067afaaSCameron Grant 	}
342c067afaaSCameron Grant 	return val;
343c067afaaSCameron Grant }
344c067afaaSCameron Grant 
345c067afaaSCameron Grant static void
346c067afaaSCameron Grant emu_wrptr(struct sc_info *sc, int chn, int reg, u_int32_t data)
347c067afaaSCameron Grant {
348c067afaaSCameron Grant 	u_int32_t ptr, mask, size, offset;
349c067afaaSCameron Grant 
350c7e0c9dbSPedro F. Giffuni 	ptr = ((reg << 16) & sc->addrmask) | (chn & EMU_PTR_CHNO_MASK);
351c7e0c9dbSPedro F. Giffuni 	emu_wr(sc, EMU_PTR, ptr, 4);
352c067afaaSCameron Grant 	if (reg & 0xff000000) {
353c067afaaSCameron Grant 		size = (reg >> 24) & 0x3f;
354c067afaaSCameron Grant 		offset = (reg >> 16) & 0x1f;
355c067afaaSCameron Grant 		mask = ((1 << size) - 1) << offset;
356c067afaaSCameron Grant 		data <<= offset;
357c067afaaSCameron Grant 		data &= mask;
358c7e0c9dbSPedro F. Giffuni 		data |= emu_rd(sc, EMU_DATA, 4) & ~mask;
359c067afaaSCameron Grant 	}
360c7e0c9dbSPedro F. Giffuni 	emu_wr(sc, EMU_DATA, data, 4);
361c067afaaSCameron Grant }
362c067afaaSCameron Grant 
363c067afaaSCameron Grant static void
364c067afaaSCameron Grant emu_wrefx(struct sc_info *sc, unsigned int pc, unsigned int data)
365c067afaaSCameron Grant {
366c7e0c9dbSPedro F. Giffuni 	pc += sc->audigy ? EMU_A_MICROCODEBASE : EMU_MICROCODEBASE;
36721f1e37cSDavid E. O'Brien 	emu_wrptr(sc, 0, pc, data);
368c067afaaSCameron Grant }
369c067afaaSCameron Grant 
3700f55ac6cSCameron Grant /* -------------------------------------------------------------------- */
3716c1146c0SCameron Grant /* ac97 codec */
37266ef8af5SCameron Grant /* no locking needed */
3730f55ac6cSCameron Grant 
3740f55ac6cSCameron Grant static int
3750f55ac6cSCameron Grant emu_rdcd(kobj_t obj, void *devinfo, int regno)
3766c1146c0SCameron Grant {
3776c1146c0SCameron Grant 	struct sc_info *sc = (struct sc_info *)devinfo;
3786c1146c0SCameron Grant 
379c7e0c9dbSPedro F. Giffuni 	emu_wr(sc, EMU_AC97ADDR, regno, 1);
380c7e0c9dbSPedro F. Giffuni 	return emu_rd(sc, EMU_AC97DATA, 2);
3816c1146c0SCameron Grant }
3826c1146c0SCameron Grant 
3830f55ac6cSCameron Grant static int
3840f55ac6cSCameron Grant emu_wrcd(kobj_t obj, void *devinfo, int regno, u_int32_t data)
3856c1146c0SCameron Grant {
3866c1146c0SCameron Grant 	struct sc_info *sc = (struct sc_info *)devinfo;
3876c1146c0SCameron Grant 
388c7e0c9dbSPedro F. Giffuni 	emu_wr(sc, EMU_AC97ADDR, regno, 1);
389c7e0c9dbSPedro F. Giffuni 	emu_wr(sc, EMU_AC97DATA, data, 2);
3900f55ac6cSCameron Grant 	return 0;
3916c1146c0SCameron Grant }
3926c1146c0SCameron Grant 
3930f55ac6cSCameron Grant static kobj_method_t emu_ac97_methods[] = {
3940f55ac6cSCameron Grant 	KOBJMETHOD(ac97_read,		emu_rdcd),
3950f55ac6cSCameron Grant 	KOBJMETHOD(ac97_write,		emu_wrcd),
39690da2b28SAriff Abdullah 	KOBJMETHOD_END
3970f55ac6cSCameron Grant };
3980f55ac6cSCameron Grant AC97_DECLARE(emu_ac97);
3990f55ac6cSCameron Grant 
4000f55ac6cSCameron Grant /* -------------------------------------------------------------------- */
4016c1146c0SCameron Grant /* stuff */
4026c1146c0SCameron Grant static int
403350a5fafSCameron Grant emu_settimer(struct sc_info *sc)
404350a5fafSCameron Grant {
405350a5fafSCameron Grant 	struct sc_pchinfo *pch;
406350a5fafSCameron Grant 	struct sc_rchinfo *rch;
407350a5fafSCameron Grant 	int i, tmp, rate;
408350a5fafSCameron Grant 
409350a5fafSCameron Grant 	rate = 0;
41021f1e37cSDavid E. O'Brien 	for (i = 0; i < sc->nchans; i++) {
411350a5fafSCameron Grant 		pch = &sc->pch[i];
412a791cfeeSCameron Grant 		if (pch->buffer) {
41390da2b28SAriff Abdullah 			tmp = (pch->spd * sndbuf_getalign(pch->buffer))
41421f1e37cSDavid E. O'Brien 			    / pch->blksz;
415350a5fafSCameron Grant 			if (tmp > rate)
416350a5fafSCameron Grant 				rate = tmp;
417350a5fafSCameron Grant 		}
418a791cfeeSCameron Grant 	}
419350a5fafSCameron Grant 
420350a5fafSCameron Grant 	for (i = 0; i < 3; i++) {
421350a5fafSCameron Grant 		rch = &sc->rch[i];
422a791cfeeSCameron Grant 		if (rch->buffer) {
42390da2b28SAriff Abdullah 			tmp = (rch->spd * sndbuf_getalign(rch->buffer))
42421f1e37cSDavid E. O'Brien 			    / rch->blksz;
425350a5fafSCameron Grant 			if (tmp > rate)
426350a5fafSCameron Grant 				rate = tmp;
427350a5fafSCameron Grant 		}
428a791cfeeSCameron Grant 	}
429350a5fafSCameron Grant 	RANGE(rate, 48, 9600);
430350a5fafSCameron Grant 	sc->timerinterval = 48000 / rate;
431c7e0c9dbSPedro F. Giffuni 	emu_wr(sc, EMU_TIMER, sc->timerinterval & 0x03ff, 2);
432350a5fafSCameron Grant 
433350a5fafSCameron Grant 	return sc->timerinterval;
434350a5fafSCameron Grant }
435350a5fafSCameron Grant 
436350a5fafSCameron Grant static int
4376c1146c0SCameron Grant emu_enatimer(struct sc_info *sc, int go)
4386c1146c0SCameron Grant {
4396c1146c0SCameron Grant 	u_int32_t x;
4406c1146c0SCameron Grant 	if (go) {
4416c1146c0SCameron Grant 		if (sc->timer++ == 0) {
442c7e0c9dbSPedro F. Giffuni 			x = emu_rd(sc, EMU_INTE, 4);
443c7e0c9dbSPedro F. Giffuni 			x |= EMU_INTE_INTERTIMERENB;
444c7e0c9dbSPedro F. Giffuni 			emu_wr(sc, EMU_INTE, x, 4);
4456c1146c0SCameron Grant 		}
4466c1146c0SCameron Grant 	} else {
4476c1146c0SCameron Grant 		sc->timer = 0;
448c7e0c9dbSPedro F. Giffuni 		x = emu_rd(sc, EMU_INTE, 4);
449c7e0c9dbSPedro F. Giffuni 		x &= ~EMU_INTE_INTERTIMERENB;
450c7e0c9dbSPedro F. Giffuni 		emu_wr(sc, EMU_INTE, x, 4);
4516c1146c0SCameron Grant 	}
4526c1146c0SCameron Grant 	return 0;
4536c1146c0SCameron Grant }
454c067afaaSCameron Grant 
455c067afaaSCameron Grant static void
456c067afaaSCameron Grant emu_enastop(struct sc_info *sc, char channel, int enable)
457c067afaaSCameron Grant {
458c7e0c9dbSPedro F. Giffuni 	int reg = (channel & 0x20) ? EMU_SOLEH : EMU_SOLEL;
459c067afaaSCameron Grant 	channel &= 0x1f;
460c067afaaSCameron Grant 	reg |= 1 << 24;
461c067afaaSCameron Grant 	reg |= channel << 16;
462c067afaaSCameron Grant 	emu_wrptr(sc, 0, reg, enable);
463c067afaaSCameron Grant }
464c067afaaSCameron Grant 
46570776a9cSCameron Grant static int
46670776a9cSCameron Grant emu_recval(int speed) {
46770776a9cSCameron Grant 	int val;
46870776a9cSCameron Grant 
46970776a9cSCameron Grant 	val = 0;
47070776a9cSCameron Grant 	while (val < 7 && speed < adcspeed[val])
47170776a9cSCameron Grant 		val++;
47270776a9cSCameron Grant 	return val;
47370776a9cSCameron Grant }
47470776a9cSCameron Grant 
47521f1e37cSDavid E. O'Brien static int
47621f1e37cSDavid E. O'Brien audigy_recval(int speed) {
47721f1e37cSDavid E. O'Brien 	int val;
47821f1e37cSDavid E. O'Brien 
47921f1e37cSDavid E. O'Brien 	val = 0;
48021f1e37cSDavid E. O'Brien 	while (val < 8 && speed < audigy_adcspeed[val])
48121f1e37cSDavid E. O'Brien 		val++;
48221f1e37cSDavid E. O'Brien 	return val;
48321f1e37cSDavid E. O'Brien }
48421f1e37cSDavid E. O'Brien 
485c067afaaSCameron Grant static u_int32_t
486c067afaaSCameron Grant emu_rate_to_pitch(u_int32_t rate)
487c067afaaSCameron Grant {
488c067afaaSCameron Grant 	static u_int32_t logMagTable[128] = {
489c067afaaSCameron Grant 		0x00000, 0x02dfc, 0x05b9e, 0x088e6, 0x0b5d6, 0x0e26f, 0x10eb3, 0x13aa2,
490c067afaaSCameron Grant 		0x1663f, 0x1918a, 0x1bc84, 0x1e72e, 0x2118b, 0x23b9a, 0x2655d, 0x28ed5,
491c067afaaSCameron Grant 		0x2b803, 0x2e0e8, 0x30985, 0x331db, 0x359eb, 0x381b6, 0x3a93d, 0x3d081,
492c067afaaSCameron Grant 		0x3f782, 0x41e42, 0x444c1, 0x46b01, 0x49101, 0x4b6c4, 0x4dc49, 0x50191,
493c067afaaSCameron Grant 		0x5269e, 0x54b6f, 0x57006, 0x59463, 0x5b888, 0x5dc74, 0x60029, 0x623a7,
494c067afaaSCameron Grant 		0x646ee, 0x66a00, 0x68cdd, 0x6af86, 0x6d1fa, 0x6f43c, 0x7164b, 0x73829,
495c067afaaSCameron Grant 		0x759d4, 0x77b4f, 0x79c9a, 0x7bdb5, 0x7dea1, 0x7ff5e, 0x81fed, 0x8404e,
496c067afaaSCameron Grant 		0x86082, 0x88089, 0x8a064, 0x8c014, 0x8df98, 0x8fef1, 0x91e20, 0x93d26,
497c067afaaSCameron Grant 		0x95c01, 0x97ab4, 0x9993e, 0x9b79f, 0x9d5d9, 0x9f3ec, 0xa11d8, 0xa2f9d,
498c067afaaSCameron Grant 		0xa4d3c, 0xa6ab5, 0xa8808, 0xaa537, 0xac241, 0xadf26, 0xafbe7, 0xb1885,
499c067afaaSCameron Grant 		0xb3500, 0xb5157, 0xb6d8c, 0xb899f, 0xba58f, 0xbc15e, 0xbdd0c, 0xbf899,
500c067afaaSCameron Grant 		0xc1404, 0xc2f50, 0xc4a7b, 0xc6587, 0xc8073, 0xc9b3f, 0xcb5ed, 0xcd07c,
501c067afaaSCameron Grant 		0xceaec, 0xd053f, 0xd1f73, 0xd398a, 0xd5384, 0xd6d60, 0xd8720, 0xda0c3,
502c067afaaSCameron Grant 		0xdba4a, 0xdd3b4, 0xded03, 0xe0636, 0xe1f4e, 0xe384a, 0xe512c, 0xe69f3,
503c067afaaSCameron Grant 		0xe829f, 0xe9b31, 0xeb3a9, 0xecc08, 0xee44c, 0xefc78, 0xf148a, 0xf2c83,
504c067afaaSCameron Grant 		0xf4463, 0xf5c2a, 0xf73da, 0xf8b71, 0xfa2f0, 0xfba57, 0xfd1a7, 0xfe8df
505c067afaaSCameron Grant 	};
506c067afaaSCameron Grant 	static char logSlopeTable[128] = {
507c067afaaSCameron Grant 		0x5c, 0x5c, 0x5b, 0x5a, 0x5a, 0x59, 0x58, 0x58,
508c067afaaSCameron Grant 		0x57, 0x56, 0x56, 0x55, 0x55, 0x54, 0x53, 0x53,
509c067afaaSCameron Grant 		0x52, 0x52, 0x51, 0x51, 0x50, 0x50, 0x4f, 0x4f,
510c067afaaSCameron Grant 		0x4e, 0x4d, 0x4d, 0x4d, 0x4c, 0x4c, 0x4b, 0x4b,
511c067afaaSCameron Grant 		0x4a, 0x4a, 0x49, 0x49, 0x48, 0x48, 0x47, 0x47,
512c067afaaSCameron Grant 		0x47, 0x46, 0x46, 0x45, 0x45, 0x45, 0x44, 0x44,
513c067afaaSCameron Grant 		0x43, 0x43, 0x43, 0x42, 0x42, 0x42, 0x41, 0x41,
514c067afaaSCameron Grant 		0x41, 0x40, 0x40, 0x40, 0x3f, 0x3f, 0x3f, 0x3e,
515c067afaaSCameron Grant 		0x3e, 0x3e, 0x3d, 0x3d, 0x3d, 0x3c, 0x3c, 0x3c,
516c067afaaSCameron Grant 		0x3b, 0x3b, 0x3b, 0x3b, 0x3a, 0x3a, 0x3a, 0x39,
517c067afaaSCameron Grant 		0x39, 0x39, 0x39, 0x38, 0x38, 0x38, 0x38, 0x37,
518c067afaaSCameron Grant 		0x37, 0x37, 0x37, 0x36, 0x36, 0x36, 0x36, 0x35,
519c067afaaSCameron Grant 		0x35, 0x35, 0x35, 0x34, 0x34, 0x34, 0x34, 0x34,
520c067afaaSCameron Grant 		0x33, 0x33, 0x33, 0x33, 0x32, 0x32, 0x32, 0x32,
521c067afaaSCameron Grant 		0x32, 0x31, 0x31, 0x31, 0x31, 0x31, 0x30, 0x30,
522c067afaaSCameron Grant 		0x30, 0x30, 0x30, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f
523c067afaaSCameron Grant 	};
524c067afaaSCameron Grant 	int i;
525c067afaaSCameron Grant 
526c067afaaSCameron Grant 	if (rate == 0)
527c067afaaSCameron Grant 		return 0;	/* Bail out if no leading "1" */
528c067afaaSCameron Grant 	rate *= 11185;	/* Scale 48000 to 0x20002380 */
529c067afaaSCameron Grant 	for (i = 31; i > 0; i--) {
530c067afaaSCameron Grant 		if (rate & 0x80000000) {	/* Detect leading "1" */
531c067afaaSCameron Grant 			return (((u_int32_t) (i - 15) << 20) +
532c067afaaSCameron Grant 			    logMagTable[0x7f & (rate >> 24)] +
533c067afaaSCameron Grant 			    (0x7f & (rate >> 17)) *
534c067afaaSCameron Grant 			    logSlopeTable[0x7f & (rate >> 24)]);
535c067afaaSCameron Grant 		}
536c067afaaSCameron Grant 		rate <<= 1;
537c067afaaSCameron Grant 	}
538c067afaaSCameron Grant 
539c067afaaSCameron Grant 	return 0;		/* Should never reach this point */
540c067afaaSCameron Grant }
541c067afaaSCameron Grant 
5426c1146c0SCameron Grant static u_int32_t
5436c1146c0SCameron Grant emu_rate_to_linearpitch(u_int32_t rate)
5446c1146c0SCameron Grant {
5456c1146c0SCameron Grant 	rate = (rate << 8) / 375;
5466c1146c0SCameron Grant 	return (rate >> 1) + (rate & 1);
5476c1146c0SCameron Grant }
5486c1146c0SCameron Grant 
549c067afaaSCameron Grant static struct emu_voice *
550c067afaaSCameron Grant emu_valloc(struct sc_info *sc)
551c067afaaSCameron Grant {
552c067afaaSCameron Grant 	struct emu_voice *v;
553c067afaaSCameron Grant 	int i;
554c067afaaSCameron Grant 
555c067afaaSCameron Grant 	v = NULL;
556c067afaaSCameron Grant 	for (i = 0; i < 64 && sc->voice[i].busy; i++);
557c067afaaSCameron Grant 	if (i < 64) {
558c067afaaSCameron Grant 		v = &sc->voice[i];
559c067afaaSCameron Grant 		v->busy = 1;
560c067afaaSCameron Grant 	}
561c067afaaSCameron Grant 	return v;
562c067afaaSCameron Grant }
563c067afaaSCameron Grant 
564c067afaaSCameron Grant static int
5656c1146c0SCameron Grant emu_vinit(struct sc_info *sc, struct emu_voice *m, struct emu_voice *s,
56666ef8af5SCameron Grant 	  u_int32_t sz, struct snd_dbuf *b)
567c067afaaSCameron Grant {
568c067afaaSCameron Grant 	void *buf;
56938cc9942SOlivier Houchard 	bus_addr_t tmp_addr;
570c067afaaSCameron Grant 
57138cc9942SOlivier Houchard 	buf = emu_memalloc(sc, sz, &tmp_addr);
572c067afaaSCameron Grant 	if (buf == NULL)
573c067afaaSCameron Grant 		return -1;
57466ef8af5SCameron Grant 	if (b != NULL)
57566ef8af5SCameron Grant 		sndbuf_setup(b, buf, sz);
576c067afaaSCameron Grant 	m->start = emu_memstart(sc, buf) * EMUPAGESIZE;
57797b3c9d8SCameron Grant 	m->end = m->start + sz;
5782d3ce9d5SCameron Grant 	m->channel = NULL;
579c067afaaSCameron Grant 	m->speed = 0;
580c067afaaSCameron Grant 	m->b16 = 0;
581c067afaaSCameron Grant 	m->stereo = 0;
5826c1146c0SCameron Grant 	m->running = 0;
5832d3ce9d5SCameron Grant 	m->ismaster = 1;
5842d3ce9d5SCameron Grant 	m->vol = 0xff;
58538cc9942SOlivier Houchard 	m->buf = tmp_addr;
586c067afaaSCameron Grant 	m->slave = s;
58721f1e37cSDavid E. O'Brien 	if (sc->audigy) {
5889190333cSAlexander Leidinger 		m->fxrt1 = FXBUS_MIDI_CHORUS | FXBUS_PCM_RIGHT << 8 |
5899190333cSAlexander Leidinger 		    FXBUS_PCM_LEFT << 16 | FXBUS_MIDI_REVERB << 24;
59021f1e37cSDavid E. O'Brien 		m->fxrt2 = 0x3f3f3f3f;	/* No effects on second route */
59121f1e37cSDavid E. O'Brien 	} else {
5929190333cSAlexander Leidinger 		m->fxrt1 = FXBUS_MIDI_CHORUS | FXBUS_PCM_RIGHT << 4 |
5939190333cSAlexander Leidinger 		    FXBUS_PCM_LEFT << 8 | FXBUS_MIDI_REVERB << 12;
59421f1e37cSDavid E. O'Brien 		m->fxrt2 = 0;
59521f1e37cSDavid E. O'Brien 	}
59621f1e37cSDavid E. O'Brien 
597c067afaaSCameron Grant 	if (s != NULL) {
598c067afaaSCameron Grant 		s->start = m->start;
599c067afaaSCameron Grant 		s->end = m->end;
600c067afaaSCameron Grant 		s->channel = NULL;
601c067afaaSCameron Grant 		s->speed = 0;
602c067afaaSCameron Grant 		s->b16 = 0;
603c067afaaSCameron Grant 		s->stereo = 0;
6046c1146c0SCameron Grant 		s->running = 0;
6052d3ce9d5SCameron Grant 		s->ismaster = 0;
6062d3ce9d5SCameron Grant 		s->vol = m->vol;
60797b3c9d8SCameron Grant 		s->buf = m->buf;
60821f1e37cSDavid E. O'Brien 		s->fxrt1 = m->fxrt1;
60921f1e37cSDavid E. O'Brien 		s->fxrt2 = m->fxrt2;
610c067afaaSCameron Grant 		s->slave = NULL;
611c067afaaSCameron Grant 	}
612c067afaaSCameron Grant 	return 0;
613c067afaaSCameron Grant }
614c067afaaSCameron Grant 
615c067afaaSCameron Grant static void
61670776a9cSCameron Grant emu_vsetup(struct sc_pchinfo *ch)
617c067afaaSCameron Grant {
618c067afaaSCameron Grant 	struct emu_voice *v = ch->master;
619c067afaaSCameron Grant 
620c067afaaSCameron Grant 	if (ch->fmt) {
621c067afaaSCameron Grant 		v->b16 = (ch->fmt & AFMT_16BIT) ? 1 : 0;
62290da2b28SAriff Abdullah 		v->stereo = (AFMT_CHANNEL(ch->fmt) > 1) ? 1 : 0;
623c067afaaSCameron Grant 		if (v->slave != NULL) {
624c067afaaSCameron Grant 			v->slave->b16 = v->b16;
625c067afaaSCameron Grant 			v->slave->stereo = v->stereo;
626c067afaaSCameron Grant 		}
627c067afaaSCameron Grant 	}
628c067afaaSCameron Grant 	if (ch->spd) {
629c067afaaSCameron Grant 		v->speed = ch->spd;
630c067afaaSCameron Grant 		if (v->slave != NULL)
631c067afaaSCameron Grant 			v->slave->speed = v->speed;
632c067afaaSCameron Grant 	}
633c067afaaSCameron Grant }
634c067afaaSCameron Grant 
635c067afaaSCameron Grant static void
636c067afaaSCameron Grant emu_vwrite(struct sc_info *sc, struct emu_voice *v)
637c067afaaSCameron Grant {
6386c1146c0SCameron Grant 	int s;
6396c1146c0SCameron Grant 	int l, r, x, y;
6406c1146c0SCameron Grant 	u_int32_t sa, ea, start, val, silent_page;
641c067afaaSCameron Grant 
642c067afaaSCameron Grant 	s = (v->stereo ? 1 : 0) + (v->b16 ? 1 : 0);
6436c1146c0SCameron Grant 
644c067afaaSCameron Grant 	sa = v->start >> s;
645c067afaaSCameron Grant 	ea = v->end >> s;
6466c1146c0SCameron Grant 
6476c1146c0SCameron Grant 	l = r = x = y = v->vol;
648c067afaaSCameron Grant 	if (v->stereo) {
6492d3ce9d5SCameron Grant 		l = v->ismaster ? l : 0;
6502d3ce9d5SCameron Grant 		r = v->ismaster ? 0 : r;
651c067afaaSCameron Grant 	}
652c067afaaSCameron Grant 
653c7e0c9dbSPedro F. Giffuni 	emu_wrptr(sc, v->vnum, EMU_CHAN_CPF, v->stereo ? EMU_CHAN_CPF_STEREO_MASK : 0);
6546c1146c0SCameron Grant 	val = v->stereo ? 28 : 30;
6556c1146c0SCameron Grant 	val *= v->b16 ? 1 : 2;
6566c1146c0SCameron Grant 	start = sa + val;
6576c1146c0SCameron Grant 
65821f1e37cSDavid E. O'Brien 	if (sc->audigy) {
659c7e0c9dbSPedro F. Giffuni 		emu_wrptr(sc, v->vnum, EMU_A_CHAN_FXRT1, v->fxrt1);
660c7e0c9dbSPedro F. Giffuni 		emu_wrptr(sc, v->vnum, EMU_A_CHAN_FXRT2, v->fxrt2);
661c7e0c9dbSPedro F. Giffuni 		emu_wrptr(sc, v->vnum, EMU_A_CHAN_SENDAMOUNTS, 0);
66221f1e37cSDavid E. O'Brien 	}
66321f1e37cSDavid E. O'Brien 	else
664c7e0c9dbSPedro F. Giffuni 		emu_wrptr(sc, v->vnum, EMU_CHAN_FXRT, v->fxrt1 << 16);
665c067afaaSCameron Grant 
666c7e0c9dbSPedro F. Giffuni 	emu_wrptr(sc, v->vnum, EMU_CHAN_PTRX, (x << 8) | r);
667c7e0c9dbSPedro F. Giffuni 	emu_wrptr(sc, v->vnum, EMU_CHAN_DSL, ea | (y << 24));
668c7e0c9dbSPedro F. Giffuni 	emu_wrptr(sc, v->vnum, EMU_CHAN_PSST, sa | (l << 24));
669c7e0c9dbSPedro F. Giffuni 	emu_wrptr(sc, v->vnum, EMU_CHAN_CCCA, start | (v->b16 ? 0 : EMU_CHAN_CCCA_8BITSELECT));
670c067afaaSCameron Grant 
671c7e0c9dbSPedro F. Giffuni 	emu_wrptr(sc, v->vnum, EMU_CHAN_Z1, 0);
672c7e0c9dbSPedro F. Giffuni 	emu_wrptr(sc, v->vnum, EMU_CHAN_Z2, 0);
673c067afaaSCameron Grant 
67421f1e37cSDavid E. O'Brien 	silent_page = ((u_int32_t)(sc->mem.silent_page_addr) << 1)
675c7e0c9dbSPedro F. Giffuni 	    | EMU_CHAN_MAP_PTI_MASK;
676c7e0c9dbSPedro F. Giffuni 	emu_wrptr(sc, v->vnum, EMU_CHAN_MAPA, silent_page);
677c7e0c9dbSPedro F. Giffuni 	emu_wrptr(sc, v->vnum, EMU_CHAN_MAPB, silent_page);
678c067afaaSCameron Grant 
679c7e0c9dbSPedro F. Giffuni 	emu_wrptr(sc, v->vnum, EMU_CHAN_CVCF, EMU_CHAN_CVCF_CURRFILTER_MASK);
680c7e0c9dbSPedro F. Giffuni 	emu_wrptr(sc, v->vnum, EMU_CHAN_VTFT, EMU_CHAN_VTFT_FILTERTARGET_MASK);
681c7e0c9dbSPedro F. Giffuni 	emu_wrptr(sc, v->vnum, EMU_CHAN_ATKHLDM, 0);
682c7e0c9dbSPedro F. Giffuni 	emu_wrptr(sc, v->vnum, EMU_CHAN_DCYSUSM, EMU_CHAN_DCYSUSM_DECAYTIME_MASK);
683c7e0c9dbSPedro F. Giffuni 	emu_wrptr(sc, v->vnum, EMU_CHAN_LFOVAL1, 0x8000);
684c7e0c9dbSPedro F. Giffuni 	emu_wrptr(sc, v->vnum, EMU_CHAN_LFOVAL2, 0x8000);
685c7e0c9dbSPedro F. Giffuni 	emu_wrptr(sc, v->vnum, EMU_CHAN_FMMOD, 0);
686c7e0c9dbSPedro F. Giffuni 	emu_wrptr(sc, v->vnum, EMU_CHAN_TREMFRQ, 0);
687c7e0c9dbSPedro F. Giffuni 	emu_wrptr(sc, v->vnum, EMU_CHAN_FM2FRQ2, 0);
688c7e0c9dbSPedro F. Giffuni 	emu_wrptr(sc, v->vnum, EMU_CHAN_ENVVAL, 0x8000);
6896c1146c0SCameron Grant 
690c7e0c9dbSPedro F. Giffuni 	emu_wrptr(sc, v->vnum, EMU_CHAN_ATKHLDV,
691c7e0c9dbSPedro F. Giffuni 	    EMU_CHAN_ATKHLDV_HOLDTIME_MASK | EMU_CHAN_ATKHLDV_ATTACKTIME_MASK);
692c7e0c9dbSPedro F. Giffuni 	emu_wrptr(sc, v->vnum, EMU_CHAN_ENVVOL, 0x8000);
6936c1146c0SCameron Grant 
694c7e0c9dbSPedro F. Giffuni 	emu_wrptr(sc, v->vnum, EMU_CHAN_PEFE_FILTERAMOUNT, 0x7f);
695c7e0c9dbSPedro F. Giffuni 	emu_wrptr(sc, v->vnum, EMU_CHAN_PEFE_PITCHAMOUNT, 0);
6962d3ce9d5SCameron Grant 
6972d3ce9d5SCameron Grant 	if (v->slave != NULL)
6982d3ce9d5SCameron Grant 		emu_vwrite(sc, v->slave);
699c067afaaSCameron Grant }
700c067afaaSCameron Grant 
701c067afaaSCameron Grant static void
702c067afaaSCameron Grant emu_vtrigger(struct sc_info *sc, struct emu_voice *v, int go)
703c067afaaSCameron Grant {
7046c1146c0SCameron Grant 	u_int32_t pitch_target, initial_pitch;
7056c1146c0SCameron Grant 	u_int32_t cra, cs, ccis;
7066c1146c0SCameron Grant 	u_int32_t sample, i;
7076c1146c0SCameron Grant 
708c067afaaSCameron Grant 	if (go) {
7096c1146c0SCameron Grant 		cra = 64;
7106c1146c0SCameron Grant 		cs = v->stereo ? 4 : 2;
7116c1146c0SCameron Grant 		ccis = v->stereo ? 28 : 30;
7126c1146c0SCameron Grant 		ccis *= v->b16 ? 1 : 2;
7136c1146c0SCameron Grant 		sample = v->b16 ? 0x00000000 : 0x80808080;
7146c1146c0SCameron Grant 
7156c1146c0SCameron Grant 		for (i = 0; i < cs; i++)
716c7e0c9dbSPedro F. Giffuni 			emu_wrptr(sc, v->vnum, EMU_CHAN_CD0 + i, sample);
717c7e0c9dbSPedro F. Giffuni 		emu_wrptr(sc, v->vnum, EMU_CHAN_CCR_CACHEINVALIDSIZE, 0);
718c7e0c9dbSPedro F. Giffuni 		emu_wrptr(sc, v->vnum, EMU_CHAN_CCR_READADDRESS, cra);
719c7e0c9dbSPedro F. Giffuni 		emu_wrptr(sc, v->vnum, EMU_CHAN_CCR_CACHEINVALIDSIZE, ccis);
7206c1146c0SCameron Grant 
721c7e0c9dbSPedro F. Giffuni 		emu_wrptr(sc, v->vnum, EMU_CHAN_IFATN, 0xff00);
722c7e0c9dbSPedro F. Giffuni 		emu_wrptr(sc, v->vnum, EMU_CHAN_VTFT, 0xffffffff);
723c7e0c9dbSPedro F. Giffuni 		emu_wrptr(sc, v->vnum, EMU_CHAN_CVCF, 0xffffffff);
724c7e0c9dbSPedro F. Giffuni 		emu_wrptr(sc, v->vnum, EMU_CHAN_DCYSUSV, 0x00007f7f);
7256c1146c0SCameron Grant 		emu_enastop(sc, v->vnum, 0);
7266c1146c0SCameron Grant 
7276c1146c0SCameron Grant 		pitch_target = emu_rate_to_linearpitch(v->speed);
7286c1146c0SCameron Grant 		initial_pitch = emu_rate_to_pitch(v->speed) >> 8;
729c7e0c9dbSPedro F. Giffuni 		emu_wrptr(sc, v->vnum, EMU_CHAN_PTRX_PITCHTARGET, pitch_target);
730c7e0c9dbSPedro F. Giffuni 		emu_wrptr(sc, v->vnum, EMU_CHAN_CPF_PITCH, pitch_target);
731c7e0c9dbSPedro F. Giffuni 		emu_wrptr(sc, v->vnum, EMU_CHAN_IP, initial_pitch);
732c067afaaSCameron Grant 	} else {
733c7e0c9dbSPedro F. Giffuni 		emu_wrptr(sc, v->vnum, EMU_CHAN_PTRX_PITCHTARGET, 0);
734c7e0c9dbSPedro F. Giffuni 		emu_wrptr(sc, v->vnum, EMU_CHAN_CPF_PITCH, 0);
735c7e0c9dbSPedro F. Giffuni 		emu_wrptr(sc, v->vnum, EMU_CHAN_IFATN, 0xffff);
736c7e0c9dbSPedro F. Giffuni 		emu_wrptr(sc, v->vnum, EMU_CHAN_VTFT, 0x0000ffff);
737c7e0c9dbSPedro F. Giffuni 		emu_wrptr(sc, v->vnum, EMU_CHAN_CVCF, 0x0000ffff);
738c7e0c9dbSPedro F. Giffuni 		emu_wrptr(sc, v->vnum, EMU_CHAN_IP, 0);
7396c1146c0SCameron Grant 		emu_enastop(sc, v->vnum, 1);
740c067afaaSCameron Grant 	}
7412d3ce9d5SCameron Grant 	if (v->slave != NULL)
7422d3ce9d5SCameron Grant 		emu_vtrigger(sc, v->slave, go);
743c067afaaSCameron Grant }
744c067afaaSCameron Grant 
745c067afaaSCameron Grant static int
746c067afaaSCameron Grant emu_vpos(struct sc_info *sc, struct emu_voice *v)
747c067afaaSCameron Grant {
74870776a9cSCameron Grant 	int s, ptr;
74970776a9cSCameron Grant 
750c067afaaSCameron Grant 	s = (v->b16 ? 1 : 0) + (v->stereo ? 1 : 0);
751c7e0c9dbSPedro F. Giffuni 	ptr = (emu_rdptr(sc, v->vnum, EMU_CHAN_CCCA_CURRADDR) - (v->start >> s)) << s;
7526c1146c0SCameron Grant 	return ptr & ~0x0000001f;
753c067afaaSCameron Grant }
754c067afaaSCameron Grant 
755c067afaaSCameron Grant #ifdef EMUDEBUG
756c067afaaSCameron Grant static void
757c067afaaSCameron Grant emu_vdump(struct sc_info *sc, struct emu_voice *v)
758c067afaaSCameron Grant {
75982d4d32dSDavid E. O'Brien 	char *regname[] = {
76082d4d32dSDavid E. O'Brien 		"cpf", "ptrx", "cvcf", "vtft", "z2", "z1", "psst", "dsl",
761c067afaaSCameron Grant 		"ccca", "ccr", "clp", "fxrt", "mapa", "mapb", NULL, NULL,
762c067afaaSCameron Grant 		"envvol", "atkhldv", "dcysusv", "lfoval1",
763c067afaaSCameron Grant 		"envval", "atkhldm", "dcysusm", "lfoval2",
764c067afaaSCameron Grant 		"ip", "ifatn", "pefe", "fmmod", "tremfrq", "fmfrq2",
76582d4d32dSDavid E. O'Brien 		"tempenv"
76682d4d32dSDavid E. O'Brien 	};
76721f1e37cSDavid E. O'Brien 	char *regname2[] = {
76821f1e37cSDavid E. O'Brien 		"mudata1", "mustat1", "mudata2", "mustat2",
76921f1e37cSDavid E. O'Brien 		"fxwc1", "fxwc2", "spdrate", NULL, NULL,
77021f1e37cSDavid E. O'Brien 		NULL, NULL, NULL, "fxrt2", "sndamnt", "fxrt1",
77121f1e37cSDavid E. O'Brien 		NULL, NULL
77221f1e37cSDavid E. O'Brien 	};
773c067afaaSCameron Grant 	int i, x;
774c067afaaSCameron Grant 
775c067afaaSCameron Grant 	printf("voice number %d\n", v->vnum);
776c067afaaSCameron Grant 	for (i = 0, x = 0; i <= 0x1e; i++) {
777c067afaaSCameron Grant 		if (regname[i] == NULL)
778c067afaaSCameron Grant 			continue;
779c067afaaSCameron Grant 		printf("%s\t[%08x]", regname[i], emu_rdptr(sc, v->vnum, i));
780c067afaaSCameron Grant 		printf("%s", (x == 2) ? "\n" : "\t");
781c067afaaSCameron Grant 		x++;
782c067afaaSCameron Grant 		if (x > 2)
783c067afaaSCameron Grant 			x = 0;
784c067afaaSCameron Grant 	}
78521f1e37cSDavid E. O'Brien 
78621f1e37cSDavid E. O'Brien 	/* Print out audigy extra registers */
78721f1e37cSDavid E. O'Brien 	if (sc->audigy) {
78821f1e37cSDavid E. O'Brien 		for (i = 0; i <= 0xe; i++) {
78921f1e37cSDavid E. O'Brien 			if (regname2[i] == NULL)
79021f1e37cSDavid E. O'Brien 				continue;
79121f1e37cSDavid E. O'Brien 			printf("%s\t[%08x]", regname2[i],
79221f1e37cSDavid E. O'Brien 			    emu_rdptr(sc, v->vnum, i + 0x70));
79321f1e37cSDavid E. O'Brien 			printf("%s", (x == 2)? "\n" : "\t");
79421f1e37cSDavid E. O'Brien 			x++;
79521f1e37cSDavid E. O'Brien 			if (x > 2)
79621f1e37cSDavid E. O'Brien 				x = 0;
79721f1e37cSDavid E. O'Brien 		}
79821f1e37cSDavid E. O'Brien 	}
799c067afaaSCameron Grant 	printf("\n\n");
800c067afaaSCameron Grant }
801c067afaaSCameron Grant #endif
802c067afaaSCameron Grant 
803c067afaaSCameron Grant /* channel interface */
8040f55ac6cSCameron Grant static void *
80521f1e37cSDavid E. O'Brien emupchan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b,
80621f1e37cSDavid E. O'Brien     struct pcm_channel *c, int dir)
807c067afaaSCameron Grant {
808c067afaaSCameron Grant 	struct sc_info *sc = devinfo;
80970776a9cSCameron Grant 	struct sc_pchinfo *ch;
81066ef8af5SCameron Grant 	void *r;
811c067afaaSCameron Grant 
81270776a9cSCameron Grant 	KASSERT(dir == PCMDIR_PLAY, ("emupchan_init: bad direction"));
81370776a9cSCameron Grant 	ch = &sc->pch[sc->pnum++];
814c067afaaSCameron Grant 	ch->buffer = b;
815c067afaaSCameron Grant 	ch->parent = sc;
816c067afaaSCameron Grant 	ch->channel = c;
817a791cfeeSCameron Grant 	ch->blksz = sc->bufsz / 2;
81890da2b28SAriff Abdullah 	ch->fmt = SND_FORMAT(AFMT_U8, 1, 0);
819350a5fafSCameron Grant 	ch->spd = 8000;
82066ef8af5SCameron Grant 	snd_mtxlock(sc->lock);
821c067afaaSCameron Grant 	ch->master = emu_valloc(sc);
822c067afaaSCameron Grant 	ch->slave = emu_valloc(sc);
82366ef8af5SCameron Grant 	snd_mtxunlock(sc->lock);
82421f1e37cSDavid E. O'Brien 	r = (emu_vinit(sc, ch->master, ch->slave, sc->bufsz, ch->buffer))
82521f1e37cSDavid E. O'Brien 	    ? NULL : ch;
82666ef8af5SCameron Grant 
82766ef8af5SCameron Grant 	return r;
828c067afaaSCameron Grant }
829c067afaaSCameron Grant 
830c067afaaSCameron Grant static int
8310f55ac6cSCameron Grant emupchan_free(kobj_t obj, void *data)
83233dbf14aSCameron Grant {
83333dbf14aSCameron Grant 	struct sc_pchinfo *ch = data;
83433dbf14aSCameron Grant 	struct sc_info *sc = ch->parent;
83566ef8af5SCameron Grant 	int r;
83633dbf14aSCameron Grant 
83766ef8af5SCameron Grant 	snd_mtxlock(sc->lock);
83866ef8af5SCameron Grant 	r = emu_memfree(sc, sndbuf_getbuf(ch->buffer));
83966ef8af5SCameron Grant 	snd_mtxunlock(sc->lock);
84066ef8af5SCameron Grant 
84166ef8af5SCameron Grant 	return r;
84233dbf14aSCameron Grant }
84333dbf14aSCameron Grant 
84433dbf14aSCameron Grant static int
8450f55ac6cSCameron Grant emupchan_setformat(kobj_t obj, void *data, u_int32_t format)
846c067afaaSCameron Grant {
84770776a9cSCameron Grant 	struct sc_pchinfo *ch = data;
848c067afaaSCameron Grant 
849c067afaaSCameron Grant 	ch->fmt = format;
850c067afaaSCameron Grant 	return 0;
851c067afaaSCameron Grant }
852c067afaaSCameron Grant 
85390da2b28SAriff Abdullah static u_int32_t
8540f55ac6cSCameron Grant emupchan_setspeed(kobj_t obj, void *data, u_int32_t speed)
855c067afaaSCameron Grant {
85670776a9cSCameron Grant 	struct sc_pchinfo *ch = data;
857c067afaaSCameron Grant 
858c067afaaSCameron Grant 	ch->spd = speed;
859c067afaaSCameron Grant 	return ch->spd;
860c067afaaSCameron Grant }
861c067afaaSCameron Grant 
86290da2b28SAriff Abdullah static u_int32_t
8630f55ac6cSCameron Grant emupchan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize)
864c067afaaSCameron Grant {
865350a5fafSCameron Grant 	struct sc_pchinfo *ch = data;
866350a5fafSCameron Grant 	struct sc_info *sc = ch->parent;
867350a5fafSCameron Grant 	int irqrate, blksz;
868350a5fafSCameron Grant 
869350a5fafSCameron Grant 	ch->blksz = blocksize;
87066ef8af5SCameron Grant 	snd_mtxlock(sc->lock);
871350a5fafSCameron Grant 	emu_settimer(sc);
872350a5fafSCameron Grant 	irqrate = 48000 / sc->timerinterval;
87366ef8af5SCameron Grant 	snd_mtxunlock(sc->lock);
87490da2b28SAriff Abdullah 	blksz = (ch->spd * sndbuf_getalign(ch->buffer)) / irqrate;
875c067afaaSCameron Grant 	return blocksize;
876c067afaaSCameron Grant }
877c067afaaSCameron Grant 
878c067afaaSCameron Grant static int
8790f55ac6cSCameron Grant emupchan_trigger(kobj_t obj, void *data, int go)
880c067afaaSCameron Grant {
88170776a9cSCameron Grant 	struct sc_pchinfo *ch = data;
882c067afaaSCameron Grant 	struct sc_info *sc = ch->parent;
883c067afaaSCameron Grant 
884bdfbdcecSAriff Abdullah 	if (!PCMTRIG_COMMON(go))
88535f9e4a1SCameron Grant 		return 0;
88635f9e4a1SCameron Grant 
88766ef8af5SCameron Grant 	snd_mtxlock(sc->lock);
888c067afaaSCameron Grant 	if (go == PCMTRIG_START) {
889c067afaaSCameron Grant 		emu_vsetup(ch);
890c067afaaSCameron Grant 		emu_vwrite(sc, ch->master);
891350a5fafSCameron Grant 		emu_settimer(sc);
8926c1146c0SCameron Grant 		emu_enatimer(sc, 1);
893c067afaaSCameron Grant #ifdef EMUDEBUG
894c067afaaSCameron Grant 		printf("start [%d bit, %s, %d hz]\n",
895c067afaaSCameron Grant 			ch->master->b16 ? 16 : 8,
896c067afaaSCameron Grant 			ch->master->stereo ? "stereo" : "mono",
897c067afaaSCameron Grant 			ch->master->speed);
898c067afaaSCameron Grant 		emu_vdump(sc, ch->master);
899c067afaaSCameron Grant 		emu_vdump(sc, ch->slave);
900c067afaaSCameron Grant #endif
901c067afaaSCameron Grant 	}
90270776a9cSCameron Grant 	ch->run = (go == PCMTRIG_START) ? 1 : 0;
90370776a9cSCameron Grant 	emu_vtrigger(sc, ch->master, ch->run);
90466ef8af5SCameron Grant 	snd_mtxunlock(sc->lock);
905c067afaaSCameron Grant 	return 0;
906c067afaaSCameron Grant }
907c067afaaSCameron Grant 
90890da2b28SAriff Abdullah static u_int32_t
9090f55ac6cSCameron Grant emupchan_getptr(kobj_t obj, void *data)
910c067afaaSCameron Grant {
91170776a9cSCameron Grant 	struct sc_pchinfo *ch = data;
912c067afaaSCameron Grant 	struct sc_info *sc = ch->parent;
91366ef8af5SCameron Grant 	int r;
914c067afaaSCameron Grant 
91566ef8af5SCameron Grant 	snd_mtxlock(sc->lock);
91666ef8af5SCameron Grant 	r = emu_vpos(sc, ch->master);
91766ef8af5SCameron Grant 	snd_mtxunlock(sc->lock);
91866ef8af5SCameron Grant 
91966ef8af5SCameron Grant 	return r;
920c067afaaSCameron Grant }
921c067afaaSCameron Grant 
92266ef8af5SCameron Grant static struct pcmchan_caps *
9230f55ac6cSCameron Grant emupchan_getcaps(kobj_t obj, void *data)
924c067afaaSCameron Grant {
92570776a9cSCameron Grant 	return &emu_playcaps;
92670776a9cSCameron Grant }
927c067afaaSCameron Grant 
9280f55ac6cSCameron Grant static kobj_method_t emupchan_methods[] = {
9290f55ac6cSCameron Grant 	KOBJMETHOD(channel_init,		emupchan_init),
9300f55ac6cSCameron Grant 	KOBJMETHOD(channel_free,		emupchan_free),
9310f55ac6cSCameron Grant 	KOBJMETHOD(channel_setformat,		emupchan_setformat),
9320f55ac6cSCameron Grant 	KOBJMETHOD(channel_setspeed,		emupchan_setspeed),
9330f55ac6cSCameron Grant 	KOBJMETHOD(channel_setblocksize,	emupchan_setblocksize),
9340f55ac6cSCameron Grant 	KOBJMETHOD(channel_trigger,		emupchan_trigger),
9350f55ac6cSCameron Grant 	KOBJMETHOD(channel_getptr,		emupchan_getptr),
9360f55ac6cSCameron Grant 	KOBJMETHOD(channel_getcaps,		emupchan_getcaps),
93790da2b28SAriff Abdullah 	KOBJMETHOD_END
9380f55ac6cSCameron Grant };
9390f55ac6cSCameron Grant CHANNEL_DECLARE(emupchan);
9400f55ac6cSCameron Grant 
94170776a9cSCameron Grant /* channel interface */
94270776a9cSCameron Grant static void *
94321f1e37cSDavid E. O'Brien emurchan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b,
94421f1e37cSDavid E. O'Brien     struct pcm_channel *c, int dir)
94570776a9cSCameron Grant {
94670776a9cSCameron Grant 	struct sc_info *sc = devinfo;
94770776a9cSCameron Grant 	struct sc_rchinfo *ch;
94870776a9cSCameron Grant 
9496c1146c0SCameron Grant 	KASSERT(dir == PCMDIR_REC, ("emurchan_init: bad direction"));
95070776a9cSCameron Grant 	ch = &sc->rch[sc->rnum];
95170776a9cSCameron Grant 	ch->buffer = b;
95270776a9cSCameron Grant 	ch->parent = sc;
95370776a9cSCameron Grant 	ch->channel = c;
954a791cfeeSCameron Grant 	ch->blksz = sc->bufsz / 2;
95590da2b28SAriff Abdullah 	ch->fmt = SND_FORMAT(AFMT_U8, 1, 0);
95670776a9cSCameron Grant 	ch->spd = 8000;
95770776a9cSCameron Grant 	ch->num = sc->rnum;
95870776a9cSCameron Grant 	switch(sc->rnum) {
95970776a9cSCameron Grant 	case 0:
960c7e0c9dbSPedro F. Giffuni 		ch->idxreg = sc->audigy ? EMU_A_ADCIDX : EMU_ADCIDX;
961c7e0c9dbSPedro F. Giffuni 		ch->basereg = EMU_ADCBA;
962c7e0c9dbSPedro F. Giffuni 		ch->sizereg = EMU_ADCBS;
963c7e0c9dbSPedro F. Giffuni 		ch->setupreg = EMU_ADCCR;
964c7e0c9dbSPedro F. Giffuni 		ch->irqmask = EMU_INTE_ADCBUFENABLE;
96570776a9cSCameron Grant 		break;
96670776a9cSCameron Grant 
96770776a9cSCameron Grant 	case 1:
968c7e0c9dbSPedro F. Giffuni 		ch->idxreg = EMU_FXIDX;
969c7e0c9dbSPedro F. Giffuni 		ch->basereg = EMU_FXBA;
970c7e0c9dbSPedro F. Giffuni 		ch->sizereg = EMU_FXBS;
971c7e0c9dbSPedro F. Giffuni 		ch->setupreg = EMU_FXWC;
972c7e0c9dbSPedro F. Giffuni 		ch->irqmask = EMU_INTE_EFXBUFENABLE;
97370776a9cSCameron Grant 		break;
974a791cfeeSCameron Grant 
975a791cfeeSCameron Grant 	case 2:
976c7e0c9dbSPedro F. Giffuni 		ch->idxreg = EMU_MICIDX;
977c7e0c9dbSPedro F. Giffuni 		ch->basereg = EMU_MICBA;
978c7e0c9dbSPedro F. Giffuni 		ch->sizereg = EMU_MICBS;
979a791cfeeSCameron Grant 		ch->setupreg = 0;
980c7e0c9dbSPedro F. Giffuni 		ch->irqmask = EMU_INTE_MICBUFENABLE;
981a791cfeeSCameron Grant 		break;
98270776a9cSCameron Grant 	}
98370776a9cSCameron Grant 	sc->rnum++;
9842e334adfSAriff Abdullah 	if (sndbuf_alloc(ch->buffer, sc->parent_dmat, 0, sc->bufsz) != 0)
98570776a9cSCameron Grant 		return NULL;
98670776a9cSCameron Grant 	else {
98766ef8af5SCameron Grant 		snd_mtxlock(sc->lock);
98838cc9942SOlivier Houchard 		emu_wrptr(sc, 0, ch->basereg, sndbuf_getbufaddr(ch->buffer));
98970776a9cSCameron Grant 		emu_wrptr(sc, 0, ch->sizereg, 0); /* off */
99066ef8af5SCameron Grant 		snd_mtxunlock(sc->lock);
99170776a9cSCameron Grant 		return ch;
99270776a9cSCameron Grant 	}
99370776a9cSCameron Grant }
99470776a9cSCameron Grant 
99570776a9cSCameron Grant static int
9960f55ac6cSCameron Grant emurchan_setformat(kobj_t obj, void *data, u_int32_t format)
99770776a9cSCameron Grant {
99870776a9cSCameron Grant 	struct sc_rchinfo *ch = data;
99970776a9cSCameron Grant 
100070776a9cSCameron Grant 	ch->fmt = format;
100170776a9cSCameron Grant 	return 0;
100270776a9cSCameron Grant }
100370776a9cSCameron Grant 
100490da2b28SAriff Abdullah static u_int32_t
10050f55ac6cSCameron Grant emurchan_setspeed(kobj_t obj, void *data, u_int32_t speed)
100670776a9cSCameron Grant {
100770776a9cSCameron Grant 	struct sc_rchinfo *ch = data;
100870776a9cSCameron Grant 
100921f1e37cSDavid E. O'Brien 	if (ch->num == 0) {
101021f1e37cSDavid E. O'Brien 		if (ch->parent->audigy)
101121f1e37cSDavid E. O'Brien 			speed = audigy_adcspeed[audigy_recval(speed)];
101221f1e37cSDavid E. O'Brien 		else
101370776a9cSCameron Grant 			speed = adcspeed[emu_recval(speed)];
101421f1e37cSDavid E. O'Brien 	}
101570776a9cSCameron Grant 	if (ch->num == 1)
101670776a9cSCameron Grant 		speed = 48000;
1017a791cfeeSCameron Grant 	if (ch->num == 2)
1018a791cfeeSCameron Grant 		speed = 8000;
101970776a9cSCameron Grant 	ch->spd = speed;
102070776a9cSCameron Grant 	return ch->spd;
102170776a9cSCameron Grant }
102270776a9cSCameron Grant 
102390da2b28SAriff Abdullah static u_int32_t
10240f55ac6cSCameron Grant emurchan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize)
102570776a9cSCameron Grant {
1026350a5fafSCameron Grant 	struct sc_rchinfo *ch = data;
1027350a5fafSCameron Grant 	struct sc_info *sc = ch->parent;
1028350a5fafSCameron Grant 	int irqrate, blksz;
1029350a5fafSCameron Grant 
1030350a5fafSCameron Grant 	ch->blksz = blocksize;
103166ef8af5SCameron Grant 	snd_mtxlock(sc->lock);
1032350a5fafSCameron Grant 	emu_settimer(sc);
1033350a5fafSCameron Grant 	irqrate = 48000 / sc->timerinterval;
103466ef8af5SCameron Grant 	snd_mtxunlock(sc->lock);
103590da2b28SAriff Abdullah 	blksz = (ch->spd * sndbuf_getalign(ch->buffer)) / irqrate;
103670776a9cSCameron Grant 	return blocksize;
103770776a9cSCameron Grant }
103870776a9cSCameron Grant 
103970776a9cSCameron Grant /* semantic note: must start at beginning of buffer */
104070776a9cSCameron Grant static int
10410f55ac6cSCameron Grant emurchan_trigger(kobj_t obj, void *data, int go)
104270776a9cSCameron Grant {
104370776a9cSCameron Grant 	struct sc_rchinfo *ch = data;
104470776a9cSCameron Grant 	struct sc_info *sc = ch->parent;
1045a791cfeeSCameron Grant 	u_int32_t val, sz;
1046a791cfeeSCameron Grant 
1047bdfbdcecSAriff Abdullah 	if (!PCMTRIG_COMMON(go))
1048bdfbdcecSAriff Abdullah 		return 0;
1049bdfbdcecSAriff Abdullah 
1050a791cfeeSCameron Grant 	switch(sc->bufsz) {
1051a791cfeeSCameron Grant 	case 4096:
1052c7e0c9dbSPedro F. Giffuni 		sz = EMU_RECBS_BUFSIZE_4096;
1053a791cfeeSCameron Grant 		break;
1054a791cfeeSCameron Grant 
1055a791cfeeSCameron Grant 	case 8192:
1056c7e0c9dbSPedro F. Giffuni 		sz = EMU_RECBS_BUFSIZE_8192;
1057a791cfeeSCameron Grant 		break;
1058a791cfeeSCameron Grant 
1059a791cfeeSCameron Grant 	case 16384:
1060c7e0c9dbSPedro F. Giffuni 		sz = EMU_RECBS_BUFSIZE_16384;
1061a791cfeeSCameron Grant 		break;
1062a791cfeeSCameron Grant 
1063a791cfeeSCameron Grant 	case 32768:
1064c7e0c9dbSPedro F. Giffuni 		sz = EMU_RECBS_BUFSIZE_32768;
1065a791cfeeSCameron Grant 		break;
1066a791cfeeSCameron Grant 
1067a791cfeeSCameron Grant 	case 65536:
1068c7e0c9dbSPedro F. Giffuni 		sz = EMU_RECBS_BUFSIZE_65536;
1069a791cfeeSCameron Grant 		break;
1070a791cfeeSCameron Grant 
1071a791cfeeSCameron Grant 	default:
1072c7e0c9dbSPedro F. Giffuni 		sz = EMU_RECBS_BUFSIZE_4096;
1073a791cfeeSCameron Grant 	}
107470776a9cSCameron Grant 
107566ef8af5SCameron Grant 	snd_mtxlock(sc->lock);
107670776a9cSCameron Grant 	switch(go) {
107770776a9cSCameron Grant 	case PCMTRIG_START:
107870776a9cSCameron Grant 		ch->run = 1;
1079a791cfeeSCameron Grant 		emu_wrptr(sc, 0, ch->sizereg, sz);
108070776a9cSCameron Grant 		if (ch->num == 0) {
108121f1e37cSDavid E. O'Brien 			if (sc->audigy) {
10827523592fSPedro F. Giffuni 				val = EMU_A_ADCCR_LCHANENABLE;
108390da2b28SAriff Abdullah 				if (AFMT_CHANNEL(ch->fmt) > 1)
10847523592fSPedro F. Giffuni 					val |= EMU_A_ADCCR_RCHANENABLE;
108521f1e37cSDavid E. O'Brien 				val |= audigy_recval(ch->spd);
108621f1e37cSDavid E. O'Brien 			} else {
1087c7e0c9dbSPedro F. Giffuni 				val = EMU_ADCCR_LCHANENABLE;
108890da2b28SAriff Abdullah 				if (AFMT_CHANNEL(ch->fmt) > 1)
1089c7e0c9dbSPedro F. Giffuni 					val |= EMU_ADCCR_RCHANENABLE;
109070776a9cSCameron Grant 				val |= emu_recval(ch->spd);
109121f1e37cSDavid E. O'Brien 			}
109221f1e37cSDavid E. O'Brien 
1093a791cfeeSCameron Grant 			emu_wrptr(sc, 0, ch->setupreg, 0);
109470776a9cSCameron Grant 			emu_wrptr(sc, 0, ch->setupreg, val);
109570776a9cSCameron Grant 		}
1096c7e0c9dbSPedro F. Giffuni 		val = emu_rd(sc, EMU_INTE, 4);
109770776a9cSCameron Grant 		val |= ch->irqmask;
1098c7e0c9dbSPedro F. Giffuni 		emu_wr(sc, EMU_INTE, val, 4);
109970776a9cSCameron Grant 		break;
110070776a9cSCameron Grant 
110170776a9cSCameron Grant 	case PCMTRIG_STOP:
110270776a9cSCameron Grant 	case PCMTRIG_ABORT:
110370776a9cSCameron Grant 		ch->run = 0;
110470776a9cSCameron Grant 		emu_wrptr(sc, 0, ch->sizereg, 0);
110570776a9cSCameron Grant 		if (ch->setupreg)
110670776a9cSCameron Grant 			emu_wrptr(sc, 0, ch->setupreg, 0);
1107c7e0c9dbSPedro F. Giffuni 		val = emu_rd(sc, EMU_INTE, 4);
110870776a9cSCameron Grant 		val &= ~ch->irqmask;
1109c7e0c9dbSPedro F. Giffuni 		emu_wr(sc, EMU_INTE, val, 4);
111070776a9cSCameron Grant 		break;
111170776a9cSCameron Grant 
111270776a9cSCameron Grant 	case PCMTRIG_EMLDMAWR:
111370776a9cSCameron Grant 	case PCMTRIG_EMLDMARD:
111470776a9cSCameron Grant 	default:
111570776a9cSCameron Grant 		break;
111670776a9cSCameron Grant 	}
111766ef8af5SCameron Grant 	snd_mtxunlock(sc->lock);
111870776a9cSCameron Grant 
111970776a9cSCameron Grant 	return 0;
112070776a9cSCameron Grant }
112170776a9cSCameron Grant 
112290da2b28SAriff Abdullah static u_int32_t
11230f55ac6cSCameron Grant emurchan_getptr(kobj_t obj, void *data)
112470776a9cSCameron Grant {
112570776a9cSCameron Grant 	struct sc_rchinfo *ch = data;
112670776a9cSCameron Grant 	struct sc_info *sc = ch->parent;
112766ef8af5SCameron Grant 	int r;
112870776a9cSCameron Grant 
112966ef8af5SCameron Grant 	snd_mtxlock(sc->lock);
113066ef8af5SCameron Grant 	r = emu_rdptr(sc, 0, ch->idxreg) & 0x0000ffff;
113166ef8af5SCameron Grant 	snd_mtxunlock(sc->lock);
113266ef8af5SCameron Grant 
113366ef8af5SCameron Grant 	return r;
113470776a9cSCameron Grant }
113570776a9cSCameron Grant 
113666ef8af5SCameron Grant static struct pcmchan_caps *
11370f55ac6cSCameron Grant emurchan_getcaps(kobj_t obj, void *data)
113870776a9cSCameron Grant {
113970776a9cSCameron Grant 	struct sc_rchinfo *ch = data;
114070776a9cSCameron Grant 
114170776a9cSCameron Grant 	return &emu_reccaps[ch->num];
1142c067afaaSCameron Grant }
1143c067afaaSCameron Grant 
1144a791cfeeSCameron Grant static kobj_method_t emurchan_methods[] = {
1145a791cfeeSCameron Grant 	KOBJMETHOD(channel_init,		emurchan_init),
1146a791cfeeSCameron Grant 	KOBJMETHOD(channel_setformat,		emurchan_setformat),
1147a791cfeeSCameron Grant 	KOBJMETHOD(channel_setspeed,		emurchan_setspeed),
1148a791cfeeSCameron Grant 	KOBJMETHOD(channel_setblocksize,	emurchan_setblocksize),
1149a791cfeeSCameron Grant 	KOBJMETHOD(channel_trigger,		emurchan_trigger),
1150a791cfeeSCameron Grant 	KOBJMETHOD(channel_getptr,		emurchan_getptr),
1151a791cfeeSCameron Grant 	KOBJMETHOD(channel_getcaps,		emurchan_getcaps),
115290da2b28SAriff Abdullah 	KOBJMETHOD_END
1153a791cfeeSCameron Grant };
1154a791cfeeSCameron Grant CHANNEL_DECLARE(emurchan);
1155a791cfeeSCameron Grant 
1156f510d240SAlexander Leidinger static unsigned char
115790da2b28SAriff Abdullah emu_mread(struct mpu401 *arg, void *sc, int reg)
1158f510d240SAlexander Leidinger {
1159f510d240SAlexander Leidinger 	unsigned int d;
1160f510d240SAlexander Leidinger 
116190da2b28SAriff Abdullah 	d = emu_rd((struct sc_info *)sc, 0x18 + reg, 1);
1162f510d240SAlexander Leidinger 	return d;
1163f510d240SAlexander Leidinger }
1164f510d240SAlexander Leidinger 
1165f510d240SAlexander Leidinger static void
116690da2b28SAriff Abdullah emu_mwrite(struct mpu401 *arg, void *sc, int reg, unsigned char b)
1167f510d240SAlexander Leidinger {
1168f510d240SAlexander Leidinger 
116990da2b28SAriff Abdullah 	emu_wr((struct sc_info *)sc, 0x18 + reg, b, 1);
1170f510d240SAlexander Leidinger }
1171f510d240SAlexander Leidinger 
1172f510d240SAlexander Leidinger static int
117390da2b28SAriff Abdullah emu_muninit(struct mpu401 *arg, void *cookie)
1174f510d240SAlexander Leidinger {
117590da2b28SAriff Abdullah 	struct sc_info *sc = cookie;
1176f510d240SAlexander Leidinger 
1177f510d240SAlexander Leidinger 	snd_mtxlock(sc->lock);
1178f510d240SAlexander Leidinger 	sc->mpu_intr = 0;
1179f510d240SAlexander Leidinger 	snd_mtxunlock(sc->lock);
1180f510d240SAlexander Leidinger 
1181f510d240SAlexander Leidinger 	return 0;
1182f510d240SAlexander Leidinger }
1183f510d240SAlexander Leidinger 
1184f510d240SAlexander Leidinger static kobj_method_t emu_mpu_methods[] = {
1185f510d240SAlexander Leidinger     	KOBJMETHOD(mpufoi_read,		emu_mread),
1186f510d240SAlexander Leidinger     	KOBJMETHOD(mpufoi_write,	emu_mwrite),
1187f510d240SAlexander Leidinger     	KOBJMETHOD(mpufoi_uninit,	emu_muninit),
118890da2b28SAriff Abdullah 	KOBJMETHOD_END
1189f510d240SAlexander Leidinger };
1190f510d240SAlexander Leidinger 
119175d7240eSAlexander Leidinger static DEFINE_CLASS(emu_mpu, emu_mpu_methods, 0);
1192f510d240SAlexander Leidinger 
1193f510d240SAlexander Leidinger static void
1194f510d240SAlexander Leidinger emu_intr2(void *p)
1195f510d240SAlexander Leidinger {
1196f510d240SAlexander Leidinger 	struct sc_info *sc = (struct sc_info *)p;
1197f510d240SAlexander Leidinger 
1198f510d240SAlexander Leidinger 	if (sc->mpu_intr)
1199f510d240SAlexander Leidinger 	    (sc->mpu_intr)(sc->mpu);
1200f510d240SAlexander Leidinger }
1201f510d240SAlexander Leidinger 
1202f510d240SAlexander Leidinger static void
1203f510d240SAlexander Leidinger emu_midiattach(struct sc_info *sc)
1204f510d240SAlexander Leidinger {
1205f510d240SAlexander Leidinger 	int i;
1206f510d240SAlexander Leidinger 
1207c7e0c9dbSPedro F. Giffuni 	i = emu_rd(sc, EMU_INTE, 4);
1208c7e0c9dbSPedro F. Giffuni 	i |= EMU_INTE_MIDIRXENABLE;
1209c7e0c9dbSPedro F. Giffuni 	emu_wr(sc, EMU_INTE, i, 4);
1210f510d240SAlexander Leidinger 
1211f510d240SAlexander Leidinger 	sc->mpu = mpu401_init(&emu_mpu_class, sc, emu_intr2, &sc->mpu_intr);
1212f510d240SAlexander Leidinger }
1213a791cfeeSCameron Grant /* -------------------------------------------------------------------- */
1214c067afaaSCameron Grant /* The interrupt handler */
1215f510d240SAlexander Leidinger 
1216c067afaaSCameron Grant static void
121705478063SMaxime Henrion emu_intr(void *data)
1218c067afaaSCameron Grant {
121905478063SMaxime Henrion 	struct sc_info *sc = data;
12206c1146c0SCameron Grant 	u_int32_t stat, ack, i, x;
1221c067afaaSCameron Grant 
122205478063SMaxime Henrion 	snd_mtxlock(sc->lock);
122370776a9cSCameron Grant 	while (1) {
1224c7e0c9dbSPedro F. Giffuni 		stat = emu_rd(sc, EMU_IPR, 4);
122570776a9cSCameron Grant 		if (stat == 0)
122670776a9cSCameron Grant 			break;
122770776a9cSCameron Grant 		ack = 0;
1228c067afaaSCameron Grant 
1229c067afaaSCameron Grant 		/* process irq */
1230c7e0c9dbSPedro F. Giffuni 		if (stat & EMU_IPR_INTERVALTIMER)
1231c7e0c9dbSPedro F. Giffuni 			ack |= EMU_IPR_INTERVALTIMER;
123270776a9cSCameron Grant 
1233c7e0c9dbSPedro F. Giffuni 		if (stat & (EMU_IPR_ADCBUFFULL | EMU_IPR_ADCBUFHALFFULL))
1234c7e0c9dbSPedro F. Giffuni 			ack |= stat & (EMU_IPR_ADCBUFFULL | EMU_IPR_ADCBUFHALFFULL);
123505478063SMaxime Henrion 
1236c7e0c9dbSPedro F. Giffuni 		if (stat & (EMU_IPR_EFXBUFFULL | EMU_IPR_EFXBUFHALFFULL))
1237c7e0c9dbSPedro F. Giffuni 			ack |= stat & (EMU_IPR_EFXBUFFULL | EMU_IPR_EFXBUFHALFFULL);
123805478063SMaxime Henrion 
1239c7e0c9dbSPedro F. Giffuni 		if (stat & (EMU_IPR_MICBUFFULL | EMU_IPR_MICBUFHALFFULL))
1240c7e0c9dbSPedro F. Giffuni 			ack |= stat & (EMU_IPR_MICBUFFULL | EMU_IPR_MICBUFHALFFULL);
124105478063SMaxime Henrion 
1242c7e0c9dbSPedro F. Giffuni 		if (stat & EMU_PCIERROR) {
1243c7e0c9dbSPedro F. Giffuni 			ack |= EMU_PCIERROR;
124470776a9cSCameron Grant 			device_printf(sc->dev, "pci error\n");
12456c1146c0SCameron Grant 			/* we still get an nmi with ecc ram even if we ack this */
12466c1146c0SCameron Grant 		}
1247c7e0c9dbSPedro F. Giffuni 		if (stat & EMU_IPR_RATETRCHANGE) {
1248c7e0c9dbSPedro F. Giffuni 			ack |= EMU_IPR_RATETRCHANGE;
124921f1e37cSDavid E. O'Brien #ifdef EMUDEBUG
125021f1e37cSDavid E. O'Brien 			device_printf(sc->dev,
125121f1e37cSDavid E. O'Brien 			    "sample rate tracker lock status change\n");
125221f1e37cSDavid E. O'Brien #endif
125370776a9cSCameron Grant 		}
125470776a9cSCameron Grant 
1255c7e0c9dbSPedro F. Giffuni 	    if (stat & EMU_IPR_MIDIRECVBUFE)
1256f510d240SAlexander Leidinger 		if (sc->mpu_intr) {
1257f510d240SAlexander Leidinger 		    (sc->mpu_intr)(sc->mpu);
1258c7e0c9dbSPedro F. Giffuni 		    ack |= EMU_IPR_MIDIRECVBUFE | EMU_IPR_MIDITRANSBUFE;
1259f510d240SAlexander Leidinger  		}
126070776a9cSCameron Grant 		if (stat & ~ack)
126105478063SMaxime Henrion 			device_printf(sc->dev, "dodgy irq: %x (harmless)\n",
126205478063SMaxime Henrion 			    stat & ~ack);
1263c067afaaSCameron Grant 
1264c7e0c9dbSPedro F. Giffuni 		emu_wr(sc, EMU_IPR, stat, 4);
126505478063SMaxime Henrion 
126605478063SMaxime Henrion 		if (ack) {
126705478063SMaxime Henrion 			snd_mtxunlock(sc->lock);
126805478063SMaxime Henrion 
1269c7e0c9dbSPedro F. Giffuni 			if (ack & EMU_IPR_INTERVALTIMER) {
127005478063SMaxime Henrion 				x = 0;
127105478063SMaxime Henrion 				for (i = 0; i < sc->nchans; i++) {
127205478063SMaxime Henrion 					if (sc->pch[i].run) {
127305478063SMaxime Henrion 						x = 1;
127405478063SMaxime Henrion 						chn_intr(sc->pch[i].channel);
127570776a9cSCameron Grant 					}
1276c067afaaSCameron Grant 				}
127705478063SMaxime Henrion 				if (x == 0)
127805478063SMaxime Henrion 					emu_enatimer(sc, 0);
127905478063SMaxime Henrion 			}
128005478063SMaxime Henrion 
128105478063SMaxime Henrion 
1282c7e0c9dbSPedro F. Giffuni 			if (ack & (EMU_IPR_ADCBUFFULL | EMU_IPR_ADCBUFHALFFULL)) {
128305478063SMaxime Henrion 				if (sc->rch[0].channel)
128405478063SMaxime Henrion 					chn_intr(sc->rch[0].channel);
128505478063SMaxime Henrion 			}
1286c7e0c9dbSPedro F. Giffuni 			if (ack & (EMU_IPR_EFXBUFFULL | EMU_IPR_EFXBUFHALFFULL)) {
128705478063SMaxime Henrion 				if (sc->rch[1].channel)
128805478063SMaxime Henrion 					chn_intr(sc->rch[1].channel);
128905478063SMaxime Henrion 			}
1290c7e0c9dbSPedro F. Giffuni 			if (ack & (EMU_IPR_MICBUFFULL | EMU_IPR_MICBUFHALFFULL)) {
129105478063SMaxime Henrion 				if (sc->rch[2].channel)
129205478063SMaxime Henrion 					chn_intr(sc->rch[2].channel);
129305478063SMaxime Henrion 			}
129405478063SMaxime Henrion 
129505478063SMaxime Henrion 			snd_mtxlock(sc->lock);
129605478063SMaxime Henrion 		}
129705478063SMaxime Henrion 	}
129805478063SMaxime Henrion 	snd_mtxunlock(sc->lock);
129905478063SMaxime Henrion }
1300c067afaaSCameron Grant 
1301c067afaaSCameron Grant /* -------------------------------------------------------------------- */
1302c067afaaSCameron Grant 
130370776a9cSCameron Grant static void
130470776a9cSCameron Grant emu_setmap(void *arg, bus_dma_segment_t *segs, int nseg, int error)
130570776a9cSCameron Grant {
130633673595SOlivier Houchard 	bus_addr_t *phys = arg;
130770776a9cSCameron Grant 
130833673595SOlivier Houchard 	*phys = error ? 0 : (bus_addr_t)segs->ds_addr;
130970776a9cSCameron Grant 
131070776a9cSCameron Grant 	if (bootverbose) {
131170776a9cSCameron Grant 		printf("emu: setmap (%lx, %lx), nseg=%d, error=%d\n",
131270776a9cSCameron Grant 		    (unsigned long)segs->ds_addr, (unsigned long)segs->ds_len,
131370776a9cSCameron Grant 		    nseg, error);
131470776a9cSCameron Grant 	}
131570776a9cSCameron Grant }
131670776a9cSCameron Grant 
1317c067afaaSCameron Grant static void *
131838cc9942SOlivier Houchard emu_malloc(struct sc_info *sc, u_int32_t sz, bus_addr_t *addr)
1319c067afaaSCameron Grant {
132038cc9942SOlivier Houchard 	void *buf;
1321c067afaaSCameron Grant 	bus_dmamap_t map;
1322c067afaaSCameron Grant 
132338cc9942SOlivier Houchard 	*addr = 0;
1324c067afaaSCameron Grant 	if (bus_dmamem_alloc(sc->parent_dmat, &buf, BUS_DMA_NOWAIT, &map))
1325c067afaaSCameron Grant 		return NULL;
132638cc9942SOlivier Houchard 	if (bus_dmamap_load(sc->parent_dmat, map, buf, sz, emu_setmap, addr, 0)
132733673595SOlivier Houchard 	    || !*addr)
132870776a9cSCameron Grant 		return NULL;
1329c067afaaSCameron Grant 	return buf;
1330c067afaaSCameron Grant }
1331c067afaaSCameron Grant 
1332c067afaaSCameron Grant static void
1333c067afaaSCameron Grant emu_free(struct sc_info *sc, void *buf)
1334c067afaaSCameron Grant {
1335c067afaaSCameron Grant 	bus_dmamem_free(sc->parent_dmat, buf, NULL);
1336c067afaaSCameron Grant }
1337c067afaaSCameron Grant 
1338c067afaaSCameron Grant static void *
133938cc9942SOlivier Houchard emu_memalloc(struct sc_info *sc, u_int32_t sz, bus_addr_t *addr)
1340c067afaaSCameron Grant {
1341c067afaaSCameron Grant 	u_int32_t blksz, start, idx, ofs, tmp, found;
1342c067afaaSCameron Grant 	struct emu_mem *mem = &sc->mem;
1343c067afaaSCameron Grant 	struct emu_memblk *blk;
1344c067afaaSCameron Grant 	void *buf;
1345c067afaaSCameron Grant 
1346c067afaaSCameron Grant 	blksz = sz / EMUPAGESIZE;
1347c067afaaSCameron Grant 	if (sz > (blksz * EMUPAGESIZE))
1348c067afaaSCameron Grant 		blksz++;
1349c067afaaSCameron Grant 	/* find a free block in the bitmap */
1350c067afaaSCameron Grant 	found = 0;
13516c1146c0SCameron Grant 	start = 1;
135221f1e37cSDavid E. O'Brien 	while (!found && start + blksz < EMUMAXPAGES) {
1353c067afaaSCameron Grant 		found = 1;
1354c067afaaSCameron Grant 		for (idx = start; idx < start + blksz; idx++)
1355c067afaaSCameron Grant 			if (mem->bmap[idx >> 3] & (1 << (idx & 7)))
1356c067afaaSCameron Grant 				found = 0;
1357c067afaaSCameron Grant 		if (!found)
1358c067afaaSCameron Grant 			start++;
1359c067afaaSCameron Grant 	}
1360c067afaaSCameron Grant 	if (!found)
1361c067afaaSCameron Grant 		return NULL;
1362c067afaaSCameron Grant 	blk = malloc(sizeof(*blk), M_DEVBUF, M_NOWAIT);
1363c067afaaSCameron Grant 	if (blk == NULL)
1364c067afaaSCameron Grant 		return NULL;
136538cc9942SOlivier Houchard 	buf = emu_malloc(sc, sz, &blk->buf_addr);
136638cc9942SOlivier Houchard 	*addr = blk->buf_addr;
1367c067afaaSCameron Grant 	if (buf == NULL) {
1368c067afaaSCameron Grant 		free(blk, M_DEVBUF);
1369c067afaaSCameron Grant 		return NULL;
1370c067afaaSCameron Grant 	}
1371c067afaaSCameron Grant 	blk->buf = buf;
1372c067afaaSCameron Grant 	blk->pte_start = start;
1373c067afaaSCameron Grant 	blk->pte_size = blksz;
137421f1e37cSDavid E. O'Brien #ifdef EMUDEBUG
137521f1e37cSDavid E. O'Brien 	printf("buf %p, pte_start %d, pte_size %d\n", blk->buf,
137621f1e37cSDavid E. O'Brien 	    blk->pte_start, blk->pte_size);
137721f1e37cSDavid E. O'Brien #endif
1378c067afaaSCameron Grant 	ofs = 0;
1379c067afaaSCameron Grant 	for (idx = start; idx < start + blksz; idx++) {
1380c067afaaSCameron Grant 		mem->bmap[idx >> 3] |= 1 << (idx & 7);
1381*3ac12483SMarius Strobl 		tmp = (bus_addr_t)((u_int8_t *)(uintptr_t)blk->buf_addr + ofs);
138221f1e37cSDavid E. O'Brien #ifdef EMUDEBUG
138321f1e37cSDavid E. O'Brien 		printf("pte[%d] -> %x phys, %x virt\n", idx, tmp,
138421f1e37cSDavid E. O'Brien 		    ((u_int32_t)buf) + ofs);
138521f1e37cSDavid E. O'Brien #endif
138619b6ac09SCameron Grant 		mem->ptb_pages[idx] = (tmp << 1) | idx;
1387c067afaaSCameron Grant 		ofs += EMUPAGESIZE;
1388c067afaaSCameron Grant 	}
1389c067afaaSCameron Grant 	SLIST_INSERT_HEAD(&mem->blocks, blk, link);
1390c067afaaSCameron Grant 	return buf;
1391c067afaaSCameron Grant }
1392c067afaaSCameron Grant 
1393c067afaaSCameron Grant static int
1394c067afaaSCameron Grant emu_memfree(struct sc_info *sc, void *buf)
1395c067afaaSCameron Grant {
1396c067afaaSCameron Grant 	u_int32_t idx, tmp;
1397c067afaaSCameron Grant 	struct emu_mem *mem = &sc->mem;
1398c067afaaSCameron Grant 	struct emu_memblk *blk, *i;
1399c067afaaSCameron Grant 
1400c067afaaSCameron Grant 	blk = NULL;
1401c067afaaSCameron Grant 	SLIST_FOREACH(i, &mem->blocks, link) {
1402c067afaaSCameron Grant 		if (i->buf == buf)
1403c067afaaSCameron Grant 			blk = i;
1404c067afaaSCameron Grant 	}
1405c067afaaSCameron Grant 	if (blk == NULL)
1406c067afaaSCameron Grant 		return EINVAL;
1407c067afaaSCameron Grant 	SLIST_REMOVE(&mem->blocks, blk, emu_memblk, link);
1408c067afaaSCameron Grant 	emu_free(sc, buf);
140938cc9942SOlivier Houchard 	tmp = (u_int32_t)(sc->mem.silent_page_addr) << 1;
1410c067afaaSCameron Grant 	for (idx = blk->pte_start; idx < blk->pte_start + blk->pte_size; idx++) {
1411c067afaaSCameron Grant 		mem->bmap[idx >> 3] &= ~(1 << (idx & 7));
1412c067afaaSCameron Grant 		mem->ptb_pages[idx] = tmp | idx;
1413c067afaaSCameron Grant 	}
1414c067afaaSCameron Grant 	free(blk, M_DEVBUF);
1415c067afaaSCameron Grant 	return 0;
1416c067afaaSCameron Grant }
1417c067afaaSCameron Grant 
1418c067afaaSCameron Grant static int
1419c067afaaSCameron Grant emu_memstart(struct sc_info *sc, void *buf)
1420c067afaaSCameron Grant {
1421c067afaaSCameron Grant 	struct emu_mem *mem = &sc->mem;
1422c067afaaSCameron Grant 	struct emu_memblk *blk, *i;
1423c067afaaSCameron Grant 
1424c067afaaSCameron Grant 	blk = NULL;
1425c067afaaSCameron Grant 	SLIST_FOREACH(i, &mem->blocks, link) {
1426c067afaaSCameron Grant 		if (i->buf == buf)
1427c067afaaSCameron Grant 			blk = i;
1428c067afaaSCameron Grant 	}
1429c067afaaSCameron Grant 	if (blk == NULL)
1430c067afaaSCameron Grant 		return -EINVAL;
1431c067afaaSCameron Grant 	return blk->pte_start;
1432c067afaaSCameron Grant }
1433c067afaaSCameron Grant 
1434c067afaaSCameron Grant static void
143521f1e37cSDavid E. O'Brien emu_addefxop(struct sc_info *sc, int op, int z, int w, int x, int y,
143621f1e37cSDavid E. O'Brien     u_int32_t *pc)
1437c067afaaSCameron Grant {
1438c067afaaSCameron Grant 	emu_wrefx(sc, (*pc) * 2, (x << 10) | y);
1439c067afaaSCameron Grant 	emu_wrefx(sc, (*pc) * 2 + 1, (op << 20) | (z << 10) | w);
1440c067afaaSCameron Grant 	(*pc)++;
1441c067afaaSCameron Grant }
1442c067afaaSCameron Grant 
1443c067afaaSCameron Grant static void
144421f1e37cSDavid E. O'Brien audigy_addefxop(struct sc_info *sc, int op, int z, int w, int x, int y,
144521f1e37cSDavid E. O'Brien     u_int32_t *pc)
144621f1e37cSDavid E. O'Brien {
144721f1e37cSDavid E. O'Brien 	emu_wrefx(sc, (*pc) * 2, (x << 12) | y);
144821f1e37cSDavid E. O'Brien 	emu_wrefx(sc, (*pc) * 2 + 1, (op << 24) | (z << 12) | w);
144921f1e37cSDavid E. O'Brien 	(*pc)++;
145021f1e37cSDavid E. O'Brien }
145121f1e37cSDavid E. O'Brien 
145221f1e37cSDavid E. O'Brien static void
145321f1e37cSDavid E. O'Brien audigy_initefx(struct sc_info *sc)
145421f1e37cSDavid E. O'Brien {
145521f1e37cSDavid E. O'Brien 	int i;
145621f1e37cSDavid E. O'Brien 	u_int32_t pc = 0;
145721f1e37cSDavid E. O'Brien 
145821f1e37cSDavid E. O'Brien 	/* skip 0, 0, -1, 0 - NOPs */
145921f1e37cSDavid E. O'Brien 	for (i = 0; i < 512; i++)
146021f1e37cSDavid E. O'Brien 		audigy_addefxop(sc, 0x0f, 0x0c0, 0x0c0, 0x0cf, 0x0c0, &pc);
146121f1e37cSDavid E. O'Brien 
146221f1e37cSDavid E. O'Brien 	for (i = 0; i < 512; i++)
1463c7e0c9dbSPedro F. Giffuni 		emu_wrptr(sc, 0, EMU_A_FXGPREGBASE + i, 0x0);
146421f1e37cSDavid E. O'Brien 
146521f1e37cSDavid E. O'Brien 	pc = 16;
146621f1e37cSDavid E. O'Brien 
146721f1e37cSDavid E. O'Brien 	/* stop fx processor */
1468c7e0c9dbSPedro F. Giffuni 	emu_wrptr(sc, 0, EMU_A_DBG, EMU_A_DBG_SINGLE_STEP);
146921f1e37cSDavid E. O'Brien 
147021f1e37cSDavid E. O'Brien 	/* Audigy 2 (EMU10K2) DSP Registers:
147121f1e37cSDavid E. O'Brien 	   FX Bus
147230baa78dSAlexander Leidinger 		0x000-0x00f : 16 registers (?)
147321f1e37cSDavid E. O'Brien 	   Input
147421f1e37cSDavid E. O'Brien 		0x040/0x041 : AC97 Codec (l/r)
147521f1e37cSDavid E. O'Brien 		0x042/0x043 : ADC, S/PDIF (l/r)
147621f1e37cSDavid E. O'Brien 		0x044/0x045 : Optical S/PDIF in (l/r)
147730baa78dSAlexander Leidinger 		0x046/0x047 : ?
147821f1e37cSDavid E. O'Brien 		0x048/0x049 : Line/Mic 2 (l/r)
147921f1e37cSDavid E. O'Brien 		0x04a/0x04b : RCA S/PDIF (l/r)
148021f1e37cSDavid E. O'Brien 		0x04c/0x04d : Aux 2 (l/r)
148121f1e37cSDavid E. O'Brien 	   Output
148221f1e37cSDavid E. O'Brien 		0x060/0x061 : Digital Front (l/r)
148321f1e37cSDavid E. O'Brien 		0x062/0x063 : Digital Center/LFE
148421f1e37cSDavid E. O'Brien 		0x064/0x065 : AudigyDrive Heaphone (l/r)
148521f1e37cSDavid E. O'Brien 		0x066/0x067 : Digital Rear (l/r)
148621f1e37cSDavid E. O'Brien 		0x068/0x069 : Analog Front (l/r)
148721f1e37cSDavid E. O'Brien 		0x06a/0x06b : Analog Center/LFE
148830baa78dSAlexander Leidinger 		0x06c/0x06d : ?
148921f1e37cSDavid E. O'Brien 		0x06e/0x06f : Analog Rear (l/r)
149021f1e37cSDavid E. O'Brien 		0x070/0x071 : AC97 Output (l/r)
149130baa78dSAlexander Leidinger 		0x072/0x073 : ?
149230baa78dSAlexander Leidinger 		0x074/0x075 : ?
149321f1e37cSDavid E. O'Brien 		0x076/0x077 : ADC Recording Buffer (l/r)
149421f1e37cSDavid E. O'Brien 	   Constants
149521f1e37cSDavid E. O'Brien 		0x0c0 - 0x0c4 = 0 - 4
149621f1e37cSDavid E. O'Brien 		0x0c5 = 0x8, 0x0c6 = 0x10, 0x0c7 = 0x20
149721f1e37cSDavid E. O'Brien 		0x0c8 = 0x100, 0x0c9 = 0x10000, 0x0ca = 0x80000
149821f1e37cSDavid E. O'Brien 		0x0cb = 0x10000000, 0x0cc = 0x20000000, 0x0cd = 0x40000000
149921f1e37cSDavid E. O'Brien 		0x0ce = 0x80000000, 0x0cf = 0x7fffffff, 0x0d0 = 0xffffffff
150021f1e37cSDavid E. O'Brien 		0x0d1 = 0xfffffffe, 0x0d2 = 0xc0000000, 0x0d3 = 0x41fbbcdc
150130baa78dSAlexander Leidinger 		0x0d4 = 0x5a7ef9db, 0x0d5 = 0x00100000, 0x0dc = 0x00000001 (?)
150221f1e37cSDavid E. O'Brien 	   Temporary Values
150330baa78dSAlexander Leidinger 		0x0d6 : Accumulator (?)
150421f1e37cSDavid E. O'Brien 		0x0d7 : Condition Register
150521f1e37cSDavid E. O'Brien 		0x0d8 : Noise source
150621f1e37cSDavid E. O'Brien 		0x0d9 : Noise source
150721f1e37cSDavid E. O'Brien 	   Tank Memory Data Registers
150821f1e37cSDavid E. O'Brien 		0x200 - 0x2ff
150921f1e37cSDavid E. O'Brien 	   Tank Memory Address Registers
151021f1e37cSDavid E. O'Brien 		0x300 - 0x3ff
151121f1e37cSDavid E. O'Brien 	   General Purpose Registers
151221f1e37cSDavid E. O'Brien 		0x400 - 0x5ff
151321f1e37cSDavid E. O'Brien 	 */
151421f1e37cSDavid E. O'Brien 
151521f1e37cSDavid E. O'Brien 	/* AC97Output[l/r] = FXBus PCM[l/r] */
151621f1e37cSDavid E. O'Brien 	audigy_addefxop(sc, iACC3, A_EXTOUT(A_EXTOUT_AC97_L), A_C_00000000,
151721f1e37cSDavid E. O'Brien 			A_C_00000000, A_FXBUS(FXBUS_PCM_LEFT), &pc);
151821f1e37cSDavid E. O'Brien 	audigy_addefxop(sc, iACC3, A_EXTOUT(A_EXTOUT_AC97_R), A_C_00000000,
151921f1e37cSDavid E. O'Brien 			A_C_00000000, A_FXBUS(FXBUS_PCM_RIGHT), &pc);
152021f1e37cSDavid E. O'Brien 
152121f1e37cSDavid E. O'Brien 	/* GPR[0/1] = RCA S/PDIF[l/r] -- Master volume */
152221f1e37cSDavid E. O'Brien 	audigy_addefxop(sc, iACC3, A_GPR(0), A_C_00000000,
152321f1e37cSDavid E. O'Brien 			A_C_00000000, A_EXTIN(EXTIN_COAX_SPDIF_L), &pc);
152421f1e37cSDavid E. O'Brien 	audigy_addefxop(sc, iACC3, A_GPR(1), A_C_00000000,
152521f1e37cSDavid E. O'Brien 			A_C_00000000, A_EXTIN(EXTIN_COAX_SPDIF_R), &pc);
152621f1e37cSDavid E. O'Brien 
152721f1e37cSDavid E. O'Brien 	/* GPR[2] = GPR[0] (Left) / 2 + GPR[1] (Right) / 2 -- Central volume */
152821f1e37cSDavid E. O'Brien 	audigy_addefxop(sc, iINTERP, A_GPR(2), A_GPR(1),
152921f1e37cSDavid E. O'Brien 			A_C_40000000, A_GPR(0), &pc);
153021f1e37cSDavid E. O'Brien 
153121f1e37cSDavid E. O'Brien 	/* Headphones[l/r] = GPR[0/1] */
153221f1e37cSDavid E. O'Brien 	audigy_addefxop(sc, iACC3, A_EXTOUT(A_EXTOUT_HEADPHONE_L),
153321f1e37cSDavid E. O'Brien 			A_C_00000000, A_C_00000000, A_GPR(0), &pc);
153421f1e37cSDavid E. O'Brien 	audigy_addefxop(sc, iACC3, A_EXTOUT(A_EXTOUT_HEADPHONE_R),
153521f1e37cSDavid E. O'Brien 			A_C_00000000, A_C_00000000, A_GPR(1), &pc);
153621f1e37cSDavid E. O'Brien 
153721f1e37cSDavid E. O'Brien 	/* Analog Front[l/r] = GPR[0/1] */
153821f1e37cSDavid E. O'Brien 	audigy_addefxop(sc, iACC3, A_EXTOUT(A_EXTOUT_AFRONT_L), A_C_00000000,
153921f1e37cSDavid E. O'Brien 			A_C_00000000, A_GPR(0), &pc);
154021f1e37cSDavid E. O'Brien 	audigy_addefxop(sc, iACC3, A_EXTOUT(A_EXTOUT_AFRONT_R), A_C_00000000,
154121f1e37cSDavid E. O'Brien 			A_C_00000000, A_GPR(1), &pc);
154221f1e37cSDavid E. O'Brien 
154321f1e37cSDavid E. O'Brien 	/* Digital Front[l/r] = GPR[0/1] */
154421f1e37cSDavid E. O'Brien 	audigy_addefxop(sc, iACC3, A_EXTOUT(A_EXTOUT_FRONT_L), A_C_00000000,
154521f1e37cSDavid E. O'Brien 			A_C_00000000, A_GPR(0), &pc);
154621f1e37cSDavid E. O'Brien 	audigy_addefxop(sc, iACC3, A_EXTOUT(A_EXTOUT_FRONT_R), A_C_00000000,
154721f1e37cSDavid E. O'Brien 			A_C_00000000, A_GPR(1), &pc);
154821f1e37cSDavid E. O'Brien 
154921f1e37cSDavid E. O'Brien 	/* Center and Subwoofer configuration */
155021f1e37cSDavid E. O'Brien 	/* Analog Center = GPR[0] + GPR[2] */
155121f1e37cSDavid E. O'Brien 	audigy_addefxop(sc, iACC3, A_EXTOUT(A_EXTOUT_ACENTER), A_C_00000000,
155221f1e37cSDavid E. O'Brien 			A_GPR(0), A_GPR(2), &pc);
155321f1e37cSDavid E. O'Brien 	/* Analog Sub = GPR[1] + GPR[2] */
155421f1e37cSDavid E. O'Brien 	audigy_addefxop(sc, iACC3, A_EXTOUT(A_EXTOUT_ALFE), A_C_00000000,
155521f1e37cSDavid E. O'Brien 			A_GPR(1), A_GPR(2), &pc);
155621f1e37cSDavid E. O'Brien 
155721f1e37cSDavid E. O'Brien 	/* Digital Center = GPR[0] + GPR[2] */
155821f1e37cSDavid E. O'Brien 	audigy_addefxop(sc, iACC3, A_EXTOUT(A_EXTOUT_CENTER), A_C_00000000,
155921f1e37cSDavid E. O'Brien 			A_GPR(0), A_GPR(2), &pc);
156021f1e37cSDavid E. O'Brien 	/* Digital Sub = GPR[1] + GPR[2] */
156121f1e37cSDavid E. O'Brien 	audigy_addefxop(sc, iACC3, A_EXTOUT(A_EXTOUT_LFE), A_C_00000000,
156221f1e37cSDavid E. O'Brien 			A_GPR(1), A_GPR(2), &pc);
156321f1e37cSDavid E. O'Brien 
156421f1e37cSDavid E. O'Brien #if 0
156521f1e37cSDavid E. O'Brien 	/* Analog Rear[l/r] = (GPR[0/1] * RearVolume[l/r]) >> 31 */
156621f1e37cSDavid E. O'Brien 	/*   RearVolume = GPR[0x10/0x11] (Will this ever be implemented?) */
156721f1e37cSDavid E. O'Brien 	audigy_addefxop(sc, iMAC0, A_EXTOUT(A_EXTOUT_AREAR_L), A_C_00000000,
156821f1e37cSDavid E. O'Brien 			A_GPR(16), A_GPR(0), &pc);
156921f1e37cSDavid E. O'Brien 	audigy_addefxop(sc, iMAC0, A_EXTOUT(A_EXTOUT_AREAR_R), A_C_00000000,
157021f1e37cSDavid E. O'Brien 			A_GPR(17), A_GPR(1), &pc);
157121f1e37cSDavid E. O'Brien 
157221f1e37cSDavid E. O'Brien 	/* Digital Rear[l/r] = (GPR[0/1] * RearVolume[l/r]) >> 31 */
157321f1e37cSDavid E. O'Brien 	/*   RearVolume = GPR[0x10/0x11] (Will this ever be implemented?) */
157421f1e37cSDavid E. O'Brien 	audigy_addefxop(sc, iMAC0, A_EXTOUT(A_EXTOUT_REAR_L), A_C_00000000,
157521f1e37cSDavid E. O'Brien 			A_GPR(16), A_GPR(0), &pc);
157621f1e37cSDavid E. O'Brien 	audigy_addefxop(sc, iMAC0, A_EXTOUT(A_EXTOUT_REAR_R), A_C_00000000,
157721f1e37cSDavid E. O'Brien 			A_GPR(17), A_GPR(1), &pc);
157821f1e37cSDavid E. O'Brien #else
157921f1e37cSDavid E. O'Brien 	/* XXX This is just a copy to the channel, since we do not have
158021f1e37cSDavid E. O'Brien 	 *     a patch manager, it is useful for have another output enabled.
158121f1e37cSDavid E. O'Brien 	 */
158221f1e37cSDavid E. O'Brien 
158321f1e37cSDavid E. O'Brien 	/* Analog Rear[l/r] = GPR[0/1] */
158421f1e37cSDavid E. O'Brien 	audigy_addefxop(sc, iACC3, A_EXTOUT(A_EXTOUT_AREAR_L), A_C_00000000,
158521f1e37cSDavid E. O'Brien 			A_C_00000000, A_GPR(0), &pc);
158621f1e37cSDavid E. O'Brien 	audigy_addefxop(sc, iACC3, A_EXTOUT(A_EXTOUT_AREAR_R), A_C_00000000,
158721f1e37cSDavid E. O'Brien 			A_C_00000000, A_GPR(1), &pc);
158821f1e37cSDavid E. O'Brien 
158921f1e37cSDavid E. O'Brien 	/* Digital Rear[l/r] = GPR[0/1] */
159021f1e37cSDavid E. O'Brien 	audigy_addefxop(sc, iACC3, A_EXTOUT(A_EXTOUT_REAR_L), A_C_00000000,
159121f1e37cSDavid E. O'Brien 			A_C_00000000, A_GPR(0), &pc);
159221f1e37cSDavid E. O'Brien 	audigy_addefxop(sc, iACC3, A_EXTOUT(A_EXTOUT_REAR_R), A_C_00000000,
159321f1e37cSDavid E. O'Brien 			A_C_00000000, A_GPR(1), &pc);
159421f1e37cSDavid E. O'Brien #endif
159521f1e37cSDavid E. O'Brien 
159621f1e37cSDavid E. O'Brien 	/* ADC Recording buffer[l/r] = AC97Input[l/r] */
159721f1e37cSDavid E. O'Brien 	audigy_addefxop(sc, iACC3, A_EXTOUT(A_EXTOUT_ADC_CAP_L), A_C_00000000,
159821f1e37cSDavid E. O'Brien 			A_C_00000000, A_EXTIN(A_EXTIN_AC97_L), &pc);
159921f1e37cSDavid E. O'Brien 	audigy_addefxop(sc, iACC3, A_EXTOUT(A_EXTOUT_ADC_CAP_R), A_C_00000000,
160021f1e37cSDavid E. O'Brien 			A_C_00000000, A_EXTIN(A_EXTIN_AC97_R), &pc);
160121f1e37cSDavid E. O'Brien 
160221f1e37cSDavid E. O'Brien 	/* resume normal operations */
1603c7e0c9dbSPedro F. Giffuni 	emu_wrptr(sc, 0, EMU_A_DBG, 0);
160421f1e37cSDavid E. O'Brien }
160521f1e37cSDavid E. O'Brien 
160621f1e37cSDavid E. O'Brien static void
1607c067afaaSCameron Grant emu_initefx(struct sc_info *sc)
1608c067afaaSCameron Grant {
1609c067afaaSCameron Grant 	int i;
1610c067afaaSCameron Grant 	u_int32_t pc = 16;
1611c067afaaSCameron Grant 
161221f1e37cSDavid E. O'Brien 	/* acc3 0,0,0,0 - NOPs */
1613c067afaaSCameron Grant 	for (i = 0; i < 512; i++) {
1614c067afaaSCameron Grant 		emu_wrefx(sc, i * 2, 0x10040);
1615c067afaaSCameron Grant 		emu_wrefx(sc, i * 2 + 1, 0x610040);
1616c067afaaSCameron Grant 	}
1617c067afaaSCameron Grant 
1618c067afaaSCameron Grant 	for (i = 0; i < 256; i++)
1619c7e0c9dbSPedro F. Giffuni 		emu_wrptr(sc, 0, EMU_FXGPREGBASE + i, 0);
1620c067afaaSCameron Grant 
1621c067afaaSCameron Grant 	/* FX-8010 DSP Registers:
1622c067afaaSCameron Grant 	   FX Bus
1623c067afaaSCameron Grant 	     0x000-0x00f : 16 registers
1624c067afaaSCameron Grant 	   Input
1625c067afaaSCameron Grant 	     0x010/0x011 : AC97 Codec (l/r)
1626c067afaaSCameron Grant 	     0x012/0x013 : ADC, S/PDIF (l/r)
1627c067afaaSCameron Grant 	     0x014/0x015 : Mic(left), Zoom (l/r)
162821f1e37cSDavid E. O'Brien 	     0x016/0x017 : TOS link in (l/r)
162921f1e37cSDavid E. O'Brien 	     0x018/0x019 : Line/Mic 1 (l/r)
163021f1e37cSDavid E. O'Brien 	     0x01a/0x01b : COAX S/PDIF (l/r)
163121f1e37cSDavid E. O'Brien 	     0x01c/0x01d : Line/Mic 2 (l/r)
1632c067afaaSCameron Grant 	   Output
1633c067afaaSCameron Grant 	     0x020/0x021 : AC97 Output (l/r)
1634c067afaaSCameron Grant 	     0x022/0x023 : TOS link out (l/r)
163521f1e37cSDavid E. O'Brien 	     0x024/0x025 : Center/LFE
1636c067afaaSCameron Grant 	     0x026/0x027 : LiveDrive Headphone (l/r)
1637c067afaaSCameron Grant 	     0x028/0x029 : Rear Channel (l/r)
1638c067afaaSCameron Grant 	     0x02a/0x02b : ADC Recording Buffer (l/r)
163921f1e37cSDavid E. O'Brien 	     0x02c       : Mic Recording Buffer
164021f1e37cSDavid E. O'Brien 	     0x031/0x032 : Analog Center/LFE
1641c067afaaSCameron Grant 	   Constants
1642c067afaaSCameron Grant 	     0x040 - 0x044 = 0 - 4
1643c067afaaSCameron Grant 	     0x045 = 0x8, 0x046 = 0x10, 0x047 = 0x20
1644c067afaaSCameron Grant 	     0x048 = 0x100, 0x049 = 0x10000, 0x04a = 0x80000
1645c067afaaSCameron Grant 	     0x04b = 0x10000000, 0x04c = 0x20000000, 0x04d = 0x40000000
164621f1e37cSDavid E. O'Brien 	     0x04e = 0x80000000, 0x04f = 0x7fffffff, 0x050 = 0xffffffff
164721f1e37cSDavid E. O'Brien 	     0x051 = 0xfffffffe, 0x052 = 0xc0000000, 0x053 = 0x41fbbcdc
164821f1e37cSDavid E. O'Brien 	     0x054 = 0x5a7ef9db, 0x055 = 0x00100000
1649c067afaaSCameron Grant 	   Temporary Values
1650c067afaaSCameron Grant 	     0x056 : Accumulator
165121f1e37cSDavid E. O'Brien 	     0x057 : Condition Register
165221f1e37cSDavid E. O'Brien 	     0x058 : Noise source
165321f1e37cSDavid E. O'Brien 	     0x059 : Noise source
165421f1e37cSDavid E. O'Brien 	     0x05a : IRQ Register
165521f1e37cSDavid E. O'Brien 	     0x05b : TRAM Delay Base Address Count
1656c067afaaSCameron Grant 	   General Purpose Registers
1657c067afaaSCameron Grant 	     0x100 - 0x1ff
1658c067afaaSCameron Grant 	   Tank Memory Data Registers
1659c067afaaSCameron Grant 	     0x200 - 0x2ff
1660c067afaaSCameron Grant 	   Tank Memory Address Registers
1661c067afaaSCameron Grant 	     0x300 - 0x3ff
1662c067afaaSCameron Grant 	     */
1663c067afaaSCameron Grant 
1664c067afaaSCameron Grant 	/* Routing - this will be configurable in later version */
1665c067afaaSCameron Grant 
1666c067afaaSCameron Grant 	/* GPR[0/1] = FX * 4 + SPDIF-in */
166721f1e37cSDavid E. O'Brien 	emu_addefxop(sc, iMACINT0, GPR(0), EXTIN(EXTIN_SPDIF_CD_L),
166821f1e37cSDavid E. O'Brien 			FXBUS(FXBUS_PCM_LEFT), C_00000004, &pc);
166921f1e37cSDavid E. O'Brien 	emu_addefxop(sc, iMACINT0, GPR(1), EXTIN(EXTIN_SPDIF_CD_R),
167021f1e37cSDavid E. O'Brien 			FXBUS(FXBUS_PCM_RIGHT), C_00000004, &pc);
1671c067afaaSCameron Grant 
167221f1e37cSDavid E. O'Brien 	/* GPR[0/1] += APS-input */
167321f1e37cSDavid E. O'Brien 	emu_addefxop(sc, iACC3, GPR(0), GPR(0), C_00000000,
167421f1e37cSDavid E. O'Brien 			sc->APS ? EXTIN(EXTIN_TOSLINK_L) : C_00000000, &pc);
167521f1e37cSDavid E. O'Brien 	emu_addefxop(sc, iACC3, GPR(1), GPR(1), C_00000000,
167621f1e37cSDavid E. O'Brien 			sc->APS ? EXTIN(EXTIN_TOSLINK_R) : C_00000000, &pc);
167721f1e37cSDavid E. O'Brien 
167821f1e37cSDavid E. O'Brien 	/* FrontOut (AC97) = GPR[0/1] */
167921f1e37cSDavid E. O'Brien 	emu_addefxop(sc, iACC3, EXTOUT(EXTOUT_AC97_L), C_00000000,
168021f1e37cSDavid E. O'Brien 			C_00000000, GPR(0), &pc);
168121f1e37cSDavid E. O'Brien 	emu_addefxop(sc, iACC3, EXTOUT(EXTOUT_AC97_R), C_00000000,
168221f1e37cSDavid E. O'Brien 			C_00000001, GPR(1), &pc);
168321f1e37cSDavid E. O'Brien 
168421f1e37cSDavid E. O'Brien 	/* GPR[2] = GPR[0] (Left) / 2 + GPR[1] (Right) / 2 -- Central volume */
168521f1e37cSDavid E. O'Brien 	emu_addefxop(sc, iINTERP, GPR(2), GPR(1), C_40000000, GPR(0), &pc);
168621f1e37cSDavid E. O'Brien 
168721f1e37cSDavid E. O'Brien #if 0
168821f1e37cSDavid E. O'Brien 	/* RearOut = (GPR[0/1] * RearVolume) >> 31 */
168921f1e37cSDavid E. O'Brien 	/*   RearVolume = GPR[0x10/0x11] */
169021f1e37cSDavid E. O'Brien 	emu_addefxop(sc, iMAC0, EXTOUT(EXTOUT_REAR_L), C_00000000,
169121f1e37cSDavid E. O'Brien 			GPR(16), GPR(0), &pc);
169221f1e37cSDavid E. O'Brien 	emu_addefxop(sc, iMAC0, EXTOUT(EXTOUT_REAR_R), C_00000000,
169321f1e37cSDavid E. O'Brien 			GPR(17), GPR(1), &pc);
169421f1e37cSDavid E. O'Brien #else
169521f1e37cSDavid E. O'Brien 	/* XXX This is just a copy to the channel, since we do not have
169621f1e37cSDavid E. O'Brien 	 *     a patch manager, it is useful for have another output enabled.
169721f1e37cSDavid E. O'Brien 	 */
169821f1e37cSDavid E. O'Brien 
169921f1e37cSDavid E. O'Brien 	/* Rear[l/r] = GPR[0/1] */
170021f1e37cSDavid E. O'Brien 	emu_addefxop(sc, iACC3, EXTOUT(EXTOUT_REAR_L), C_00000000,
170121f1e37cSDavid E. O'Brien 			C_00000000, GPR(0), &pc);
170221f1e37cSDavid E. O'Brien 	emu_addefxop(sc, iACC3, EXTOUT(EXTOUT_REAR_R), C_00000000,
170321f1e37cSDavid E. O'Brien 			C_00000000, GPR(1), &pc);
170421f1e37cSDavid E. O'Brien #endif
170521f1e37cSDavid E. O'Brien 
170621f1e37cSDavid E. O'Brien 	/* TOS out[l/r] = GPR[0/1] */
170721f1e37cSDavid E. O'Brien 	emu_addefxop(sc, iACC3, EXTOUT(EXTOUT_TOSLINK_L), C_00000000,
170821f1e37cSDavid E. O'Brien 			C_00000000, GPR(0), &pc);
170921f1e37cSDavid E. O'Brien 	emu_addefxop(sc, iACC3, EXTOUT(EXTOUT_TOSLINK_R), C_00000000,
171021f1e37cSDavid E. O'Brien 			C_00000000, GPR(1), &pc);
171121f1e37cSDavid E. O'Brien 
171221f1e37cSDavid E. O'Brien 	/* Center and Subwoofer configuration */
171321f1e37cSDavid E. O'Brien 	/* Analog Center = GPR[0] + GPR[2] */
171421f1e37cSDavid E. O'Brien 	emu_addefxop(sc, iACC3, EXTOUT(EXTOUT_ACENTER), C_00000000,
171521f1e37cSDavid E. O'Brien 			GPR(0), GPR(2), &pc);
171621f1e37cSDavid E. O'Brien 	/* Analog Sub = GPR[1] + GPR[2] */
171721f1e37cSDavid E. O'Brien 	emu_addefxop(sc, iACC3, EXTOUT(EXTOUT_ALFE), C_00000000,
171821f1e37cSDavid E. O'Brien 			GPR(1), GPR(2), &pc);
171921f1e37cSDavid E. O'Brien 	/* Digital Center = GPR[0] + GPR[2] */
172053f4fb11SAlexander Leidinger 	emu_addefxop(sc, iACC3, EXTOUT(EXTOUT_AC97_CENTER), C_00000000,
172121f1e37cSDavid E. O'Brien 			GPR(0), GPR(2), &pc);
172221f1e37cSDavid E. O'Brien 	/* Digital Sub = GPR[1] + GPR[2] */
172353f4fb11SAlexander Leidinger 	emu_addefxop(sc, iACC3, EXTOUT(EXTOUT_AC97_LFE), C_00000000,
172421f1e37cSDavid E. O'Brien 			GPR(1), GPR(2), &pc);
172521f1e37cSDavid E. O'Brien 
172621f1e37cSDavid E. O'Brien 	/* Headphones[l/r] = GPR[0/1] */
172721f1e37cSDavid E. O'Brien 	emu_addefxop(sc, iACC3, EXTOUT(EXTOUT_HEADPHONE_L), C_00000000,
172821f1e37cSDavid E. O'Brien 			C_00000000, GPR(0), &pc);
172921f1e37cSDavid E. O'Brien 	emu_addefxop(sc, iACC3, EXTOUT(EXTOUT_HEADPHONE_R), C_00000000,
173021f1e37cSDavid E. O'Brien 			C_00000000, GPR(1), &pc);
173121f1e37cSDavid E. O'Brien 
173221f1e37cSDavid E. O'Brien 	/* ADC Recording buffer[l/r] = AC97Input[l/r] */
173321f1e37cSDavid E. O'Brien 	emu_addefxop(sc, iACC3, EXTOUT(EXTOUT_ADC_CAP_L), C_00000000,
173421f1e37cSDavid E. O'Brien 			C_00000000, EXTIN(EXTIN_AC97_L), &pc);
173521f1e37cSDavid E. O'Brien 	emu_addefxop(sc, iACC3, EXTOUT(EXTOUT_ADC_CAP_R), C_00000000,
173621f1e37cSDavid E. O'Brien 			C_00000000, EXTIN(EXTIN_AC97_R), &pc);
173721f1e37cSDavid E. O'Brien 
173821f1e37cSDavid E. O'Brien 	/* resume normal operations */
1739c7e0c9dbSPedro F. Giffuni 	emu_wrptr(sc, 0, EMU_DBG, 0);
1740c067afaaSCameron Grant }
1741c067afaaSCameron Grant 
1742c067afaaSCameron Grant /* Probe and attach the card */
1743c067afaaSCameron Grant static int
1744c067afaaSCameron Grant emu_init(struct sc_info *sc)
1745c067afaaSCameron Grant {
1746c067afaaSCameron Grant 	u_int32_t spcs, ch, tmp, i;
1747c067afaaSCameron Grant 
174821f1e37cSDavid E. O'Brien 	if (sc->audigy) {
174921f1e37cSDavid E. O'Brien 		/* enable additional AC97 slots */
1750c7e0c9dbSPedro F. Giffuni 		emu_wrptr(sc, 0, EMU_AC97SLOT, EMU_AC97SLOT_CENTER | EMU_AC97SLOT_LFE);
175121f1e37cSDavid E. O'Brien 	}
175221f1e37cSDavid E. O'Brien 
1753c067afaaSCameron Grant 	/* disable audio and lock cache */
1754c7e0c9dbSPedro F. Giffuni 	emu_wr(sc, EMU_HCFG,
1755c7e0c9dbSPedro F. Giffuni 	    EMU_HCFG_LOCKSOUNDCACHE | EMU_HCFG_LOCKTANKCACHE_MASK | EMU_HCFG_MUTEBUTTONENABLE,
175621f1e37cSDavid E. O'Brien 	    4);
1757c067afaaSCameron Grant 
1758c067afaaSCameron Grant 	/* reset recording buffers */
1759c7e0c9dbSPedro F. Giffuni 	emu_wrptr(sc, 0, EMU_MICBS, EMU_RECBS_BUFSIZE_NONE);
1760c7e0c9dbSPedro F. Giffuni 	emu_wrptr(sc, 0, EMU_MICBA, 0);
1761c7e0c9dbSPedro F. Giffuni 	emu_wrptr(sc, 0, EMU_FXBS, EMU_RECBS_BUFSIZE_NONE);
1762c7e0c9dbSPedro F. Giffuni 	emu_wrptr(sc, 0, EMU_FXBA, 0);
1763c7e0c9dbSPedro F. Giffuni 	emu_wrptr(sc, 0, EMU_ADCBS, EMU_RECBS_BUFSIZE_NONE);
1764c7e0c9dbSPedro F. Giffuni 	emu_wrptr(sc, 0, EMU_ADCBA, 0);
1765c067afaaSCameron Grant 
1766c067afaaSCameron Grant 	/* disable channel interrupt */
1767c7e0c9dbSPedro F. Giffuni 	emu_wr(sc, EMU_INTE,
1768c7e0c9dbSPedro F. Giffuni 	    EMU_INTE_INTERTIMERENB | EMU_INTE_SAMPLERATER | EMU_INTE_PCIERRENABLE,
176921f1e37cSDavid E. O'Brien 	    4);
1770c7e0c9dbSPedro F. Giffuni 	emu_wrptr(sc, 0, EMU_CLIEL, 0);
1771c7e0c9dbSPedro F. Giffuni 	emu_wrptr(sc, 0, EMU_CLIEH, 0);
1772c7e0c9dbSPedro F. Giffuni 	emu_wrptr(sc, 0, EMU_SOLEL, 0);
1773c7e0c9dbSPedro F. Giffuni 	emu_wrptr(sc, 0, EMU_SOLEH, 0);
1774c067afaaSCameron Grant 
177521f1e37cSDavid E. O'Brien 	/* wonder what these do... */
177621f1e37cSDavid E. O'Brien 	if (sc->audigy) {
1777c7e0c9dbSPedro F. Giffuni 		emu_wrptr(sc, 0, EMU_SPBYPASS, 0xf00);
1778c7e0c9dbSPedro F. Giffuni 		emu_wrptr(sc, 0, EMU_AC97SLOT, 0x3);
177921f1e37cSDavid E. O'Brien 	}
178021f1e37cSDavid E. O'Brien 
1781c067afaaSCameron Grant 	/* init envelope engine */
1782c067afaaSCameron Grant 	for (ch = 0; ch < NUM_G; ch++) {
1783c7e0c9dbSPedro F. Giffuni 		emu_wrptr(sc, ch, EMU_CHAN_DCYSUSV, ENV_OFF);
1784c7e0c9dbSPedro F. Giffuni 		emu_wrptr(sc, ch, EMU_CHAN_IP, 0);
1785c7e0c9dbSPedro F. Giffuni 		emu_wrptr(sc, ch, EMU_CHAN_VTFT, 0xffff);
1786c7e0c9dbSPedro F. Giffuni 		emu_wrptr(sc, ch, EMU_CHAN_CVCF, 0xffff);
1787c7e0c9dbSPedro F. Giffuni 		emu_wrptr(sc, ch, EMU_CHAN_PTRX, 0);
1788c7e0c9dbSPedro F. Giffuni 		emu_wrptr(sc, ch, EMU_CHAN_CPF, 0);
1789c7e0c9dbSPedro F. Giffuni 		emu_wrptr(sc, ch, EMU_CHAN_CCR, 0);
1790c067afaaSCameron Grant 
1791c7e0c9dbSPedro F. Giffuni 		emu_wrptr(sc, ch, EMU_CHAN_PSST, 0);
1792c7e0c9dbSPedro F. Giffuni 		emu_wrptr(sc, ch, EMU_CHAN_DSL, 0x10);
1793c7e0c9dbSPedro F. Giffuni 		emu_wrptr(sc, ch, EMU_CHAN_CCCA, 0);
1794c7e0c9dbSPedro F. Giffuni 		emu_wrptr(sc, ch, EMU_CHAN_Z1, 0);
1795c7e0c9dbSPedro F. Giffuni 		emu_wrptr(sc, ch, EMU_CHAN_Z2, 0);
1796c7e0c9dbSPedro F. Giffuni 		emu_wrptr(sc, ch, EMU_CHAN_FXRT, 0xd01c0000);
1797c067afaaSCameron Grant 
1798c7e0c9dbSPedro F. Giffuni 		emu_wrptr(sc, ch, EMU_CHAN_ATKHLDM, 0);
1799c7e0c9dbSPedro F. Giffuni 		emu_wrptr(sc, ch, EMU_CHAN_DCYSUSM, 0);
1800c7e0c9dbSPedro F. Giffuni 		emu_wrptr(sc, ch, EMU_CHAN_IFATN, 0xffff);
1801c7e0c9dbSPedro F. Giffuni 		emu_wrptr(sc, ch, EMU_CHAN_PEFE, 0);
1802c7e0c9dbSPedro F. Giffuni 		emu_wrptr(sc, ch, EMU_CHAN_FMMOD, 0);
1803c7e0c9dbSPedro F. Giffuni 		emu_wrptr(sc, ch, EMU_CHAN_TREMFRQ, 24);	/* 1 Hz */
1804c7e0c9dbSPedro F. Giffuni 		emu_wrptr(sc, ch, EMU_CHAN_FM2FRQ2, 24);	/* 1 Hz */
1805c7e0c9dbSPedro F. Giffuni 		emu_wrptr(sc, ch, EMU_CHAN_TEMPENV, 0);
1806c067afaaSCameron Grant 
1807c067afaaSCameron Grant 		/*** these are last so OFF prevents writing ***/
1808c7e0c9dbSPedro F. Giffuni 		emu_wrptr(sc, ch, EMU_CHAN_LFOVAL2, 0);
1809c7e0c9dbSPedro F. Giffuni 		emu_wrptr(sc, ch, EMU_CHAN_LFOVAL1, 0);
1810c7e0c9dbSPedro F. Giffuni 		emu_wrptr(sc, ch, EMU_CHAN_ATKHLDV, 0);
1811c7e0c9dbSPedro F. Giffuni 		emu_wrptr(sc, ch, EMU_CHAN_ENVVOL, 0);
1812c7e0c9dbSPedro F. Giffuni 		emu_wrptr(sc, ch, EMU_CHAN_ENVVAL, 0);
1813c067afaaSCameron Grant 
181421f1e37cSDavid E. O'Brien 		if (sc->audigy) {
181521f1e37cSDavid E. O'Brien 			/* audigy cards need this to initialize correctly */
181621f1e37cSDavid E. O'Brien 			emu_wrptr(sc, ch, 0x4c, 0);
181721f1e37cSDavid E. O'Brien 			emu_wrptr(sc, ch, 0x4d, 0);
181821f1e37cSDavid E. O'Brien 			emu_wrptr(sc, ch, 0x4e, 0);
181921f1e37cSDavid E. O'Brien 			emu_wrptr(sc, ch, 0x4f, 0);
182021f1e37cSDavid E. O'Brien 			/* set default routing */
1821c7e0c9dbSPedro F. Giffuni 			emu_wrptr(sc, ch, EMU_A_CHAN_FXRT1, 0x03020100);
1822c7e0c9dbSPedro F. Giffuni 			emu_wrptr(sc, ch, EMU_A_CHAN_FXRT2, 0x3f3f3f3f);
1823c7e0c9dbSPedro F. Giffuni 			emu_wrptr(sc, ch, EMU_A_CHAN_SENDAMOUNTS, 0);
182421f1e37cSDavid E. O'Brien 		}
182521f1e37cSDavid E. O'Brien 
1826c067afaaSCameron Grant 		sc->voice[ch].vnum = ch;
1827c067afaaSCameron Grant 		sc->voice[ch].slave = NULL;
1828c067afaaSCameron Grant 		sc->voice[ch].busy = 0;
182970776a9cSCameron Grant 		sc->voice[ch].ismaster = 0;
18306c1146c0SCameron Grant 		sc->voice[ch].running = 0;
1831c067afaaSCameron Grant 		sc->voice[ch].b16 = 0;
1832c067afaaSCameron Grant 		sc->voice[ch].stereo = 0;
1833c067afaaSCameron Grant 		sc->voice[ch].speed = 0;
1834c067afaaSCameron Grant 		sc->voice[ch].start = 0;
1835c067afaaSCameron Grant 		sc->voice[ch].end = 0;
1836c067afaaSCameron Grant 		sc->voice[ch].channel = NULL;
1837c067afaaSCameron Grant 	}
183870776a9cSCameron Grant 	sc->pnum = sc->rnum = 0;
1839c067afaaSCameron Grant 
1840c067afaaSCameron Grant 	/*
1841c067afaaSCameron Grant 	 *  Init to 0x02109204 :
1842c067afaaSCameron Grant 	 *  Clock accuracy    = 0     (1000ppm)
1843c067afaaSCameron Grant 	 *  Sample Rate       = 2     (48kHz)
1844c067afaaSCameron Grant 	 *  Audio Channel     = 1     (Left of 2)
1845c067afaaSCameron Grant 	 *  Source Number     = 0     (Unspecified)
1846c067afaaSCameron Grant 	 *  Generation Status = 1     (Original for Cat Code 12)
1847c067afaaSCameron Grant 	 *  Cat Code          = 12    (Digital Signal Mixer)
1848c067afaaSCameron Grant 	 *  Mode              = 0     (Mode 0)
1849c067afaaSCameron Grant 	 *  Emphasis          = 0     (None)
1850c067afaaSCameron Grant 	 *  CP                = 1     (Copyright unasserted)
1851c067afaaSCameron Grant 	 *  AN                = 0     (Audio data)
1852c067afaaSCameron Grant 	 *  P                 = 0     (Consumer)
1853c067afaaSCameron Grant 	 */
1854c7e0c9dbSPedro F. Giffuni 	spcs = EMU_SPCS_CLKACCY_1000PPM | EMU_SPCS_SAMPLERATE_48 |
1855c7e0c9dbSPedro F. Giffuni 	    EMU_SPCS_CHANNELNUM_LEFT | EMU_SPCS_SOURCENUM_UNSPEC |
1856c7e0c9dbSPedro F. Giffuni 	    EMU_SPCS_GENERATIONSTATUS | 0x00001200 | 0x00000000 |
1857c7e0c9dbSPedro F. Giffuni 	    EMU_SPCS_EMPHASIS_NONE | EMU_SPCS_COPYRIGHT;
1858c7e0c9dbSPedro F. Giffuni 	emu_wrptr(sc, 0, EMU_SPCS0, spcs);
1859c7e0c9dbSPedro F. Giffuni 	emu_wrptr(sc, 0, EMU_SPCS1, spcs);
1860c7e0c9dbSPedro F. Giffuni 	emu_wrptr(sc, 0, EMU_SPCS2, spcs);
1861c067afaaSCameron Grant 
186221f1e37cSDavid E. O'Brien 	if (!sc->audigy)
1863c067afaaSCameron Grant 		emu_initefx(sc);
186421f1e37cSDavid E. O'Brien 	else if (sc->audigy2) {	/* Audigy 2 */
186521f1e37cSDavid E. O'Brien 		/* from ALSA initialization code: */
186621f1e37cSDavid E. O'Brien 
186721f1e37cSDavid E. O'Brien 		/* Hack for Alice3 to work independent of haP16V driver */
186821f1e37cSDavid E. O'Brien 		u_int32_t tmp;
186921f1e37cSDavid E. O'Brien 
187021f1e37cSDavid E. O'Brien 		/* Setup SRCMulti_I2S SamplingRate */
1871c7e0c9dbSPedro F. Giffuni 		tmp = emu_rdptr(sc, 0, EMU_A_SPDIF_SAMPLERATE) & 0xfffff1ff;
1872c7e0c9dbSPedro F. Giffuni 		emu_wrptr(sc, 0, EMU_A_SPDIF_SAMPLERATE, tmp | 0x400);
187321f1e37cSDavid E. O'Brien 
187421f1e37cSDavid E. O'Brien 		/* Setup SRCSel (Enable SPDIF, I2S SRCMulti) */
187521f1e37cSDavid E. O'Brien 		emu_wr(sc, 0x20, 0x00600000, 4);
187621f1e37cSDavid E. O'Brien 		emu_wr(sc, 0x24, 0x00000014, 4);
187721f1e37cSDavid E. O'Brien 
187821f1e37cSDavid E. O'Brien 		/* Setup SRCMulti Input Audio Enable */
187921f1e37cSDavid E. O'Brien 		emu_wr(sc, 0x20, 0x006e0000, 4);
188021f1e37cSDavid E. O'Brien 		emu_wr(sc, 0x24, 0xff00ff00, 4);
188121f1e37cSDavid E. O'Brien 	}
1882c067afaaSCameron Grant 
188397b3c9d8SCameron Grant 	SLIST_INIT(&sc->mem.blocks);
188421f1e37cSDavid E. O'Brien 	sc->mem.ptb_pages = emu_malloc(sc, EMUMAXPAGES * sizeof(u_int32_t),
188521f1e37cSDavid E. O'Brien 	    &sc->mem.ptb_pages_addr);
1886c067afaaSCameron Grant 	if (sc->mem.ptb_pages == NULL)
1887c067afaaSCameron Grant 		return -1;
188897b3c9d8SCameron Grant 
188921f1e37cSDavid E. O'Brien 	sc->mem.silent_page = emu_malloc(sc, EMUPAGESIZE,
189021f1e37cSDavid E. O'Brien 	    &sc->mem.silent_page_addr);
189197b3c9d8SCameron Grant 	if (sc->mem.silent_page == NULL) {
189297b3c9d8SCameron Grant 		emu_free(sc, sc->mem.ptb_pages);
189397b3c9d8SCameron Grant 		return -1;
189497b3c9d8SCameron Grant 	}
189597b3c9d8SCameron Grant 	/* Clear page with silence & setup all pointers to this page */
189697b3c9d8SCameron Grant 	bzero(sc->mem.silent_page, EMUPAGESIZE);
189738cc9942SOlivier Houchard 	tmp = (u_int32_t)(sc->mem.silent_page_addr) << 1;
189821f1e37cSDavid E. O'Brien 	for (i = 0; i < EMUMAXPAGES; i++)
189919b6ac09SCameron Grant 		sc->mem.ptb_pages[i] = tmp | i;
190097b3c9d8SCameron Grant 
1901c7e0c9dbSPedro F. Giffuni 	emu_wrptr(sc, 0, EMU_PTB, (sc->mem.ptb_pages_addr));
1902c7e0c9dbSPedro F. Giffuni 	emu_wrptr(sc, 0, EMU_TCB, 0);	/* taken from original driver */
1903c7e0c9dbSPedro F. Giffuni 	emu_wrptr(sc, 0, EMU_TCBS, 0);	/* taken from original driver */
1904c067afaaSCameron Grant 
1905c067afaaSCameron Grant 	for (ch = 0; ch < NUM_G; ch++) {
1906c7e0c9dbSPedro F. Giffuni 		emu_wrptr(sc, ch, EMU_CHAN_MAPA, tmp | EMU_CHAN_MAP_PTI_MASK);
1907c7e0c9dbSPedro F. Giffuni 		emu_wrptr(sc, ch, EMU_CHAN_MAPB, tmp | EMU_CHAN_MAP_PTI_MASK);
1908c067afaaSCameron Grant 	}
1909c067afaaSCameron Grant 
1910c067afaaSCameron Grant 	/* emu_memalloc(sc, EMUPAGESIZE); */
1911c067afaaSCameron Grant 	/*
1912c067afaaSCameron Grant 	 *  Hokay, now enable the AUD bit
191321f1e37cSDavid E. O'Brien 	 *
191421f1e37cSDavid E. O'Brien 	 *  Audigy
191521f1e37cSDavid E. O'Brien 	 *   Enable Audio = 0 (enabled after fx processor initialization)
191621f1e37cSDavid E. O'Brien 	 *   Mute Disable Audio = 0
191721f1e37cSDavid E. O'Brien 	 *   Joystick = 1
191821f1e37cSDavid E. O'Brien 	 *
191921f1e37cSDavid E. O'Brien 	 *  Audigy 2
192021f1e37cSDavid E. O'Brien 	 *   Enable Audio = 1
192121f1e37cSDavid E. O'Brien 	 *   Mute Disable Audio = 0
192221f1e37cSDavid E. O'Brien 	 *   Joystick = 1
192321f1e37cSDavid E. O'Brien 	 *   GP S/PDIF AC3 Enable = 1
192421f1e37cSDavid E. O'Brien 	 *   CD S/PDIF AC3 Enable = 1
192521f1e37cSDavid E. O'Brien 	 *
192621f1e37cSDavid E. O'Brien 	 *  EMU10K1
1927c067afaaSCameron Grant 	 *   Enable Audio = 1
1928c067afaaSCameron Grant 	 *   Mute Disable Audio = 0
1929c067afaaSCameron Grant 	 *   Lock Tank Memory = 1
1930c067afaaSCameron Grant 	 *   Lock Sound Memory = 0
1931c067afaaSCameron Grant 	 *   Auto Mute = 1
1932c067afaaSCameron Grant 	 */
193321f1e37cSDavid E. O'Brien 
193421f1e37cSDavid E. O'Brien 	if (sc->audigy) {
1935c7e0c9dbSPedro F. Giffuni 		tmp = EMU_HCFG_AUTOMUTE | EMU_HCFG_JOYENABLE;
193621f1e37cSDavid E. O'Brien 		if (sc->audigy2)	/* Audigy 2 */
1937c7e0c9dbSPedro F. Giffuni 			tmp = EMU_HCFG_AUDIOENABLE | EMU_HCFG_AC3ENABLE_CDSPDIF |
1938c7e0c9dbSPedro F. Giffuni 			    EMU_HCFG_AC3ENABLE_GPSPDIF;
1939c7e0c9dbSPedro F. Giffuni 		emu_wr(sc, EMU_HCFG, tmp, 4);
194021f1e37cSDavid E. O'Brien 
194121f1e37cSDavid E. O'Brien 		audigy_initefx(sc);
194221f1e37cSDavid E. O'Brien 
194321f1e37cSDavid E. O'Brien 		/* from ALSA initialization code: */
194421f1e37cSDavid E. O'Brien 
194521f1e37cSDavid E. O'Brien 		/* enable audio and disable both audio/digital outputs */
1946c7e0c9dbSPedro F. Giffuni 		emu_wr(sc, EMU_HCFG, emu_rd(sc, EMU_HCFG, 4) | EMU_HCFG_AUDIOENABLE, 4);
1947c7e0c9dbSPedro F. Giffuni 		emu_wr(sc, EMU_A_IOCFG, emu_rd(sc, EMU_A_IOCFG, 4) & ~EMU_A_IOCFG_GPOUT_AD,
194821f1e37cSDavid E. O'Brien 		    4);
194921f1e37cSDavid E. O'Brien 		if (sc->audigy2) {	/* Audigy 2 */
195021f1e37cSDavid E. O'Brien 			/* Unmute Analog.
195121f1e37cSDavid E. O'Brien 			 * Set GPO6 to 1 for Apollo. This has to be done after
195221f1e37cSDavid E. O'Brien 			 * init Alice3 I2SOut beyond 48kHz.
195321f1e37cSDavid E. O'Brien 			 * So, sequence is important.
195421f1e37cSDavid E. O'Brien 			 */
1955c7e0c9dbSPedro F. Giffuni 			emu_wr(sc, EMU_A_IOCFG,
1956c7e0c9dbSPedro F. Giffuni 			    emu_rd(sc, EMU_A_IOCFG, 4) | EMU_A_IOCFG_GPOUT_A, 4);
195721f1e37cSDavid E. O'Brien 		}
195821f1e37cSDavid E. O'Brien 	} else {
195921f1e37cSDavid E. O'Brien 		/* EMU10K1 initialization code */
1960c7e0c9dbSPedro F. Giffuni 		tmp = EMU_HCFG_AUDIOENABLE | EMU_HCFG_LOCKTANKCACHE_MASK
1961c7e0c9dbSPedro F. Giffuni 		    | EMU_HCFG_AUTOMUTE;
1962c067afaaSCameron Grant 		if (sc->rev >= 6)
1963c7e0c9dbSPedro F. Giffuni 			tmp |= EMU_HCFG_JOYENABLE;
196421f1e37cSDavid E. O'Brien 
1965c7e0c9dbSPedro F. Giffuni 		emu_wr(sc, EMU_HCFG, tmp, 4);
1966c067afaaSCameron Grant 
1967c067afaaSCameron Grant 		/* TOSLink detection */
1968c067afaaSCameron Grant 		sc->tos_link = 0;
1969c7e0c9dbSPedro F. Giffuni 		tmp = emu_rd(sc, EMU_HCFG, 4);
1970c7e0c9dbSPedro F. Giffuni 		if (tmp & (EMU_HCFG_GPINPUT0 | EMU_HCFG_GPINPUT1)) {
1971c7e0c9dbSPedro F. Giffuni 			emu_wr(sc, EMU_HCFG, tmp | EMU_HCFG_GPOUT1, 4);
1972c067afaaSCameron Grant 			DELAY(50);
1973c7e0c9dbSPedro F. Giffuni 			if (tmp != (emu_rd(sc, EMU_HCFG, 4) & ~EMU_HCFG_GPOUT1)) {
1974c067afaaSCameron Grant 				sc->tos_link = 1;
1975c7e0c9dbSPedro F. Giffuni 				emu_wr(sc, EMU_HCFG, tmp, 4);
1976c067afaaSCameron Grant 			}
1977c067afaaSCameron Grant 		}
197821f1e37cSDavid E. O'Brien 	}
1979c067afaaSCameron Grant 
1980c067afaaSCameron Grant 	return 0;
1981c067afaaSCameron Grant }
1982c067afaaSCameron Grant 
1983c067afaaSCameron Grant static int
1984c02f3f7aSCameron Grant emu_uninit(struct sc_info *sc)
1985c02f3f7aSCameron Grant {
1986c02f3f7aSCameron Grant 	u_int32_t ch;
1987c02f3f7aSCameron Grant 
1988c7e0c9dbSPedro F. Giffuni 	emu_wr(sc, EMU_INTE, 0, 4);
1989c02f3f7aSCameron Grant 	for (ch = 0; ch < NUM_G; ch++)
1990c7e0c9dbSPedro F. Giffuni 		emu_wrptr(sc, ch, EMU_CHAN_DCYSUSV, ENV_OFF);
1991c02f3f7aSCameron Grant 	for (ch = 0; ch < NUM_G; ch++) {
1992c7e0c9dbSPedro F. Giffuni 		emu_wrptr(sc, ch, EMU_CHAN_VTFT, 0);
1993c7e0c9dbSPedro F. Giffuni 		emu_wrptr(sc, ch, EMU_CHAN_CVCF, 0);
1994c7e0c9dbSPedro F. Giffuni 		emu_wrptr(sc, ch, EMU_CHAN_PTRX, 0);
1995c7e0c9dbSPedro F. Giffuni 		emu_wrptr(sc, ch, EMU_CHAN_CPF, 0);
1996c02f3f7aSCameron Grant 	}
1997c02f3f7aSCameron Grant 
199821f1e37cSDavid E. O'Brien 	if (sc->audigy) {	/* stop fx processor */
1999c7e0c9dbSPedro F. Giffuni 		emu_wrptr(sc, 0, EMU_A_DBG, EMU_A_DBG_SINGLE_STEP);
200021f1e37cSDavid E. O'Brien 	}
200121f1e37cSDavid E. O'Brien 
2002c02f3f7aSCameron Grant 	/* disable audio and lock cache */
2003c7e0c9dbSPedro F. Giffuni 	emu_wr(sc, EMU_HCFG,
2004c7e0c9dbSPedro F. Giffuni 	    EMU_HCFG_LOCKSOUNDCACHE | EMU_HCFG_LOCKTANKCACHE_MASK | EMU_HCFG_MUTEBUTTONENABLE,
200521f1e37cSDavid E. O'Brien 	    4);
2006c02f3f7aSCameron Grant 
2007c7e0c9dbSPedro F. Giffuni 	emu_wrptr(sc, 0, EMU_PTB, 0);
2008c02f3f7aSCameron Grant 	/* reset recording buffers */
2009c7e0c9dbSPedro F. Giffuni 	emu_wrptr(sc, 0, EMU_MICBS, EMU_RECBS_BUFSIZE_NONE);
2010c7e0c9dbSPedro F. Giffuni 	emu_wrptr(sc, 0, EMU_MICBA, 0);
2011c7e0c9dbSPedro F. Giffuni 	emu_wrptr(sc, 0, EMU_FXBS, EMU_RECBS_BUFSIZE_NONE);
2012c7e0c9dbSPedro F. Giffuni 	emu_wrptr(sc, 0, EMU_FXBA, 0);
2013c7e0c9dbSPedro F. Giffuni 	emu_wrptr(sc, 0, EMU_FXWC, 0);
2014c7e0c9dbSPedro F. Giffuni 	emu_wrptr(sc, 0, EMU_ADCBS, EMU_RECBS_BUFSIZE_NONE);
2015c7e0c9dbSPedro F. Giffuni 	emu_wrptr(sc, 0, EMU_ADCBA, 0);
2016c7e0c9dbSPedro F. Giffuni 	emu_wrptr(sc, 0, EMU_TCB, 0);
2017c7e0c9dbSPedro F. Giffuni 	emu_wrptr(sc, 0, EMU_TCBS, 0);
2018c02f3f7aSCameron Grant 
2019c02f3f7aSCameron Grant 	/* disable channel interrupt */
2020c7e0c9dbSPedro F. Giffuni 	emu_wrptr(sc, 0, EMU_CLIEL, 0);
2021c7e0c9dbSPedro F. Giffuni 	emu_wrptr(sc, 0, EMU_CLIEH, 0);
2022c7e0c9dbSPedro F. Giffuni 	emu_wrptr(sc, 0, EMU_SOLEL, 0);
2023c7e0c9dbSPedro F. Giffuni 	emu_wrptr(sc, 0, EMU_SOLEH, 0);
2024c02f3f7aSCameron Grant 
2025c02f3f7aSCameron Grant 	/* init envelope engine */
2026c02f3f7aSCameron Grant 	if (!SLIST_EMPTY(&sc->mem.blocks))
2027c02f3f7aSCameron Grant 		device_printf(sc->dev, "warning: memblock list not empty\n");
2028c02f3f7aSCameron Grant 	emu_free(sc, sc->mem.ptb_pages);
2029c02f3f7aSCameron Grant 	emu_free(sc, sc->mem.silent_page);
2030c02f3f7aSCameron Grant 
2031f510d240SAlexander Leidinger 	if(sc->mpu)
2032f510d240SAlexander Leidinger 	    mpu401_uninit(sc->mpu);
2033c02f3f7aSCameron Grant 	return 0;
2034c02f3f7aSCameron Grant }
2035c02f3f7aSCameron Grant 
2036c02f3f7aSCameron Grant static int
2037c067afaaSCameron Grant emu_pci_probe(device_t dev)
2038c067afaaSCameron Grant {
2039c067afaaSCameron Grant 	char *s = NULL;
2040c067afaaSCameron Grant 
2041c067afaaSCameron Grant 	switch (pci_get_devid(dev)) {
2042c067afaaSCameron Grant 	case EMU10K1_PCI_ID:
2043c067afaaSCameron Grant 		s = "Creative EMU10K1";
2044c067afaaSCameron Grant 		break;
204521f1e37cSDavid E. O'Brien 
2046a791cfeeSCameron Grant 	case EMU10K2_PCI_ID:
204721f1e37cSDavid E. O'Brien 		if (pci_get_revid(dev) == 0x04)
204821f1e37cSDavid E. O'Brien 			s = "Creative Audigy 2 (EMU10K2)";
204921f1e37cSDavid E. O'Brien 		else
205021f1e37cSDavid E. O'Brien 			s = "Creative Audigy (EMU10K2)";
2051a791cfeeSCameron Grant 		break;
205221f1e37cSDavid E. O'Brien 
2053d2b677bbSWarner Losh 	case EMU10K3_PCI_ID:
2054d2b677bbSWarner Losh 		s = "Creative Audigy 2 (EMU10K3)";
2055d2b677bbSWarner Losh 		break;
2056d2b677bbSWarner Losh 
2057a791cfeeSCameron Grant 	default:
2058a791cfeeSCameron Grant 		return ENXIO;
2059c067afaaSCameron Grant 	}
2060c067afaaSCameron Grant 
2061a791cfeeSCameron Grant 	device_set_desc(dev, s);
206204d895e8SAlexander Leidinger 	return BUS_PROBE_LOW_PRIORITY;
2063c067afaaSCameron Grant }
2064c067afaaSCameron Grant 
2065c067afaaSCameron Grant static int
2066c067afaaSCameron Grant emu_pci_attach(device_t dev)
2067c067afaaSCameron Grant {
2068306f91b6SCameron Grant 	struct ac97_info *codec = NULL;
2069a791cfeeSCameron Grant 	struct sc_info *sc;
2070a791cfeeSCameron Grant 	int i, gotmic;
2071c067afaaSCameron Grant 	char status[SND_STATUSLEN];
2072c067afaaSCameron Grant 
2073082f6383SAriff Abdullah 	sc = malloc(sizeof(*sc), M_DEVBUF, M_WAITOK | M_ZERO);
20744582b3a1SAriff Abdullah 	sc->lock = snd_mtxcreate(device_get_nameunit(dev), "snd_emu10k1 softc");
207570776a9cSCameron Grant 	sc->dev = dev;
2076c067afaaSCameron Grant 	sc->type = pci_get_devid(dev);
2077c067afaaSCameron Grant 	sc->rev = pci_get_revid(dev);
2078d2b677bbSWarner Losh 	sc->audigy = sc->type == EMU10K2_PCI_ID || sc->type == EMU10K3_PCI_ID;
207921f1e37cSDavid E. O'Brien 	sc->audigy2 = (sc->audigy && sc->rev == 0x04);
208021f1e37cSDavid E. O'Brien 	sc->nchans = sc->audigy ? 8 : 4;
2081c7e0c9dbSPedro F. Giffuni 	sc->addrmask = sc->audigy ? EMU_A_PTR_ADDR_MASK : EMU_PTR_ADDR_MASK;
2082c067afaaSCameron Grant 
2083c68534f1SScott Long 	pci_enable_busmaster(dev);
2084c067afaaSCameron Grant 
2085e27951b2SJohn Baldwin 	i = PCIR_BAR(0);
20865f96beb9SNate Lawson 	sc->reg = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &i, RF_ACTIVE);
2087a791cfeeSCameron Grant 	if (sc->reg == NULL) {
2088c067afaaSCameron Grant 		device_printf(dev, "unable to map register space\n");
2089c067afaaSCameron Grant 		goto bad;
2090c067afaaSCameron Grant 	}
2091a791cfeeSCameron Grant 	sc->st = rman_get_bustag(sc->reg);
2092a791cfeeSCameron Grant 	sc->sh = rman_get_bushandle(sc->reg);
2093a791cfeeSCameron Grant 
2094a791cfeeSCameron Grant 	sc->bufsz = pcm_getbuffersize(dev, 4096, EMU_DEFAULT_BUFSZ, 65536);
2095c067afaaSCameron Grant 
20960b989078SAlexander Leidinger 	if (bus_dma_tag_create(/*parent*/bus_get_dma_tag(dev), /*alignment*/2,
20970b989078SAlexander Leidinger 		/*boundary*/0,
20981115e138SMarius Strobl 		/*lowaddr*/(1U << 31) - 1, /* can only access 0-2gb */
2099c067afaaSCameron Grant 		/*highaddr*/BUS_SPACE_MAXADDR,
2100c067afaaSCameron Grant 		/*filter*/NULL, /*filterarg*/NULL,
2101a791cfeeSCameron Grant 		/*maxsize*/sc->bufsz, /*nsegments*/1, /*maxsegz*/0x3ffff,
2102f6b1c44dSScott Long 		/*flags*/0, /*lockfunc*/busdma_lock_mutex,
2103f6b1c44dSScott Long 		/*lockarg*/&Giant, &sc->parent_dmat) != 0) {
2104c067afaaSCameron Grant 		device_printf(dev, "unable to create dma tag\n");
2105c067afaaSCameron Grant 		goto bad;
2106c067afaaSCameron Grant 	}
2107c067afaaSCameron Grant 
2108c067afaaSCameron Grant 	if (emu_init(sc) == -1) {
2109c067afaaSCameron Grant 		device_printf(dev, "unable to initialize the card\n");
2110c067afaaSCameron Grant 		goto bad;
2111c067afaaSCameron Grant 	}
2112c067afaaSCameron Grant 
21130f55ac6cSCameron Grant 	codec = AC97_CREATE(dev, sc, emu_ac97);
2114c067afaaSCameron Grant 	if (codec == NULL) goto bad;
2115a791cfeeSCameron Grant 	gotmic = (ac97_getcaps(codec) & AC97_CAP_MICCHANNEL) ? 1 : 0;
21160f55ac6cSCameron Grant 	if (mixer_init(dev, ac97_getmixerclass(), codec) == -1) goto bad;
2117c067afaaSCameron Grant 
2118f510d240SAlexander Leidinger 	emu_midiattach(sc);
2119f510d240SAlexander Leidinger 
2120a791cfeeSCameron Grant 	i = 0;
21215f96beb9SNate Lawson 	sc->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &i,
212221f1e37cSDavid E. O'Brien 	    RF_ACTIVE | RF_SHAREABLE);
212321f1e37cSDavid E. O'Brien 	if (!sc->irq ||
212405478063SMaxime Henrion 	    snd_setup_intr(dev, sc->irq, INTR_MPSAFE, emu_intr, sc, &sc->ih)) {
2125c067afaaSCameron Grant 		device_printf(dev, "unable to map interrupt\n");
2126c067afaaSCameron Grant 		goto bad;
2127c067afaaSCameron Grant 	}
2128c067afaaSCameron Grant 
21290d8ed52eSMathew Kanner 	snprintf(status, SND_STATUSLEN, "at io 0x%lx irq %ld %s",
21300d8ed52eSMathew Kanner 	    rman_get_start(sc->reg), rman_get_start(sc->irq),
21310d8ed52eSMathew Kanner 	    PCM_KLDSTRING(snd_emu10k1));
2132c067afaaSCameron Grant 
213321f1e37cSDavid E. O'Brien 	if (pcm_register(dev, sc, sc->nchans, gotmic ? 3 : 2)) goto bad;
213421f1e37cSDavid E. O'Brien 	for (i = 0; i < sc->nchans; i++)
21350f55ac6cSCameron Grant 		pcm_addchan(dev, PCMDIR_PLAY, &emupchan_class, sc);
2136a791cfeeSCameron Grant 	for (i = 0; i < (gotmic ? 3 : 2); i++)
21370f55ac6cSCameron Grant 		pcm_addchan(dev, PCMDIR_REC, &emurchan_class, sc);
2138c067afaaSCameron Grant 
2139c067afaaSCameron Grant 	pcm_setstatus(dev, status);
2140c067afaaSCameron Grant 
2141c067afaaSCameron Grant 	return 0;
2142c067afaaSCameron Grant 
2143c067afaaSCameron Grant bad:
2144306f91b6SCameron Grant 	if (codec) ac97_destroy(codec);
2145e27951b2SJohn Baldwin 	if (sc->reg) bus_release_resource(dev, SYS_RES_IOPORT, PCIR_BAR(0), sc->reg);
2146c067afaaSCameron Grant 	if (sc->ih) bus_teardown_intr(dev, sc->irq, sc->ih);
2147a791cfeeSCameron Grant 	if (sc->irq) bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq);
2148306f91b6SCameron Grant 	if (sc->parent_dmat) bus_dma_tag_destroy(sc->parent_dmat);
214966ef8af5SCameron Grant 	if (sc->lock) snd_mtxfree(sc->lock);
2150c067afaaSCameron Grant 	free(sc, M_DEVBUF);
2151c067afaaSCameron Grant 	return ENXIO;
2152c067afaaSCameron Grant }
2153c067afaaSCameron Grant 
2154c02f3f7aSCameron Grant static int
2155c02f3f7aSCameron Grant emu_pci_detach(device_t dev)
2156c02f3f7aSCameron Grant {
2157c02f3f7aSCameron Grant 	int r;
2158c02f3f7aSCameron Grant 	struct sc_info *sc;
2159c02f3f7aSCameron Grant 
2160c02f3f7aSCameron Grant 	r = pcm_unregister(dev);
2161c02f3f7aSCameron Grant 	if (r)
2162c02f3f7aSCameron Grant 		return r;
2163c02f3f7aSCameron Grant 
2164c02f3f7aSCameron Grant 	sc = pcm_getdevinfo(dev);
2165c02f3f7aSCameron Grant 	/* shutdown chip */
2166c02f3f7aSCameron Grant 	emu_uninit(sc);
2167c02f3f7aSCameron Grant 
2168e27951b2SJohn Baldwin 	bus_release_resource(dev, SYS_RES_IOPORT, PCIR_BAR(0), sc->reg);
2169c02f3f7aSCameron Grant 	bus_teardown_intr(dev, sc->irq, sc->ih);
2170a791cfeeSCameron Grant 	bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq);
2171306f91b6SCameron Grant 	bus_dma_tag_destroy(sc->parent_dmat);
217266ef8af5SCameron Grant 	snd_mtxfree(sc->lock);
2173c02f3f7aSCameron Grant 	free(sc, M_DEVBUF);
2174c02f3f7aSCameron Grant 
2175c02f3f7aSCameron Grant 	return 0;
2176c02f3f7aSCameron Grant }
2177c02f3f7aSCameron Grant 
2178c02f3f7aSCameron Grant /* add suspend, resume */
2179c067afaaSCameron Grant static device_method_t emu_methods[] = {
2180c067afaaSCameron Grant 	/* Device interface */
2181c067afaaSCameron Grant 	DEVMETHOD(device_probe,		emu_pci_probe),
2182c067afaaSCameron Grant 	DEVMETHOD(device_attach,	emu_pci_attach),
2183c02f3f7aSCameron Grant 	DEVMETHOD(device_detach,	emu_pci_detach),
2184c067afaaSCameron Grant 
2185*3ac12483SMarius Strobl 	DEVMETHOD_END
2186c067afaaSCameron Grant };
2187c067afaaSCameron Grant 
2188c067afaaSCameron Grant static driver_t emu_driver = {
2189c067afaaSCameron Grant 	"pcm",
2190c067afaaSCameron Grant 	emu_methods,
219167b1dce3SCameron Grant 	PCM_SOFTC_SIZE,
2192c067afaaSCameron Grant };
2193c067afaaSCameron Grant 
2194*3ac12483SMarius Strobl DRIVER_MODULE(snd_emu10k1, pci, emu_driver, pcm_devclass, NULL, NULL);
21950739ea1dSSeigo Tanimura MODULE_DEPEND(snd_emu10k1, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER);
2196f314f3daSCameron Grant MODULE_VERSION(snd_emu10k1, 1);
2197f510d240SAlexander Leidinger MODULE_DEPEND(snd_emu10k1, midi, 1, 1, 1);
219870776a9cSCameron Grant 
21996c1146c0SCameron Grant /* dummy driver to silence the joystick device */
220070776a9cSCameron Grant static int
220170776a9cSCameron Grant emujoy_pci_probe(device_t dev)
220270776a9cSCameron Grant {
220370776a9cSCameron Grant 	char *s = NULL;
220470776a9cSCameron Grant 
220570776a9cSCameron Grant 	switch (pci_get_devid(dev)) {
220670776a9cSCameron Grant 	case 0x70021102:
220770776a9cSCameron Grant 		s = "Creative EMU10K1 Joystick";
220870776a9cSCameron Grant 		device_quiet(dev);
220970776a9cSCameron Grant 		break;
2210a791cfeeSCameron Grant 	case 0x70031102:
2211a791cfeeSCameron Grant 		s = "Creative EMU10K2 Joystick";
2212a791cfeeSCameron Grant 		device_quiet(dev);
2213a791cfeeSCameron Grant 		break;
221470776a9cSCameron Grant 	}
221570776a9cSCameron Grant 
221670776a9cSCameron Grant 	if (s) device_set_desc(dev, s);
2217a791cfeeSCameron Grant 	return s ? -1000 : ENXIO;
221870776a9cSCameron Grant }
221970776a9cSCameron Grant 
222070776a9cSCameron Grant static int
222170776a9cSCameron Grant emujoy_pci_attach(device_t dev)
222270776a9cSCameron Grant {
2223*3ac12483SMarius Strobl 
222470776a9cSCameron Grant 	return 0;
222570776a9cSCameron Grant }
222670776a9cSCameron Grant 
2227c02f3f7aSCameron Grant static int
2228c02f3f7aSCameron Grant emujoy_pci_detach(device_t dev)
2229c02f3f7aSCameron Grant {
2230*3ac12483SMarius Strobl 
2231c02f3f7aSCameron Grant 	return 0;
2232c02f3f7aSCameron Grant }
2233c02f3f7aSCameron Grant 
223470776a9cSCameron Grant static device_method_t emujoy_methods[] = {
223570776a9cSCameron Grant 	DEVMETHOD(device_probe,		emujoy_pci_probe),
223670776a9cSCameron Grant 	DEVMETHOD(device_attach,	emujoy_pci_attach),
2237c02f3f7aSCameron Grant 	DEVMETHOD(device_detach,	emujoy_pci_detach),
223870776a9cSCameron Grant 
2239*3ac12483SMarius Strobl 	DEVMETHOD_END
224070776a9cSCameron Grant };
224170776a9cSCameron Grant 
224270776a9cSCameron Grant static driver_t emujoy_driver = {
224370776a9cSCameron Grant 	"emujoy",
224470776a9cSCameron Grant 	emujoy_methods,
2245*3ac12483SMarius Strobl 	1	/* no softc */
224670776a9cSCameron Grant };
224770776a9cSCameron Grant 
224870776a9cSCameron Grant static devclass_t emujoy_devclass;
224970776a9cSCameron Grant 
2250*3ac12483SMarius Strobl DRIVER_MODULE(emujoy, pci, emujoy_driver, emujoy_devclass, NULL, NULL);
2251