1992413f4SGarrett D'Amore /*
2992413f4SGarrett D'Amore  * CDDL HEADER START
3992413f4SGarrett D'Amore  *
4992413f4SGarrett D'Amore  * The contents of this file are subject to the terms of the
5992413f4SGarrett D'Amore  * Common Development and Distribution License (the "License").
6992413f4SGarrett D'Amore  * You may not use this file except in compliance with the License.
7992413f4SGarrett D'Amore  *
8992413f4SGarrett D'Amore  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9992413f4SGarrett D'Amore  * or http://www.opensolaris.org/os/licensing.
10992413f4SGarrett D'Amore  * See the License for the specific language governing permissions
11992413f4SGarrett D'Amore  * and limitations under the License.
12992413f4SGarrett D'Amore  *
13992413f4SGarrett D'Amore  * When distributing Covered Code, include this CDDL HEADER in each
14992413f4SGarrett D'Amore  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15992413f4SGarrett D'Amore  * If applicable, add the following below this CDDL HEADER, with the
16992413f4SGarrett D'Amore  * fields enclosed by brackets "[]" replaced with your own identifying
17992413f4SGarrett D'Amore  * information: Portions Copyright [yyyy] [name of copyright owner]
18992413f4SGarrett D'Amore  *
19992413f4SGarrett D'Amore  * CDDL HEADER END
20992413f4SGarrett D'Amore  */
21992413f4SGarrett D'Amore 
22992413f4SGarrett D'Amore /*
2368c47f65SGarrett D'Amore  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
24992413f4SGarrett D'Amore  * Use is subject to license terms.
25992413f4SGarrett D'Amore  */
26992413f4SGarrett D'Amore 
27992413f4SGarrett D'Amore /*
28992413f4SGarrett D'Amore  * Copyright (C) 4Front Technologies 1996-2009.
29992413f4SGarrett D'Amore  */
30992413f4SGarrett D'Amore 
31992413f4SGarrett D'Amore /*
32992413f4SGarrett D'Amore  * Purpose: Driver for the Creative Sound Blaster Live! and Audigy/2/4
33992413f4SGarrett D'Amore  * sound cards
34992413f4SGarrett D'Amore  */
35992413f4SGarrett D'Amore 
36992413f4SGarrett D'Amore #include <sys/types.h>
37992413f4SGarrett D'Amore #include <sys/modctl.h>
38992413f4SGarrett D'Amore #include <sys/kmem.h>
39992413f4SGarrett D'Amore #include <sys/conf.h>
40992413f4SGarrett D'Amore #include <sys/ddi.h>
41992413f4SGarrett D'Amore #include <sys/sunddi.h>
42992413f4SGarrett D'Amore #include <sys/pci.h>
43992413f4SGarrett D'Amore #include <sys/note.h>
44992413f4SGarrett D'Amore #include <sys/stdbool.h>
45992413f4SGarrett D'Amore #include <sys/audio/audio_driver.h>
46992413f4SGarrett D'Amore #include <sys/audio/ac97.h>
47992413f4SGarrett D'Amore 
48992413f4SGarrett D'Amore #include "audioemu10k.h"
49992413f4SGarrett D'Amore #include <sys/promif.h>
50992413f4SGarrett D'Amore 
51992413f4SGarrett D'Amore /*
52992413f4SGarrett D'Amore  * Include the DSP files for emu10k1 (Live!) and emu10k2 (Audigy)
53992413f4SGarrett D'Amore  */
54992413f4SGarrett D'Amore #include "emu10k_gpr.h"
55992413f4SGarrett D'Amore #include "emu10k1_dsp.h"
56992413f4SGarrett D'Amore #include "emu10k2_dsp.h"
57992413f4SGarrett D'Amore 
58992413f4SGarrett D'Amore static struct ddi_device_acc_attr dev_attr = {
59992413f4SGarrett D'Amore 	DDI_DEVICE_ATTR_V0,
60992413f4SGarrett D'Amore 	DDI_STRUCTURE_LE_ACC,
61992413f4SGarrett D'Amore 	DDI_STRICTORDER_ACC
62992413f4SGarrett D'Amore };
63992413f4SGarrett D'Amore 
64992413f4SGarrett D'Amore static struct ddi_device_acc_attr buf_attr = {
65992413f4SGarrett D'Amore 	DDI_DEVICE_ATTR_V0,
66992413f4SGarrett D'Amore 	DDI_NEVERSWAP_ACC,
67992413f4SGarrett D'Amore 	DDI_STRICTORDER_ACC
68992413f4SGarrett D'Amore };
69992413f4SGarrett D'Amore 
70992413f4SGarrett D'Amore 
71992413f4SGarrett D'Amore /*
72992413f4SGarrett D'Amore  * EMU10K routing stuff.
73992413f4SGarrett D'Amore  */
74992413f4SGarrett D'Amore #define	MAX_SENDS		4
75992413f4SGarrett D'Amore #define	SEND_L			0
76992413f4SGarrett D'Amore #define	SEND_R			1
77992413f4SGarrett D'Amore #define	SEND_SURRL		2
78992413f4SGarrett D'Amore #define	SEND_SURRR		3
79992413f4SGarrett D'Amore #define	SEND_CEN		4
80992413f4SGarrett D'Amore #define	SEND_LFE		5
81992413f4SGarrett D'Amore #define	SEND_SIDEL		6
82992413f4SGarrett D'Amore #define	SEND_SIDER		7
83992413f4SGarrett D'Amore 
84992413f4SGarrett D'Amore #define	SPDIF_L			20
85992413f4SGarrett D'Amore #define	SPDIF_R			21
86992413f4SGarrett D'Amore 
87992413f4SGarrett D'Amore /*
88992413f4SGarrett D'Amore  * Recording sources... we start from 16 to ensure that the
89992413f4SGarrett D'Amore  * record sources don't collide with AC'97 record sources in
90992413f4SGarrett D'Amore  * the control value.
91992413f4SGarrett D'Amore  */
92992413f4SGarrett D'Amore #define	INPUT_AC97		1
93992413f4SGarrett D'Amore #define	INPUT_SPD1		2
94992413f4SGarrett D'Amore #define	INPUT_SPD2		3
95992413f4SGarrett D'Amore #define	INPUT_DIGCD		4
96992413f4SGarrett D'Amore #define	INPUT_AUX2		5
97992413f4SGarrett D'Amore #define	INPUT_LINE2		6
98992413f4SGarrett D'Amore #define	INPUT_STEREOMIX		7
99992413f4SGarrett D'Amore 
100992413f4SGarrett D'Amore static uint8_t front_routing[MAX_SENDS] = {
101992413f4SGarrett D'Amore 	SEND_L, SEND_R, 0x3f, 0x3f
102992413f4SGarrett D'Amore };
103992413f4SGarrett D'Amore static uint8_t surr_routing[MAX_SENDS] = {
104992413f4SGarrett D'Amore 	SEND_SURRL, SEND_SURRR, 0x3f, 0x3f
105992413f4SGarrett D'Amore };
106992413f4SGarrett D'Amore static uint8_t clfe_routing[MAX_SENDS] = {
107992413f4SGarrett D'Amore 	SEND_CEN, SEND_LFE, 0x3f, 0x3f
108992413f4SGarrett D'Amore };
109992413f4SGarrett D'Amore static uint8_t side_routing[MAX_SENDS] = {
110992413f4SGarrett D'Amore 	SEND_SIDEL, SEND_SIDER, 0x3f, 0x3f
111992413f4SGarrett D'Amore };
112992413f4SGarrett D'Amore 
113992413f4SGarrett D'Amore /*
114992413f4SGarrett D'Amore  * SB Live! cannot do DMA above 2G addresses. Audigy/2/4 have special 8k page
115992413f4SGarrett D'Amore  * mode that supports high addresses.  However, we should not need this except
116992413f4SGarrett D'Amore  * on SPARC.  For simplicity's sake, we are only delivering this driver for
117992413f4SGarrett D'Amore  * x86 platforms.  If SPARC support is desired, then the code will have to
118992413f4SGarrett D'Amore  * be modified to support full 32-bit addressing.  (And again, SB Live!
119992413f4SGarrett D'Amore  * can't do it anyway.)
120992413f4SGarrett D'Amore  */
121992413f4SGarrett D'Amore 
122992413f4SGarrett D'Amore static ddi_dma_attr_t dma_attr_buf = {
123992413f4SGarrett D'Amore 	DMA_ATTR_V0,		/* Version */
124992413f4SGarrett D'Amore 	0x00000000ULL,		/* Address low */
125992413f4SGarrett D'Amore 	0x7ffffff0ULL,		/* Address high */
126992413f4SGarrett D'Amore 	0xffffffffULL,		/* Counter max */
127992413f4SGarrett D'Amore 	1ULL,			/* Default byte align */
128992413f4SGarrett D'Amore 	0x7f,			/* Burst size */
129992413f4SGarrett D'Amore 	0x1,			/* Minimum xfer size */
130992413f4SGarrett D'Amore 	0xffffffffULL,		/* Maximum xfer size */
131992413f4SGarrett D'Amore 	0xffffffffULL,		/* Max segment size */
132992413f4SGarrett D'Amore 	1,			/* S/G list length */
133992413f4SGarrett D'Amore 	1,			/* Granularity */
134992413f4SGarrett D'Amore 	0			/* Flag */
135992413f4SGarrett D'Amore };
136992413f4SGarrett D'Amore 
137992413f4SGarrett D'Amore static int emu10k_attach(dev_info_t *);
138992413f4SGarrett D'Amore static int emu10k_resume(dev_info_t *);
139992413f4SGarrett D'Amore static int emu10k_detach(emu10k_devc_t *);
140992413f4SGarrett D'Amore static int emu10k_suspend(emu10k_devc_t *);
141992413f4SGarrett D'Amore 
14268c47f65SGarrett D'Amore static int emu10k_open(void *, int, unsigned *, caddr_t *);
143992413f4SGarrett D'Amore static void emu10k_close(void *);
144992413f4SGarrett D'Amore static int emu10k_start(void *);
145992413f4SGarrett D'Amore static void emu10k_stop(void *);
146992413f4SGarrett D'Amore static int emu10k_format(void *);
147992413f4SGarrett D'Amore static int emu10k_channels(void *);
148992413f4SGarrett D'Amore static int emu10k_rate(void *);
149992413f4SGarrett D'Amore static uint64_t emu10k_count(void *);
150992413f4SGarrett D'Amore static void emu10k_sync(void *, unsigned);
151992413f4SGarrett D'Amore static void emu10k_chinfo(void *, int, unsigned *, unsigned *);
152992413f4SGarrett D'Amore 
153992413f4SGarrett D'Amore static uint16_t emu10k_read_ac97(void *, uint8_t);
154992413f4SGarrett D'Amore static void emu10k_write_ac97(void *, uint8_t, uint16_t);
155992413f4SGarrett D'Amore static int emu10k_alloc_port(emu10k_devc_t *, int);
156992413f4SGarrett D'Amore static void emu10k_destroy(emu10k_devc_t *);
157992413f4SGarrett D'Amore static int emu10k_hwinit(emu10k_devc_t *);
158992413f4SGarrett D'Amore static void emu10k_init_effects(emu10k_devc_t *);
159992413f4SGarrett D'Amore 
160992413f4SGarrett D'Amore static audio_engine_ops_t emu10k_engine_ops = {
161992413f4SGarrett D'Amore 	AUDIO_ENGINE_VERSION,
162992413f4SGarrett D'Amore 	emu10k_open,
163992413f4SGarrett D'Amore 	emu10k_close,
164992413f4SGarrett D'Amore 	emu10k_start,
165992413f4SGarrett D'Amore 	emu10k_stop,
166992413f4SGarrett D'Amore 	emu10k_count,
167992413f4SGarrett D'Amore 	emu10k_format,
168992413f4SGarrett D'Amore 	emu10k_channels,
169992413f4SGarrett D'Amore 	emu10k_rate,
170992413f4SGarrett D'Amore 	emu10k_sync,
171f9ead4a5SGarrett D'Amore 	NULL,
172f9ead4a5SGarrett D'Amore 	emu10k_chinfo,
173f9ead4a5SGarrett D'Amore 	NULL
174992413f4SGarrett D'Amore };
175992413f4SGarrett D'Amore 
176992413f4SGarrett D'Amore static uint16_t
emu10k_read_ac97(void * arg,uint8_t index)177992413f4SGarrett D'Amore emu10k_read_ac97(void *arg, uint8_t index)
178992413f4SGarrett D'Amore {
179992413f4SGarrett D'Amore 	emu10k_devc_t *devc = arg;
180992413f4SGarrett D'Amore 	int dtemp = 0, i;
181992413f4SGarrett D'Amore 
182992413f4SGarrett D'Amore 	mutex_enter(&devc->mutex);
183992413f4SGarrett D'Amore 	OUTB(devc, index, devc->regs + 0x1e);
184992413f4SGarrett D'Amore 	for (i = 0; i < 10000; i++)
185992413f4SGarrett D'Amore 		if (INB(devc, devc->regs + 0x1e) & 0x80)
186992413f4SGarrett D'Amore 			break;
187992413f4SGarrett D'Amore 
188992413f4SGarrett D'Amore 	if (i == 1000) {
189992413f4SGarrett D'Amore 		mutex_exit(&devc->mutex);
190992413f4SGarrett D'Amore 		return (0);			/* Timeout */
191992413f4SGarrett D'Amore 	}
192992413f4SGarrett D'Amore 	dtemp = INW(devc, devc->regs + 0x1c);
193992413f4SGarrett D'Amore 
194992413f4SGarrett D'Amore 	mutex_exit(&devc->mutex);
195992413f4SGarrett D'Amore 
196992413f4SGarrett D'Amore 	return (dtemp & 0xffff);
197992413f4SGarrett D'Amore }
198992413f4SGarrett D'Amore 
199992413f4SGarrett D'Amore static void
emu10k_write_ac97(void * arg,uint8_t index,uint16_t data)200992413f4SGarrett D'Amore emu10k_write_ac97(void *arg, uint8_t index, uint16_t data)
201992413f4SGarrett D'Amore {
202992413f4SGarrett D'Amore 	emu10k_devc_t *devc = arg;
203992413f4SGarrett D'Amore 	int i;
204992413f4SGarrett D'Amore 
205992413f4SGarrett D'Amore 	mutex_enter(&devc->mutex);
206992413f4SGarrett D'Amore 
207992413f4SGarrett D'Amore 	OUTB(devc, index, devc->regs + 0x1e);
208992413f4SGarrett D'Amore 	for (i = 0; i < 10000; i++)
209992413f4SGarrett D'Amore 		if (INB(devc, devc->regs + 0x1e) & 0x80)
210992413f4SGarrett D'Amore 			break;
211992413f4SGarrett D'Amore 	OUTW(devc, data, devc->regs + 0x1c);
212992413f4SGarrett D'Amore 
213992413f4SGarrett D'Amore 	mutex_exit(&devc->mutex);
214992413f4SGarrett D'Amore }
215992413f4SGarrett D'Amore 
216992413f4SGarrett D'Amore static uint32_t
emu10k_read_reg(emu10k_devc_t * devc,int reg,int chn)217992413f4SGarrett D'Amore emu10k_read_reg(emu10k_devc_t *devc, int reg, int chn)
218992413f4SGarrett D'Amore {
219992413f4SGarrett D'Amore 	uint32_t ptr, ptr_addr_mask, val, mask, size, offset;
220992413f4SGarrett D'Amore 
221992413f4SGarrett D'Amore 	ptr_addr_mask = (devc->feature_mask &
222992413f4SGarrett D'Amore 	    (SB_AUDIGY|SB_AUDIGY2|SB_AUDIGY2VAL)) ?
223992413f4SGarrett D'Amore 	    0x0fff0000 : 0x07ff0000;
224992413f4SGarrett D'Amore 	ptr = ((reg << 16) & ptr_addr_mask) | (chn & 0x3f);
225992413f4SGarrett D'Amore 	OUTL(devc, ptr, devc->regs + 0x00);	/* Pointer */
226992413f4SGarrett D'Amore 	val = INL(devc, devc->regs + 0x04);	/* Data */
227992413f4SGarrett D'Amore 	if (reg & 0xff000000) {
228992413f4SGarrett D'Amore 		size = (reg >> 24) & 0x3f;
229992413f4SGarrett D'Amore 		offset = (reg >> 16) & 0x1f;
230992413f4SGarrett D'Amore 		mask = ((1 << size) - 1) << offset;
231992413f4SGarrett D'Amore 		val &= mask;
232992413f4SGarrett D'Amore 		val >>= offset;
233992413f4SGarrett D'Amore 	}
234992413f4SGarrett D'Amore 
235992413f4SGarrett D'Amore 	return (val);
236992413f4SGarrett D'Amore }
237992413f4SGarrett D'Amore 
238992413f4SGarrett D'Amore static void
emu10k_write_reg(emu10k_devc_t * devc,int reg,int chn,uint32_t value)239992413f4SGarrett D'Amore emu10k_write_reg(emu10k_devc_t *devc, int reg, int chn, uint32_t value)
240992413f4SGarrett D'Amore {
241992413f4SGarrett D'Amore 	uint32_t ptr, ptr_addr_mask, mask, size, offset;
242992413f4SGarrett D'Amore 
243992413f4SGarrett D'Amore 	ptr_addr_mask = (devc->feature_mask &
244992413f4SGarrett D'Amore 	    (SB_AUDIGY|SB_AUDIGY2|SB_AUDIGY2VAL)) ?
245992413f4SGarrett D'Amore 	    0x0fff0000 : 0x07ff0000;
246992413f4SGarrett D'Amore 	ptr = ((reg << 16) & ptr_addr_mask) | (chn & 0x3f);
247992413f4SGarrett D'Amore 	OUTL(devc, ptr, devc->regs + 0x00);	/* Pointer */
248992413f4SGarrett D'Amore 	if (reg & 0xff000000) {
249992413f4SGarrett D'Amore 		size = (reg >> 24) & 0x3f;
250992413f4SGarrett D'Amore 		offset = (reg >> 16) & 0x1f;
251992413f4SGarrett D'Amore 		mask = ((1 << size) - 1) << offset;
252992413f4SGarrett D'Amore 		value <<= offset;
253992413f4SGarrett D'Amore 		value &= mask;
254992413f4SGarrett D'Amore 		value |= INL(devc, devc->regs + 0x04) & ~mask;	/* data */
255992413f4SGarrett D'Amore 	}
256992413f4SGarrett D'Amore 	OUTL(devc, value, devc->regs + 0x04);	/* Data */
257992413f4SGarrett D'Amore }
258992413f4SGarrett D'Amore 
259992413f4SGarrett D'Amore static void
emu10k_write_routing(emu10k_devc_t * devc,int voice,unsigned char * routing)260992413f4SGarrett D'Amore emu10k_write_routing(emu10k_devc_t *devc, int voice, unsigned char *routing)
261992413f4SGarrett D'Amore {
262992413f4SGarrett D'Amore 	int i;
263992413f4SGarrett D'Amore 
264992413f4SGarrett D'Amore 	ASSERT(routing != NULL);
265992413f4SGarrett D'Amore 
266992413f4SGarrett D'Amore 	if (devc->feature_mask & (SB_AUDIGY|SB_AUDIGY2|SB_AUDIGY2VAL)) {
267992413f4SGarrett D'Amore 		unsigned int srda = 0;
268992413f4SGarrett D'Amore 
269992413f4SGarrett D'Amore 		for (i = 0; i < 4; i++)
270992413f4SGarrett D'Amore 			srda |= routing[i] << (i * 8);
271992413f4SGarrett D'Amore 
272992413f4SGarrett D'Amore 		emu10k_write_reg(devc, SRDA, voice, srda);
273992413f4SGarrett D'Amore 	} else {
274992413f4SGarrett D'Amore 		int fxrt = 0;
275992413f4SGarrett D'Amore 
276992413f4SGarrett D'Amore 		for (i = 0; i < 4; i++)
277992413f4SGarrett D'Amore 			fxrt |= routing[i] << ((i * 4) + 16);
278992413f4SGarrett D'Amore 		emu10k_write_reg(devc, FXRT, voice, fxrt);
279992413f4SGarrett D'Amore 	}
280992413f4SGarrett D'Amore }
281992413f4SGarrett D'Amore 
282992413f4SGarrett D'Amore static void
emu10k_write_efx(emu10k_devc_t * devc,int reg,unsigned int value)283992413f4SGarrett D'Amore emu10k_write_efx(emu10k_devc_t *devc, int reg, unsigned int value)
284992413f4SGarrett D'Amore {
285992413f4SGarrett D'Amore 	emu10k_write_reg(devc, reg, 0, value);
286992413f4SGarrett D'Amore }
287992413f4SGarrett D'Amore 
288992413f4SGarrett D'Amore /*
289992413f4SGarrett D'Amore  * Audio routines
290992413f4SGarrett D'Amore  */
291992413f4SGarrett D'Amore 
292992413f4SGarrett D'Amore static void
emu10k_update_output_volume(emu10k_portc_t * portc,int voice,int chn)293992413f4SGarrett D'Amore emu10k_update_output_volume(emu10k_portc_t *portc, int voice, int chn)
294992413f4SGarrett D'Amore {
295992413f4SGarrett D'Amore 	emu10k_devc_t *devc = portc->devc;
296992413f4SGarrett D'Amore 	unsigned int tmp;
297992413f4SGarrett D'Amore 	unsigned char send[2];
298992413f4SGarrett D'Amore 
299992413f4SGarrett D'Amore 	/*
300992413f4SGarrett D'Amore 	 * Each voice operator of EMU10k has 4 sends (0=left, 1=right,
301992413f4SGarrett D'Amore 	 * 2=surround_left, 3=surround_right). The original OSS driver
302992413f4SGarrett D'Amore 	 * used all of them to spread stereo output to two different
303992413f4SGarrett D'Amore 	 * speaker pairs. This Boomer version uses only the first two
304992413f4SGarrett D'Amore 	 * sends. The other sends are set to 0.
305992413f4SGarrett D'Amore 	 *
306992413f4SGarrett D'Amore 	 * Boomer uses multiple voice pairs to play multichannel
307992413f4SGarrett D'Amore 	 * audio. This function is used to update only one of these
308992413f4SGarrett D'Amore 	 * pairs.
309992413f4SGarrett D'Amore 	 */
310992413f4SGarrett D'Amore 
311992413f4SGarrett D'Amore 	send[0] = 0xff;		/* Max */
312992413f4SGarrett D'Amore 	send[1] = 0xff;		/* Max */
313992413f4SGarrett D'Amore 
314992413f4SGarrett D'Amore 	/* Analog voice */
315992413f4SGarrett D'Amore 	if (chn == LEFT_CH) {
316992413f4SGarrett D'Amore 		send[1] = 0;
317992413f4SGarrett D'Amore 	} else {
318992413f4SGarrett D'Amore 		send[0] = 0;
319992413f4SGarrett D'Amore 	}
320992413f4SGarrett D'Amore 
321992413f4SGarrett D'Amore 	tmp = emu10k_read_reg(devc, PTAB, voice) & 0xffff0000;
322992413f4SGarrett D'Amore 	emu10k_write_reg(devc, PTAB, voice, tmp | (send[0] << 8) | send[1]);
323992413f4SGarrett D'Amore }
324992413f4SGarrett D'Amore 
325992413f4SGarrett D'Amore static void
emu10k_setup_voice(emu10k_portc_t * portc,int voice,int chn,int buf_offset)326992413f4SGarrett D'Amore emu10k_setup_voice(emu10k_portc_t *portc, int voice, int chn, int buf_offset)
327992413f4SGarrett D'Amore {
328992413f4SGarrett D'Amore 	emu10k_devc_t *devc = portc->devc;
329992413f4SGarrett D'Amore 	unsigned int nCRA = 0;
330992413f4SGarrett D'Amore 
331992413f4SGarrett D'Amore 	unsigned int loop_start, loop_end, buf_size;
332992413f4SGarrett D'Amore 
333992413f4SGarrett D'Amore 	int sz;
334992413f4SGarrett D'Amore 	int start_pos;
335992413f4SGarrett D'Amore 
336992413f4SGarrett D'Amore 	emu10k_write_reg(devc, VEDS, voice, 0x0);	/* OFF */
337992413f4SGarrett D'Amore 	emu10k_write_reg(devc, VTFT, voice, 0xffff);
338992413f4SGarrett D'Amore 	emu10k_write_reg(devc, CVCF, voice, 0xffff);
339992413f4SGarrett D'Amore 
340992413f4SGarrett D'Amore 	sz = 2;			/* Shift value for 16 bits stereo */
341992413f4SGarrett D'Amore 
342992413f4SGarrett D'Amore 	/* Size of one stereo sub buffer */
343992413f4SGarrett D'Amore 	buf_size = (portc->buf_size / portc->channels) * 2;
344992413f4SGarrett D'Amore 	loop_start = (portc->memptr + buf_offset) >> sz;
345992413f4SGarrett D'Amore 	loop_end = (portc->memptr + buf_offset + buf_size) >> sz;
346992413f4SGarrett D'Amore 
347992413f4SGarrett D'Amore 	/* set stereo */
348992413f4SGarrett D'Amore 	emu10k_write_reg(devc, CPF, voice, 0x8000);
349992413f4SGarrett D'Amore 
350992413f4SGarrett D'Amore 	nCRA = 28;			/* Stereo (16 bits) */
351992413f4SGarrett D'Amore 	start_pos = loop_start + nCRA;
352992413f4SGarrett D'Amore 
353992413f4SGarrett D'Amore 	/* SDL, ST, CA */
354992413f4SGarrett D'Amore 
355992413f4SGarrett D'Amore 	emu10k_write_reg(devc, SDL, voice, loop_end);
356992413f4SGarrett D'Amore 	emu10k_write_reg(devc, SCSA, voice, loop_start);
357992413f4SGarrett D'Amore 	emu10k_write_reg(devc, PTAB, voice, 0);
358992413f4SGarrett D'Amore 
359992413f4SGarrett D'Amore 	emu10k_update_output_volume(portc, voice, chn);	/* Set volume */
360992413f4SGarrett D'Amore 
361992413f4SGarrett D'Amore 	emu10k_write_reg(devc, QKBCA, voice, start_pos);
362992413f4SGarrett D'Amore 
363992413f4SGarrett D'Amore 	emu10k_write_reg(devc, Z1, voice, 0);
364992413f4SGarrett D'Amore 	emu10k_write_reg(devc, Z2, voice, 0);
365992413f4SGarrett D'Amore 
366992413f4SGarrett D'Amore 	/* This is really a physical address */
367992413f4SGarrett D'Amore 	emu10k_write_reg(devc, MAPA, voice,
368992413f4SGarrett D'Amore 	    0x1fff | (devc->silence_paddr << 1));
369992413f4SGarrett D'Amore 	emu10k_write_reg(devc, MAPB, voice,
370992413f4SGarrett D'Amore 	    0x1fff | (devc->silence_paddr << 1));
371992413f4SGarrett D'Amore 
372992413f4SGarrett D'Amore 	emu10k_write_reg(devc, VTFT, voice, 0x0000ffff);
373992413f4SGarrett D'Amore 	emu10k_write_reg(devc, CVCF, voice, 0x0000ffff);
374992413f4SGarrett D'Amore 	emu10k_write_reg(devc, MEHA, voice, 0);
375992413f4SGarrett D'Amore 	emu10k_write_reg(devc, MEDS, voice, 0x7f);
376992413f4SGarrett D'Amore 	emu10k_write_reg(devc, MLV, voice, 0x8000);
377992413f4SGarrett D'Amore 	emu10k_write_reg(devc, VLV, voice, 0x8000);
378992413f4SGarrett D'Amore 	emu10k_write_reg(devc, VFM, voice, 0);
379992413f4SGarrett D'Amore 	emu10k_write_reg(devc, TMFQ, voice, 0);
380992413f4SGarrett D'Amore 	emu10k_write_reg(devc, VVFQ, voice, 0);
381992413f4SGarrett D'Amore 	emu10k_write_reg(devc, MEV, voice, 0x8000);
382992413f4SGarrett D'Amore 	emu10k_write_reg(devc, VEHA, voice, 0x7f7f);	/* OK */
383992413f4SGarrett D'Amore 	/* No volume envelope delay (OK) */
384992413f4SGarrett D'Amore 	emu10k_write_reg(devc, VEV, voice, 0x8000);
385992413f4SGarrett D'Amore 	emu10k_write_reg(devc, PEFE_FILTERAMOUNT, voice, 0x7f);
386992413f4SGarrett D'Amore 	emu10k_write_reg(devc, PEFE_PITCHAMOUNT, voice, 0x00);
387992413f4SGarrett D'Amore }
388992413f4SGarrett D'Amore 
389992413f4SGarrett D'Amore int
emu10k_open(void * arg,int flag,unsigned * nframes,caddr_t * bufp)39068c47f65SGarrett D'Amore emu10k_open(void *arg, int flag, unsigned *nframes, caddr_t *bufp)
391992413f4SGarrett D'Amore {
392992413f4SGarrett D'Amore 	emu10k_portc_t *portc = arg;
393992413f4SGarrett D'Amore 	emu10k_devc_t *devc = portc->devc;
394992413f4SGarrett D'Amore 
395992413f4SGarrett D'Amore 	_NOTE(ARGUNUSED(flag));
396992413f4SGarrett D'Amore 
397992413f4SGarrett D'Amore 	portc->active = B_FALSE;
39868c47f65SGarrett D'Amore 	*nframes = portc->nframes;
399992413f4SGarrett D'Amore 	*bufp = portc->buf_kaddr;
400992413f4SGarrett D'Amore 
401992413f4SGarrett D'Amore 	mutex_enter(&devc->mutex);
402992413f4SGarrett D'Amore 	portc->count = 0;
403992413f4SGarrett D'Amore 	mutex_exit(&devc->mutex);
404992413f4SGarrett D'Amore 
405992413f4SGarrett D'Amore 	return (0);
406992413f4SGarrett D'Amore }
407992413f4SGarrett D'Amore 
408992413f4SGarrett D'Amore void
emu10k_close(void * arg)409992413f4SGarrett D'Amore emu10k_close(void *arg)
410992413f4SGarrett D'Amore {
41168c47f65SGarrett D'Amore 	_NOTE(ARGUNUSED(arg));
412992413f4SGarrett D'Amore }
413992413f4SGarrett D'Amore 
414992413f4SGarrett D'Amore int
emu10k_start(void * arg)415992413f4SGarrett D'Amore emu10k_start(void *arg)
416992413f4SGarrett D'Amore {
417992413f4SGarrett D'Amore 	emu10k_portc_t *portc = arg;
418992413f4SGarrett D'Amore 	emu10k_devc_t *devc = portc->devc;
419992413f4SGarrett D'Amore 
420992413f4SGarrett D'Amore 	mutex_enter(&devc->mutex);
42168c47f65SGarrett D'Amore 	portc->reset_port(portc);
42268c47f65SGarrett D'Amore 	portc->start_port(portc);
423992413f4SGarrett D'Amore 	mutex_exit(&devc->mutex);
424992413f4SGarrett D'Amore 	return (0);
425992413f4SGarrett D'Amore }
426992413f4SGarrett D'Amore 
427992413f4SGarrett D'Amore void
emu10k_stop(void * arg)428992413f4SGarrett D'Amore emu10k_stop(void *arg)
429992413f4SGarrett D'Amore {
430992413f4SGarrett D'Amore 	emu10k_portc_t *portc = arg;
431992413f4SGarrett D'Amore 	emu10k_devc_t *devc = portc->devc;
432992413f4SGarrett D'Amore 
433992413f4SGarrett D'Amore 	mutex_enter(&devc->mutex);
43468c47f65SGarrett D'Amore 	portc->stop_port(portc);
435992413f4SGarrett D'Amore 	mutex_exit(&devc->mutex);
436992413f4SGarrett D'Amore }
437992413f4SGarrett D'Amore 
438992413f4SGarrett D'Amore int
emu10k_format(void * arg)439992413f4SGarrett D'Amore emu10k_format(void *arg)
440992413f4SGarrett D'Amore {
441992413f4SGarrett D'Amore 	_NOTE(ARGUNUSED(arg));
442992413f4SGarrett D'Amore 
443992413f4SGarrett D'Amore 	return (AUDIO_FORMAT_S16_LE);
444992413f4SGarrett D'Amore }
445992413f4SGarrett D'Amore 
446992413f4SGarrett D'Amore int
emu10k_channels(void * arg)447992413f4SGarrett D'Amore emu10k_channels(void *arg)
448992413f4SGarrett D'Amore {
449992413f4SGarrett D'Amore 	emu10k_portc_t *portc = arg;
450992413f4SGarrett D'Amore 
451992413f4SGarrett D'Amore 	return (portc->channels);
452992413f4SGarrett D'Amore }
453992413f4SGarrett D'Amore 
454992413f4SGarrett D'Amore int
emu10k_rate(void * arg)455992413f4SGarrett D'Amore emu10k_rate(void *arg)
456992413f4SGarrett D'Amore {
457992413f4SGarrett D'Amore 	_NOTE(ARGUNUSED(arg));
458992413f4SGarrett D'Amore 
459992413f4SGarrett D'Amore 	return (SAMPLE_RATE);
460992413f4SGarrett D'Amore }
461992413f4SGarrett D'Amore 
462992413f4SGarrett D'Amore void
emu10k_sync(void * arg,unsigned nframes)463992413f4SGarrett D'Amore emu10k_sync(void *arg, unsigned nframes)
464992413f4SGarrett D'Amore {
465992413f4SGarrett D'Amore 	emu10k_portc_t *portc = arg;
466992413f4SGarrett D'Amore 	_NOTE(ARGUNUSED(nframes));
467992413f4SGarrett D'Amore 
468992413f4SGarrett D'Amore 	(void) ddi_dma_sync(portc->buf_dmah, 0, 0, portc->syncdir);
469992413f4SGarrett D'Amore }
470992413f4SGarrett D'Amore 
471992413f4SGarrett D'Amore uint64_t
emu10k_count(void * arg)472992413f4SGarrett D'Amore emu10k_count(void *arg)
473992413f4SGarrett D'Amore {
474992413f4SGarrett D'Amore 	emu10k_portc_t *portc = arg;
475992413f4SGarrett D'Amore 	emu10k_devc_t *devc = portc->devc;
476992413f4SGarrett D'Amore 	uint64_t count;
477992413f4SGarrett D'Amore 
478992413f4SGarrett D'Amore 	mutex_enter(&devc->mutex);
47968c47f65SGarrett D'Amore 	portc->update_port(portc);
480992413f4SGarrett D'Amore 	count = portc->count;
481992413f4SGarrett D'Amore 	mutex_exit(&devc->mutex);
482992413f4SGarrett D'Amore 
483992413f4SGarrett D'Amore 	return (count);
484992413f4SGarrett D'Amore }
485992413f4SGarrett D'Amore 
486992413f4SGarrett D'Amore static void
emu10k_chinfo(void * arg,int chan,unsigned * offset,unsigned * incr)487992413f4SGarrett D'Amore emu10k_chinfo(void *arg, int chan, unsigned *offset, unsigned *incr)
488992413f4SGarrett D'Amore {
489992413f4SGarrett D'Amore 	emu10k_portc_t *portc = arg;
490992413f4SGarrett D'Amore 
491992413f4SGarrett D'Amore 	*offset = portc->nframes * (chan / 2) * 2 + (chan % 2);
492992413f4SGarrett D'Amore 	*incr = 2;
493992413f4SGarrett D'Amore }
494992413f4SGarrett D'Amore 
495992413f4SGarrett D'Amore /* private implementation bits */
496992413f4SGarrett D'Amore 
497992413f4SGarrett D'Amore static void
emu10k_set_loop_stop(emu10k_devc_t * devc,int voice,int s)498992413f4SGarrett D'Amore emu10k_set_loop_stop(emu10k_devc_t *devc, int voice, int s)
499992413f4SGarrett D'Amore {
500992413f4SGarrett D'Amore 	unsigned int tmp;
501992413f4SGarrett D'Amore 	int offs, bit;
502992413f4SGarrett D'Amore 
503992413f4SGarrett D'Amore 	offs = voice / 32;
504992413f4SGarrett D'Amore 	bit = voice % 32;
505992413f4SGarrett D'Amore 	s = !!s;
506992413f4SGarrett D'Amore 
507992413f4SGarrett D'Amore 	tmp = emu10k_read_reg(devc, SOLL + offs, 0);
508992413f4SGarrett D'Amore 	tmp &= ~(1 << bit);
509992413f4SGarrett D'Amore 
510992413f4SGarrett D'Amore 	if (s)
511992413f4SGarrett D'Amore 		tmp |= (1 << bit);
512992413f4SGarrett D'Amore 	emu10k_write_reg(devc, SOLL + offs, 0, tmp);
513992413f4SGarrett D'Amore }
514992413f4SGarrett D'Amore 
515992413f4SGarrett D'Amore static unsigned int
emu10k_rate_to_pitch(unsigned int rate)516992413f4SGarrett D'Amore emu10k_rate_to_pitch(unsigned int rate)
517992413f4SGarrett D'Amore {
518992413f4SGarrett D'Amore 	static unsigned int logMagTable[128] = {
519992413f4SGarrett D'Amore 		0x00000, 0x02dfc, 0x05b9e, 0x088e6,
520992413f4SGarrett D'Amore 		0x0b5d6, 0x0e26f, 0x10eb3, 0x13aa2,
521992413f4SGarrett D'Amore 		0x1663f, 0x1918a, 0x1bc84, 0x1e72e,
522992413f4SGarrett D'Amore 		0x2118b, 0x23b9a, 0x2655d, 0x28ed5,
523992413f4SGarrett D'Amore 		0x2b803, 0x2e0e8, 0x30985, 0x331db,
524992413f4SGarrett D'Amore 		0x359eb, 0x381b6, 0x3a93d, 0x3d081,
525992413f4SGarrett D'Amore 		0x3f782, 0x41e42, 0x444c1, 0x46b01,
526992413f4SGarrett D'Amore 		0x49101, 0x4b6c4, 0x4dc49, 0x50191,
527992413f4SGarrett D'Amore 		0x5269e, 0x54b6f, 0x57006, 0x59463,
528992413f4SGarrett D'Amore 		0x5b888, 0x5dc74, 0x60029, 0x623a7,
529992413f4SGarrett D'Amore 		0x646ee, 0x66a00, 0x68cdd, 0x6af86,
530992413f4SGarrett D'Amore 		0x6d1fa, 0x6f43c, 0x7164b, 0x73829,
531992413f4SGarrett D'Amore 		0x759d4, 0x77b4f, 0x79c9a, 0x7bdb5,
532992413f4SGarrett D'Amore 		0x7dea1, 0x7ff5e, 0x81fed, 0x8404e,
533992413f4SGarrett D'Amore 		0x86082, 0x88089, 0x8a064, 0x8c014,
534992413f4SGarrett D'Amore 		0x8df98, 0x8fef1, 0x91e20, 0x93d26,
535992413f4SGarrett D'Amore 		0x95c01, 0x97ab4, 0x9993e, 0x9b79f,
536992413f4SGarrett D'Amore 		0x9d5d9, 0x9f3ec, 0xa11d8, 0xa2f9d,
537992413f4SGarrett D'Amore 		0xa4d3c, 0xa6ab5, 0xa8808, 0xaa537,
538992413f4SGarrett D'Amore 		0xac241, 0xadf26, 0xafbe7, 0xb1885,
539992413f4SGarrett D'Amore 		0xb3500, 0xb5157, 0xb6d8c, 0xb899f,
540992413f4SGarrett D'Amore 		0xba58f, 0xbc15e, 0xbdd0c, 0xbf899,
541992413f4SGarrett D'Amore 		0xc1404, 0xc2f50, 0xc4a7b, 0xc6587,
542992413f4SGarrett D'Amore 		0xc8073, 0xc9b3f, 0xcb5ed, 0xcd07c,
543992413f4SGarrett D'Amore 		0xceaec, 0xd053f, 0xd1f73, 0xd398a,
544992413f4SGarrett D'Amore 		0xd5384, 0xd6d60, 0xd8720, 0xda0c3,
545992413f4SGarrett D'Amore 		0xdba4a, 0xdd3b4, 0xded03, 0xe0636,
546992413f4SGarrett D'Amore 		0xe1f4e, 0xe384a, 0xe512c, 0xe69f3,
547992413f4SGarrett D'Amore 		0xe829f, 0xe9b31, 0xeb3a9, 0xecc08,
548992413f4SGarrett D'Amore 		0xee44c, 0xefc78, 0xf148a, 0xf2c83,
549992413f4SGarrett D'Amore 		0xf4463, 0xf5c2a, 0xf73da, 0xf8b71,
550992413f4SGarrett D'Amore 		0xfa2f0, 0xfba57, 0xfd1a7, 0xfe8df
551992413f4SGarrett D'Amore 	};
552992413f4SGarrett D'Amore 	static char logSlopeTable[128] = {
553992413f4SGarrett D'Amore 		0x5c, 0x5c, 0x5b, 0x5a, 0x5a, 0x59, 0x58, 0x58,
554992413f4SGarrett D'Amore 		0x57, 0x56, 0x56, 0x55, 0x55, 0x54, 0x53, 0x53,
555992413f4SGarrett D'Amore 		0x52, 0x52, 0x51, 0x51, 0x50, 0x50, 0x4f, 0x4f,
556992413f4SGarrett D'Amore 		0x4e, 0x4d, 0x4d, 0x4d, 0x4c, 0x4c, 0x4b, 0x4b,
557992413f4SGarrett D'Amore 		0x4a, 0x4a, 0x49, 0x49, 0x48, 0x48, 0x47, 0x47,
558992413f4SGarrett D'Amore 		0x47, 0x46, 0x46, 0x45, 0x45, 0x45, 0x44, 0x44,
559992413f4SGarrett D'Amore 		0x43, 0x43, 0x43, 0x42, 0x42, 0x42, 0x41, 0x41,
560992413f4SGarrett D'Amore 		0x41, 0x40, 0x40, 0x40, 0x3f, 0x3f, 0x3f, 0x3e,
561992413f4SGarrett D'Amore 		0x3e, 0x3e, 0x3d, 0x3d, 0x3d, 0x3c, 0x3c, 0x3c,
562992413f4SGarrett D'Amore 		0x3b, 0x3b, 0x3b, 0x3b, 0x3a, 0x3a, 0x3a, 0x39,
563992413f4SGarrett D'Amore 		0x39, 0x39, 0x39, 0x38, 0x38, 0x38, 0x38, 0x37,
564992413f4SGarrett D'Amore 		0x37, 0x37, 0x37, 0x36, 0x36, 0x36, 0x36, 0x35,
565992413f4SGarrett D'Amore 		0x35, 0x35, 0x35, 0x34, 0x34, 0x34, 0x34, 0x34,
566992413f4SGarrett D'Amore 		0x33, 0x33, 0x33, 0x33, 0x32, 0x32, 0x32, 0x32,
567992413f4SGarrett D'Amore 		0x32, 0x31, 0x31, 0x31, 0x31, 0x31, 0x30, 0x30,
568992413f4SGarrett D'Amore 		0x30, 0x30, 0x30, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f
569992413f4SGarrett D'Amore 	};
570992413f4SGarrett D'Amore 	int i;
571992413f4SGarrett D'Amore 
572992413f4SGarrett D'Amore 	if (rate == 0)
573992413f4SGarrett D'Amore 		return (0);			/* Bail out if no leading "1" */
574992413f4SGarrett D'Amore 	rate *= 11185;		/* Scale 48000 to 0x20002380 */
575992413f4SGarrett D'Amore 	for (i = 31; i > 0; i--) {
576992413f4SGarrett D'Amore 		if (rate & 0x80000000) {	/* Detect leading "1" */
577992413f4SGarrett D'Amore 			return (((unsigned int) (i - 15) << 20) +
578992413f4SGarrett D'Amore 			    logMagTable[0x7f & (rate >> 24)] +
579992413f4SGarrett D'Amore 			    (0x7f & (rate >> 17)) *
580992413f4SGarrett D'Amore 			    logSlopeTable[0x7f & (rate >> 24)]);
581992413f4SGarrett D'Amore 		}
582992413f4SGarrett D'Amore 		rate <<= 1;
583992413f4SGarrett D'Amore 	}
584992413f4SGarrett D'Amore 
585992413f4SGarrett D'Amore 	return (0);			/* Should never reach this point */
586992413f4SGarrett D'Amore }
587992413f4SGarrett D'Amore 
588992413f4SGarrett D'Amore static unsigned int
emu10k_rate_to_linearpitch(unsigned int rate)589992413f4SGarrett D'Amore emu10k_rate_to_linearpitch(unsigned int rate)
590992413f4SGarrett D'Amore {
591992413f4SGarrett D'Amore 	rate = (rate << 8) / 375;
592992413f4SGarrett D'Amore 	return (rate >> 1) + (rate & 1);
593992413f4SGarrett D'Amore }
594992413f4SGarrett D'Amore 
595992413f4SGarrett D'Amore static void
emu10k_prepare_voice(emu10k_devc_t * devc,int voice)596992413f4SGarrett D'Amore emu10k_prepare_voice(emu10k_devc_t *devc, int voice)
597992413f4SGarrett D'Amore {
598992413f4SGarrett D'Amore 	unsigned int sample, initial_pitch, pitch_target;
599992413f4SGarrett D'Amore 	unsigned int cra, cs, ccis, i;
600992413f4SGarrett D'Amore 
601992413f4SGarrett D'Amore 	/* setup CCR regs */
602992413f4SGarrett D'Amore 	cra = 64;
603992413f4SGarrett D'Amore 	cs = 4;			/* Stereo */
604992413f4SGarrett D'Amore 	ccis = 28;		/* Stereo */
605992413f4SGarrett D'Amore 	sample = 0;		/* 16 bit silence */
606992413f4SGarrett D'Amore 
607992413f4SGarrett D'Amore 	for (i = 0; i < cs; i++)
608992413f4SGarrett D'Amore 		emu10k_write_reg(devc, CD0 + i, voice, sample);
609992413f4SGarrett D'Amore 
610992413f4SGarrett D'Amore 	emu10k_write_reg(devc, CCR_CACHEINVALIDSIZE, voice, 0);
611992413f4SGarrett D'Amore 	emu10k_write_reg(devc, CCR_READADDRESS, voice, cra);
612992413f4SGarrett D'Amore 	emu10k_write_reg(devc, CCR_CACHEINVALIDSIZE, voice, ccis);
613992413f4SGarrett D'Amore 
614992413f4SGarrett D'Amore 	/* Set current pitch */
615992413f4SGarrett D'Amore 	emu10k_write_reg(devc, IFA, voice, 0xff00);
616992413f4SGarrett D'Amore 	emu10k_write_reg(devc, VTFT, voice, 0xffffffff);
617992413f4SGarrett D'Amore 	emu10k_write_reg(devc, CVCF, voice, 0xffffffff);
618992413f4SGarrett D'Amore 	emu10k_set_loop_stop(devc, voice, 0);
619992413f4SGarrett D'Amore 
620992413f4SGarrett D'Amore 	pitch_target = emu10k_rate_to_linearpitch(SAMPLE_RATE);
621992413f4SGarrett D'Amore 	initial_pitch = emu10k_rate_to_pitch(SAMPLE_RATE) >> 8;
622992413f4SGarrett D'Amore 	emu10k_write_reg(devc, PTRX_PITCHTARGET, voice, pitch_target);
623992413f4SGarrett D'Amore 	emu10k_write_reg(devc, CPF_CURRENTPITCH, voice, pitch_target);
624992413f4SGarrett D'Amore 	emu10k_write_reg(devc, IP, voice, initial_pitch);
625992413f4SGarrett D'Amore }
626992413f4SGarrett D'Amore 
627992413f4SGarrett D'Amore static void
emu10k_stop_voice(emu10k_devc_t * devc,int voice)628992413f4SGarrett D'Amore emu10k_stop_voice(emu10k_devc_t *devc, int voice)
629992413f4SGarrett D'Amore {
630992413f4SGarrett D'Amore 	emu10k_write_reg(devc, IFA, voice, 0xffff);
631992413f4SGarrett D'Amore 	emu10k_write_reg(devc, VTFT, voice, 0xffff);
632992413f4SGarrett D'Amore 	emu10k_write_reg(devc, PTRX_PITCHTARGET, voice, 0);
633992413f4SGarrett D'Amore 	emu10k_write_reg(devc, CPF_CURRENTPITCH, voice, 0);
634992413f4SGarrett D'Amore 	emu10k_write_reg(devc, IP, voice, 0);
635992413f4SGarrett D'Amore 	emu10k_set_loop_stop(devc, voice, 1);
636992413f4SGarrett D'Amore }
637992413f4SGarrett D'Amore 
638992413f4SGarrett D'Amore static void
emu10k_reset_pair(emu10k_portc_t * portc,int voice,uint8_t * routing,int buf_offset)639992413f4SGarrett D'Amore emu10k_reset_pair(emu10k_portc_t *portc, int voice, uint8_t *routing,
640992413f4SGarrett D'Amore     int buf_offset)
641992413f4SGarrett D'Amore {
642992413f4SGarrett D'Amore 	emu10k_devc_t *devc = portc->devc;
643992413f4SGarrett D'Amore 
644992413f4SGarrett D'Amore 	/* Left channel */
645992413f4SGarrett D'Amore 	/* Intial filter cutoff and attenuation */
646992413f4SGarrett D'Amore 	emu10k_write_reg(devc, IFA, voice, 0xffff);
647992413f4SGarrett D'Amore 	/* Volume envelope decay and sustain */
648992413f4SGarrett D'Amore 	emu10k_write_reg(devc, VEDS, voice, 0x0);
649992413f4SGarrett D'Amore 	/* Volume target and Filter cutoff target */
650992413f4SGarrett D'Amore 	emu10k_write_reg(devc, VTFT, voice, 0xffff);
651992413f4SGarrett D'Amore 	/* Pitch target and sends A and B */
652992413f4SGarrett D'Amore 	emu10k_write_reg(devc, PTAB, voice, 0x0);
653992413f4SGarrett D'Amore 
654992413f4SGarrett D'Amore 	/* The same for right channel */
655992413f4SGarrett D'Amore 	emu10k_write_reg(devc, IFA, voice + 1, 0xffff);
656992413f4SGarrett D'Amore 	emu10k_write_reg(devc, VEDS, voice + 1, 0x0);
657992413f4SGarrett D'Amore 	emu10k_write_reg(devc, VTFT, voice + 1, 0xffff);
658992413f4SGarrett D'Amore 	emu10k_write_reg(devc, PTAB, voice + 1, 0x0);
659992413f4SGarrett D'Amore 
660992413f4SGarrett D'Amore 	/* now setup the voices and go! */
661992413f4SGarrett D'Amore 	emu10k_setup_voice(portc, voice, LEFT_CH, buf_offset);
662992413f4SGarrett D'Amore 	emu10k_setup_voice(portc, voice + 1, RIGHT_CH, buf_offset);
663992413f4SGarrett D'Amore 
664992413f4SGarrett D'Amore 	emu10k_write_routing(devc, voice, routing);
665992413f4SGarrett D'Amore 	emu10k_write_routing(devc, voice + 1, routing);
666992413f4SGarrett D'Amore }
667992413f4SGarrett D'Amore 
668992413f4SGarrett D'Amore void
emu10k_start_play(emu10k_portc_t * portc)669992413f4SGarrett D'Amore emu10k_start_play(emu10k_portc_t *portc)
670992413f4SGarrett D'Amore {
671992413f4SGarrett D'Amore 	emu10k_devc_t *devc = portc->devc;
672992413f4SGarrett D'Amore 
673992413f4SGarrett D'Amore 	ASSERT(mutex_owned(&devc->mutex));
674992413f4SGarrett D'Amore 	emu10k_prepare_voice(devc, 0);
675992413f4SGarrett D'Amore 	emu10k_prepare_voice(devc, 1);
676992413f4SGarrett D'Amore 
677992413f4SGarrett D'Amore 	emu10k_prepare_voice(devc, 2);
678992413f4SGarrett D'Amore 	emu10k_prepare_voice(devc, 3);
679992413f4SGarrett D'Amore 
680992413f4SGarrett D'Amore 	emu10k_prepare_voice(devc, 4);
681992413f4SGarrett D'Amore 	emu10k_prepare_voice(devc, 5);
682992413f4SGarrett D'Amore 
683992413f4SGarrett D'Amore 	emu10k_prepare_voice(devc, 6);
684992413f4SGarrett D'Amore 	emu10k_prepare_voice(devc, 7);
685992413f4SGarrett D'Amore 
686992413f4SGarrett D'Amore 	/* Trigger playback on all voices */
687992413f4SGarrett D'Amore 	emu10k_write_reg(devc, VEDS, 0, 0x7f7f);
688992413f4SGarrett D'Amore 	emu10k_write_reg(devc, VEDS, 1, 0x7f7f);
689992413f4SGarrett D'Amore 	emu10k_write_reg(devc, VEDS, 2, 0x7f7f);
690992413f4SGarrett D'Amore 	emu10k_write_reg(devc, VEDS, 3, 0x7f7f);
691992413f4SGarrett D'Amore 	emu10k_write_reg(devc, VEDS, 4, 0x7f7f);
692992413f4SGarrett D'Amore 	emu10k_write_reg(devc, VEDS, 5, 0x7f7f);
693992413f4SGarrett D'Amore 	emu10k_write_reg(devc, VEDS, 6, 0x7f7f);
694992413f4SGarrett D'Amore 	emu10k_write_reg(devc, VEDS, 7, 0x7f7f);
695992413f4SGarrett D'Amore 
696992413f4SGarrett D'Amore 	portc->active = B_TRUE;
697992413f4SGarrett D'Amore }
698992413f4SGarrett D'Amore 
699992413f4SGarrett D'Amore void
emu10k_stop_play(emu10k_portc_t * portc)700992413f4SGarrett D'Amore emu10k_stop_play(emu10k_portc_t *portc)
701992413f4SGarrett D'Amore {
702992413f4SGarrett D'Amore 	emu10k_devc_t *devc = portc->devc;
703992413f4SGarrett D'Amore 
704992413f4SGarrett D'Amore 	emu10k_stop_voice(devc, 0);
705992413f4SGarrett D'Amore 	emu10k_stop_voice(devc, 1);
706992413f4SGarrett D'Amore 	emu10k_stop_voice(devc, 2);
707992413f4SGarrett D'Amore 	emu10k_stop_voice(devc, 3);
708992413f4SGarrett D'Amore 	emu10k_stop_voice(devc, 4);
709992413f4SGarrett D'Amore 	emu10k_stop_voice(devc, 5);
710992413f4SGarrett D'Amore 	emu10k_stop_voice(devc, 6);
711992413f4SGarrett D'Amore 	emu10k_stop_voice(devc, 7);
712992413f4SGarrett D'Amore 
713992413f4SGarrett D'Amore 	portc->active = B_FALSE;
714992413f4SGarrett D'Amore }
715992413f4SGarrett D'Amore 
716992413f4SGarrett D'Amore void
emu10k_reset_play(emu10k_portc_t * portc)717992413f4SGarrett D'Amore emu10k_reset_play(emu10k_portc_t *portc)
718992413f4SGarrett D'Amore {
719992413f4SGarrett D'Amore 	emu10k_devc_t *devc = portc->devc;
720992413f4SGarrett D'Amore 	uint32_t offs;
721992413f4SGarrett D'Amore 
722992413f4SGarrett D'Amore 	offs = (portc->buf_size / portc->channels) * 2;
723992413f4SGarrett D'Amore 
724992413f4SGarrett D'Amore 	if (devc->feature_mask & SB_71) {
725992413f4SGarrett D'Amore 		emu10k_reset_pair(portc, 0, front_routing, 0);
726992413f4SGarrett D'Amore 		emu10k_reset_pair(portc, 2, clfe_routing, offs);
727992413f4SGarrett D'Amore 		emu10k_reset_pair(portc, 4, surr_routing, 2 * offs);
728992413f4SGarrett D'Amore 		emu10k_reset_pair(portc, 6, side_routing, 3 * offs);
729992413f4SGarrett D'Amore 	} else if (devc->feature_mask & SB_51) {
730992413f4SGarrett D'Amore 		emu10k_reset_pair(portc, 0, front_routing, 0);
731992413f4SGarrett D'Amore 		emu10k_reset_pair(portc, 2, clfe_routing, offs);
732992413f4SGarrett D'Amore 		emu10k_reset_pair(portc, 4, surr_routing, 2 * offs);
733992413f4SGarrett D'Amore 	} else {
734992413f4SGarrett D'Amore 		emu10k_reset_pair(portc, 0, front_routing, 0);
735992413f4SGarrett D'Amore 		emu10k_reset_pair(portc, 2, surr_routing, offs);
736992413f4SGarrett D'Amore 	}
737992413f4SGarrett D'Amore 
738992413f4SGarrett D'Amore 	portc->pos = 0;
739992413f4SGarrett D'Amore }
740992413f4SGarrett D'Amore 
741992413f4SGarrett D'Amore uint32_t emu10k_vars[5];
742992413f4SGarrett D'Amore 
743992413f4SGarrett D'Amore void
emu10k_update_play(emu10k_portc_t * portc)744992413f4SGarrett D'Amore emu10k_update_play(emu10k_portc_t *portc)
745992413f4SGarrett D'Amore {
746992413f4SGarrett D'Amore 	emu10k_devc_t *devc = portc->devc;
747992413f4SGarrett D'Amore 	uint32_t cnt, pos;
748992413f4SGarrett D'Amore 
749992413f4SGarrett D'Amore 	/*
750992413f4SGarrett D'Amore 	 * Note: position is given as stereo samples, i.e. frames.
751992413f4SGarrett D'Amore 	 */
752992413f4SGarrett D'Amore 	pos = emu10k_read_reg(devc, QKBCA, 0) & 0xffffff;
753992413f4SGarrett D'Amore 	pos -= (portc->memptr >> 2);
75468c47f65SGarrett D'Amore 	if (pos > portc->nframes) {
75568c47f65SGarrett D'Amore 		/*
75668c47f65SGarrett D'Amore 		 * This should never happen!  If it happens, we should
75768c47f65SGarrett D'Amore 		 * throw an FMA fault.  (When we support FMA.)  For now
75868c47f65SGarrett D'Amore 		 * we just assume the device is stuck, and report no
75968c47f65SGarrett D'Amore 		 * change in position.
76068c47f65SGarrett D'Amore 		 */
76168c47f65SGarrett D'Amore 		pos = portc->pos;
76268c47f65SGarrett D'Amore 	}
76368c47f65SGarrett D'Amore 	ASSERT(pos <= portc->nframes);
764992413f4SGarrett D'Amore 
76568c47f65SGarrett D'Amore 	if (pos < portc->pos) {
76668c47f65SGarrett D'Amore 		cnt = (portc->nframes - portc->pos) + pos;
767992413f4SGarrett D'Amore 	} else {
768992413f4SGarrett D'Amore 		cnt = (pos - portc->pos);
769992413f4SGarrett D'Amore 	}
77068c47f65SGarrett D'Amore 	ASSERT(cnt <= portc->nframes);
771992413f4SGarrett D'Amore 	if (portc->dopos) {
772992413f4SGarrett D'Amore 		emu10k_vars[0] = portc->pos;
773992413f4SGarrett D'Amore 		emu10k_vars[1] = pos;
774992413f4SGarrett D'Amore 		emu10k_vars[2] = (uint32_t)portc->count;
775992413f4SGarrett D'Amore 		emu10k_vars[3] = cnt;
776992413f4SGarrett D'Amore 		portc->dopos = 0;
777992413f4SGarrett D'Amore 	}
778992413f4SGarrett D'Amore 	portc->count += cnt;
779992413f4SGarrett D'Amore 	portc->pos = pos;
780992413f4SGarrett D'Amore }
781992413f4SGarrett D'Amore 
782992413f4SGarrett D'Amore void
emu10k_start_rec(emu10k_portc_t * portc)783992413f4SGarrett D'Amore emu10k_start_rec(emu10k_portc_t *portc)
784992413f4SGarrett D'Amore {
785992413f4SGarrett D'Amore 	emu10k_devc_t *devc = portc->devc;
786992413f4SGarrett D'Amore 	uint32_t tmp;
787992413f4SGarrett D'Amore 
788992413f4SGarrett D'Amore 	tmp = 0;			/* setup 48Kz */
789992413f4SGarrett D'Amore 	if (devc->feature_mask & (SB_AUDIGY|SB_AUDIGY2|SB_AUDIGY2VAL))
790992413f4SGarrett D'Amore 		tmp |= 0x30;		/* Left/right channel enable */
791992413f4SGarrett D'Amore 	else
792992413f4SGarrett D'Amore 		tmp |= 0x18;		/* Left/right channel enable */
793992413f4SGarrett D'Amore 	emu10k_write_reg(devc, ADCSR, 0, tmp);	/* GO */
794992413f4SGarrett D'Amore 
795992413f4SGarrett D'Amore 	portc->active = B_TRUE;
796992413f4SGarrett D'Amore }
797992413f4SGarrett D'Amore 
798992413f4SGarrett D'Amore void
emu10k_stop_rec(emu10k_portc_t * portc)799992413f4SGarrett D'Amore emu10k_stop_rec(emu10k_portc_t *portc)
800992413f4SGarrett D'Amore {
801992413f4SGarrett D'Amore 	emu10k_devc_t *devc = portc->devc;
802992413f4SGarrett D'Amore 
803992413f4SGarrett D'Amore 	ASSERT(mutex_owned(&devc->mutex));
804992413f4SGarrett D'Amore 	emu10k_write_reg(devc, ADCSR, 0, 0);
805992413f4SGarrett D'Amore 
806992413f4SGarrett D'Amore 	portc->active = B_FALSE;
807992413f4SGarrett D'Amore }
808992413f4SGarrett D'Amore void
emu10k_reset_rec(emu10k_portc_t * portc)809992413f4SGarrett D'Amore emu10k_reset_rec(emu10k_portc_t *portc)
810992413f4SGarrett D'Amore {
811992413f4SGarrett D'Amore 	emu10k_devc_t *devc = portc->devc;
812992413f4SGarrett D'Amore 	uint32_t sz;
813992413f4SGarrett D'Amore 
814992413f4SGarrett D'Amore 	switch (portc->buf_size) {
815992413f4SGarrett D'Amore 	case 4096:
816992413f4SGarrett D'Amore 		sz = 15;
817992413f4SGarrett D'Amore 		break;
818992413f4SGarrett D'Amore 	case 8192:
819992413f4SGarrett D'Amore 		sz = 19;
820992413f4SGarrett D'Amore 		break;
821992413f4SGarrett D'Amore 	case 16384:
822992413f4SGarrett D'Amore 		sz = 23;
823992413f4SGarrett D'Amore 		break;
824992413f4SGarrett D'Amore 	case 32768:
825992413f4SGarrett D'Amore 		sz = 27;
826992413f4SGarrett D'Amore 		break;
827992413f4SGarrett D'Amore 	case 65536:
828992413f4SGarrett D'Amore 		sz = 31;
829992413f4SGarrett D'Amore 		break;
830*8c16a0e6SToomas Soome 	default:
831*8c16a0e6SToomas Soome 		/*
832*8c16a0e6SToomas Soome 		 * Can't really reach here, but this keeps the compiler quiet.
833*8c16a0e6SToomas Soome 		 */
834*8c16a0e6SToomas Soome 		return;
835992413f4SGarrett D'Amore 	}
836992413f4SGarrett D'Amore 	emu10k_write_reg(devc, ADCBA, 0, portc->buf_paddr);
837992413f4SGarrett D'Amore 	emu10k_write_reg(devc, ADCBS, 0, sz);
838992413f4SGarrett D'Amore 	emu10k_write_reg(devc, ADCSR, 0, 0);	/* reset for phase */
839992413f4SGarrett D'Amore 	portc->pos = 0;
840992413f4SGarrett D'Amore }
841992413f4SGarrett D'Amore 
842992413f4SGarrett D'Amore void
emu10k_update_rec(emu10k_portc_t * portc)843992413f4SGarrett D'Amore emu10k_update_rec(emu10k_portc_t *portc)
844992413f4SGarrett D'Amore {
845992413f4SGarrett D'Amore 	emu10k_devc_t *devc = portc->devc;
846992413f4SGarrett D'Amore 	uint32_t cnt, pos;
847992413f4SGarrett D'Amore 
848992413f4SGarrett D'Amore 	/* given in bytes, we divide all counts by 4 to get samples */
849992413f4SGarrett D'Amore 	pos = emu10k_read_reg(devc,
850992413f4SGarrett D'Amore 	    (devc->feature_mask & SB_LIVE) ? MIDX : ADCIDX, 0);
851992413f4SGarrett D'Amore 	if (pos <= portc->pos) {
852992413f4SGarrett D'Amore 		cnt = ((portc->buf_size) - portc->pos) >> 2;
853992413f4SGarrett D'Amore 		cnt += (pos >> 2);
854992413f4SGarrett D'Amore 	} else {
855992413f4SGarrett D'Amore 		cnt = ((pos - portc->pos) >> 2);
856992413f4SGarrett D'Amore 	}
857992413f4SGarrett D'Amore 	portc->count += cnt;
858992413f4SGarrett D'Amore 	portc->pos = pos;
859992413f4SGarrett D'Amore }
860992413f4SGarrett D'Amore 
861992413f4SGarrett D'Amore int
emu10k_alloc_port(emu10k_devc_t * devc,int num)862992413f4SGarrett D'Amore emu10k_alloc_port(emu10k_devc_t *devc, int num)
863992413f4SGarrett D'Amore {
864992413f4SGarrett D'Amore 	emu10k_portc_t *portc;
865992413f4SGarrett D'Amore 	size_t len;
866992413f4SGarrett D'Amore 	ddi_dma_cookie_t cookie;
867992413f4SGarrett D'Amore 	uint_t count;
868992413f4SGarrett D'Amore 	int dir;
869992413f4SGarrett D'Amore 	unsigned caps;
870992413f4SGarrett D'Amore 	audio_dev_t *adev;
871992413f4SGarrett D'Amore 	int i, n;
872992413f4SGarrett D'Amore 
873992413f4SGarrett D'Amore 	adev = devc->adev;
874992413f4SGarrett D'Amore 	portc = kmem_zalloc(sizeof (*portc), KM_SLEEP);
875992413f4SGarrett D'Amore 	devc->portc[num] = portc;
876992413f4SGarrett D'Amore 	portc->devc = devc;
877992413f4SGarrett D'Amore 
878992413f4SGarrett D'Amore 	portc->memptr = devc->audio_memptr;
879992413f4SGarrett D'Amore 	devc->audio_memptr += (DMABUF_SIZE + 4095) & ~4095;
880992413f4SGarrett D'Amore 
881992413f4SGarrett D'Amore 	switch (num) {
882992413f4SGarrett D'Amore 	case EMU10K_REC:
883992413f4SGarrett D'Amore 		portc->syncdir = DDI_DMA_SYNC_FORKERNEL;
884992413f4SGarrett D'Amore 		caps = ENGINE_INPUT_CAP;
885992413f4SGarrett D'Amore 		dir = DDI_DMA_READ;
886992413f4SGarrett D'Amore 		portc->channels = 2;
887992413f4SGarrett D'Amore 		portc->start_port = emu10k_start_rec;
888992413f4SGarrett D'Amore 		portc->stop_port = emu10k_stop_rec;
889992413f4SGarrett D'Amore 		portc->reset_port = emu10k_reset_rec;
890992413f4SGarrett D'Amore 		portc->update_port = emu10k_update_rec;
891992413f4SGarrett D'Amore 		/* This is the minimum record buffer size. */
892992413f4SGarrett D'Amore 		portc->buf_size = 4096;
89368c47f65SGarrett D'Amore 		portc->nframes = portc->buf_size / 4;
894992413f4SGarrett D'Amore 		break;
895992413f4SGarrett D'Amore 	case EMU10K_PLAY:
896992413f4SGarrett D'Amore 		portc->syncdir = DDI_DMA_SYNC_FORDEV;
897992413f4SGarrett D'Amore 		caps = ENGINE_OUTPUT_CAP;
898992413f4SGarrett D'Amore 		dir = DDI_DMA_WRITE;
899992413f4SGarrett D'Amore 		portc->channels = 8;
900992413f4SGarrett D'Amore 		portc->start_port = emu10k_start_play;
901992413f4SGarrett D'Amore 		portc->stop_port = emu10k_stop_play;
902992413f4SGarrett D'Amore 		portc->reset_port = emu10k_reset_play;
903992413f4SGarrett D'Amore 		portc->update_port = emu10k_update_play;
90468c47f65SGarrett D'Amore 		/* This could probably be tunable. */
90568c47f65SGarrett D'Amore 		portc->nframes = 2048;
906992413f4SGarrett D'Amore 		portc->buf_size = portc->nframes * portc->channels * 2;
907992413f4SGarrett D'Amore 		break;
908992413f4SGarrett D'Amore 	default:
909992413f4SGarrett D'Amore 		return (DDI_FAILURE);
910992413f4SGarrett D'Amore 	}
911992413f4SGarrett D'Amore 
912992413f4SGarrett D'Amore 	/*
913992413f4SGarrett D'Amore 	 * Fragments that are not powers of two don't seem to work
914992413f4SGarrett D'Amore 	 * at all with EMU10K.  For simplicity's sake, we eliminate
915992413f4SGarrett D'Amore 	 * the question and fix the interrupt rate.  This is also the
916992413f4SGarrett D'Amore 	 * logical minimum for record, which requires at least 4K for
917992413f4SGarrett D'Amore 	 * the record size.
918992413f4SGarrett D'Amore 	 */
919992413f4SGarrett D'Amore 
920992413f4SGarrett D'Amore 	if (portc->buf_size > DMABUF_SIZE) {
921992413f4SGarrett D'Amore 		cmn_err(CE_NOTE, "Buffer size %d is too large (max %d)",
922992413f4SGarrett D'Amore 		    (int)portc->buf_size, DMABUF_SIZE);
923992413f4SGarrett D'Amore 		portc->buf_size = DMABUF_SIZE;
924992413f4SGarrett D'Amore 	}
925992413f4SGarrett D'Amore 
926992413f4SGarrett D'Amore 	/* Alloc buffers */
927992413f4SGarrett D'Amore 	if (ddi_dma_alloc_handle(devc->dip, &dma_attr_buf, DDI_DMA_SLEEP, NULL,
928992413f4SGarrett D'Amore 	    &portc->buf_dmah) != DDI_SUCCESS) {
929992413f4SGarrett D'Amore 		audio_dev_warn(adev, "failed to allocate BUF handle");
930992413f4SGarrett D'Amore 		return (DDI_FAILURE);
931992413f4SGarrett D'Amore 	}
932992413f4SGarrett D'Amore 
933992413f4SGarrett D'Amore 	if (ddi_dma_mem_alloc(portc->buf_dmah, portc->buf_size,
934992413f4SGarrett D'Amore 	    &dev_attr, DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL,
935992413f4SGarrett D'Amore 	    &portc->buf_kaddr, &len, &portc->buf_acch) != DDI_SUCCESS) {
936992413f4SGarrett D'Amore 		audio_dev_warn(adev, "failed to allocate BUF memory");
937992413f4SGarrett D'Amore 		return (DDI_FAILURE);
938992413f4SGarrett D'Amore 	}
939992413f4SGarrett D'Amore 
940992413f4SGarrett D'Amore 	if (ddi_dma_addr_bind_handle(portc->buf_dmah, NULL, portc->buf_kaddr,
941992413f4SGarrett D'Amore 	    len, DDI_DMA_CONSISTENT | dir, DDI_DMA_SLEEP,
942992413f4SGarrett D'Amore 	    NULL, &cookie, &count) != DDI_SUCCESS) {
943992413f4SGarrett D'Amore 		audio_dev_warn(adev, "failed binding BUF DMA handle");
944992413f4SGarrett D'Amore 		return (DDI_FAILURE);
945992413f4SGarrett D'Amore 	}
946992413f4SGarrett D'Amore 	portc->buf_paddr = cookie.dmac_address;
947992413f4SGarrett D'Amore 
948992413f4SGarrett D'Amore 	if ((devc->feature_mask & SB_LIVE) &&
949992413f4SGarrett D'Amore 	    (portc->buf_paddr & 0x80000000)) {
950992413f4SGarrett D'Amore 		audio_dev_warn(adev, "Got DMA buffer beyond 2G limit.");
951992413f4SGarrett D'Amore 		return (DDI_FAILURE);
952992413f4SGarrett D'Amore 	}
953992413f4SGarrett D'Amore 
954992413f4SGarrett D'Amore 	if (num == EMU10K_PLAY) {	/* Output device */
955992413f4SGarrett D'Amore 		n = portc->memptr / 4096;
956992413f4SGarrett D'Amore 		/*
957992413f4SGarrett D'Amore 		 * Fill the page table
958992413f4SGarrett D'Amore 		 */
959992413f4SGarrett D'Amore 		for (i = 0; i < portc->buf_size / 4096; i++) {
960992413f4SGarrett D'Amore 			FILL_PAGE_MAP_ENTRY(n + i,
961992413f4SGarrett D'Amore 			    portc->buf_paddr + i * 4096);
962992413f4SGarrett D'Amore 		}
963992413f4SGarrett D'Amore 
964992413f4SGarrett D'Amore 		(void) ddi_dma_sync(devc->pt_dmah, 0, 0, DDI_DMA_SYNC_FORDEV);
965992413f4SGarrett D'Amore 	}
966992413f4SGarrett D'Amore 
967992413f4SGarrett D'Amore 	portc->engine = audio_engine_alloc(&emu10k_engine_ops, caps);
968992413f4SGarrett D'Amore 	if (portc->engine == NULL) {
969992413f4SGarrett D'Amore 		audio_dev_warn(adev, "audio_engine_alloc failed");
970992413f4SGarrett D'Amore 		return (DDI_FAILURE);
971992413f4SGarrett D'Amore 	}
972992413f4SGarrett D'Amore 
973992413f4SGarrett D'Amore 	audio_engine_set_private(portc->engine, portc);
974992413f4SGarrett D'Amore 	audio_dev_add_engine(adev, portc->engine);
975992413f4SGarrett D'Amore 
976992413f4SGarrett D'Amore 	return (DDI_SUCCESS);
977992413f4SGarrett D'Amore }
978992413f4SGarrett D'Amore 
979992413f4SGarrett D'Amore void
emu10k_destroy(emu10k_devc_t * devc)980992413f4SGarrett D'Amore emu10k_destroy(emu10k_devc_t *devc)
981992413f4SGarrett D'Amore {
98268c47f65SGarrett D'Amore 	mutex_destroy(&devc->mutex);
983992413f4SGarrett D'Amore 
984992413f4SGarrett D'Amore 	if (devc->silence_paddr) {
985992413f4SGarrett D'Amore 		(void) ddi_dma_unbind_handle(devc->silence_dmah);
986992413f4SGarrett D'Amore 	}
987992413f4SGarrett D'Amore 	if (devc->silence_acch) {
988992413f4SGarrett D'Amore 		ddi_dma_mem_free(&devc->silence_acch);
989992413f4SGarrett D'Amore 	}
990992413f4SGarrett D'Amore 	if (devc->silence_dmah) {
991992413f4SGarrett D'Amore 		ddi_dma_free_handle(&devc->silence_dmah);
992992413f4SGarrett D'Amore 	}
993992413f4SGarrett D'Amore 
994992413f4SGarrett D'Amore 	if (devc->pt_paddr) {
995992413f4SGarrett D'Amore 		(void) ddi_dma_unbind_handle(devc->pt_dmah);
996992413f4SGarrett D'Amore 	}
997992413f4SGarrett D'Amore 	if (devc->pt_acch) {
998992413f4SGarrett D'Amore 		ddi_dma_mem_free(&devc->pt_acch);
999992413f4SGarrett D'Amore 	}
1000992413f4SGarrett D'Amore 	if (devc->pt_dmah) {
1001992413f4SGarrett D'Amore 		ddi_dma_free_handle(&devc->pt_dmah);
1002992413f4SGarrett D'Amore 	}
1003992413f4SGarrett D'Amore 
1004992413f4SGarrett D'Amore 
1005992413f4SGarrett D'Amore 	for (int i = 0; i < CTL_MAX; i++) {
1006992413f4SGarrett D'Amore 		emu10k_ctrl_t *ec = &devc->ctrls[i];
1007992413f4SGarrett D'Amore 		if (ec->ctrl != NULL) {
1008992413f4SGarrett D'Amore 			audio_dev_del_control(ec->ctrl);
1009992413f4SGarrett D'Amore 			ec->ctrl = NULL;
1010992413f4SGarrett D'Amore 		}
1011992413f4SGarrett D'Amore 	}
1012992413f4SGarrett D'Amore 
1013992413f4SGarrett D'Amore 	for (int i = 0; i < EMU10K_NUM_PORTC; i++) {
1014992413f4SGarrett D'Amore 		emu10k_portc_t *portc = devc->portc[i];
1015992413f4SGarrett D'Amore 		if (!portc)
1016992413f4SGarrett D'Amore 			continue;
1017992413f4SGarrett D'Amore 		if (portc->engine) {
1018992413f4SGarrett D'Amore 			audio_dev_remove_engine(devc->adev, portc->engine);
1019992413f4SGarrett D'Amore 			audio_engine_free(portc->engine);
1020992413f4SGarrett D'Amore 		}
1021992413f4SGarrett D'Amore 		if (portc->buf_paddr) {
1022992413f4SGarrett D'Amore 			(void) ddi_dma_unbind_handle(portc->buf_dmah);
1023992413f4SGarrett D'Amore 		}
1024992413f4SGarrett D'Amore 		if (portc->buf_acch) {
1025992413f4SGarrett D'Amore 			ddi_dma_mem_free(&portc->buf_acch);
1026992413f4SGarrett D'Amore 		}
1027992413f4SGarrett D'Amore 		if (portc->buf_dmah) {
1028992413f4SGarrett D'Amore 			ddi_dma_free_handle(&portc->buf_dmah);
1029992413f4SGarrett D'Amore 		}
1030992413f4SGarrett D'Amore 		kmem_free(portc, sizeof (*portc));
1031992413f4SGarrett D'Amore 	}
1032992413f4SGarrett D'Amore 
1033992413f4SGarrett D'Amore 	if (devc->ac97 != NULL) {
1034992413f4SGarrett D'Amore 		ac97_free(devc->ac97);
1035992413f4SGarrett D'Amore 	}
1036992413f4SGarrett D'Amore 	if (devc->adev != NULL) {
1037992413f4SGarrett D'Amore 		audio_dev_free(devc->adev);
1038992413f4SGarrett D'Amore 	}
1039992413f4SGarrett D'Amore 	if (devc->regsh != NULL) {
1040992413f4SGarrett D'Amore 		ddi_regs_map_free(&devc->regsh);
1041992413f4SGarrett D'Amore 	}
1042992413f4SGarrett D'Amore 	if (devc->pcih != NULL) {
1043992413f4SGarrett D'Amore 		pci_config_teardown(&devc->pcih);
1044992413f4SGarrett D'Amore 	}
1045992413f4SGarrett D'Amore 
1046992413f4SGarrett D'Amore 	kmem_free(devc, sizeof (*devc));
1047992413f4SGarrett D'Amore }
1048992413f4SGarrett D'Amore 
1049992413f4SGarrett D'Amore static void
emu10k_init_voice(emu10k_devc_t * devc,int voice)1050992413f4SGarrett D'Amore emu10k_init_voice(emu10k_devc_t *devc, int voice)
1051992413f4SGarrett D'Amore {
1052992413f4SGarrett D'Amore 	emu10k_set_loop_stop(devc, voice, 1);
1053992413f4SGarrett D'Amore 
1054992413f4SGarrett D'Amore 	emu10k_write_reg(devc, VEDS, voice, 0x0);
1055992413f4SGarrett D'Amore 	emu10k_write_reg(devc, IP, voice, 0x0);
1056992413f4SGarrett D'Amore 	emu10k_write_reg(devc, VTFT, voice, 0xffff);
1057992413f4SGarrett D'Amore 	emu10k_write_reg(devc, CVCF, voice, 0xffff);
1058992413f4SGarrett D'Amore 	emu10k_write_reg(devc, PTAB, voice, 0x0);
1059992413f4SGarrett D'Amore 	emu10k_write_reg(devc, CPF, voice, 0x0);
1060992413f4SGarrett D'Amore 	emu10k_write_reg(devc, CCR, voice, 0x0);
1061992413f4SGarrett D'Amore 	emu10k_write_reg(devc, SCSA, voice, 0x0);
1062992413f4SGarrett D'Amore 	emu10k_write_reg(devc, SDL, voice, 0x10);
1063992413f4SGarrett D'Amore 	emu10k_write_reg(devc, QKBCA, voice, 0x0);
1064992413f4SGarrett D'Amore 	emu10k_write_reg(devc, Z1, voice, 0x0);
1065992413f4SGarrett D'Amore 	emu10k_write_reg(devc, Z2, voice, 0x0);
1066992413f4SGarrett D'Amore 
1067992413f4SGarrett D'Amore 	if (devc->feature_mask & (SB_AUDIGY|SB_AUDIGY2|SB_AUDIGY2VAL))
1068992413f4SGarrett D'Amore 		emu10k_write_reg(devc, SRDA, voice, 0x03020100);
1069992413f4SGarrett D'Amore 	else
1070992413f4SGarrett D'Amore 		emu10k_write_reg(devc, FXRT, voice, 0x32100000);
1071992413f4SGarrett D'Amore 
1072992413f4SGarrett D'Amore 	emu10k_write_reg(devc, MEHA, voice, 0x0);
1073992413f4SGarrett D'Amore 	emu10k_write_reg(devc, MEDS, voice, 0x0);
1074992413f4SGarrett D'Amore 	emu10k_write_reg(devc, IFA, voice, 0xffff);
1075992413f4SGarrett D'Amore 	emu10k_write_reg(devc, PEFE, voice, 0x0);
1076992413f4SGarrett D'Amore 	emu10k_write_reg(devc, VFM, voice, 0x0);
1077992413f4SGarrett D'Amore 	emu10k_write_reg(devc, TMFQ, voice, 24);
1078992413f4SGarrett D'Amore 	emu10k_write_reg(devc, VVFQ, voice, 24);
1079992413f4SGarrett D'Amore 	emu10k_write_reg(devc, TMPE, voice, 0x0);
1080992413f4SGarrett D'Amore 	emu10k_write_reg(devc, VLV, voice, 0x0);
1081992413f4SGarrett D'Amore 	emu10k_write_reg(devc, MLV, voice, 0x0);
1082992413f4SGarrett D'Amore 	emu10k_write_reg(devc, VEHA, voice, 0x0);
1083992413f4SGarrett D'Amore 	emu10k_write_reg(devc, VEV, voice, 0x0);
1084992413f4SGarrett D'Amore 	emu10k_write_reg(devc, MEV, voice, 0x0);
1085992413f4SGarrett D'Amore 
1086992413f4SGarrett D'Amore 	if (devc->feature_mask & (SB_AUDIGY|SB_AUDIGY2|SB_AUDIGY2VAL)) {
1087992413f4SGarrett D'Amore 		emu10k_write_reg(devc, CSBA, voice, 0x0);
1088992413f4SGarrett D'Amore 		emu10k_write_reg(devc, CSDC, voice, 0x0);
1089992413f4SGarrett D'Amore 		emu10k_write_reg(devc, CSFE, voice, 0x0);
1090992413f4SGarrett D'Amore 		emu10k_write_reg(devc, CSHG, voice, 0x0);
1091992413f4SGarrett D'Amore 		emu10k_write_reg(devc, SRHE, voice, 0x3f3f3f3f);
1092992413f4SGarrett D'Amore 	}
1093992413f4SGarrett D'Amore }
1094992413f4SGarrett D'Amore 
1095992413f4SGarrett D'Amore int
emu10k_hwinit(emu10k_devc_t * devc)1096992413f4SGarrett D'Amore emu10k_hwinit(emu10k_devc_t *devc)
1097992413f4SGarrett D'Amore {
1098992413f4SGarrett D'Amore 
1099992413f4SGarrett D'Amore 	unsigned int tmp, i;
1100992413f4SGarrett D'Amore 	unsigned int reg;
1101992413f4SGarrett D'Amore 
1102992413f4SGarrett D'Amore 	ASSERT(mutex_owned(&devc->mutex));
1103992413f4SGarrett D'Amore 
1104992413f4SGarrett D'Amore 	emu10k_write_reg(devc, AC97SLOT, 0, AC97SLOT_CENTER | AC97SLOT_LFE);
1105992413f4SGarrett D'Amore 
1106992413f4SGarrett D'Amore 	OUTL(devc, 0x00000000, devc->regs + 0x0c);	/* Intr disable */
1107992413f4SGarrett D'Amore 	OUTL(devc, HCFG_LOCKSOUNDCACHE | HCFG_LOCKTANKCACHE_MASK |
1108992413f4SGarrett D'Amore 	    HCFG_MUTEBUTTONENABLE,
1109992413f4SGarrett D'Amore 	    devc->regs + HCFG);
1110992413f4SGarrett D'Amore 
1111992413f4SGarrett D'Amore 	emu10k_write_reg(devc, MBS, 0, 0x0);
1112992413f4SGarrett D'Amore 	emu10k_write_reg(devc, MBA, 0, 0x0);
1113992413f4SGarrett D'Amore 	emu10k_write_reg(devc, FXBS, 0, 0x0);
1114992413f4SGarrett D'Amore 	emu10k_write_reg(devc, FXBA, 0, 0x0);
1115992413f4SGarrett D'Amore 	emu10k_write_reg(devc, ADCBS, 0, 0x0);
1116992413f4SGarrett D'Amore 	emu10k_write_reg(devc, ADCBA, 0, 0x0);
1117992413f4SGarrett D'Amore 
111868c47f65SGarrett D'Amore 	/* Ensure all interrupts are disabled */
1119992413f4SGarrett D'Amore 	OUTL(devc, 0, devc->regs + IE);
1120992413f4SGarrett D'Amore 	emu10k_write_reg(devc, CLIEL, 0, 0x0);
1121992413f4SGarrett D'Amore 	emu10k_write_reg(devc, CLIEH, 0, 0x0);
1122992413f4SGarrett D'Amore 	if (!(devc->feature_mask & SB_LIVE)) {
1123992413f4SGarrett D'Amore 		emu10k_write_reg(devc, HLIEL, 0, 0x0);
1124992413f4SGarrett D'Amore 		emu10k_write_reg(devc, HLIEH, 0, 0x0);
1125992413f4SGarrett D'Amore 	}
1126992413f4SGarrett D'Amore 	emu10k_write_reg(devc, CLIPL, 0, 0xffffffff);
1127992413f4SGarrett D'Amore 	emu10k_write_reg(devc, CLIPH, 0, 0xffffffff);
1128992413f4SGarrett D'Amore 	emu10k_write_reg(devc, SOLL, 0, 0xffffffff);
1129992413f4SGarrett D'Amore 	emu10k_write_reg(devc, SOLH, 0, 0xffffffff);
1130992413f4SGarrett D'Amore 
1131992413f4SGarrett D'Amore 
1132992413f4SGarrett D'Amore 	if (devc->feature_mask & (SB_AUDIGY|SB_AUDIGY2|SB_AUDIGY2VAL)) {
1133992413f4SGarrett D'Amore 		emu10k_write_reg(devc, SOC, 0, 0xf00);	/* ?? */
1134992413f4SGarrett D'Amore 		emu10k_write_reg(devc, AC97SLOT, 0, 0x3);	/* ?? */
1135992413f4SGarrett D'Amore 	}
1136992413f4SGarrett D'Amore 
1137992413f4SGarrett D'Amore 	for (i = 0; i < 64; i++)
1138992413f4SGarrett D'Amore 		emu10k_init_voice(devc, i);
1139992413f4SGarrett D'Amore 
1140992413f4SGarrett D'Amore 	emu10k_write_reg(devc, SCS0, 0, 0x2109204);
1141992413f4SGarrett D'Amore 	emu10k_write_reg(devc, SCS1, 0, 0x2109204);
1142992413f4SGarrett D'Amore 	emu10k_write_reg(devc, SCS2, 0, 0x2109204);
1143992413f4SGarrett D'Amore 
1144992413f4SGarrett D'Amore 	emu10k_write_reg(devc, PTBA, 0, devc->pt_paddr);
1145992413f4SGarrett D'Amore 	tmp = emu10k_read_reg(devc, PTBA, 0);
1146992413f4SGarrett D'Amore 
1147992413f4SGarrett D'Amore 	emu10k_write_reg(devc, TCBA, 0, 0x0);
1148992413f4SGarrett D'Amore 	emu10k_write_reg(devc, TCBS, 0, 0x4);
1149992413f4SGarrett D'Amore 
1150992413f4SGarrett D'Amore 	reg = 0;
1151992413f4SGarrett D'Amore 	if (devc->feature_mask & SB_71) {
1152992413f4SGarrett D'Amore 		reg = AC97SLOT_CENTER | AC97SLOT_LFE | AC97SLOT_REAR_LEFT |
1153992413f4SGarrett D'Amore 		    AC97SLOT_REAR_RIGHT;
1154992413f4SGarrett D'Amore 	} else if (devc->feature_mask & SB_51) {
1155992413f4SGarrett D'Amore 		reg = AC97SLOT_CENTER | AC97SLOT_LFE;
1156992413f4SGarrett D'Amore 	}
1157992413f4SGarrett D'Amore 	if (devc->feature_mask & (SB_AUDIGY|SB_AUDIGY2|SB_AUDIGY2VAL))
1158992413f4SGarrett D'Amore 		reg |= 0x40;
1159992413f4SGarrett D'Amore 	emu10k_write_reg(devc, AC97SLOT, 0, reg);
1160992413f4SGarrett D'Amore 
1161992413f4SGarrett D'Amore 	if (devc->feature_mask & SB_AUDIGY2) {
1162992413f4SGarrett D'Amore 		/* Enable analog outputs on Audigy2 */
1163992413f4SGarrett D'Amore 		int tmp;
1164992413f4SGarrett D'Amore 
1165992413f4SGarrett D'Amore 		/* Setup SRCMulti_I2S SamplingRate */
1166992413f4SGarrett D'Amore 		tmp = emu10k_read_reg(devc, EHC, 0);
1167992413f4SGarrett D'Amore 		tmp &= 0xfffff1ff;
1168992413f4SGarrett D'Amore 		tmp |= (0x2 << 9);
1169992413f4SGarrett D'Amore 		emu10k_write_reg(devc, EHC, 0, tmp);
1170992413f4SGarrett D'Amore 		/* emu10k_write_reg (devc, SOC, 0, 0x00000000); */
1171992413f4SGarrett D'Amore 
1172992413f4SGarrett D'Amore 		/* Setup SRCSel (Enable Spdif,I2S SRCMulti) */
1173992413f4SGarrett D'Amore 		OUTL(devc, 0x600000, devc->regs + 0x20);
1174992413f4SGarrett D'Amore 		OUTL(devc, 0x14, devc->regs + 0x24);
1175992413f4SGarrett D'Amore 
1176992413f4SGarrett D'Amore 		/* Setup SRCMulti Input Audio Enable */
1177992413f4SGarrett D'Amore 		OUTL(devc, 0x6E0000, devc->regs + 0x20);
1178992413f4SGarrett D'Amore 
1179992413f4SGarrett D'Amore 		OUTL(devc, 0xFF00FF00, devc->regs + 0x24);
1180992413f4SGarrett D'Amore 
1181992413f4SGarrett D'Amore 		/* Setup I2S ASRC Enable  (HC register) */
1182992413f4SGarrett D'Amore 		tmp = INL(devc, devc->regs + HCFG);
1183992413f4SGarrett D'Amore 		tmp |= 0x00000070;
1184992413f4SGarrett D'Amore 		OUTL(devc, tmp, devc->regs + HCFG);
1185992413f4SGarrett D'Amore 
1186992413f4SGarrett D'Amore 		/*
1187992413f4SGarrett D'Amore 		 * Unmute Analog now.  Set GPO6 to 1 for Apollo.
1188992413f4SGarrett D'Amore 		 * This has to be done after init ALice3 I2SOut beyond 48KHz.
1189992413f4SGarrett D'Amore 		 * So, sequence is important
1190992413f4SGarrett D'Amore 		 */
1191992413f4SGarrett D'Amore 		tmp = INL(devc, devc->regs + 0x18);
1192992413f4SGarrett D'Amore 		tmp |= 0x0040;
1193992413f4SGarrett D'Amore 
1194992413f4SGarrett D'Amore 		OUTL(devc, tmp, devc->regs + 0x18);
1195992413f4SGarrett D'Amore 	}
1196992413f4SGarrett D'Amore 
1197992413f4SGarrett D'Amore 	if (devc->feature_mask & SB_AUDIGY2VAL) {
1198992413f4SGarrett D'Amore 		/* Enable analog outputs on Audigy2 */
1199992413f4SGarrett D'Amore 		int tmp;
1200992413f4SGarrett D'Amore 
1201992413f4SGarrett D'Amore 		/* Setup SRCMulti_I2S SamplingRate */
1202992413f4SGarrett D'Amore 		tmp = emu10k_read_reg(devc, EHC, 0);
1203992413f4SGarrett D'Amore 		tmp &= 0xfffff1ff;
1204992413f4SGarrett D'Amore 		tmp |= (0x2 << 9);
1205992413f4SGarrett D'Amore 		emu10k_write_reg(devc, EHC, 0, tmp);
1206992413f4SGarrett D'Amore 
1207992413f4SGarrett D'Amore 		/* Setup SRCSel (Enable Spdif,I2S SRCMulti) */
1208992413f4SGarrett D'Amore 		OUTL(devc, 0x600000, devc->regs + 0x20);
1209992413f4SGarrett D'Amore 		OUTL(devc, 0x14, devc->regs + 0x24);
1210992413f4SGarrett D'Amore 
1211992413f4SGarrett D'Amore 		/* Setup SRCMulti Input Audio Enable */
1212992413f4SGarrett D'Amore 		OUTL(devc, 0x7B0000, devc->regs + 0x20);
1213992413f4SGarrett D'Amore 		OUTL(devc, 0xFF000000, devc->regs + 0x24);
1214992413f4SGarrett D'Amore 
1215992413f4SGarrett D'Amore 		/* SPDIF output enable */
1216992413f4SGarrett D'Amore 		OUTL(devc, 0x7A0000, devc->regs + 0x20);
1217992413f4SGarrett D'Amore 		OUTL(devc, 0xFF000000, devc->regs + 0x24);
1218992413f4SGarrett D'Amore 
1219992413f4SGarrett D'Amore 		tmp = INL(devc, devc->regs + 0x18) & ~0x8;
1220992413f4SGarrett D'Amore 		OUTL(devc, tmp, devc->regs + 0x18);
1221992413f4SGarrett D'Amore 	}
1222992413f4SGarrett D'Amore 
1223992413f4SGarrett D'Amore 	emu10k_write_reg(devc, SOLL, 0, 0xffffffff);
1224992413f4SGarrett D'Amore 	emu10k_write_reg(devc, SOLH, 0, 0xffffffff);
1225992413f4SGarrett D'Amore 
1226992413f4SGarrett D'Amore 	if (devc->feature_mask & (SB_AUDIGY|SB_AUDIGY2|SB_AUDIGY2VAL)) {
1227992413f4SGarrett D'Amore 		unsigned int mode = 0;
1228992413f4SGarrett D'Amore 
1229992413f4SGarrett D'Amore 		if (devc->feature_mask & (SB_AUDIGY2|SB_AUDIGY2VAL))
1230992413f4SGarrett D'Amore 			mode |= HCFG_AC3ENABLE_GPSPDIF | HCFG_AC3ENABLE_CDSPDIF;
1231992413f4SGarrett D'Amore 		OUTL(devc,
1232992413f4SGarrett D'Amore 		    HCFG_AUDIOENABLE | HCFG_AUTOMUTE |
1233992413f4SGarrett D'Amore 		    HCFG_JOYENABLE | A_HCFG_VMUTE |
1234992413f4SGarrett D'Amore 		    A_HCFG_AUTOMUTE | mode, devc->regs + HCFG);
1235992413f4SGarrett D'Amore 
1236992413f4SGarrett D'Amore 		OUTL(devc, INL(devc, devc->regs + 0x18) |
1237992413f4SGarrett D'Amore 		    0x0004, devc->regs + 0x18);	/* GPIO (S/PDIF enable) */
1238992413f4SGarrett D'Amore 
1239992413f4SGarrett D'Amore 
1240992413f4SGarrett D'Amore 		/* enable IR port */
1241992413f4SGarrett D'Amore 		tmp = INL(devc, devc->regs + 0x18);
1242992413f4SGarrett D'Amore 		OUTL(devc, tmp | A_IOCFG_GPOUT2, devc->regs + 0x18);
1243992413f4SGarrett D'Amore 		drv_usecwait(500);
1244992413f4SGarrett D'Amore 		OUTL(devc, tmp | A_IOCFG_GPOUT1 | A_IOCFG_GPOUT2,
1245992413f4SGarrett D'Amore 		    devc->regs + 0x18);
1246992413f4SGarrett D'Amore 		drv_usecwait(100);
1247992413f4SGarrett D'Amore 		OUTL(devc, tmp, devc->regs + 0x18);
1248992413f4SGarrett D'Amore 	} else {
1249992413f4SGarrett D'Amore 		OUTL(devc,
1250992413f4SGarrett D'Amore 		    HCFG_AUDIOENABLE | HCFG_LOCKTANKCACHE_MASK |
1251992413f4SGarrett D'Amore 		    HCFG_AUTOMUTE | HCFG_JOYENABLE, devc->regs + HCFG);
1252992413f4SGarrett D'Amore 	}
1253992413f4SGarrett D'Amore 
1254992413f4SGarrett D'Amore 
1255992413f4SGarrett D'Amore 	/* enable IR port */
1256992413f4SGarrett D'Amore 	tmp = INL(devc, devc->regs + HCFG);
1257992413f4SGarrett D'Amore 	OUTL(devc, tmp | HCFG_GPOUT2, devc->regs + HCFG);
1258992413f4SGarrett D'Amore 	drv_usecwait(500);
1259992413f4SGarrett D'Amore 	OUTL(devc, tmp | HCFG_GPOUT1 | HCFG_GPOUT2, devc->regs + HCFG);
1260992413f4SGarrett D'Amore 	drv_usecwait(100);
1261992413f4SGarrett D'Amore 	OUTL(devc, tmp, devc->regs + HCFG);
1262992413f4SGarrett D'Amore 
1263992413f4SGarrett D'Amore 
1264992413f4SGarrett D'Amore 	/*
1265992413f4SGarrett D'Amore 	 * Start by configuring for analog mode.
1266992413f4SGarrett D'Amore 	 */
1267992413f4SGarrett D'Amore 	if (devc->feature_mask & (SB_AUDIGY|SB_AUDIGY2|SB_AUDIGY2VAL)) {
1268992413f4SGarrett D'Amore 		reg = INL(devc, devc->regs + 0x18) & ~A_IOCFG_GPOUT0;
1269992413f4SGarrett D'Amore 		reg |= ((devc->feature_mask & SB_INVSP) ? 0x4 : 0);
1270992413f4SGarrett D'Amore 		OUTL(devc, reg, devc->regs + 0x18);
1271992413f4SGarrett D'Amore 	}
1272992413f4SGarrett D'Amore 	if (devc->feature_mask & SB_LIVE) {	/* SBLIVE */
1273992413f4SGarrett D'Amore 		reg = INL(devc, devc->regs + HCFG) & ~HCFG_GPOUT0;
1274992413f4SGarrett D'Amore 		reg |= ((devc->feature_mask & SB_INVSP) ? HCFG_GPOUT0 : 0);
1275992413f4SGarrett D'Amore 		OUTL(devc, reg, devc->regs + HCFG);
1276992413f4SGarrett D'Amore 	}
1277992413f4SGarrett D'Amore 
1278992413f4SGarrett D'Amore 	if (devc->feature_mask & SB_AUDIGY2VAL) {
1279992413f4SGarrett D'Amore 		OUTL(devc, INL(devc, devc->regs + 0x18) | 0x0060,
1280992413f4SGarrett D'Amore 		    devc->regs + 0x18);
1281992413f4SGarrett D'Amore 	} else if (devc->feature_mask & SB_AUDIGY2) {
1282992413f4SGarrett D'Amore 		OUTL(devc, INL(devc, devc->regs + 0x18) | 0x0040,
1283992413f4SGarrett D'Amore 		    devc->regs + 0x18);
1284992413f4SGarrett D'Amore 	} else if (devc->feature_mask & SB_AUDIGY) {
1285992413f4SGarrett D'Amore 		OUTL(devc, INL(devc, devc->regs + 0x18) | 0x0080,
1286992413f4SGarrett D'Amore 		    devc->regs + 0x18);
1287992413f4SGarrett D'Amore 	}
1288992413f4SGarrett D'Amore 
1289992413f4SGarrett D'Amore 	emu10k_init_effects(devc);
1290992413f4SGarrett D'Amore 
1291992413f4SGarrett D'Amore 	return (DDI_SUCCESS);
1292992413f4SGarrett D'Amore }
1293992413f4SGarrett D'Amore 
1294992413f4SGarrett D'Amore static const int db2lin_101[101] = {
1295992413f4SGarrett D'Amore 	0x00000000,
1296992413f4SGarrett D'Amore 	0x0024B53A, 0x002750CA, 0x002A1BC6, 0x002D198D, 0x00304DBA, 0x0033BC2A,
1297992413f4SGarrett D'Amore 	0x00376901, 0x003B58AF, 0x003F8FF1, 0x004413DF, 0x0048E9EA, 0x004E17E9,
1298992413f4SGarrett D'Amore 	0x0053A419, 0x0059952C, 0x005FF24E, 0x0066C32A, 0x006E0FFB, 0x0075E18D,
1299992413f4SGarrett D'Amore 	0x007E414F, 0x0087395B, 0x0090D482, 0x009B1E5B, 0x00A6234F, 0x00B1F0A7,
1300992413f4SGarrett D'Amore 	0x00BE94A1, 0x00CC1E7C, 0x00DA9E8D, 0x00EA2650, 0x00FAC881, 0x010C9931,
1301992413f4SGarrett D'Amore 	0x011FADDC, 0x01341D87, 0x014A00D8, 0x01617235, 0x017A8DE6, 0x01957233,
1302992413f4SGarrett D'Amore 	0x01B23F8D, 0x01D118B1, 0x01F222D4, 0x021585D1, 0x023B6C57, 0x0264041D,
1303992413f4SGarrett D'Amore 	0x028F7E19, 0x02BE0EBD, 0x02EFEE33, 0x032558A2, 0x035E8E7A, 0x039BD4BC,
1304992413f4SGarrett D'Amore 	0x03DD7551, 0x0423BF61, 0x046F07B5, 0x04BFA91B, 0x051604D5, 0x0572830D,
1305992413f4SGarrett D'Amore 	0x05D59354, 0x063FAD27, 0x06B15080, 0x072B0673, 0x07AD61CD, 0x0838FFCA,
1306992413f4SGarrett D'Amore 	0x08CE88D3, 0x096EB147, 0x0A1A3A53, 0x0AD1F2E0, 0x0B96B889, 0x0C6978A5,
1307992413f4SGarrett D'Amore 	0x0D4B316A, 0x0E3CF31B, 0x0F3FE155, 0x10553469, 0x117E3AD9, 0x12BC5AEA,
1308992413f4SGarrett D'Amore 	0x14111457, 0x157E0219, 0x1704DC5E, 0x18A77A97, 0x1A67D5B6, 0x1C480A87,
1309992413f4SGarrett D'Amore 	0x1E4A5C45, 0x2071374D, 0x22BF3412, 0x25371A37, 0x27DBE3EF, 0x2AB0C18F,
1310992413f4SGarrett D'Amore 	0x2DB91D6F, 0x30F89FFD, 0x34733433, 0x382D0C46, 0x3C2AA6BD, 0x4070D3D9,
1311992413f4SGarrett D'Amore 	0x4504BB66, 0x49EBE2F1, 0x4F2C346F, 0x54CC0565, 0x5AD21E86, 0x6145C3E7,
1312992413f4SGarrett D'Amore 	0x682EBDBD, 0x6F9561C4, 0x77829D4D,
1313992413f4SGarrett D'Amore 	0x7fffffff
1314992413f4SGarrett D'Amore };
1315992413f4SGarrett D'Amore 
1316992413f4SGarrett D'Amore static int
emu10k_convert_fixpoint(int val)1317992413f4SGarrett D'Amore emu10k_convert_fixpoint(int val)
1318992413f4SGarrett D'Amore {
1319992413f4SGarrett D'Amore 	if (val < 0)
1320992413f4SGarrett D'Amore 		val = 0;
1321992413f4SGarrett D'Amore 	if (val > 100)
1322992413f4SGarrett D'Amore 		val = 100;
1323992413f4SGarrett D'Amore 	return (db2lin_101[val]);
1324992413f4SGarrett D'Amore }
1325992413f4SGarrett D'Amore 
1326992413f4SGarrett D'Amore static void
emu10k_write_gpr(emu10k_devc_t * devc,int gpr,uint32_t value)1327992413f4SGarrett D'Amore emu10k_write_gpr(emu10k_devc_t *devc, int gpr, uint32_t value)
1328992413f4SGarrett D'Amore {
1329992413f4SGarrett D'Amore 	ASSERT(gpr < MAX_GPR);
1330992413f4SGarrett D'Amore 	devc->gpr_shadow[gpr].valid = B_TRUE;
1331992413f4SGarrett D'Amore 	devc->gpr_shadow[gpr].value = value;
133268c47f65SGarrett D'Amore 	emu10k_write_reg(devc, gpr + GPR0, 0, value);
1333992413f4SGarrett D'Amore }
1334992413f4SGarrett D'Amore 
1335992413f4SGarrett D'Amore static int
emu10k_set_stereo(void * arg,uint64_t val)1336992413f4SGarrett D'Amore emu10k_set_stereo(void *arg, uint64_t val)
1337992413f4SGarrett D'Amore {
1338992413f4SGarrett D'Amore 	emu10k_ctrl_t *ec = arg;
1339992413f4SGarrett D'Amore 	emu10k_devc_t *devc = ec->devc;
1340992413f4SGarrett D'Amore 	uint32_t left, right;
1341992413f4SGarrett D'Amore 
1342992413f4SGarrett D'Amore 	left = (val >> 8) & 0xff;
1343992413f4SGarrett D'Amore 	right = val & 0xff;
1344992413f4SGarrett D'Amore 	if ((left > 100) || (right > 100) || (val & ~(0xffff)))
1345992413f4SGarrett D'Amore 		return (EINVAL);
1346992413f4SGarrett D'Amore 
1347992413f4SGarrett D'Amore 	left = emu10k_convert_fixpoint(left);
1348992413f4SGarrett D'Amore 	right = emu10k_convert_fixpoint(right);
1349992413f4SGarrett D'Amore 
1350992413f4SGarrett D'Amore 	mutex_enter(&devc->mutex);
1351992413f4SGarrett D'Amore 	ec->val = val;
1352992413f4SGarrett D'Amore 
1353992413f4SGarrett D'Amore 	emu10k_write_gpr(devc, ec->gpr_num, left);
1354992413f4SGarrett D'Amore 	emu10k_write_gpr(devc, ec->gpr_num + 1, right);
1355992413f4SGarrett D'Amore 
1356992413f4SGarrett D'Amore 	mutex_exit(&devc->mutex);
1357992413f4SGarrett D'Amore 	return (0);
1358992413f4SGarrett D'Amore }
1359992413f4SGarrett D'Amore 
1360992413f4SGarrett D'Amore static int
emu10k_set_mono(void * arg,uint64_t val)1361992413f4SGarrett D'Amore emu10k_set_mono(void *arg, uint64_t val)
1362992413f4SGarrett D'Amore {
1363992413f4SGarrett D'Amore 	emu10k_ctrl_t *ec = arg;
1364992413f4SGarrett D'Amore 	emu10k_devc_t *devc = ec->devc;
1365992413f4SGarrett D'Amore 	uint32_t v;
1366992413f4SGarrett D'Amore 
1367992413f4SGarrett D'Amore 	if (val > 100)
1368992413f4SGarrett D'Amore 		return (EINVAL);
1369992413f4SGarrett D'Amore 
1370992413f4SGarrett D'Amore 	v = emu10k_convert_fixpoint(val & 0xff);
1371992413f4SGarrett D'Amore 
1372992413f4SGarrett D'Amore 	mutex_enter(&devc->mutex);
1373992413f4SGarrett D'Amore 	ec->val = val;
1374992413f4SGarrett D'Amore 	emu10k_write_gpr(devc, ec->gpr_num, v);
1375992413f4SGarrett D'Amore 	mutex_exit(&devc->mutex);
1376992413f4SGarrett D'Amore 	return (0);
1377992413f4SGarrett D'Amore }
1378992413f4SGarrett D'Amore 
1379992413f4SGarrett D'Amore static int
emu10k_get_control(void * arg,uint64_t * val)1380992413f4SGarrett D'Amore emu10k_get_control(void *arg, uint64_t *val)
1381992413f4SGarrett D'Amore {
1382992413f4SGarrett D'Amore 	emu10k_ctrl_t *ec = arg;
1383992413f4SGarrett D'Amore 	emu10k_devc_t *devc = ec->devc;
1384992413f4SGarrett D'Amore 
1385992413f4SGarrett D'Amore 	mutex_enter(&devc->mutex);
1386992413f4SGarrett D'Amore 	*val = ec->val;
1387992413f4SGarrett D'Amore 	mutex_exit(&devc->mutex);
1388992413f4SGarrett D'Amore 	return (0);
1389992413f4SGarrett D'Amore }
1390992413f4SGarrett D'Amore 
1391992413f4SGarrett D'Amore #define	PLAYCTL	(AUDIO_CTRL_FLAG_RW | AUDIO_CTRL_FLAG_PLAY)
1392992413f4SGarrett D'Amore #define	RECCTL	(AUDIO_CTRL_FLAG_RW | AUDIO_CTRL_FLAG_REC)
1393992413f4SGarrett D'Amore #define	MONCTL	(AUDIO_CTRL_FLAG_RW | AUDIO_CTRL_FLAG_MONITOR)
1394992413f4SGarrett D'Amore #define	MAINVOL	(PLAYCTL | AUDIO_CTRL_FLAG_MAINVOL)
1395992413f4SGarrett D'Amore #define	PCMVOL	(PLAYCTL | AUDIO_CTRL_FLAG_PCMVOL)
1396992413f4SGarrett D'Amore #define	RECVOL	(RECCTL | AUDIO_CTRL_FLAG_RECVOL)
1397992413f4SGarrett D'Amore #define	MONVOL	(MONCTL | AUDIO_CTRL_FLAG_MONVOL)
1398992413f4SGarrett D'Amore 
1399992413f4SGarrett D'Amore static int
emu10k_get_ac97src(void * arg,uint64_t * valp)1400992413f4SGarrett D'Amore emu10k_get_ac97src(void *arg, uint64_t *valp)
1401992413f4SGarrett D'Amore {
1402992413f4SGarrett D'Amore 	ac97_ctrl_t *ctrl = arg;
1403992413f4SGarrett D'Amore 
1404992413f4SGarrett D'Amore 	return (ac97_control_get(ctrl, valp));
1405992413f4SGarrett D'Amore }
1406992413f4SGarrett D'Amore 
1407992413f4SGarrett D'Amore static int
emu10k_set_ac97src(void * arg,uint64_t value)1408992413f4SGarrett D'Amore emu10k_set_ac97src(void *arg, uint64_t value)
1409992413f4SGarrett D'Amore {
1410992413f4SGarrett D'Amore 	ac97_ctrl_t	*ctrl = arg;
1411992413f4SGarrett D'Amore 
1412992413f4SGarrett D'Amore 	return (ac97_control_set(ctrl, value));
1413992413f4SGarrett D'Amore }
1414992413f4SGarrett D'Amore 
1415992413f4SGarrett D'Amore static int
emu10k_set_jack3(void * arg,uint64_t value)1416992413f4SGarrett D'Amore emu10k_set_jack3(void *arg, uint64_t value)
1417992413f4SGarrett D'Amore {
1418992413f4SGarrett D'Amore 	emu10k_ctrl_t	*ec = arg;
1419992413f4SGarrett D'Amore 	emu10k_devc_t	*devc = ec->devc;
1420992413f4SGarrett D'Amore 	uint32_t	set_val;
1421992413f4SGarrett D'Amore 	uint32_t	val;
1422992413f4SGarrett D'Amore 
1423992413f4SGarrett D'Amore 	set_val = ddi_ffs(value & 0xffffffffU);
1424992413f4SGarrett D'Amore 	set_val--;
1425992413f4SGarrett D'Amore 	mutex_enter(&devc->mutex);
1426992413f4SGarrett D'Amore 	switch (set_val) {
1427992413f4SGarrett D'Amore 	case 0:
1428992413f4SGarrett D'Amore 	case 1:
1429992413f4SGarrett D'Amore 		break;
1430992413f4SGarrett D'Amore 	default:
1431992413f4SGarrett D'Amore 		mutex_exit(&devc->mutex);
1432992413f4SGarrett D'Amore 		return (EINVAL);
1433992413f4SGarrett D'Amore 	}
1434992413f4SGarrett D'Amore 	ec->val = value;
1435992413f4SGarrett D'Amore 	/* center/lfe */
1436992413f4SGarrett D'Amore 	if (devc->feature_mask & SB_INVSP) {
1437992413f4SGarrett D'Amore 		set_val = !set_val;
1438992413f4SGarrett D'Amore 	}
143968c47f65SGarrett D'Amore 	if (devc->feature_mask & (SB_AUDIGY|SB_AUDIGY2|SB_AUDIGY2VAL)) {
144068c47f65SGarrett D'Amore 		val = INL(devc, devc->regs + 0x18);
144168c47f65SGarrett D'Amore 		val &= ~A_IOCFG_GPOUT0;
144268c47f65SGarrett D'Amore 		val |= set_val ? 0x44 : 0x40;
144368c47f65SGarrett D'Amore 		OUTL(devc, val, devc->regs + 0x18);
144468c47f65SGarrett D'Amore 
144568c47f65SGarrett D'Amore 	} else if (devc->feature_mask & SB_LIVE) {
144668c47f65SGarrett D'Amore 		val = INL(devc, devc->regs + HCFG);
144768c47f65SGarrett D'Amore 		val &= ~HCFG_GPOUT0;
144868c47f65SGarrett D'Amore 		val |= set_val ? HCFG_GPOUT0 : 0;
144968c47f65SGarrett D'Amore 		OUTL(devc, val, devc->regs + HCFG);
1450992413f4SGarrett D'Amore 	}
1451992413f4SGarrett D'Amore 	mutex_exit(&devc->mutex);
1452992413f4SGarrett D'Amore 	return (0);
1453992413f4SGarrett D'Amore }
1454992413f4SGarrett D'Amore 
1455992413f4SGarrett D'Amore static int
emu10k_set_recsrc(void * arg,uint64_t value)1456992413f4SGarrett D'Amore emu10k_set_recsrc(void *arg, uint64_t value)
1457992413f4SGarrett D'Amore {
1458992413f4SGarrett D'Amore 	emu10k_ctrl_t	*ec = arg;
1459992413f4SGarrett D'Amore 	emu10k_devc_t	*devc = ec->devc;
1460992413f4SGarrett D'Amore 	uint32_t	set_val;
1461992413f4SGarrett D'Amore 
1462992413f4SGarrett D'Amore 	set_val = ddi_ffs(value & 0xffffffffU);
1463992413f4SGarrett D'Amore 	set_val--;
1464992413f4SGarrett D'Amore 
1465992413f4SGarrett D'Amore 	/*
1466992413f4SGarrett D'Amore 	 * We start assuming well set up AC'97 for stereomix recording.
1467992413f4SGarrett D'Amore 	 */
1468992413f4SGarrett D'Amore 	switch (set_val) {
1469992413f4SGarrett D'Amore 	case INPUT_AC97:
1470992413f4SGarrett D'Amore 	case INPUT_SPD1:
1471992413f4SGarrett D'Amore 	case INPUT_SPD2:
1472992413f4SGarrett D'Amore 	case INPUT_DIGCD:
1473992413f4SGarrett D'Amore 	case INPUT_AUX2:
1474992413f4SGarrett D'Amore 	case INPUT_LINE2:
1475992413f4SGarrett D'Amore 	case INPUT_STEREOMIX:
1476992413f4SGarrett D'Amore 		break;
1477992413f4SGarrett D'Amore 	default:
1478992413f4SGarrett D'Amore 		return (EINVAL);
1479992413f4SGarrett D'Amore 	}
1480992413f4SGarrett D'Amore 
1481992413f4SGarrett D'Amore 	mutex_enter(&devc->mutex);
1482992413f4SGarrett D'Amore 	ec->val = value;
1483992413f4SGarrett D'Amore 
1484992413f4SGarrett D'Amore 	emu10k_write_gpr(devc, GPR_REC_AC97, (set_val == INPUT_AC97));
1485992413f4SGarrett D'Amore 	emu10k_write_gpr(devc, GPR_REC_SPDIF1, (set_val == INPUT_SPD1));
1486992413f4SGarrett D'Amore 	emu10k_write_gpr(devc, GPR_REC_SPDIF2, (set_val == INPUT_SPD2));
1487992413f4SGarrett D'Amore 	emu10k_write_gpr(devc, GPR_REC_DIGCD, (set_val == INPUT_DIGCD));
1488992413f4SGarrett D'Amore 	emu10k_write_gpr(devc, GPR_REC_AUX2, (set_val == INPUT_AUX2));
1489992413f4SGarrett D'Amore 	emu10k_write_gpr(devc, GPR_REC_LINE2, (set_val == INPUT_LINE2));
1490992413f4SGarrett D'Amore 	emu10k_write_gpr(devc, GPR_REC_PCM, (set_val == INPUT_STEREOMIX));
1491992413f4SGarrett D'Amore 
1492992413f4SGarrett D'Amore 	mutex_exit(&devc->mutex);
1493992413f4SGarrett D'Amore 
1494992413f4SGarrett D'Amore 	return (0);
1495992413f4SGarrett D'Amore }
1496992413f4SGarrett D'Amore 
1497992413f4SGarrett D'Amore static void
emu10k_create_stereo(emu10k_devc_t * devc,int ctl,int gpr,const char * id,int flags,int defval)1498992413f4SGarrett D'Amore emu10k_create_stereo(emu10k_devc_t *devc, int ctl, int gpr,
1499992413f4SGarrett D'Amore     const char *id, int flags, int defval)
1500992413f4SGarrett D'Amore {
1501992413f4SGarrett D'Amore 	emu10k_ctrl_t *ec;
1502992413f4SGarrett D'Amore 	audio_ctrl_desc_t desc;
1503992413f4SGarrett D'Amore 
1504992413f4SGarrett D'Amore 	bzero(&desc, sizeof (desc));
1505992413f4SGarrett D'Amore 
1506992413f4SGarrett D'Amore 	ec = &devc->ctrls[ctl];
1507992413f4SGarrett D'Amore 	ec->devc = devc;
1508992413f4SGarrett D'Amore 	ec->gpr_num = gpr;
1509992413f4SGarrett D'Amore 
1510992413f4SGarrett D'Amore 	desc.acd_name = id;
1511992413f4SGarrett D'Amore 	desc.acd_type = AUDIO_CTRL_TYPE_STEREO;
1512992413f4SGarrett D'Amore 	desc.acd_minvalue = 0;
1513992413f4SGarrett D'Amore 	desc.acd_maxvalue = 100;
1514992413f4SGarrett D'Amore 	desc.acd_flags = flags;
1515992413f4SGarrett D'Amore 
1516992413f4SGarrett D'Amore 	ec->val = (defval << 8) | defval;
1517992413f4SGarrett D'Amore 	ec->ctrl = audio_dev_add_control(devc->adev, &desc,
1518992413f4SGarrett D'Amore 	    emu10k_get_control, emu10k_set_stereo, ec);
1519992413f4SGarrett D'Amore 
1520992413f4SGarrett D'Amore 	mutex_enter(&devc->mutex);
1521992413f4SGarrett D'Amore 	emu10k_write_gpr(devc, gpr, emu10k_convert_fixpoint(defval));
1522992413f4SGarrett D'Amore 	emu10k_write_gpr(devc, gpr + 1, emu10k_convert_fixpoint(defval));
1523992413f4SGarrett D'Amore 	mutex_exit(&devc->mutex);
1524992413f4SGarrett D'Amore }
1525992413f4SGarrett D'Amore 
1526992413f4SGarrett D'Amore static void
emu10k_create_mono(emu10k_devc_t * devc,int ctl,int gpr,const char * id,int flags,int defval)1527992413f4SGarrett D'Amore emu10k_create_mono(emu10k_devc_t *devc, int ctl, int gpr,
1528992413f4SGarrett D'Amore     const char *id, int flags, int defval)
1529992413f4SGarrett D'Amore {
1530992413f4SGarrett D'Amore 	emu10k_ctrl_t *ec;
1531992413f4SGarrett D'Amore 	audio_ctrl_desc_t desc;
1532992413f4SGarrett D'Amore 
1533992413f4SGarrett D'Amore 	bzero(&desc, sizeof (desc));
1534992413f4SGarrett D'Amore 
1535992413f4SGarrett D'Amore 	ec = &devc->ctrls[ctl];
1536992413f4SGarrett D'Amore 	ec->devc = devc;
1537992413f4SGarrett D'Amore 	ec->gpr_num = gpr;
1538992413f4SGarrett D'Amore 
1539992413f4SGarrett D'Amore 	desc.acd_name = id;
1540992413f4SGarrett D'Amore 	desc.acd_type = AUDIO_CTRL_TYPE_MONO;
1541992413f4SGarrett D'Amore 	desc.acd_minvalue = 0;
1542992413f4SGarrett D'Amore 	desc.acd_maxvalue = 100;
1543992413f4SGarrett D'Amore 	desc.acd_flags = flags;
1544992413f4SGarrett D'Amore 
1545992413f4SGarrett D'Amore 	ec->val = defval;
1546992413f4SGarrett D'Amore 	ec->ctrl = audio_dev_add_control(devc->adev, &desc,
1547992413f4SGarrett D'Amore 	    emu10k_get_control, emu10k_set_mono, ec);
1548992413f4SGarrett D'Amore 
1549992413f4SGarrett D'Amore 	mutex_enter(&devc->mutex);
1550992413f4SGarrett D'Amore 	emu10k_write_gpr(devc, gpr, emu10k_convert_fixpoint(defval));
1551992413f4SGarrett D'Amore 	mutex_exit(&devc->mutex);
1552992413f4SGarrett D'Amore }
1553992413f4SGarrett D'Amore 
1554992413f4SGarrett D'Amore /*
1555992413f4SGarrett D'Amore  * AC'97 source.  The AC'97 PCM record channel is routed to our
1556992413f4SGarrett D'Amore  * mixer.  While we could support the direct monitoring capability of
1557992413f4SGarrett D'Amore  * the AC'97 part itself, this would not work correctly with outputs
1558992413f4SGarrett D'Amore  * that are not routed via AC'97 (such as the Live Drive headphones
1559992413f4SGarrett D'Amore  * or digital outputs.)  So we just offer the ability to select one
1560992413f4SGarrett D'Amore  * AC'97 source, and then offer independent ability to either monitor
1561992413f4SGarrett D'Amore  * or record from the AC'97 mixer's PCM record channel.
1562992413f4SGarrett D'Amore  */
1563992413f4SGarrett D'Amore static void
emu10k_create_ac97src(emu10k_devc_t * devc)1564992413f4SGarrett D'Amore emu10k_create_ac97src(emu10k_devc_t *devc)
1565992413f4SGarrett D'Amore {
1566992413f4SGarrett D'Amore 	emu10k_ctrl_t *ec;
1567992413f4SGarrett D'Amore 	audio_ctrl_desc_t desc;
1568992413f4SGarrett D'Amore 	ac97_ctrl_t *ac;
1569992413f4SGarrett D'Amore 	const audio_ctrl_desc_t *acd;
1570992413f4SGarrett D'Amore 
1571992413f4SGarrett D'Amore 	bzero(&desc, sizeof (desc));
1572992413f4SGarrett D'Amore 
1573992413f4SGarrett D'Amore 	ec = &devc->ctrls[CTL_AC97SRC];
1574992413f4SGarrett D'Amore 	desc.acd_name = "ac97-source";
1575992413f4SGarrett D'Amore 	desc.acd_type = AUDIO_CTRL_TYPE_ENUM;
1576992413f4SGarrett D'Amore 	desc.acd_flags = RECCTL;
1577992413f4SGarrett D'Amore 	ec->devc = devc;
1578992413f4SGarrett D'Amore 	ac = ac97_control_find(devc->ac97, AUDIO_CTRL_ID_RECSRC);
1579992413f4SGarrett D'Amore 	if (ac == NULL) {
1580992413f4SGarrett D'Amore 		return;
1581992413f4SGarrett D'Amore 	}
1582992413f4SGarrett D'Amore 
1583992413f4SGarrett D'Amore 	acd = ac97_control_desc(ac);
1584992413f4SGarrett D'Amore 
1585992413f4SGarrett D'Amore 	for (int i = 0; i < 64; i++) {
1586992413f4SGarrett D'Amore 		const char *n;
1587992413f4SGarrett D'Amore 		if (((acd->acd_minvalue & (1ULL << i)) == 0) ||
1588992413f4SGarrett D'Amore 		    ((n = acd->acd_enum[i]) == NULL)) {
1589992413f4SGarrett D'Amore 			continue;
1590992413f4SGarrett D'Amore 		}
1591992413f4SGarrett D'Amore 		desc.acd_enum[i] = acd->acd_enum[i];
1592992413f4SGarrett D'Amore 		/* we suppress some port options */
1593992413f4SGarrett D'Amore 		if ((strcmp(n, AUDIO_PORT_STEREOMIX) == 0) ||
1594992413f4SGarrett D'Amore 		    (strcmp(n, AUDIO_PORT_MONOMIX) == 0) ||
1595992413f4SGarrett D'Amore 		    (strcmp(n, AUDIO_PORT_VIDEO) == 0)) {
1596992413f4SGarrett D'Amore 			continue;
1597992413f4SGarrett D'Amore 		}
1598992413f4SGarrett D'Amore 		desc.acd_minvalue |= (1ULL << i);
1599992413f4SGarrett D'Amore 		desc.acd_maxvalue |= (1ULL << i);
1600992413f4SGarrett D'Amore 	}
1601992413f4SGarrett D'Amore 
1602992413f4SGarrett D'Amore 	ec->ctrl = audio_dev_add_control(devc->adev, &desc,
1603992413f4SGarrett D'Amore 	    emu10k_get_ac97src, emu10k_set_ac97src, ac);
1604992413f4SGarrett D'Amore }
1605992413f4SGarrett D'Amore 
1606992413f4SGarrett D'Amore /*
1607992413f4SGarrett D'Amore  * Record source... this one is tricky.  While the chip will
1608992413f4SGarrett D'Amore  * conceivably let us *mix* some of the audio streams for recording,
1609992413f4SGarrett D'Amore  * the AC'97 inputs don't have this capability.  Offering it to users
1610992413f4SGarrett D'Amore  * is likely to be confusing, so we offer a single record source
1611992413f4SGarrett D'Amore  * selection option.  Its not ideal, but it ought to be good enough
1612992413f4SGarrett D'Amore  * for the vast majority of users.
1613992413f4SGarrett D'Amore  */
1614992413f4SGarrett D'Amore static void
emu10k_create_recsrc(emu10k_devc_t * devc)1615992413f4SGarrett D'Amore emu10k_create_recsrc(emu10k_devc_t *devc)
1616992413f4SGarrett D'Amore {
1617992413f4SGarrett D'Amore 	emu10k_ctrl_t *ec;
1618992413f4SGarrett D'Amore 	audio_ctrl_desc_t desc;
1619992413f4SGarrett D'Amore 	ac97_ctrl_t *ac;
1620992413f4SGarrett D'Amore 
1621992413f4SGarrett D'Amore 	bzero(&desc, sizeof (desc));
1622992413f4SGarrett D'Amore 
1623992413f4SGarrett D'Amore 	ec = &devc->ctrls[CTL_RECSRC];
1624992413f4SGarrett D'Amore 	desc.acd_name = AUDIO_CTRL_ID_RECSRC;
1625992413f4SGarrett D'Amore 	desc.acd_type = AUDIO_CTRL_TYPE_ENUM;
1626992413f4SGarrett D'Amore 	desc.acd_flags = RECCTL;
1627992413f4SGarrett D'Amore 	desc.acd_minvalue = 0;
1628992413f4SGarrett D'Amore 	desc.acd_maxvalue = 0;
1629992413f4SGarrett D'Amore 	bzero(desc.acd_enum, sizeof (desc.acd_enum));
1630992413f4SGarrett D'Amore 	ec->devc = devc;
1631992413f4SGarrett D'Amore 	ac = ac97_control_find(devc->ac97, AUDIO_CTRL_ID_RECSRC);
1632992413f4SGarrett D'Amore 
1633992413f4SGarrett D'Amore 	/* only low order bits set by AC'97 */
1634992413f4SGarrett D'Amore 	ASSERT(desc.acd_minvalue == desc.acd_maxvalue);
1635992413f4SGarrett D'Amore 	ASSERT((desc.acd_minvalue & ~0xffff) == 0);
1636992413f4SGarrett D'Amore 
1637992413f4SGarrett D'Amore 	/*
1638992413f4SGarrett D'Amore 	 * It would be really cool if we could detect whether these
1639992413f4SGarrett D'Amore 	 * options are all sensible on a given configuration.  Units
1640992413f4SGarrett D'Amore 	 * without live-drive support, and units without a physical
1641992413f4SGarrett D'Amore 	 * live-drive, simply can't do all these.
1642992413f4SGarrett D'Amore 	 */
1643992413f4SGarrett D'Amore 	if (ac != NULL) {
1644992413f4SGarrett D'Amore 		desc.acd_minvalue |= (1 << INPUT_AC97);
1645992413f4SGarrett D'Amore 		desc.acd_maxvalue |= (1 << INPUT_AC97);
1646992413f4SGarrett D'Amore 		desc.acd_enum[INPUT_AC97] = "ac97";
1647992413f4SGarrett D'Amore 		ec->val = (1 << INPUT_AC97);
1648992413f4SGarrett D'Amore 	} else {
1649992413f4SGarrett D'Amore 		/* next best guess */
1650992413f4SGarrett D'Amore 		ec->val = (1 << INPUT_LINE2);
1651992413f4SGarrett D'Amore 	}
1652992413f4SGarrett D'Amore 
1653992413f4SGarrett D'Amore 	desc.acd_minvalue |= (1 << INPUT_SPD1);
1654992413f4SGarrett D'Amore 	desc.acd_maxvalue |= (1 << INPUT_SPD1);
1655992413f4SGarrett D'Amore 	desc.acd_enum[INPUT_SPD1] = AUDIO_PORT_SPDIFIN;
1656992413f4SGarrett D'Amore 
1657992413f4SGarrett D'Amore 	desc.acd_minvalue |= (1 << INPUT_SPD2);
1658992413f4SGarrett D'Amore 	desc.acd_maxvalue |= (1 << INPUT_SPD2);
1659992413f4SGarrett D'Amore 	desc.acd_enum[INPUT_SPD2] = "spdif2-in";
1660992413f4SGarrett D'Amore 
1661992413f4SGarrett D'Amore 	desc.acd_minvalue |= (1 << INPUT_DIGCD);
1662992413f4SGarrett D'Amore 	desc.acd_maxvalue |= (1 << INPUT_DIGCD);
1663992413f4SGarrett D'Amore 	desc.acd_enum[INPUT_DIGCD] = "digital-cd";
1664992413f4SGarrett D'Amore 
1665992413f4SGarrett D'Amore 	desc.acd_minvalue |= (1 << INPUT_AUX2);
1666992413f4SGarrett D'Amore 	desc.acd_maxvalue |= (1 << INPUT_AUX2);
1667992413f4SGarrett D'Amore 	desc.acd_enum[INPUT_AUX2] = AUDIO_PORT_AUX2IN;
1668992413f4SGarrett D'Amore 
1669992413f4SGarrett D'Amore 	desc.acd_minvalue |= (1 << INPUT_LINE2);
1670992413f4SGarrett D'Amore 	desc.acd_maxvalue |= (1 << INPUT_LINE2);
1671992413f4SGarrett D'Amore 	desc.acd_enum[INPUT_LINE2] = "line2-in";
1672992413f4SGarrett D'Amore 
1673992413f4SGarrett D'Amore 	desc.acd_minvalue |= (1 << INPUT_STEREOMIX);
1674992413f4SGarrett D'Amore 	desc.acd_maxvalue |= (1 << INPUT_STEREOMIX);
1675992413f4SGarrett D'Amore 	desc.acd_enum[INPUT_STEREOMIX] = AUDIO_PORT_STEREOMIX;
1676992413f4SGarrett D'Amore 
1677992413f4SGarrett D'Amore 	emu10k_write_gpr(devc, GPR_REC_SPDIF1, 0);
1678992413f4SGarrett D'Amore 	emu10k_write_gpr(devc, GPR_REC_SPDIF2, 0);
1679992413f4SGarrett D'Amore 	emu10k_write_gpr(devc, GPR_REC_DIGCD, 0);
1680992413f4SGarrett D'Amore 	emu10k_write_gpr(devc, GPR_REC_AUX2, 0);
1681992413f4SGarrett D'Amore 	emu10k_write_gpr(devc, GPR_REC_LINE2, 0);
1682992413f4SGarrett D'Amore 	emu10k_write_gpr(devc, GPR_REC_PCM, 0);
1683992413f4SGarrett D'Amore 	emu10k_write_gpr(devc, GPR_REC_AC97, 1);
1684992413f4SGarrett D'Amore 
1685992413f4SGarrett D'Amore 	ec->ctrl = audio_dev_add_control(devc->adev, &desc,
1686992413f4SGarrett D'Amore 	    emu10k_get_control, emu10k_set_recsrc, ec);
1687992413f4SGarrett D'Amore }
1688992413f4SGarrett D'Amore 
1689992413f4SGarrett D'Amore static void
emu10k_create_jack3(emu10k_devc_t * devc)1690992413f4SGarrett D'Amore emu10k_create_jack3(emu10k_devc_t *devc)
1691992413f4SGarrett D'Amore {
1692992413f4SGarrett D'Amore 	emu10k_ctrl_t *ec;
1693992413f4SGarrett D'Amore 	audio_ctrl_desc_t desc;
1694992413f4SGarrett D'Amore 
1695992413f4SGarrett D'Amore 	bzero(&desc, sizeof (desc));
1696992413f4SGarrett D'Amore 
1697992413f4SGarrett D'Amore 	ec = &devc->ctrls[CTL_JACK3];
1698992413f4SGarrett D'Amore 	desc.acd_name = AUDIO_CTRL_ID_JACK3;
1699992413f4SGarrett D'Amore 	desc.acd_type = AUDIO_CTRL_TYPE_ENUM;
1700992413f4SGarrett D'Amore 	desc.acd_flags = AUDIO_CTRL_FLAG_RW;
1701992413f4SGarrett D'Amore 	desc.acd_minvalue = 0x3;
1702992413f4SGarrett D'Amore 	desc.acd_maxvalue = 0x3;
1703992413f4SGarrett D'Amore 	bzero(desc.acd_enum, sizeof (desc.acd_enum));
1704992413f4SGarrett D'Amore 	ec->devc = devc;
1705992413f4SGarrett D'Amore 	ec->val = 0x1;
1706992413f4SGarrett D'Amore 
1707992413f4SGarrett D'Amore 	desc.acd_enum[0] = AUDIO_PORT_CENLFE;
1708992413f4SGarrett D'Amore 	desc.acd_enum[1] = AUDIO_PORT_SPDIFOUT;
1709992413f4SGarrett D'Amore 
1710992413f4SGarrett D'Amore 	ec->ctrl = audio_dev_add_control(devc->adev, &desc,
1711992413f4SGarrett D'Amore 	    emu10k_get_control, emu10k_set_jack3, ec);
1712992413f4SGarrett D'Amore }
1713992413f4SGarrett D'Amore 
1714992413f4SGarrett D'Amore 
1715992413f4SGarrett D'Amore static void
emu10k_create_controls(emu10k_devc_t * devc)1716992413f4SGarrett D'Amore emu10k_create_controls(emu10k_devc_t *devc)
1717992413f4SGarrett D'Amore {
1718992413f4SGarrett D'Amore 	ac97_t		*ac97;
1719992413f4SGarrett D'Amore 	ac97_ctrl_t	*ac;
1720992413f4SGarrett D'Amore 
1721992413f4SGarrett D'Amore 	emu10k_create_mono(devc, CTL_VOLUME, GPR_VOL_PCM,
1722992413f4SGarrett D'Amore 	    AUDIO_CTRL_ID_VOLUME, PCMVOL, 75);
1723992413f4SGarrett D'Amore 
1724992413f4SGarrett D'Amore 	emu10k_create_stereo(devc, CTL_FRONT, GPR_VOL_FRONT,
1725992413f4SGarrett D'Amore 	    AUDIO_CTRL_ID_FRONT, MAINVOL, 100);
1726992413f4SGarrett D'Amore 	emu10k_create_stereo(devc, CTL_SURROUND, GPR_VOL_SURR,
1727992413f4SGarrett D'Amore 	    AUDIO_CTRL_ID_SURROUND, MAINVOL, 100);
1728992413f4SGarrett D'Amore 	if (devc->feature_mask & (SB_51 | SB_71)) {
1729992413f4SGarrett D'Amore 		emu10k_create_mono(devc, CTL_CENTER, GPR_VOL_CEN,
1730992413f4SGarrett D'Amore 		    AUDIO_CTRL_ID_CENTER, MAINVOL, 100);
1731992413f4SGarrett D'Amore 		emu10k_create_mono(devc, CTL_LFE, GPR_VOL_LFE,
1732992413f4SGarrett D'Amore 		    AUDIO_CTRL_ID_LFE, MAINVOL, 100);
1733992413f4SGarrett D'Amore 	}
1734992413f4SGarrett D'Amore 	if (devc->feature_mask & SB_71) {
1735992413f4SGarrett D'Amore 		emu10k_create_stereo(devc, CTL_SIDE, GPR_VOL_SIDE,
1736992413f4SGarrett D'Amore 		    "side", MAINVOL, 100);
1737992413f4SGarrett D'Amore 	}
1738992413f4SGarrett D'Amore 
1739992413f4SGarrett D'Amore 	emu10k_create_stereo(devc, CTL_RECGAIN, GPR_VOL_REC,
1740992413f4SGarrett D'Amore 	    AUDIO_CTRL_ID_RECGAIN, RECVOL, 50);
1741992413f4SGarrett D'Amore 
1742992413f4SGarrett D'Amore 	emu10k_create_ac97src(devc);
1743992413f4SGarrett D'Amore 	emu10k_create_recsrc(devc);
1744992413f4SGarrett D'Amore 	/*
1745992413f4SGarrett D'Amore 	 * 5.1 devices have versa jack.  Note that from what we can
1746992413f4SGarrett D'Amore 	 * tell, none of the 7.1 devices have or need this versa jack,
1747992413f4SGarrett D'Amore 	 * as they all seem to have a dedicated digital I/O port.
1748992413f4SGarrett D'Amore 	 */
1749992413f4SGarrett D'Amore 	if ((devc->feature_mask & SB_51) &&
1750992413f4SGarrett D'Amore 	    !(devc->feature_mask & SB_AUDIGY2VAL)) {
1751992413f4SGarrett D'Amore 		emu10k_create_jack3(devc);
1752992413f4SGarrett D'Amore 	}
1753992413f4SGarrett D'Amore 
1754992413f4SGarrett D'Amore 	/* these ones AC'97 can manage directly */
1755992413f4SGarrett D'Amore 	ac97 = devc->ac97;
1756992413f4SGarrett D'Amore 
1757992413f4SGarrett D'Amore 	if ((ac = ac97_control_find(ac97, AUDIO_CTRL_ID_MICBOOST)) != NULL)
1758992413f4SGarrett D'Amore 		ac97_control_register(ac);
1759992413f4SGarrett D'Amore 	if ((ac = ac97_control_find(ac97, AUDIO_CTRL_ID_MICGAIN)) != NULL)
1760992413f4SGarrett D'Amore 		ac97_control_register(ac);
1761992413f4SGarrett D'Amore 
1762992413f4SGarrett D'Amore 	/* set any AC'97 analog outputs to full volume (no attenuation) */
1763992413f4SGarrett D'Amore 	if ((ac = ac97_control_find(ac97, AUDIO_CTRL_ID_FRONT)) != NULL)
1764a99f0428SGarrett D'Amore 		(void) ac97_control_set(ac, (100 << 8) | 100);
1765992413f4SGarrett D'Amore 	if ((ac = ac97_control_find(ac97, AUDIO_CTRL_ID_LINEOUT)) != NULL)
1766a99f0428SGarrett D'Amore 		(void) ac97_control_set(ac, (100 << 8) | 100);
1767992413f4SGarrett D'Amore 	if ((ac = ac97_control_find(ac97, AUDIO_CTRL_ID_SURROUND)) != NULL)
1768a99f0428SGarrett D'Amore 		(void) ac97_control_set(ac, (100 << 8) | 100);
1769992413f4SGarrett D'Amore 	if ((ac = ac97_control_find(ac97, AUDIO_CTRL_ID_CENTER)) != NULL)
1770a99f0428SGarrett D'Amore 		(void) ac97_control_set(ac, 100);
1771992413f4SGarrett D'Amore 	if ((ac = ac97_control_find(ac97, AUDIO_CTRL_ID_LFE)) != NULL)
1772a99f0428SGarrett D'Amore 		(void) ac97_control_set(ac, 100);
1773992413f4SGarrett D'Amore 
1774992413f4SGarrett D'Amore 	/* Monitor sources */
1775992413f4SGarrett D'Amore 	emu10k_create_stereo(devc, CTL_AC97, GPR_MON_AC97,
1776992413f4SGarrett D'Amore 	    "ac97-monitor", MONVOL, 0);
1777992413f4SGarrett D'Amore 	emu10k_create_stereo(devc, CTL_SPD1, GPR_MON_SPDIF1,
1778992413f4SGarrett D'Amore 	    AUDIO_PORT_SPDIFIN, MONVOL, 0);
1779992413f4SGarrett D'Amore 	emu10k_create_stereo(devc, CTL_DIGCD, GPR_MON_DIGCD,
1780992413f4SGarrett D'Amore 	    "digital-cd", MONVOL, 0);
1781992413f4SGarrett D'Amore 	emu10k_create_stereo(devc, CTL_SPD1, GPR_MON_SPDIF1,
1782992413f4SGarrett D'Amore 	    AUDIO_PORT_SPDIFIN, MONVOL, 0);
1783992413f4SGarrett D'Amore 
1784992413f4SGarrett D'Amore 	if ((devc->feature_mask & SB_NOEXP) == 0) {
1785992413f4SGarrett D'Amore 		/*
1786992413f4SGarrett D'Amore 		 * These ports are only available via an external
1787992413f4SGarrett D'Amore 		 * expansion box.  Don't expose them for cards  that
1788992413f4SGarrett D'Amore 		 * don't have support for it.
1789992413f4SGarrett D'Amore 		 */
1790992413f4SGarrett D'Amore 		emu10k_create_stereo(devc, CTL_HEADPH, GPR_VOL_HEADPH,
1791992413f4SGarrett D'Amore 		    AUDIO_CTRL_ID_HEADPHONE, MAINVOL, 100);
1792992413f4SGarrett D'Amore 		emu10k_create_stereo(devc, CTL_SPD2, GPR_MON_SPDIF2,
1793992413f4SGarrett D'Amore 		    "spdif2-in", MONVOL, 0);
1794992413f4SGarrett D'Amore 		emu10k_create_stereo(devc, CTL_LINE2, GPR_MON_LINE2,
1795992413f4SGarrett D'Amore 		    "line2-in", MONVOL, 0);
1796992413f4SGarrett D'Amore 		emu10k_create_stereo(devc, CTL_AUX2, GPR_MON_AUX2,
1797992413f4SGarrett D'Amore 		    AUDIO_PORT_AUX2IN, MONVOL, 0);
1798992413f4SGarrett D'Amore 	}
1799992413f4SGarrett D'Amore }
1800992413f4SGarrett D'Amore 
1801992413f4SGarrett D'Amore static void
emu10k_load_dsp(emu10k_devc_t * devc,uint32_t * code,int ncode,uint32_t * init,int ninit)1802992413f4SGarrett D'Amore emu10k_load_dsp(emu10k_devc_t *devc, uint32_t *code, int ncode,
1803992413f4SGarrett D'Amore     uint32_t *init, int ninit)
1804992413f4SGarrett D'Amore {
1805992413f4SGarrett D'Amore 	int i;
1806992413f4SGarrett D'Amore 
1807992413f4SGarrett D'Amore 	if (ncode > 1024) {
1808992413f4SGarrett D'Amore 		audio_dev_warn(devc->adev, "DSP file size too big");
1809992413f4SGarrett D'Amore 		return;
1810992413f4SGarrett D'Amore 	}
1811992413f4SGarrett D'Amore 	if (ninit > MAX_GPR) {
1812992413f4SGarrett D'Amore 		audio_dev_warn(devc->adev, "Too many inits");
1813992413f4SGarrett D'Amore 		return;
1814992413f4SGarrett D'Amore 	}
1815992413f4SGarrett D'Amore 
1816992413f4SGarrett D'Amore 	/* Upload our DSP code */
1817992413f4SGarrett D'Amore 	for (i = 0; i < ncode; i++) {
1818992413f4SGarrett D'Amore 		emu10k_write_efx(devc, UC0 + i, code[i]);
1819992413f4SGarrett D'Amore 	}
1820992413f4SGarrett D'Amore 
1821992413f4SGarrett D'Amore 	/* Upload the initialization settings */
1822992413f4SGarrett D'Amore 	for (i = 0; i < ninit; i += 2) {
1823992413f4SGarrett D'Amore 		emu10k_write_reg(devc, init[i] + GPR0, 0, init[i + 1]);
1824992413f4SGarrett D'Amore 	}
1825992413f4SGarrett D'Amore }
1826992413f4SGarrett D'Amore 
1827992413f4SGarrett D'Amore #define	LIVE_NOP()					\
1828992413f4SGarrett D'Amore 	emu10k_write_efx(devc, UC0 + (pc * 2), 0x10040);	\
1829992413f4SGarrett D'Amore 	emu10k_write_efx(devc, UC0 + (pc * 2 + 1), 0x610040);	\
1830992413f4SGarrett D'Amore 	pc++
1831992413f4SGarrett D'Amore #define	LIVE_ACC3(r, a, x, y) /* z=w+x+y */				\
1832992413f4SGarrett D'Amore 	emu10k_write_efx(devc, UC0 + (pc * 2), (x << 10) | y);		\
1833992413f4SGarrett D'Amore 	emu10k_write_efx(devc, UC0 + (pc * 2 + 1), (6 << 20) | (r << 10) | a); \
1834992413f4SGarrett D'Amore 	pc++
1835992413f4SGarrett D'Amore 
1836992413f4SGarrett D'Amore #define	AUDIGY_ACC3(r, a, x, y) /* z=w+x+y */				\
1837992413f4SGarrett D'Amore 	emu10k_write_efx(devc, UC0 + (pc * 2), (x << 12) | y);		\
1838992413f4SGarrett D'Amore 	emu10k_write_efx(devc, UC0 + (pc * 2+1), (6 << 24) | (r << 12) | a); \
1839992413f4SGarrett D'Amore 	pc++
1840992413f4SGarrett D'Amore #define	AUDIGY_NOP() AUDIGY_ACC3(0xc0, 0xc0, 0xc0, 0xc0)
1841992413f4SGarrett D'Amore 
1842992413f4SGarrett D'Amore static void
emu10k_init_effects(emu10k_devc_t * devc)1843992413f4SGarrett D'Amore emu10k_init_effects(emu10k_devc_t *devc)
1844992413f4SGarrett D'Amore {
1845992413f4SGarrett D'Amore 	int i;
1846992413f4SGarrett D'Amore 	unsigned short pc;
1847992413f4SGarrett D'Amore 
1848992413f4SGarrett D'Amore 	ASSERT(mutex_owned(&devc->mutex));
1849992413f4SGarrett D'Amore 
1850992413f4SGarrett D'Amore 	if (devc->feature_mask & (SB_AUDIGY|SB_AUDIGY2|SB_AUDIGY2VAL)) {
1851992413f4SGarrett D'Amore 		pc = 0;
1852992413f4SGarrett D'Amore 		for (i = 0; i < 512; i++) {
1853992413f4SGarrett D'Amore 			AUDIGY_NOP();
1854992413f4SGarrett D'Amore 		}
1855992413f4SGarrett D'Amore 
1856992413f4SGarrett D'Amore 		for (i = 0; i < 256; i++)
1857992413f4SGarrett D'Amore 			emu10k_write_efx(devc, GPR0 + i, 0);
1858992413f4SGarrett D'Amore 		emu10k_write_reg(devc, AUDIGY_DBG, 0, 0);
1859992413f4SGarrett D'Amore 		emu10k_load_dsp(devc,
1860992413f4SGarrett D'Amore 		    emu10k2_code,
1861992413f4SGarrett D'Amore 		    sizeof (emu10k2_code) / sizeof (emu10k2_code[0]),
1862992413f4SGarrett D'Amore 		    emu10k2_init,
1863992413f4SGarrett D'Amore 		    sizeof (emu10k2_init) / sizeof (emu10k2_init[0]));
1864992413f4SGarrett D'Amore 
1865992413f4SGarrett D'Amore 	} else {
1866992413f4SGarrett D'Amore 		pc = 0;
1867992413f4SGarrett D'Amore 		for (i = 0; i < 512; i++) {
1868992413f4SGarrett D'Amore 			LIVE_NOP();
1869992413f4SGarrett D'Amore 		}
1870992413f4SGarrett D'Amore 
1871992413f4SGarrett D'Amore 		for (i = 0; i < 256; i++)
1872992413f4SGarrett D'Amore 			emu10k_write_efx(devc, GPR0 + i, 0);
1873992413f4SGarrett D'Amore 		emu10k_write_reg(devc, DBG, 0, 0);
1874992413f4SGarrett D'Amore 		emu10k_load_dsp(devc,
1875992413f4SGarrett D'Amore 		    emu10k1_code,
1876992413f4SGarrett D'Amore 		    sizeof (emu10k1_code) / sizeof (emu10k1_code[0]),
1877992413f4SGarrett D'Amore 		    emu10k1_init,
1878992413f4SGarrett D'Amore 		    sizeof (emu10k1_init) / sizeof (emu10k1_init[0]));
1879992413f4SGarrett D'Amore 	}
1880992413f4SGarrett D'Amore }
1881992413f4SGarrett D'Amore 
1882992413f4SGarrett D'Amore /* mixer */
1883992413f4SGarrett D'Amore 
1884992413f4SGarrett D'Amore static struct {
1885992413f4SGarrett D'Amore 	uint16_t	devid;
1886992413f4SGarrett D'Amore 	uint16_t	subid;
1887992413f4SGarrett D'Amore 	const char	*model;
1888992413f4SGarrett D'Amore 	const char	*prod;
1889992413f4SGarrett D'Amore 	unsigned	feature_mask;
1890992413f4SGarrett D'Amore } emu10k_cards[] = {
1891992413f4SGarrett D'Amore 	{ 0x2, 0x0020, "CT4670", "Live! Value", SB_LIVE | SB_NOEXP },
1892992413f4SGarrett D'Amore 	{ 0x2, 0x0021, "CT4621", "Live!", SB_LIVE },
1893992413f4SGarrett D'Amore 	{ 0x2, 0x100a, "SB0220", "Live! 5.1 Digital",
1894992413f4SGarrett D'Amore 	    SB_LIVE | SB_51 | SB_NOEXP },
1895992413f4SGarrett D'Amore 	{ 0x2, 0x8022, "CT4780", "Live! Value", SB_LIVE },
1896992413f4SGarrett D'Amore 	{ 0x2, 0x8023, "CT4790", "PCI512", SB_LIVE | SB_NOEXP },
1897992413f4SGarrett D'Amore 	{ 0x2, 0x8026, "CT4830", "Live! Value", SB_LIVE },
1898992413f4SGarrett D'Amore 	{ 0x2, 0x8028, "CT4870", "Live! Value", SB_LIVE },
1899992413f4SGarrett D'Amore 	{ 0x2, 0x8031, "CT4831", "Live! Value", SB_LIVE },
1900992413f4SGarrett D'Amore 	{ 0x2, 0x8040, "CT4760", "Live!", SB_LIVE },
1901992413f4SGarrett D'Amore 	{ 0x2, 0x8051, "CT4850", "Live! Value", SB_LIVE },
1902992413f4SGarrett D'Amore 	{ 0x2, 0x8061, "SB0060", "Live! 5.1", SB_LIVE | SB_51 },
1903992413f4SGarrett D'Amore 	{ 0x2, 0x8064, "SB0100", "Live! 5.1", SB_LIVE | SB_51 },
1904992413f4SGarrett D'Amore 	{ 0x2, 0x8065, "SB0220", "Live! 5.1", SB_LIVE | SB_51 },
1905992413f4SGarrett D'Amore 	{ 0x2, 0x8066, "SB0228", "Live! 5.1", SB_LIVE | SB_51 },
1906992413f4SGarrett D'Amore 	{ 0x4, 0x0051, "SB0090", "Audigy", SB_AUDIGY | SB_51 },
1907992413f4SGarrett D'Amore 	{ 0x4, 0x0052, "SB0160", "Audigy ES", SB_AUDIGY | SB_51 },
1908992413f4SGarrett D'Amore 	{ 0x4, 0x0053, "SB0092", "Audigy", SB_AUDIGY | SB_51 },
1909992413f4SGarrett D'Amore 	{ 0x4, 0x1002, "SB0240P", "Audigy 2 Platinum",
1910992413f4SGarrett D'Amore 	    SB_AUDIGY2 | SB_71 | SB_INVSP },
1911992413f4SGarrett D'Amore 	{ 0x4, 0x1003, "SB0353", "Audigy 2 ZS", SB_AUDIGY2 | SB_71 | SB_INVSP },
1912992413f4SGarrett D'Amore 	{ 0x4, 0x1005, "SB0280", "Audigy 2 Platinum EX", SB_AUDIGY2 | SB_71 },
1913992413f4SGarrett D'Amore 	{ 0x4, 0x1007, "SB0240", "Audigy 2", SB_AUDIGY2 | SB_71 },
1914992413f4SGarrett D'Amore 	{ 0x4, 0x2001, "SB0360", "Audigy 2 ZS", SB_AUDIGY2 | SB_71 | SB_INVSP },
1915992413f4SGarrett D'Amore 	{ 0x4, 0x2002, "SB0350", "Audigy 2 ZS", SB_AUDIGY2 | SB_71 | SB_INVSP },
1916992413f4SGarrett D'Amore 	{ 0x4, 0x2006, "SB0350", "Audigy 2", SB_AUDIGY2 | SB_71 | SB_INVSP },
1917992413f4SGarrett D'Amore 	{ 0x4, 0x2007, "SB0380", "Audigy 4 Pro", SB_AUDIGY2 | SB_71 },
1918992413f4SGarrett D'Amore 	{ 0x8, 0x1001, "SB0400", "Audigy 2 Value",
1919992413f4SGarrett D'Amore 	    SB_AUDIGY2VAL | SB_71 | SB_NOEXP },
1920992413f4SGarrett D'Amore 	{ 0x8, 0x1021, "SB0610", "Audigy 4",
1921992413f4SGarrett D'Amore 	    SB_AUDIGY2VAL | SB_71 | SB_NOEXP },
192229a35cb6SHans Rosenfeld 	{ 0x8, 0x1024, "SB1550", "Audigy RX",
192329a35cb6SHans Rosenfeld 	    SB_AUDIGY2VAL | SB_71 | SB_NOEXP },
1924992413f4SGarrett D'Amore 	{ 0x8, 0x2001, "SB0530", "Audigy 2 ZS Notebook",
1925992413f4SGarrett D'Amore 	    SB_AUDIGY2VAL | SB_71 },
1926992413f4SGarrett D'Amore 	{ 0, 0, NULL, NULL, 0 },
1927992413f4SGarrett D'Amore };
1928992413f4SGarrett D'Amore 
1929992413f4SGarrett D'Amore int
emu10k_attach(dev_info_t * dip)1930992413f4SGarrett D'Amore emu10k_attach(dev_info_t *dip)
1931992413f4SGarrett D'Amore {
1932992413f4SGarrett D'Amore 	uint16_t pci_command;
1933992413f4SGarrett D'Amore 	uint16_t subid;
1934992413f4SGarrett D'Amore 	uint16_t devid;
1935992413f4SGarrett D'Amore 	emu10k_devc_t *devc;
1936992413f4SGarrett D'Amore 	ddi_acc_handle_t pcih;
1937992413f4SGarrett D'Amore 	ddi_dma_cookie_t cookie;
1938992413f4SGarrett D'Amore 	uint_t count;
1939992413f4SGarrett D'Amore 	ulong_t len;
1940992413f4SGarrett D'Amore 	int i;
1941992413f4SGarrett D'Amore 	const char *name;
1942992413f4SGarrett D'Amore 	const char *model;
1943992413f4SGarrett D'Amore 	char namebuf[64];
1944992413f4SGarrett D'Amore 	int feature_mask;
1945992413f4SGarrett D'Amore 
1946992413f4SGarrett D'Amore 	devc = kmem_zalloc(sizeof (*devc), KM_SLEEP);
1947992413f4SGarrett D'Amore 	devc->dip = dip;
1948992413f4SGarrett D'Amore 	ddi_set_driver_private(dip, devc);
1949992413f4SGarrett D'Amore 
1950992413f4SGarrett D'Amore 	if ((devc->adev = audio_dev_alloc(dip, 0)) == NULL) {
1951992413f4SGarrett D'Amore 		cmn_err(CE_WARN, "audio_dev_alloc failed");
1952992413f4SGarrett D'Amore 		goto error;
1953992413f4SGarrett D'Amore 	}
1954992413f4SGarrett D'Amore 
1955992413f4SGarrett D'Amore 	if (pci_config_setup(dip, &pcih) != DDI_SUCCESS) {
1956992413f4SGarrett D'Amore 		audio_dev_warn(devc->adev, "pci_config_setup failed");
1957992413f4SGarrett D'Amore 		goto error;
1958992413f4SGarrett D'Amore 	}
1959992413f4SGarrett D'Amore 	devc->pcih = pcih;
1960992413f4SGarrett D'Amore 
1961992413f4SGarrett D'Amore 	devid = pci_config_get16(pcih, PCI_CONF_DEVID);
1962992413f4SGarrett D'Amore 	subid = pci_config_get16(pcih, PCI_CONF_SUBSYSID);
1963992413f4SGarrett D'Amore 
1964992413f4SGarrett D'Amore 	pci_command = pci_config_get16(pcih, PCI_CONF_COMM);
1965992413f4SGarrett D'Amore 	pci_command |= PCI_COMM_ME | PCI_COMM_IO;
1966992413f4SGarrett D'Amore 	pci_config_put16(pcih, PCI_CONF_COMM, pci_command);
1967992413f4SGarrett D'Amore 
1968992413f4SGarrett D'Amore 	if ((ddi_regs_map_setup(dip, 1, &devc->regs, 0, 0, &dev_attr,
1969992413f4SGarrett D'Amore 	    &devc->regsh)) != DDI_SUCCESS) {
1970992413f4SGarrett D'Amore 		audio_dev_warn(devc->adev, "failed to map registers");
1971992413f4SGarrett D'Amore 		goto error;
1972992413f4SGarrett D'Amore 	}
1973992413f4SGarrett D'Amore 
1974992413f4SGarrett D'Amore 	switch (devid) {
1975992413f4SGarrett D'Amore 	case PCI_DEVICE_ID_SBLIVE:
1976992413f4SGarrett D'Amore 		name = "Live!";
1977992413f4SGarrett D'Amore 		model = "CT????";
1978992413f4SGarrett D'Amore 		feature_mask = SB_LIVE;
1979992413f4SGarrett D'Amore 		break;
1980992413f4SGarrett D'Amore 
1981992413f4SGarrett D'Amore 	case PCI_DEVICE_ID_AUDIGYVALUE:
1982992413f4SGarrett D'Amore 		name = "Audigy 2 Value";
1983992413f4SGarrett D'Amore 		model = "SB????";
1984992413f4SGarrett D'Amore 		feature_mask = SB_AUDIGY2VAL;
1985992413f4SGarrett D'Amore 		break;
1986992413f4SGarrett D'Amore 
1987992413f4SGarrett D'Amore 	case PCI_DEVICE_ID_AUDIGY:
1988992413f4SGarrett D'Amore 		if (subid >= 0x1002 && subid <= 0x2005) {
1989992413f4SGarrett D'Amore 			name = "Audigy 2";
1990992413f4SGarrett D'Amore 			model = "SB????";
1991992413f4SGarrett D'Amore 			feature_mask = SB_AUDIGY2;
1992992413f4SGarrett D'Amore 		} else {
1993992413f4SGarrett D'Amore 			name = "Audigy";
1994992413f4SGarrett D'Amore 			model = "SB????";
1995992413f4SGarrett D'Amore 			feature_mask = SB_AUDIGY;
1996992413f4SGarrett D'Amore 		}
1997992413f4SGarrett D'Amore 		break;
1998992413f4SGarrett D'Amore 
1999992413f4SGarrett D'Amore 	default:
2000992413f4SGarrett D'Amore 		audio_dev_warn(devc->adev, "Unrecognized device");
2001992413f4SGarrett D'Amore 		goto error;
2002992413f4SGarrett D'Amore 	}
2003992413f4SGarrett D'Amore 
2004992413f4SGarrett D'Amore 	for (i = 0; emu10k_cards[i].prod; i++) {
2005992413f4SGarrett D'Amore 		if ((devid == emu10k_cards[i].devid) &&
2006992413f4SGarrett D'Amore 		    (subid == emu10k_cards[i].subid)) {
2007992413f4SGarrett D'Amore 			name = emu10k_cards[i].prod;
2008992413f4SGarrett D'Amore 			model = emu10k_cards[i].model;
2009992413f4SGarrett D'Amore 			feature_mask = emu10k_cards[i].feature_mask;
2010992413f4SGarrett D'Amore 			break;
2011992413f4SGarrett D'Amore 		}
2012992413f4SGarrett D'Amore 	}
2013992413f4SGarrett D'Amore 	devc->feature_mask = feature_mask;
2014992413f4SGarrett D'Amore 
2015992413f4SGarrett D'Amore 	(void) snprintf(namebuf, sizeof (namebuf), "Sound Blaster %s", name);
2016992413f4SGarrett D'Amore 
2017992413f4SGarrett D'Amore 	audio_dev_set_description(devc->adev, namebuf);
2018992413f4SGarrett D'Amore 	audio_dev_set_version(devc->adev, model);
2019992413f4SGarrett D'Amore 
202068c47f65SGarrett D'Amore 	mutex_init(&devc->mutex, NULL, MUTEX_DRIVER, 0);
2021992413f4SGarrett D'Amore 
2022992413f4SGarrett D'Amore 	/* allocate static page table memory */
2023992413f4SGarrett D'Amore 
2024992413f4SGarrett D'Amore 	devc->max_mem = AUDIO_MEMSIZE;
2025992413f4SGarrett D'Amore 
2026992413f4SGarrett D'Amore 	/* SB Live/Audigy supports at most 32M of memory) */
2027992413f4SGarrett D'Amore 	if (devc->max_mem > 32 * 1024 * 1024)
2028992413f4SGarrett D'Amore 		devc->max_mem = 32 * 1024 * 1024;
2029992413f4SGarrett D'Amore 
2030992413f4SGarrett D'Amore 	devc->max_pages = devc->max_mem / 4096;
2031992413f4SGarrett D'Amore 	if (devc->max_pages < 1024)
2032992413f4SGarrett D'Amore 		devc->max_pages = 1024;
2033992413f4SGarrett D'Amore 
2034992413f4SGarrett D'Amore 	/* Allocate page table */
2035992413f4SGarrett D'Amore 	if (ddi_dma_alloc_handle(devc->dip, &dma_attr_buf, DDI_DMA_SLEEP, NULL,
2036992413f4SGarrett D'Amore 	    &devc->pt_dmah) != DDI_SUCCESS) {
2037992413f4SGarrett D'Amore 		audio_dev_warn(devc->adev,
2038992413f4SGarrett D'Amore 		    "failed to allocate page table handle");
203968c47f65SGarrett D'Amore 		goto error;
2040992413f4SGarrett D'Amore 	}
2041992413f4SGarrett D'Amore 
2042992413f4SGarrett D'Amore 	if (ddi_dma_mem_alloc(devc->pt_dmah, devc->max_pages * 4,
2043992413f4SGarrett D'Amore 	    &dev_attr, DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL,
2044992413f4SGarrett D'Amore 	    &devc->pt_kaddr, &len, &devc->pt_acch) !=
2045992413f4SGarrett D'Amore 	    DDI_SUCCESS) {
2046992413f4SGarrett D'Amore 		audio_dev_warn(devc->adev,
2047992413f4SGarrett D'Amore 		    "failed to allocate memory for page table");
204868c47f65SGarrett D'Amore 		goto error;
2049992413f4SGarrett D'Amore 	}
2050992413f4SGarrett D'Amore 
2051992413f4SGarrett D'Amore 	if (ddi_dma_addr_bind_handle(devc->pt_dmah, NULL,
2052992413f4SGarrett D'Amore 	    devc->pt_kaddr, len, DDI_DMA_CONSISTENT | DDI_DMA_WRITE,
2053992413f4SGarrett D'Amore 	    DDI_DMA_SLEEP, NULL, &cookie, &count) != DDI_SUCCESS) {
2054992413f4SGarrett D'Amore 		audio_dev_warn(devc->adev,
2055992413f4SGarrett D'Amore 		    "failed binding page table DMA handle");
205668c47f65SGarrett D'Amore 		goto error;
2057992413f4SGarrett D'Amore 	}
2058992413f4SGarrett D'Amore 
2059992413f4SGarrett D'Amore 	devc->page_map = (void *)devc->pt_kaddr;
2060992413f4SGarrett D'Amore 	devc->pt_paddr = cookie.dmac_address;
2061992413f4SGarrett D'Amore 	bzero(devc->pt_kaddr, devc->max_pages * 4);
2062992413f4SGarrett D'Amore 
2063992413f4SGarrett D'Amore 	/* Allocate silent page */
2064992413f4SGarrett D'Amore 	if (ddi_dma_alloc_handle(devc->dip, &dma_attr_buf, DDI_DMA_SLEEP, NULL,
2065992413f4SGarrett D'Amore 	    &devc->silence_dmah) != DDI_SUCCESS) {
2066992413f4SGarrett D'Amore 		audio_dev_warn(devc->adev,
2067992413f4SGarrett D'Amore 		    "failed to allocate silent page handle");
206868c47f65SGarrett D'Amore 		goto error;
2069992413f4SGarrett D'Amore 	}
2070992413f4SGarrett D'Amore 
2071992413f4SGarrett D'Amore 	if (ddi_dma_mem_alloc(devc->silence_dmah, 4096,
2072992413f4SGarrett D'Amore 	    &buf_attr, DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL,
2073992413f4SGarrett D'Amore 	    &devc->silence_kaddr, &len,
2074992413f4SGarrett D'Amore 	    &devc->silence_acch) != DDI_SUCCESS) {
2075992413f4SGarrett D'Amore 		audio_dev_warn(devc->adev,
2076992413f4SGarrett D'Amore 		    "failed to allocate silent page memory");
207768c47f65SGarrett D'Amore 		goto error;
2078992413f4SGarrett D'Amore 	}
2079992413f4SGarrett D'Amore 
2080992413f4SGarrett D'Amore 	(void) ddi_dma_sync(devc->silence_dmah, 0, 0, DDI_DMA_SYNC_FORDEV);
2081992413f4SGarrett D'Amore 
2082992413f4SGarrett D'Amore 	if (ddi_dma_addr_bind_handle(devc->silence_dmah, NULL,
2083992413f4SGarrett D'Amore 	    devc->silence_kaddr, len, DDI_DMA_CONSISTENT | DDI_DMA_WRITE,
2084992413f4SGarrett D'Amore 	    DDI_DMA_SLEEP, NULL, &cookie, &count) != DDI_SUCCESS) {
2085992413f4SGarrett D'Amore 		audio_dev_warn(devc->adev,
2086992413f4SGarrett D'Amore 		    "failed binding silent page DMA handle");
208768c47f65SGarrett D'Amore 		goto error;
2088992413f4SGarrett D'Amore 	}
2089992413f4SGarrett D'Amore 
2090992413f4SGarrett D'Amore 	devc->silence_paddr = cookie.dmac_address;
2091992413f4SGarrett D'Amore 	bzero(devc->silence_kaddr, 4096);
2092992413f4SGarrett D'Amore 	devc->audio_memptr = 4096;	/* Skip the silence page */
2093992413f4SGarrett D'Amore 
2094992413f4SGarrett D'Amore 	for (i = 0; i < devc->max_pages; i++)
2095992413f4SGarrett D'Amore 		FILL_PAGE_MAP_ENTRY(i, devc->silence_paddr);
2096992413f4SGarrett D'Amore 
2097992413f4SGarrett D'Amore 	(void) ddi_dma_sync(devc->pt_dmah, 0, 0, DDI_DMA_SYNC_FORDEV);
2098992413f4SGarrett D'Amore 
2099992413f4SGarrett D'Amore 	devc->ac97 = ac97_allocate(devc->adev, dip,
2100992413f4SGarrett D'Amore 	    emu10k_read_ac97, emu10k_write_ac97, devc);
2101992413f4SGarrett D'Amore 	if (devc->ac97 == NULL) {
2102992413f4SGarrett D'Amore 		audio_dev_warn(devc->adev, "failed to allocate ac97 handle");
2103992413f4SGarrett D'Amore 		goto error;
2104992413f4SGarrett D'Amore 	}
2105992413f4SGarrett D'Amore 
2106992413f4SGarrett D'Amore 	ac97_probe_controls(devc->ac97);
2107992413f4SGarrett D'Amore 
2108992413f4SGarrett D'Amore 	/* allocate voice 0 for play */
2109992413f4SGarrett D'Amore 	if (emu10k_alloc_port(devc, EMU10K_REC) != DDI_SUCCESS)
2110992413f4SGarrett D'Amore 		goto error;
2111992413f4SGarrett D'Amore 
2112992413f4SGarrett D'Amore 	if (emu10k_alloc_port(devc, EMU10K_PLAY) != DDI_SUCCESS)
2113992413f4SGarrett D'Amore 		goto error;
2114992413f4SGarrett D'Amore 
2115992413f4SGarrett D'Amore 	/* now initialize the hardware */
2116992413f4SGarrett D'Amore 	mutex_enter(&devc->mutex);
2117992413f4SGarrett D'Amore 	if (emu10k_hwinit(devc) != DDI_SUCCESS) {
2118992413f4SGarrett D'Amore 		mutex_exit(&devc->mutex);
2119992413f4SGarrett D'Amore 		goto error;
2120992413f4SGarrett D'Amore 	}
2121992413f4SGarrett D'Amore 	mutex_exit(&devc->mutex);
2122992413f4SGarrett D'Amore 
2123992413f4SGarrett D'Amore 	emu10k_create_controls(devc);
2124992413f4SGarrett D'Amore 
2125992413f4SGarrett D'Amore 	if (audio_dev_register(devc->adev) != DDI_SUCCESS) {
2126992413f4SGarrett D'Amore 		audio_dev_warn(devc->adev, "unable to register audio device");
2127992413f4SGarrett D'Amore 		goto error;
2128992413f4SGarrett D'Amore 	}
2129992413f4SGarrett D'Amore 
2130992413f4SGarrett D'Amore 	ddi_report_dev(dip);
2131992413f4SGarrett D'Amore 
2132992413f4SGarrett D'Amore 	return (DDI_SUCCESS);
2133992413f4SGarrett D'Amore 
2134992413f4SGarrett D'Amore error:
2135992413f4SGarrett D'Amore 	emu10k_destroy(devc);
2136992413f4SGarrett D'Amore 	return (DDI_FAILURE);
2137992413f4SGarrett D'Amore }
2138992413f4SGarrett D'Amore 
2139992413f4SGarrett D'Amore int
emu10k_resume(dev_info_t * dip)2140992413f4SGarrett D'Amore emu10k_resume(dev_info_t *dip)
2141992413f4SGarrett D'Amore {
2142992413f4SGarrett D'Amore 	emu10k_devc_t *devc;
2143992413f4SGarrett D'Amore 
2144992413f4SGarrett D'Amore 	devc = ddi_get_driver_private(dip);
2145992413f4SGarrett D'Amore 
2146992413f4SGarrett D'Amore 	mutex_enter(&devc->mutex);
2147992413f4SGarrett D'Amore 	if (emu10k_hwinit(devc) != DDI_SUCCESS) {
2148992413f4SGarrett D'Amore 		mutex_exit(&devc->mutex);
2149992413f4SGarrett D'Amore 		/*
2150992413f4SGarrett D'Amore 		 * In case of failure, we leave the chip suspended,
2151992413f4SGarrett D'Amore 		 * but don't panic.  Audio service is not normally a a
2152992413f4SGarrett D'Amore 		 * critical service.
2153992413f4SGarrett D'Amore 		 */
2154992413f4SGarrett D'Amore 		audio_dev_warn(devc->adev, "FAILED to RESUME device");
2155992413f4SGarrett D'Amore 		return (DDI_SUCCESS);
2156992413f4SGarrett D'Amore 	}
2157992413f4SGarrett D'Amore 
2158992413f4SGarrett D'Amore 	mutex_exit(&devc->mutex);
2159992413f4SGarrett D'Amore 
2160992413f4SGarrett D'Amore 	/* resume ac97 */
216168c47f65SGarrett D'Amore 	ac97_reset(devc->ac97);
216268c47f65SGarrett D'Amore 
216368c47f65SGarrett D'Amore 	audio_dev_resume(devc->adev);
2164992413f4SGarrett D'Amore 
2165992413f4SGarrett D'Amore 	return (DDI_SUCCESS);
2166992413f4SGarrett D'Amore }
2167992413f4SGarrett D'Amore 
2168992413f4SGarrett D'Amore int
emu10k_detach(emu10k_devc_t * devc)2169992413f4SGarrett D'Amore emu10k_detach(emu10k_devc_t *devc)
2170992413f4SGarrett D'Amore {
2171992413f4SGarrett D'Amore 	if (audio_dev_unregister(devc->adev) != DDI_SUCCESS)
2172992413f4SGarrett D'Amore 		return (DDI_FAILURE);
2173992413f4SGarrett D'Amore 
2174992413f4SGarrett D'Amore 	emu10k_destroy(devc);
2175992413f4SGarrett D'Amore 	return (DDI_SUCCESS);
2176992413f4SGarrett D'Amore }
2177992413f4SGarrett D'Amore 
2178992413f4SGarrett D'Amore int
emu10k_suspend(emu10k_devc_t * devc)2179992413f4SGarrett D'Amore emu10k_suspend(emu10k_devc_t *devc)
2180992413f4SGarrett D'Amore {
218168c47f65SGarrett D'Amore 	audio_dev_suspend(devc->adev);
2182992413f4SGarrett D'Amore 
2183992413f4SGarrett D'Amore 	return (DDI_SUCCESS);
2184992413f4SGarrett D'Amore }
2185992413f4SGarrett D'Amore 
2186992413f4SGarrett D'Amore static int emu10k_ddi_attach(dev_info_t *, ddi_attach_cmd_t);
2187992413f4SGarrett D'Amore static int emu10k_ddi_detach(dev_info_t *, ddi_detach_cmd_t);
2188992413f4SGarrett D'Amore static int emu10k_ddi_quiesce(dev_info_t *);
2189992413f4SGarrett D'Amore 
2190992413f4SGarrett D'Amore static struct dev_ops emu10k_dev_ops = {
2191992413f4SGarrett D'Amore 	DEVO_REV,			/* rev */
2192992413f4SGarrett D'Amore 	0,				/* refcnt */
2193992413f4SGarrett D'Amore 	NULL,				/* getinfo */
2194992413f4SGarrett D'Amore 	nulldev,			/* identify */
2195992413f4SGarrett D'Amore 	nulldev,			/* probe */
2196992413f4SGarrett D'Amore 	emu10k_ddi_attach,		/* attach */
2197992413f4SGarrett D'Amore 	emu10k_ddi_detach,		/* detach */
2198992413f4SGarrett D'Amore 	nodev,				/* reset */
2199992413f4SGarrett D'Amore 	NULL,				/* cb_ops */
2200992413f4SGarrett D'Amore 	NULL,				/* bus_ops */
2201992413f4SGarrett D'Amore 	NULL,				/* power */
2202992413f4SGarrett D'Amore 	emu10k_ddi_quiesce,		/* quiesce */
2203992413f4SGarrett D'Amore };
2204992413f4SGarrett D'Amore 
2205992413f4SGarrett D'Amore static struct modldrv emu10k_modldrv = {
2206992413f4SGarrett D'Amore 	&mod_driverops,			/* drv_modops */
2207992413f4SGarrett D'Amore 	"Creative EMU10K Audio",	/* linkinfo */
2208992413f4SGarrett D'Amore 	&emu10k_dev_ops,		/* dev_ops */
2209992413f4SGarrett D'Amore };
2210992413f4SGarrett D'Amore 
2211992413f4SGarrett D'Amore static struct modlinkage modlinkage = {
2212992413f4SGarrett D'Amore 	MODREV_1,
2213992413f4SGarrett D'Amore 	{ &emu10k_modldrv, NULL }
2214992413f4SGarrett D'Amore };
2215992413f4SGarrett D'Amore 
2216992413f4SGarrett D'Amore int
_init(void)2217992413f4SGarrett D'Amore _init(void)
2218992413f4SGarrett D'Amore {
2219992413f4SGarrett D'Amore 	int rv;
2220992413f4SGarrett D'Amore 
2221992413f4SGarrett D'Amore 	audio_init_ops(&emu10k_dev_ops, EMU10K_NAME);
2222992413f4SGarrett D'Amore 	if ((rv = mod_install(&modlinkage)) != 0) {
2223992413f4SGarrett D'Amore 		audio_fini_ops(&emu10k_dev_ops);
2224992413f4SGarrett D'Amore 	}
2225992413f4SGarrett D'Amore 	return (rv);
2226992413f4SGarrett D'Amore }
2227992413f4SGarrett D'Amore 
2228992413f4SGarrett D'Amore int
_fini(void)2229992413f4SGarrett D'Amore _fini(void)
2230992413f4SGarrett D'Amore {
2231992413f4SGarrett D'Amore 	int rv;
2232992413f4SGarrett D'Amore 
2233992413f4SGarrett D'Amore 	if ((rv = mod_remove(&modlinkage)) == 0) {
2234992413f4SGarrett D'Amore 		audio_fini_ops(&emu10k_dev_ops);
2235992413f4SGarrett D'Amore 	}
2236992413f4SGarrett D'Amore 	return (rv);
2237992413f4SGarrett D'Amore }
2238992413f4SGarrett D'Amore 
2239992413f4SGarrett D'Amore int
_info(struct modinfo * modinfop)2240992413f4SGarrett D'Amore _info(struct modinfo *modinfop)
2241992413f4SGarrett D'Amore {
2242992413f4SGarrett D'Amore 	return (mod_info(&modlinkage, modinfop));
2243992413f4SGarrett D'Amore }
2244992413f4SGarrett D'Amore 
2245992413f4SGarrett D'Amore int
emu10k_ddi_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)2246992413f4SGarrett D'Amore emu10k_ddi_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
2247992413f4SGarrett D'Amore {
2248992413f4SGarrett D'Amore 	switch (cmd) {
2249992413f4SGarrett D'Amore 	case DDI_ATTACH:
2250992413f4SGarrett D'Amore 		return (emu10k_attach(dip));
2251992413f4SGarrett D'Amore 
2252992413f4SGarrett D'Amore 	case DDI_RESUME:
2253992413f4SGarrett D'Amore 		return (emu10k_resume(dip));
2254992413f4SGarrett D'Amore 
2255992413f4SGarrett D'Amore 	default:
2256992413f4SGarrett D'Amore 		return (DDI_FAILURE);
2257992413f4SGarrett D'Amore 	}
2258992413f4SGarrett D'Amore }
2259992413f4SGarrett D'Amore 
2260992413f4SGarrett D'Amore int
emu10k_ddi_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)2261992413f4SGarrett D'Amore emu10k_ddi_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
2262992413f4SGarrett D'Amore {
2263992413f4SGarrett D'Amore 	emu10k_devc_t *devc;
2264992413f4SGarrett D'Amore 
2265992413f4SGarrett D'Amore 	devc = ddi_get_driver_private(dip);
2266992413f4SGarrett D'Amore 
2267992413f4SGarrett D'Amore 	switch (cmd) {
2268992413f4SGarrett D'Amore 	case DDI_DETACH:
2269992413f4SGarrett D'Amore 		return (emu10k_detach(devc));
2270992413f4SGarrett D'Amore 
2271992413f4SGarrett D'Amore 	case DDI_SUSPEND:
2272992413f4SGarrett D'Amore 		return (emu10k_suspend(devc));
2273992413f4SGarrett D'Amore 
2274992413f4SGarrett D'Amore 	default:
2275992413f4SGarrett D'Amore 		return (DDI_FAILURE);
2276992413f4SGarrett D'Amore 	}
2277992413f4SGarrett D'Amore }
2278992413f4SGarrett D'Amore 
2279992413f4SGarrett D'Amore int
emu10k_ddi_quiesce(dev_info_t * dip)2280992413f4SGarrett D'Amore emu10k_ddi_quiesce(dev_info_t *dip)
2281992413f4SGarrett D'Amore {
2282992413f4SGarrett D'Amore 	emu10k_devc_t *devc;
2283992413f4SGarrett D'Amore 
2284992413f4SGarrett D'Amore 	devc = ddi_get_driver_private(dip);
2285992413f4SGarrett D'Amore 
2286992413f4SGarrett D'Amore 	/* stop all voices */
2287992413f4SGarrett D'Amore 	for (int i = 0; i < 64; i++) {
2288992413f4SGarrett D'Amore 		emu10k_write_reg(devc, VEDS, i, 0);
2289992413f4SGarrett D'Amore 	}
2290992413f4SGarrett D'Amore 	for (int i = 0; i < 64; i++) {
2291992413f4SGarrett D'Amore 		emu10k_write_reg(devc, VTFT, i, 0);
2292992413f4SGarrett D'Amore 		emu10k_write_reg(devc, CVCF, i, 0);
2293992413f4SGarrett D'Amore 		emu10k_write_reg(devc, PTAB, i, 0);
2294992413f4SGarrett D'Amore 		emu10k_write_reg(devc, CPF, i, 0);
2295992413f4SGarrett D'Amore 	}
2296992413f4SGarrett D'Amore 
2297992413f4SGarrett D'Amore 	/*
2298992413f4SGarrett D'Amore 	 * Turn off the hardware
2299992413f4SGarrett D'Amore 	 */
2300992413f4SGarrett D'Amore 	OUTL(devc,
2301992413f4SGarrett D'Amore 	    HCFG_LOCKSOUNDCACHE | HCFG_LOCKTANKCACHE_MASK |
2302992413f4SGarrett D'Amore 	    HCFG_MUTEBUTTONENABLE, devc->regs + HCFG);
2303992413f4SGarrett D'Amore 
2304992413f4SGarrett D'Amore 	/* stop ADC recording */
2305992413f4SGarrett D'Amore 	emu10k_write_reg(devc, ADCSR, 0, 0x0);
2306992413f4SGarrett D'Amore 	emu10k_write_reg(devc, ADCBA, 0, 0x0);
2307992413f4SGarrett D'Amore 	emu10k_write_reg(devc, ADCBA, 0, 0x0);
2308992413f4SGarrett D'Amore 
2309992413f4SGarrett D'Amore 	emu10k_write_reg(devc, PTBA, 0, 0);
2310992413f4SGarrett D'Amore 
2311992413f4SGarrett D'Amore 	return (DDI_SUCCESS);
2312992413f4SGarrett D'Amore }
2313