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);
422992413f4SGarrett 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);
434992413f4SGarrett 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);
479992413f4SGarrett 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 {
982992413f4SGarrett 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;
1332992413f4SGarrett 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 }
1439992413f4SGarrett D'Amore if (devc->feature_mask & (SB_AUDIGY|SB_AUDIGY2|SB_AUDIGY2VAL)) {
1440992413f4SGarrett D'Amore val = INL(devc, devc->regs + 0x18);
1441992413f4SGarrett D'Amore val &= ~A_IOCFG_GPOUT0;
1442992413f4SGarrett D'Amore val |= set_val ? 0x44 : 0x40;
1443992413f4SGarrett D'Amore OUTL(devc, val, devc->regs + 0x18);
1444992413f4SGarrett D'Amore
1445992413f4SGarrett D'Amore } else if (devc->feature_mask & SB_LIVE) {
1446992413f4SGarrett D'Amore val = INL(devc, devc->regs + HCFG);
1447992413f4SGarrett D'Amore val &= ~HCFG_GPOUT0;
1448992413f4SGarrett D'Amore val |= set_val ? HCFG_GPOUT0 : 0;
1449992413f4SGarrett 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