xref: /onnv-gate/usr/src/uts/common/io/audio/drv/audioemu10k/audioemu10k.c (revision 11936:54dc8a89ba0d)
110913Sgdamore@opensolaris.org /*
210913Sgdamore@opensolaris.org  * CDDL HEADER START
310913Sgdamore@opensolaris.org  *
410913Sgdamore@opensolaris.org  * The contents of this file are subject to the terms of the
510913Sgdamore@opensolaris.org  * Common Development and Distribution License (the "License").
610913Sgdamore@opensolaris.org  * You may not use this file except in compliance with the License.
710913Sgdamore@opensolaris.org  *
810913Sgdamore@opensolaris.org  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
910913Sgdamore@opensolaris.org  * or http://www.opensolaris.org/os/licensing.
1010913Sgdamore@opensolaris.org  * See the License for the specific language governing permissions
1110913Sgdamore@opensolaris.org  * and limitations under the License.
1210913Sgdamore@opensolaris.org  *
1310913Sgdamore@opensolaris.org  * When distributing Covered Code, include this CDDL HEADER in each
1410913Sgdamore@opensolaris.org  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1510913Sgdamore@opensolaris.org  * If applicable, add the following below this CDDL HEADER, with the
1610913Sgdamore@opensolaris.org  * fields enclosed by brackets "[]" replaced with your own identifying
1710913Sgdamore@opensolaris.org  * information: Portions Copyright [yyyy] [name of copyright owner]
1810913Sgdamore@opensolaris.org  *
1910913Sgdamore@opensolaris.org  * CDDL HEADER END
2010913Sgdamore@opensolaris.org  */
2110913Sgdamore@opensolaris.org 
2210913Sgdamore@opensolaris.org /*
23*11936Sgdamore@opensolaris.org  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
2410913Sgdamore@opensolaris.org  * Use is subject to license terms.
2510913Sgdamore@opensolaris.org  */
2610913Sgdamore@opensolaris.org 
2710913Sgdamore@opensolaris.org /*
2810913Sgdamore@opensolaris.org  * Copyright (C) 4Front Technologies 1996-2009.
2910913Sgdamore@opensolaris.org  */
3010913Sgdamore@opensolaris.org 
3110913Sgdamore@opensolaris.org /*
3210913Sgdamore@opensolaris.org  * Purpose: Driver for the Creative Sound Blaster Live! and Audigy/2/4
3310913Sgdamore@opensolaris.org  * sound cards
3410913Sgdamore@opensolaris.org  */
3510913Sgdamore@opensolaris.org 
3610913Sgdamore@opensolaris.org #include <sys/types.h>
3710913Sgdamore@opensolaris.org #include <sys/modctl.h>
3810913Sgdamore@opensolaris.org #include <sys/kmem.h>
3910913Sgdamore@opensolaris.org #include <sys/conf.h>
4010913Sgdamore@opensolaris.org #include <sys/ddi.h>
4110913Sgdamore@opensolaris.org #include <sys/sunddi.h>
4210913Sgdamore@opensolaris.org #include <sys/pci.h>
4310913Sgdamore@opensolaris.org #include <sys/note.h>
4410913Sgdamore@opensolaris.org #include <sys/stdbool.h>
4510913Sgdamore@opensolaris.org #include <sys/audio/audio_driver.h>
4610913Sgdamore@opensolaris.org #include <sys/audio/ac97.h>
4710913Sgdamore@opensolaris.org 
4810913Sgdamore@opensolaris.org #include "audioemu10k.h"
4910913Sgdamore@opensolaris.org #include <sys/promif.h>
5010913Sgdamore@opensolaris.org 
5110913Sgdamore@opensolaris.org /*
5210913Sgdamore@opensolaris.org  * Include the DSP files for emu10k1 (Live!) and emu10k2 (Audigy)
5310913Sgdamore@opensolaris.org  */
5410913Sgdamore@opensolaris.org #include "emu10k_gpr.h"
5510913Sgdamore@opensolaris.org #include "emu10k1_dsp.h"
5610913Sgdamore@opensolaris.org #include "emu10k2_dsp.h"
5710913Sgdamore@opensolaris.org 
5810913Sgdamore@opensolaris.org static struct ddi_device_acc_attr dev_attr = {
5910913Sgdamore@opensolaris.org 	DDI_DEVICE_ATTR_V0,
6010913Sgdamore@opensolaris.org 	DDI_STRUCTURE_LE_ACC,
6110913Sgdamore@opensolaris.org 	DDI_STRICTORDER_ACC
6210913Sgdamore@opensolaris.org };
6310913Sgdamore@opensolaris.org 
6410913Sgdamore@opensolaris.org static struct ddi_device_acc_attr buf_attr = {
6510913Sgdamore@opensolaris.org 	DDI_DEVICE_ATTR_V0,
6610913Sgdamore@opensolaris.org 	DDI_NEVERSWAP_ACC,
6710913Sgdamore@opensolaris.org 	DDI_STRICTORDER_ACC
6810913Sgdamore@opensolaris.org };
6910913Sgdamore@opensolaris.org 
7010913Sgdamore@opensolaris.org 
7110913Sgdamore@opensolaris.org /*
7210913Sgdamore@opensolaris.org  * EMU10K routing stuff.
7310913Sgdamore@opensolaris.org  */
7410913Sgdamore@opensolaris.org #define	MAX_SENDS		4
7510913Sgdamore@opensolaris.org #define	SEND_L			0
7610913Sgdamore@opensolaris.org #define	SEND_R			1
7710913Sgdamore@opensolaris.org #define	SEND_SURRL		2
7810913Sgdamore@opensolaris.org #define	SEND_SURRR		3
7910913Sgdamore@opensolaris.org #define	SEND_CEN		4
8010913Sgdamore@opensolaris.org #define	SEND_LFE		5
8110913Sgdamore@opensolaris.org #define	SEND_SIDEL		6
8210913Sgdamore@opensolaris.org #define	SEND_SIDER		7
8310913Sgdamore@opensolaris.org 
8410913Sgdamore@opensolaris.org #define	SPDIF_L			20
8510913Sgdamore@opensolaris.org #define	SPDIF_R			21
8610913Sgdamore@opensolaris.org 
8710913Sgdamore@opensolaris.org /*
8810913Sgdamore@opensolaris.org  * Recording sources... we start from 16 to ensure that the
8910913Sgdamore@opensolaris.org  * record sources don't collide with AC'97 record sources in
9010913Sgdamore@opensolaris.org  * the control value.
9110913Sgdamore@opensolaris.org  */
9210913Sgdamore@opensolaris.org #define	INPUT_AC97		1
9310913Sgdamore@opensolaris.org #define	INPUT_SPD1		2
9410913Sgdamore@opensolaris.org #define	INPUT_SPD2		3
9510913Sgdamore@opensolaris.org #define	INPUT_DIGCD		4
9610913Sgdamore@opensolaris.org #define	INPUT_AUX2		5
9710913Sgdamore@opensolaris.org #define	INPUT_LINE2		6
9810913Sgdamore@opensolaris.org #define	INPUT_STEREOMIX		7
9910913Sgdamore@opensolaris.org 
10010913Sgdamore@opensolaris.org static uint8_t front_routing[MAX_SENDS] = {
10110913Sgdamore@opensolaris.org 	SEND_L, SEND_R, 0x3f, 0x3f
10210913Sgdamore@opensolaris.org };
10310913Sgdamore@opensolaris.org static uint8_t surr_routing[MAX_SENDS] = {
10410913Sgdamore@opensolaris.org 	SEND_SURRL, SEND_SURRR, 0x3f, 0x3f
10510913Sgdamore@opensolaris.org };
10610913Sgdamore@opensolaris.org static uint8_t clfe_routing[MAX_SENDS] = {
10710913Sgdamore@opensolaris.org 	SEND_CEN, SEND_LFE, 0x3f, 0x3f
10810913Sgdamore@opensolaris.org };
10910913Sgdamore@opensolaris.org static uint8_t side_routing[MAX_SENDS] = {
11010913Sgdamore@opensolaris.org 	SEND_SIDEL, SEND_SIDER, 0x3f, 0x3f
11110913Sgdamore@opensolaris.org };
11210913Sgdamore@opensolaris.org 
11310913Sgdamore@opensolaris.org /*
11410913Sgdamore@opensolaris.org  * SB Live! cannot do DMA above 2G addresses. Audigy/2/4 have special 8k page
11510913Sgdamore@opensolaris.org  * mode that supports high addresses.  However, we should not need this except
11610913Sgdamore@opensolaris.org  * on SPARC.  For simplicity's sake, we are only delivering this driver for
11710913Sgdamore@opensolaris.org  * x86 platforms.  If SPARC support is desired, then the code will have to
11810913Sgdamore@opensolaris.org  * be modified to support full 32-bit addressing.  (And again, SB Live!
11910913Sgdamore@opensolaris.org  * can't do it anyway.)
12010913Sgdamore@opensolaris.org  */
12110913Sgdamore@opensolaris.org 
12210913Sgdamore@opensolaris.org static ddi_dma_attr_t dma_attr_buf = {
12310913Sgdamore@opensolaris.org 	DMA_ATTR_V0,		/* Version */
12410913Sgdamore@opensolaris.org 	0x00000000ULL,		/* Address low */
12510913Sgdamore@opensolaris.org 	0x7ffffff0ULL,		/* Address high */
12610913Sgdamore@opensolaris.org 	0xffffffffULL,		/* Counter max */
12710913Sgdamore@opensolaris.org 	1ULL,			/* Default byte align */
12810913Sgdamore@opensolaris.org 	0x7f,			/* Burst size */
12910913Sgdamore@opensolaris.org 	0x1,			/* Minimum xfer size */
13010913Sgdamore@opensolaris.org 	0xffffffffULL,		/* Maximum xfer size */
13110913Sgdamore@opensolaris.org 	0xffffffffULL,		/* Max segment size */
13210913Sgdamore@opensolaris.org 	1,			/* S/G list length */
13310913Sgdamore@opensolaris.org 	1,			/* Granularity */
13410913Sgdamore@opensolaris.org 	0			/* Flag */
13510913Sgdamore@opensolaris.org };
13610913Sgdamore@opensolaris.org 
13710913Sgdamore@opensolaris.org static int emu10k_attach(dev_info_t *);
13810913Sgdamore@opensolaris.org static int emu10k_resume(dev_info_t *);
13910913Sgdamore@opensolaris.org static int emu10k_detach(emu10k_devc_t *);
14010913Sgdamore@opensolaris.org static int emu10k_suspend(emu10k_devc_t *);
14110913Sgdamore@opensolaris.org 
142*11936Sgdamore@opensolaris.org static int emu10k_open(void *, int, unsigned *, caddr_t *);
14310913Sgdamore@opensolaris.org static void emu10k_close(void *);
14410913Sgdamore@opensolaris.org static int emu10k_start(void *);
14510913Sgdamore@opensolaris.org static void emu10k_stop(void *);
14610913Sgdamore@opensolaris.org static int emu10k_format(void *);
14710913Sgdamore@opensolaris.org static int emu10k_channels(void *);
14810913Sgdamore@opensolaris.org static int emu10k_rate(void *);
14910913Sgdamore@opensolaris.org static uint64_t emu10k_count(void *);
15010913Sgdamore@opensolaris.org static void emu10k_sync(void *, unsigned);
15110913Sgdamore@opensolaris.org static void emu10k_chinfo(void *, int, unsigned *, unsigned *);
15210913Sgdamore@opensolaris.org 
15310913Sgdamore@opensolaris.org static uint16_t emu10k_read_ac97(void *, uint8_t);
15410913Sgdamore@opensolaris.org static void emu10k_write_ac97(void *, uint8_t, uint16_t);
15510913Sgdamore@opensolaris.org static int emu10k_alloc_port(emu10k_devc_t *, int);
15610913Sgdamore@opensolaris.org static void emu10k_destroy(emu10k_devc_t *);
15710913Sgdamore@opensolaris.org static int emu10k_hwinit(emu10k_devc_t *);
15810913Sgdamore@opensolaris.org static void emu10k_init_effects(emu10k_devc_t *);
15910913Sgdamore@opensolaris.org 
16010913Sgdamore@opensolaris.org static audio_engine_ops_t emu10k_engine_ops = {
16110913Sgdamore@opensolaris.org 	AUDIO_ENGINE_VERSION,
16210913Sgdamore@opensolaris.org 	emu10k_open,
16310913Sgdamore@opensolaris.org 	emu10k_close,
16410913Sgdamore@opensolaris.org 	emu10k_start,
16510913Sgdamore@opensolaris.org 	emu10k_stop,
16610913Sgdamore@opensolaris.org 	emu10k_count,
16710913Sgdamore@opensolaris.org 	emu10k_format,
16810913Sgdamore@opensolaris.org 	emu10k_channels,
16910913Sgdamore@opensolaris.org 	emu10k_rate,
17010913Sgdamore@opensolaris.org 	emu10k_sync,
17111213Sgdamore@opensolaris.org 	NULL,
17211213Sgdamore@opensolaris.org 	emu10k_chinfo,
17311213Sgdamore@opensolaris.org 	NULL
17410913Sgdamore@opensolaris.org };
17510913Sgdamore@opensolaris.org 
17610913Sgdamore@opensolaris.org static uint16_t
emu10k_read_ac97(void * arg,uint8_t index)17710913Sgdamore@opensolaris.org emu10k_read_ac97(void *arg, uint8_t index)
17810913Sgdamore@opensolaris.org {
17910913Sgdamore@opensolaris.org 	emu10k_devc_t *devc = arg;
18010913Sgdamore@opensolaris.org 	int dtemp = 0, i;
18110913Sgdamore@opensolaris.org 
18210913Sgdamore@opensolaris.org 	mutex_enter(&devc->mutex);
18310913Sgdamore@opensolaris.org 	OUTB(devc, index, devc->regs + 0x1e);
18410913Sgdamore@opensolaris.org 	for (i = 0; i < 10000; i++)
18510913Sgdamore@opensolaris.org 		if (INB(devc, devc->regs + 0x1e) & 0x80)
18610913Sgdamore@opensolaris.org 			break;
18710913Sgdamore@opensolaris.org 
18810913Sgdamore@opensolaris.org 	if (i == 1000) {
18910913Sgdamore@opensolaris.org 		mutex_exit(&devc->mutex);
19010913Sgdamore@opensolaris.org 		return (0);			/* Timeout */
19110913Sgdamore@opensolaris.org 	}
19210913Sgdamore@opensolaris.org 	dtemp = INW(devc, devc->regs + 0x1c);
19310913Sgdamore@opensolaris.org 
19410913Sgdamore@opensolaris.org 	mutex_exit(&devc->mutex);
19510913Sgdamore@opensolaris.org 
19610913Sgdamore@opensolaris.org 	return (dtemp & 0xffff);
19710913Sgdamore@opensolaris.org }
19810913Sgdamore@opensolaris.org 
19910913Sgdamore@opensolaris.org static void
emu10k_write_ac97(void * arg,uint8_t index,uint16_t data)20010913Sgdamore@opensolaris.org emu10k_write_ac97(void *arg, uint8_t index, uint16_t data)
20110913Sgdamore@opensolaris.org {
20210913Sgdamore@opensolaris.org 	emu10k_devc_t *devc = arg;
20310913Sgdamore@opensolaris.org 	int i;
20410913Sgdamore@opensolaris.org 
20510913Sgdamore@opensolaris.org 	mutex_enter(&devc->mutex);
20610913Sgdamore@opensolaris.org 
20710913Sgdamore@opensolaris.org 	OUTB(devc, index, devc->regs + 0x1e);
20810913Sgdamore@opensolaris.org 	for (i = 0; i < 10000; i++)
20910913Sgdamore@opensolaris.org 		if (INB(devc, devc->regs + 0x1e) & 0x80)
21010913Sgdamore@opensolaris.org 			break;
21110913Sgdamore@opensolaris.org 	OUTW(devc, data, devc->regs + 0x1c);
21210913Sgdamore@opensolaris.org 
21310913Sgdamore@opensolaris.org 	mutex_exit(&devc->mutex);
21410913Sgdamore@opensolaris.org }
21510913Sgdamore@opensolaris.org 
21610913Sgdamore@opensolaris.org static uint32_t
emu10k_read_reg(emu10k_devc_t * devc,int reg,int chn)21710913Sgdamore@opensolaris.org emu10k_read_reg(emu10k_devc_t *devc, int reg, int chn)
21810913Sgdamore@opensolaris.org {
21910913Sgdamore@opensolaris.org 	uint32_t ptr, ptr_addr_mask, val, mask, size, offset;
22010913Sgdamore@opensolaris.org 
22110913Sgdamore@opensolaris.org 	ptr_addr_mask = (devc->feature_mask &
22210913Sgdamore@opensolaris.org 	    (SB_AUDIGY|SB_AUDIGY2|SB_AUDIGY2VAL)) ?
22310913Sgdamore@opensolaris.org 	    0x0fff0000 : 0x07ff0000;
22410913Sgdamore@opensolaris.org 	ptr = ((reg << 16) & ptr_addr_mask) | (chn & 0x3f);
22510913Sgdamore@opensolaris.org 	OUTL(devc, ptr, devc->regs + 0x00);	/* Pointer */
22610913Sgdamore@opensolaris.org 	val = INL(devc, devc->regs + 0x04);	/* Data */
22710913Sgdamore@opensolaris.org 	if (reg & 0xff000000) {
22810913Sgdamore@opensolaris.org 		size = (reg >> 24) & 0x3f;
22910913Sgdamore@opensolaris.org 		offset = (reg >> 16) & 0x1f;
23010913Sgdamore@opensolaris.org 		mask = ((1 << size) - 1) << offset;
23110913Sgdamore@opensolaris.org 		val &= mask;
23210913Sgdamore@opensolaris.org 		val >>= offset;
23310913Sgdamore@opensolaris.org 	}
23410913Sgdamore@opensolaris.org 
23510913Sgdamore@opensolaris.org 	return (val);
23610913Sgdamore@opensolaris.org }
23710913Sgdamore@opensolaris.org 
23810913Sgdamore@opensolaris.org static void
emu10k_write_reg(emu10k_devc_t * devc,int reg,int chn,uint32_t value)23910913Sgdamore@opensolaris.org emu10k_write_reg(emu10k_devc_t *devc, int reg, int chn, uint32_t value)
24010913Sgdamore@opensolaris.org {
24110913Sgdamore@opensolaris.org 	uint32_t ptr, ptr_addr_mask, mask, size, offset;
24210913Sgdamore@opensolaris.org 
24310913Sgdamore@opensolaris.org 	ptr_addr_mask = (devc->feature_mask &
24410913Sgdamore@opensolaris.org 	    (SB_AUDIGY|SB_AUDIGY2|SB_AUDIGY2VAL)) ?
24510913Sgdamore@opensolaris.org 	    0x0fff0000 : 0x07ff0000;
24610913Sgdamore@opensolaris.org 	ptr = ((reg << 16) & ptr_addr_mask) | (chn & 0x3f);
24710913Sgdamore@opensolaris.org 	OUTL(devc, ptr, devc->regs + 0x00);	/* Pointer */
24810913Sgdamore@opensolaris.org 	if (reg & 0xff000000) {
24910913Sgdamore@opensolaris.org 		size = (reg >> 24) & 0x3f;
25010913Sgdamore@opensolaris.org 		offset = (reg >> 16) & 0x1f;
25110913Sgdamore@opensolaris.org 		mask = ((1 << size) - 1) << offset;
25210913Sgdamore@opensolaris.org 		value <<= offset;
25310913Sgdamore@opensolaris.org 		value &= mask;
25410913Sgdamore@opensolaris.org 		value |= INL(devc, devc->regs + 0x04) & ~mask;	/* data */
25510913Sgdamore@opensolaris.org 	}
25610913Sgdamore@opensolaris.org 	OUTL(devc, value, devc->regs + 0x04);	/* Data */
25710913Sgdamore@opensolaris.org }
25810913Sgdamore@opensolaris.org 
25910913Sgdamore@opensolaris.org static void
emu10k_write_routing(emu10k_devc_t * devc,int voice,unsigned char * routing)26010913Sgdamore@opensolaris.org emu10k_write_routing(emu10k_devc_t *devc, int voice, unsigned char *routing)
26110913Sgdamore@opensolaris.org {
26210913Sgdamore@opensolaris.org 	int i;
26310913Sgdamore@opensolaris.org 
26410913Sgdamore@opensolaris.org 	ASSERT(routing != NULL);
26510913Sgdamore@opensolaris.org 
26610913Sgdamore@opensolaris.org 	if (devc->feature_mask & (SB_AUDIGY|SB_AUDIGY2|SB_AUDIGY2VAL)) {
26710913Sgdamore@opensolaris.org 		unsigned int srda = 0;
26810913Sgdamore@opensolaris.org 
26910913Sgdamore@opensolaris.org 		for (i = 0; i < 4; i++)
27010913Sgdamore@opensolaris.org 			srda |= routing[i] << (i * 8);
27110913Sgdamore@opensolaris.org 
27210913Sgdamore@opensolaris.org 		emu10k_write_reg(devc, SRDA, voice, srda);
27310913Sgdamore@opensolaris.org 	} else {
27410913Sgdamore@opensolaris.org 		int fxrt = 0;
27510913Sgdamore@opensolaris.org 
27610913Sgdamore@opensolaris.org 		for (i = 0; i < 4; i++)
27710913Sgdamore@opensolaris.org 			fxrt |= routing[i] << ((i * 4) + 16);
27810913Sgdamore@opensolaris.org 		emu10k_write_reg(devc, FXRT, voice, fxrt);
27910913Sgdamore@opensolaris.org 	}
28010913Sgdamore@opensolaris.org }
28110913Sgdamore@opensolaris.org 
28210913Sgdamore@opensolaris.org static void
emu10k_write_efx(emu10k_devc_t * devc,int reg,unsigned int value)28310913Sgdamore@opensolaris.org emu10k_write_efx(emu10k_devc_t *devc, int reg, unsigned int value)
28410913Sgdamore@opensolaris.org {
28510913Sgdamore@opensolaris.org 	emu10k_write_reg(devc, reg, 0, value);
28610913Sgdamore@opensolaris.org }
28710913Sgdamore@opensolaris.org 
28810913Sgdamore@opensolaris.org /*
28910913Sgdamore@opensolaris.org  * Audio routines
29010913Sgdamore@opensolaris.org  */
29110913Sgdamore@opensolaris.org 
29210913Sgdamore@opensolaris.org static void
emu10k_update_output_volume(emu10k_portc_t * portc,int voice,int chn)29310913Sgdamore@opensolaris.org emu10k_update_output_volume(emu10k_portc_t *portc, int voice, int chn)
29410913Sgdamore@opensolaris.org {
29510913Sgdamore@opensolaris.org 	emu10k_devc_t *devc = portc->devc;
29610913Sgdamore@opensolaris.org 	unsigned int tmp;
29710913Sgdamore@opensolaris.org 	unsigned char send[2];
29810913Sgdamore@opensolaris.org 
29910913Sgdamore@opensolaris.org 	/*
30010913Sgdamore@opensolaris.org 	 * Each voice operator of EMU10k has 4 sends (0=left, 1=right,
30110913Sgdamore@opensolaris.org 	 * 2=surround_left, 3=surround_right). The original OSS driver
30210913Sgdamore@opensolaris.org 	 * used all of them to spread stereo output to two different
30310913Sgdamore@opensolaris.org 	 * speaker pairs. This Boomer version uses only the first two
30410913Sgdamore@opensolaris.org 	 * sends. The other sends are set to 0.
30510913Sgdamore@opensolaris.org 	 *
30610913Sgdamore@opensolaris.org 	 * Boomer uses multiple voice pairs to play multichannel
30710913Sgdamore@opensolaris.org 	 * audio. This function is used to update only one of these
30810913Sgdamore@opensolaris.org 	 * pairs.
30910913Sgdamore@opensolaris.org 	 */
31010913Sgdamore@opensolaris.org 
31110913Sgdamore@opensolaris.org 	send[0] = 0xff;		/* Max */
31210913Sgdamore@opensolaris.org 	send[1] = 0xff;		/* Max */
31310913Sgdamore@opensolaris.org 
31410913Sgdamore@opensolaris.org 	/* Analog voice */
31510913Sgdamore@opensolaris.org 	if (chn == LEFT_CH) {
31610913Sgdamore@opensolaris.org 		send[1] = 0;
31710913Sgdamore@opensolaris.org 	} else {
31810913Sgdamore@opensolaris.org 		send[0] = 0;
31910913Sgdamore@opensolaris.org 	}
32010913Sgdamore@opensolaris.org 
32110913Sgdamore@opensolaris.org 	tmp = emu10k_read_reg(devc, PTAB, voice) & 0xffff0000;
32210913Sgdamore@opensolaris.org 	emu10k_write_reg(devc, PTAB, voice, tmp | (send[0] << 8) | send[1]);
32310913Sgdamore@opensolaris.org }
32410913Sgdamore@opensolaris.org 
32510913Sgdamore@opensolaris.org static void
emu10k_setup_voice(emu10k_portc_t * portc,int voice,int chn,int buf_offset)32610913Sgdamore@opensolaris.org emu10k_setup_voice(emu10k_portc_t *portc, int voice, int chn, int buf_offset)
32710913Sgdamore@opensolaris.org {
32810913Sgdamore@opensolaris.org 	emu10k_devc_t *devc = portc->devc;
32910913Sgdamore@opensolaris.org 	unsigned int nCRA = 0;
33010913Sgdamore@opensolaris.org 
33110913Sgdamore@opensolaris.org 	unsigned int loop_start, loop_end, buf_size;
33210913Sgdamore@opensolaris.org 
33310913Sgdamore@opensolaris.org 	int sz;
33410913Sgdamore@opensolaris.org 	int start_pos;
33510913Sgdamore@opensolaris.org 
33610913Sgdamore@opensolaris.org 	emu10k_write_reg(devc, VEDS, voice, 0x0);	/* OFF */
33710913Sgdamore@opensolaris.org 	emu10k_write_reg(devc, VTFT, voice, 0xffff);
33810913Sgdamore@opensolaris.org 	emu10k_write_reg(devc, CVCF, voice, 0xffff);
33910913Sgdamore@opensolaris.org 
34010913Sgdamore@opensolaris.org 	sz = 2;			/* Shift value for 16 bits stereo */
34110913Sgdamore@opensolaris.org 
34210913Sgdamore@opensolaris.org 	/* Size of one stereo sub buffer */
34310913Sgdamore@opensolaris.org 	buf_size = (portc->buf_size / portc->channels) * 2;
34410913Sgdamore@opensolaris.org 	loop_start = (portc->memptr + buf_offset) >> sz;
34510913Sgdamore@opensolaris.org 	loop_end = (portc->memptr + buf_offset + buf_size) >> sz;
34610913Sgdamore@opensolaris.org 
34710913Sgdamore@opensolaris.org 	/* set stereo */
34810913Sgdamore@opensolaris.org 	emu10k_write_reg(devc, CPF, voice, 0x8000);
34910913Sgdamore@opensolaris.org 
35010913Sgdamore@opensolaris.org 	nCRA = 28;			/* Stereo (16 bits) */
35110913Sgdamore@opensolaris.org 	start_pos = loop_start + nCRA;
35210913Sgdamore@opensolaris.org 
35310913Sgdamore@opensolaris.org 	/* SDL, ST, CA */
35410913Sgdamore@opensolaris.org 
35510913Sgdamore@opensolaris.org 	emu10k_write_reg(devc, SDL, voice, loop_end);
35610913Sgdamore@opensolaris.org 	emu10k_write_reg(devc, SCSA, voice, loop_start);
35710913Sgdamore@opensolaris.org 	emu10k_write_reg(devc, PTAB, voice, 0);
35810913Sgdamore@opensolaris.org 
35910913Sgdamore@opensolaris.org 	emu10k_update_output_volume(portc, voice, chn);	/* Set volume */
36010913Sgdamore@opensolaris.org 
36110913Sgdamore@opensolaris.org 	emu10k_write_reg(devc, QKBCA, voice, start_pos);
36210913Sgdamore@opensolaris.org 
36310913Sgdamore@opensolaris.org 	emu10k_write_reg(devc, Z1, voice, 0);
36410913Sgdamore@opensolaris.org 	emu10k_write_reg(devc, Z2, voice, 0);
36510913Sgdamore@opensolaris.org 
36610913Sgdamore@opensolaris.org 	/* This is really a physical address */
36710913Sgdamore@opensolaris.org 	emu10k_write_reg(devc, MAPA, voice,
36810913Sgdamore@opensolaris.org 	    0x1fff | (devc->silence_paddr << 1));
36910913Sgdamore@opensolaris.org 	emu10k_write_reg(devc, MAPB, voice,
37010913Sgdamore@opensolaris.org 	    0x1fff | (devc->silence_paddr << 1));
37110913Sgdamore@opensolaris.org 
37210913Sgdamore@opensolaris.org 	emu10k_write_reg(devc, VTFT, voice, 0x0000ffff);
37310913Sgdamore@opensolaris.org 	emu10k_write_reg(devc, CVCF, voice, 0x0000ffff);
37410913Sgdamore@opensolaris.org 	emu10k_write_reg(devc, MEHA, voice, 0);
37510913Sgdamore@opensolaris.org 	emu10k_write_reg(devc, MEDS, voice, 0x7f);
37610913Sgdamore@opensolaris.org 	emu10k_write_reg(devc, MLV, voice, 0x8000);
37710913Sgdamore@opensolaris.org 	emu10k_write_reg(devc, VLV, voice, 0x8000);
37810913Sgdamore@opensolaris.org 	emu10k_write_reg(devc, VFM, voice, 0);
37910913Sgdamore@opensolaris.org 	emu10k_write_reg(devc, TMFQ, voice, 0);
38010913Sgdamore@opensolaris.org 	emu10k_write_reg(devc, VVFQ, voice, 0);
38110913Sgdamore@opensolaris.org 	emu10k_write_reg(devc, MEV, voice, 0x8000);
38210913Sgdamore@opensolaris.org 	emu10k_write_reg(devc, VEHA, voice, 0x7f7f);	/* OK */
38310913Sgdamore@opensolaris.org 	/* No volume envelope delay (OK) */
38410913Sgdamore@opensolaris.org 	emu10k_write_reg(devc, VEV, voice, 0x8000);
38510913Sgdamore@opensolaris.org 	emu10k_write_reg(devc, PEFE_FILTERAMOUNT, voice, 0x7f);
38610913Sgdamore@opensolaris.org 	emu10k_write_reg(devc, PEFE_PITCHAMOUNT, voice, 0x00);
38710913Sgdamore@opensolaris.org }
38810913Sgdamore@opensolaris.org 
38910913Sgdamore@opensolaris.org int
emu10k_open(void * arg,int flag,unsigned * nframes,caddr_t * bufp)390*11936Sgdamore@opensolaris.org emu10k_open(void *arg, int flag, unsigned *nframes, caddr_t *bufp)
39110913Sgdamore@opensolaris.org {
39210913Sgdamore@opensolaris.org 	emu10k_portc_t *portc = arg;
39310913Sgdamore@opensolaris.org 	emu10k_devc_t *devc = portc->devc;
39410913Sgdamore@opensolaris.org 
39510913Sgdamore@opensolaris.org 	_NOTE(ARGUNUSED(flag));
39610913Sgdamore@opensolaris.org 
39710913Sgdamore@opensolaris.org 	portc->active = B_FALSE;
398*11936Sgdamore@opensolaris.org 	*nframes = portc->nframes;
39910913Sgdamore@opensolaris.org 	*bufp = portc->buf_kaddr;
40010913Sgdamore@opensolaris.org 
40110913Sgdamore@opensolaris.org 	mutex_enter(&devc->mutex);
40210913Sgdamore@opensolaris.org 	portc->count = 0;
40310913Sgdamore@opensolaris.org 	mutex_exit(&devc->mutex);
40410913Sgdamore@opensolaris.org 
40510913Sgdamore@opensolaris.org 	return (0);
40610913Sgdamore@opensolaris.org }
40710913Sgdamore@opensolaris.org 
40810913Sgdamore@opensolaris.org void
emu10k_close(void * arg)40910913Sgdamore@opensolaris.org emu10k_close(void *arg)
41010913Sgdamore@opensolaris.org {
411*11936Sgdamore@opensolaris.org 	_NOTE(ARGUNUSED(arg));
41210913Sgdamore@opensolaris.org }
41310913Sgdamore@opensolaris.org 
41410913Sgdamore@opensolaris.org int
emu10k_start(void * arg)41510913Sgdamore@opensolaris.org emu10k_start(void *arg)
41610913Sgdamore@opensolaris.org {
41710913Sgdamore@opensolaris.org 	emu10k_portc_t *portc = arg;
41810913Sgdamore@opensolaris.org 	emu10k_devc_t *devc = portc->devc;
41910913Sgdamore@opensolaris.org 
42010913Sgdamore@opensolaris.org 	mutex_enter(&devc->mutex);
421*11936Sgdamore@opensolaris.org 	portc->reset_port(portc);
422*11936Sgdamore@opensolaris.org 	portc->start_port(portc);
42310913Sgdamore@opensolaris.org 	mutex_exit(&devc->mutex);
42410913Sgdamore@opensolaris.org 	return (0);
42510913Sgdamore@opensolaris.org }
42610913Sgdamore@opensolaris.org 
42710913Sgdamore@opensolaris.org void
emu10k_stop(void * arg)42810913Sgdamore@opensolaris.org emu10k_stop(void *arg)
42910913Sgdamore@opensolaris.org {
43010913Sgdamore@opensolaris.org 	emu10k_portc_t *portc = arg;
43110913Sgdamore@opensolaris.org 	emu10k_devc_t *devc = portc->devc;
43210913Sgdamore@opensolaris.org 
43310913Sgdamore@opensolaris.org 	mutex_enter(&devc->mutex);
434*11936Sgdamore@opensolaris.org 	portc->stop_port(portc);
43510913Sgdamore@opensolaris.org 	mutex_exit(&devc->mutex);
43610913Sgdamore@opensolaris.org }
43710913Sgdamore@opensolaris.org 
43810913Sgdamore@opensolaris.org int
emu10k_format(void * arg)43910913Sgdamore@opensolaris.org emu10k_format(void *arg)
44010913Sgdamore@opensolaris.org {
44110913Sgdamore@opensolaris.org 	_NOTE(ARGUNUSED(arg));
44210913Sgdamore@opensolaris.org 
44310913Sgdamore@opensolaris.org 	return (AUDIO_FORMAT_S16_LE);
44410913Sgdamore@opensolaris.org }
44510913Sgdamore@opensolaris.org 
44610913Sgdamore@opensolaris.org int
emu10k_channels(void * arg)44710913Sgdamore@opensolaris.org emu10k_channels(void *arg)
44810913Sgdamore@opensolaris.org {
44910913Sgdamore@opensolaris.org 	emu10k_portc_t *portc = arg;
45010913Sgdamore@opensolaris.org 
45110913Sgdamore@opensolaris.org 	return (portc->channels);
45210913Sgdamore@opensolaris.org }
45310913Sgdamore@opensolaris.org 
45410913Sgdamore@opensolaris.org int
emu10k_rate(void * arg)45510913Sgdamore@opensolaris.org emu10k_rate(void *arg)
45610913Sgdamore@opensolaris.org {
45710913Sgdamore@opensolaris.org 	_NOTE(ARGUNUSED(arg));
45810913Sgdamore@opensolaris.org 
45910913Sgdamore@opensolaris.org 	return (SAMPLE_RATE);
46010913Sgdamore@opensolaris.org }
46110913Sgdamore@opensolaris.org 
46210913Sgdamore@opensolaris.org void
emu10k_sync(void * arg,unsigned nframes)46310913Sgdamore@opensolaris.org emu10k_sync(void *arg, unsigned nframes)
46410913Sgdamore@opensolaris.org {
46510913Sgdamore@opensolaris.org 	emu10k_portc_t *portc = arg;
46610913Sgdamore@opensolaris.org 	_NOTE(ARGUNUSED(nframes));
46710913Sgdamore@opensolaris.org 
46810913Sgdamore@opensolaris.org 	(void) ddi_dma_sync(portc->buf_dmah, 0, 0, portc->syncdir);
46910913Sgdamore@opensolaris.org }
47010913Sgdamore@opensolaris.org 
47110913Sgdamore@opensolaris.org uint64_t
emu10k_count(void * arg)47210913Sgdamore@opensolaris.org emu10k_count(void *arg)
47310913Sgdamore@opensolaris.org {
47410913Sgdamore@opensolaris.org 	emu10k_portc_t *portc = arg;
47510913Sgdamore@opensolaris.org 	emu10k_devc_t *devc = portc->devc;
47610913Sgdamore@opensolaris.org 	uint64_t count;
47710913Sgdamore@opensolaris.org 
47810913Sgdamore@opensolaris.org 	mutex_enter(&devc->mutex);
479*11936Sgdamore@opensolaris.org 	portc->update_port(portc);
48010913Sgdamore@opensolaris.org 	count = portc->count;
48110913Sgdamore@opensolaris.org 	mutex_exit(&devc->mutex);
48210913Sgdamore@opensolaris.org 
48310913Sgdamore@opensolaris.org 	return (count);
48410913Sgdamore@opensolaris.org }
48510913Sgdamore@opensolaris.org 
48610913Sgdamore@opensolaris.org static void
emu10k_chinfo(void * arg,int chan,unsigned * offset,unsigned * incr)48710913Sgdamore@opensolaris.org emu10k_chinfo(void *arg, int chan, unsigned *offset, unsigned *incr)
48810913Sgdamore@opensolaris.org {
48910913Sgdamore@opensolaris.org 	emu10k_portc_t *portc = arg;
49010913Sgdamore@opensolaris.org 
49110913Sgdamore@opensolaris.org 	*offset = portc->nframes * (chan / 2) * 2 + (chan % 2);
49210913Sgdamore@opensolaris.org 	*incr = 2;
49310913Sgdamore@opensolaris.org }
49410913Sgdamore@opensolaris.org 
49510913Sgdamore@opensolaris.org /* private implementation bits */
49610913Sgdamore@opensolaris.org 
49710913Sgdamore@opensolaris.org static void
emu10k_set_loop_stop(emu10k_devc_t * devc,int voice,int s)49810913Sgdamore@opensolaris.org emu10k_set_loop_stop(emu10k_devc_t *devc, int voice, int s)
49910913Sgdamore@opensolaris.org {
50010913Sgdamore@opensolaris.org 	unsigned int tmp;
50110913Sgdamore@opensolaris.org 	int offs, bit;
50210913Sgdamore@opensolaris.org 
50310913Sgdamore@opensolaris.org 	offs = voice / 32;
50410913Sgdamore@opensolaris.org 	bit = voice % 32;
50510913Sgdamore@opensolaris.org 	s = !!s;
50610913Sgdamore@opensolaris.org 
50710913Sgdamore@opensolaris.org 	tmp = emu10k_read_reg(devc, SOLL + offs, 0);
50810913Sgdamore@opensolaris.org 	tmp &= ~(1 << bit);
50910913Sgdamore@opensolaris.org 
51010913Sgdamore@opensolaris.org 	if (s)
51110913Sgdamore@opensolaris.org 		tmp |= (1 << bit);
51210913Sgdamore@opensolaris.org 	emu10k_write_reg(devc, SOLL + offs, 0, tmp);
51310913Sgdamore@opensolaris.org }
51410913Sgdamore@opensolaris.org 
51510913Sgdamore@opensolaris.org static unsigned int
emu10k_rate_to_pitch(unsigned int rate)51610913Sgdamore@opensolaris.org emu10k_rate_to_pitch(unsigned int rate)
51710913Sgdamore@opensolaris.org {
51810913Sgdamore@opensolaris.org 	static unsigned int logMagTable[128] = {
51910913Sgdamore@opensolaris.org 		0x00000, 0x02dfc, 0x05b9e, 0x088e6,
52010913Sgdamore@opensolaris.org 		0x0b5d6, 0x0e26f, 0x10eb3, 0x13aa2,
52110913Sgdamore@opensolaris.org 		0x1663f, 0x1918a, 0x1bc84, 0x1e72e,
52210913Sgdamore@opensolaris.org 		0x2118b, 0x23b9a, 0x2655d, 0x28ed5,
52310913Sgdamore@opensolaris.org 		0x2b803, 0x2e0e8, 0x30985, 0x331db,
52410913Sgdamore@opensolaris.org 		0x359eb, 0x381b6, 0x3a93d, 0x3d081,
52510913Sgdamore@opensolaris.org 		0x3f782, 0x41e42, 0x444c1, 0x46b01,
52610913Sgdamore@opensolaris.org 		0x49101, 0x4b6c4, 0x4dc49, 0x50191,
52710913Sgdamore@opensolaris.org 		0x5269e, 0x54b6f, 0x57006, 0x59463,
52810913Sgdamore@opensolaris.org 		0x5b888, 0x5dc74, 0x60029, 0x623a7,
52910913Sgdamore@opensolaris.org 		0x646ee, 0x66a00, 0x68cdd, 0x6af86,
53010913Sgdamore@opensolaris.org 		0x6d1fa, 0x6f43c, 0x7164b, 0x73829,
53110913Sgdamore@opensolaris.org 		0x759d4, 0x77b4f, 0x79c9a, 0x7bdb5,
53210913Sgdamore@opensolaris.org 		0x7dea1, 0x7ff5e, 0x81fed, 0x8404e,
53310913Sgdamore@opensolaris.org 		0x86082, 0x88089, 0x8a064, 0x8c014,
53410913Sgdamore@opensolaris.org 		0x8df98, 0x8fef1, 0x91e20, 0x93d26,
53510913Sgdamore@opensolaris.org 		0x95c01, 0x97ab4, 0x9993e, 0x9b79f,
53610913Sgdamore@opensolaris.org 		0x9d5d9, 0x9f3ec, 0xa11d8, 0xa2f9d,
53710913Sgdamore@opensolaris.org 		0xa4d3c, 0xa6ab5, 0xa8808, 0xaa537,
53810913Sgdamore@opensolaris.org 		0xac241, 0xadf26, 0xafbe7, 0xb1885,
53910913Sgdamore@opensolaris.org 		0xb3500, 0xb5157, 0xb6d8c, 0xb899f,
54010913Sgdamore@opensolaris.org 		0xba58f, 0xbc15e, 0xbdd0c, 0xbf899,
54110913Sgdamore@opensolaris.org 		0xc1404, 0xc2f50, 0xc4a7b, 0xc6587,
54210913Sgdamore@opensolaris.org 		0xc8073, 0xc9b3f, 0xcb5ed, 0xcd07c,
54310913Sgdamore@opensolaris.org 		0xceaec, 0xd053f, 0xd1f73, 0xd398a,
54410913Sgdamore@opensolaris.org 		0xd5384, 0xd6d60, 0xd8720, 0xda0c3,
54510913Sgdamore@opensolaris.org 		0xdba4a, 0xdd3b4, 0xded03, 0xe0636,
54610913Sgdamore@opensolaris.org 		0xe1f4e, 0xe384a, 0xe512c, 0xe69f3,
54710913Sgdamore@opensolaris.org 		0xe829f, 0xe9b31, 0xeb3a9, 0xecc08,
54810913Sgdamore@opensolaris.org 		0xee44c, 0xefc78, 0xf148a, 0xf2c83,
54910913Sgdamore@opensolaris.org 		0xf4463, 0xf5c2a, 0xf73da, 0xf8b71,
55010913Sgdamore@opensolaris.org 		0xfa2f0, 0xfba57, 0xfd1a7, 0xfe8df
55110913Sgdamore@opensolaris.org 	};
55210913Sgdamore@opensolaris.org 	static char logSlopeTable[128] = {
55310913Sgdamore@opensolaris.org 		0x5c, 0x5c, 0x5b, 0x5a, 0x5a, 0x59, 0x58, 0x58,
55410913Sgdamore@opensolaris.org 		0x57, 0x56, 0x56, 0x55, 0x55, 0x54, 0x53, 0x53,
55510913Sgdamore@opensolaris.org 		0x52, 0x52, 0x51, 0x51, 0x50, 0x50, 0x4f, 0x4f,
55610913Sgdamore@opensolaris.org 		0x4e, 0x4d, 0x4d, 0x4d, 0x4c, 0x4c, 0x4b, 0x4b,
55710913Sgdamore@opensolaris.org 		0x4a, 0x4a, 0x49, 0x49, 0x48, 0x48, 0x47, 0x47,
55810913Sgdamore@opensolaris.org 		0x47, 0x46, 0x46, 0x45, 0x45, 0x45, 0x44, 0x44,
55910913Sgdamore@opensolaris.org 		0x43, 0x43, 0x43, 0x42, 0x42, 0x42, 0x41, 0x41,
56010913Sgdamore@opensolaris.org 		0x41, 0x40, 0x40, 0x40, 0x3f, 0x3f, 0x3f, 0x3e,
56110913Sgdamore@opensolaris.org 		0x3e, 0x3e, 0x3d, 0x3d, 0x3d, 0x3c, 0x3c, 0x3c,
56210913Sgdamore@opensolaris.org 		0x3b, 0x3b, 0x3b, 0x3b, 0x3a, 0x3a, 0x3a, 0x39,
56310913Sgdamore@opensolaris.org 		0x39, 0x39, 0x39, 0x38, 0x38, 0x38, 0x38, 0x37,
56410913Sgdamore@opensolaris.org 		0x37, 0x37, 0x37, 0x36, 0x36, 0x36, 0x36, 0x35,
56510913Sgdamore@opensolaris.org 		0x35, 0x35, 0x35, 0x34, 0x34, 0x34, 0x34, 0x34,
56610913Sgdamore@opensolaris.org 		0x33, 0x33, 0x33, 0x33, 0x32, 0x32, 0x32, 0x32,
56710913Sgdamore@opensolaris.org 		0x32, 0x31, 0x31, 0x31, 0x31, 0x31, 0x30, 0x30,
56810913Sgdamore@opensolaris.org 		0x30, 0x30, 0x30, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f
56910913Sgdamore@opensolaris.org 	};
57010913Sgdamore@opensolaris.org 	int i;
57110913Sgdamore@opensolaris.org 
57210913Sgdamore@opensolaris.org 	if (rate == 0)
57310913Sgdamore@opensolaris.org 		return (0);			/* Bail out if no leading "1" */
57410913Sgdamore@opensolaris.org 	rate *= 11185;		/* Scale 48000 to 0x20002380 */
57510913Sgdamore@opensolaris.org 	for (i = 31; i > 0; i--) {
57610913Sgdamore@opensolaris.org 		if (rate & 0x80000000) {	/* Detect leading "1" */
57710913Sgdamore@opensolaris.org 			return (((unsigned int) (i - 15) << 20) +
57810913Sgdamore@opensolaris.org 			    logMagTable[0x7f & (rate >> 24)] +
57910913Sgdamore@opensolaris.org 			    (0x7f & (rate >> 17)) *
58010913Sgdamore@opensolaris.org 			    logSlopeTable[0x7f & (rate >> 24)]);
58110913Sgdamore@opensolaris.org 		}
58210913Sgdamore@opensolaris.org 		rate <<= 1;
58310913Sgdamore@opensolaris.org 	}
58410913Sgdamore@opensolaris.org 
58510913Sgdamore@opensolaris.org 	return (0);			/* Should never reach this point */
58610913Sgdamore@opensolaris.org }
58710913Sgdamore@opensolaris.org 
58810913Sgdamore@opensolaris.org static unsigned int
emu10k_rate_to_linearpitch(unsigned int rate)58910913Sgdamore@opensolaris.org emu10k_rate_to_linearpitch(unsigned int rate)
59010913Sgdamore@opensolaris.org {
59110913Sgdamore@opensolaris.org 	rate = (rate << 8) / 375;
59210913Sgdamore@opensolaris.org 	return (rate >> 1) + (rate & 1);
59310913Sgdamore@opensolaris.org }
59410913Sgdamore@opensolaris.org 
59510913Sgdamore@opensolaris.org static void
emu10k_prepare_voice(emu10k_devc_t * devc,int voice)59610913Sgdamore@opensolaris.org emu10k_prepare_voice(emu10k_devc_t *devc, int voice)
59710913Sgdamore@opensolaris.org {
59810913Sgdamore@opensolaris.org 	unsigned int sample, initial_pitch, pitch_target;
59910913Sgdamore@opensolaris.org 	unsigned int cra, cs, ccis, i;
60010913Sgdamore@opensolaris.org 
60110913Sgdamore@opensolaris.org 	/* setup CCR regs */
60210913Sgdamore@opensolaris.org 	cra = 64;
60310913Sgdamore@opensolaris.org 	cs = 4;			/* Stereo */
60410913Sgdamore@opensolaris.org 	ccis = 28;		/* Stereo */
60510913Sgdamore@opensolaris.org 	sample = 0;		/* 16 bit silence */
60610913Sgdamore@opensolaris.org 
60710913Sgdamore@opensolaris.org 	for (i = 0; i < cs; i++)
60810913Sgdamore@opensolaris.org 		emu10k_write_reg(devc, CD0 + i, voice, sample);
60910913Sgdamore@opensolaris.org 
61010913Sgdamore@opensolaris.org 	emu10k_write_reg(devc, CCR_CACHEINVALIDSIZE, voice, 0);
61110913Sgdamore@opensolaris.org 	emu10k_write_reg(devc, CCR_READADDRESS, voice, cra);
61210913Sgdamore@opensolaris.org 	emu10k_write_reg(devc, CCR_CACHEINVALIDSIZE, voice, ccis);
61310913Sgdamore@opensolaris.org 
61410913Sgdamore@opensolaris.org 	/* Set current pitch */
61510913Sgdamore@opensolaris.org 	emu10k_write_reg(devc, IFA, voice, 0xff00);
61610913Sgdamore@opensolaris.org 	emu10k_write_reg(devc, VTFT, voice, 0xffffffff);
61710913Sgdamore@opensolaris.org 	emu10k_write_reg(devc, CVCF, voice, 0xffffffff);
61810913Sgdamore@opensolaris.org 	emu10k_set_loop_stop(devc, voice, 0);
61910913Sgdamore@opensolaris.org 
62010913Sgdamore@opensolaris.org 	pitch_target = emu10k_rate_to_linearpitch(SAMPLE_RATE);
62110913Sgdamore@opensolaris.org 	initial_pitch = emu10k_rate_to_pitch(SAMPLE_RATE) >> 8;
62210913Sgdamore@opensolaris.org 	emu10k_write_reg(devc, PTRX_PITCHTARGET, voice, pitch_target);
62310913Sgdamore@opensolaris.org 	emu10k_write_reg(devc, CPF_CURRENTPITCH, voice, pitch_target);
62410913Sgdamore@opensolaris.org 	emu10k_write_reg(devc, IP, voice, initial_pitch);
62510913Sgdamore@opensolaris.org }
62610913Sgdamore@opensolaris.org 
62710913Sgdamore@opensolaris.org static void
emu10k_stop_voice(emu10k_devc_t * devc,int voice)62810913Sgdamore@opensolaris.org emu10k_stop_voice(emu10k_devc_t *devc, int voice)
62910913Sgdamore@opensolaris.org {
63010913Sgdamore@opensolaris.org 	emu10k_write_reg(devc, IFA, voice, 0xffff);
63110913Sgdamore@opensolaris.org 	emu10k_write_reg(devc, VTFT, voice, 0xffff);
63210913Sgdamore@opensolaris.org 	emu10k_write_reg(devc, PTRX_PITCHTARGET, voice, 0);
63310913Sgdamore@opensolaris.org 	emu10k_write_reg(devc, CPF_CURRENTPITCH, voice, 0);
63410913Sgdamore@opensolaris.org 	emu10k_write_reg(devc, IP, voice, 0);
63510913Sgdamore@opensolaris.org 	emu10k_set_loop_stop(devc, voice, 1);
63610913Sgdamore@opensolaris.org }
63710913Sgdamore@opensolaris.org 
63810913Sgdamore@opensolaris.org static void
emu10k_reset_pair(emu10k_portc_t * portc,int voice,uint8_t * routing,int buf_offset)63910913Sgdamore@opensolaris.org emu10k_reset_pair(emu10k_portc_t *portc, int voice, uint8_t *routing,
64010913Sgdamore@opensolaris.org     int buf_offset)
64110913Sgdamore@opensolaris.org {
64210913Sgdamore@opensolaris.org 	emu10k_devc_t *devc = portc->devc;
64310913Sgdamore@opensolaris.org 
64410913Sgdamore@opensolaris.org 	/* Left channel */
64510913Sgdamore@opensolaris.org 	/* Intial filter cutoff and attenuation */
64610913Sgdamore@opensolaris.org 	emu10k_write_reg(devc, IFA, voice, 0xffff);
64710913Sgdamore@opensolaris.org 	/* Volume envelope decay and sustain */
64810913Sgdamore@opensolaris.org 	emu10k_write_reg(devc, VEDS, voice, 0x0);
64910913Sgdamore@opensolaris.org 	/* Volume target and Filter cutoff target */
65010913Sgdamore@opensolaris.org 	emu10k_write_reg(devc, VTFT, voice, 0xffff);
65110913Sgdamore@opensolaris.org 	/* Pitch target and sends A and B */
65210913Sgdamore@opensolaris.org 	emu10k_write_reg(devc, PTAB, voice, 0x0);
65310913Sgdamore@opensolaris.org 
65410913Sgdamore@opensolaris.org 	/* The same for right channel */
65510913Sgdamore@opensolaris.org 	emu10k_write_reg(devc, IFA, voice + 1, 0xffff);
65610913Sgdamore@opensolaris.org 	emu10k_write_reg(devc, VEDS, voice + 1, 0x0);
65710913Sgdamore@opensolaris.org 	emu10k_write_reg(devc, VTFT, voice + 1, 0xffff);
65810913Sgdamore@opensolaris.org 	emu10k_write_reg(devc, PTAB, voice + 1, 0x0);
65910913Sgdamore@opensolaris.org 
66010913Sgdamore@opensolaris.org 	/* now setup the voices and go! */
66110913Sgdamore@opensolaris.org 	emu10k_setup_voice(portc, voice, LEFT_CH, buf_offset);
66210913Sgdamore@opensolaris.org 	emu10k_setup_voice(portc, voice + 1, RIGHT_CH, buf_offset);
66310913Sgdamore@opensolaris.org 
66410913Sgdamore@opensolaris.org 	emu10k_write_routing(devc, voice, routing);
66510913Sgdamore@opensolaris.org 	emu10k_write_routing(devc, voice + 1, routing);
66610913Sgdamore@opensolaris.org }
66710913Sgdamore@opensolaris.org 
66810913Sgdamore@opensolaris.org void
emu10k_start_play(emu10k_portc_t * portc)66910913Sgdamore@opensolaris.org emu10k_start_play(emu10k_portc_t *portc)
67010913Sgdamore@opensolaris.org {
67110913Sgdamore@opensolaris.org 	emu10k_devc_t *devc = portc->devc;
67210913Sgdamore@opensolaris.org 
67310913Sgdamore@opensolaris.org 	ASSERT(mutex_owned(&devc->mutex));
67410913Sgdamore@opensolaris.org 	emu10k_prepare_voice(devc, 0);
67510913Sgdamore@opensolaris.org 	emu10k_prepare_voice(devc, 1);
67610913Sgdamore@opensolaris.org 
67710913Sgdamore@opensolaris.org 	emu10k_prepare_voice(devc, 2);
67810913Sgdamore@opensolaris.org 	emu10k_prepare_voice(devc, 3);
67910913Sgdamore@opensolaris.org 
68010913Sgdamore@opensolaris.org 	emu10k_prepare_voice(devc, 4);
68110913Sgdamore@opensolaris.org 	emu10k_prepare_voice(devc, 5);
68210913Sgdamore@opensolaris.org 
68310913Sgdamore@opensolaris.org 	emu10k_prepare_voice(devc, 6);
68410913Sgdamore@opensolaris.org 	emu10k_prepare_voice(devc, 7);
68510913Sgdamore@opensolaris.org 
68610913Sgdamore@opensolaris.org 	/* Trigger playback on all voices */
68710913Sgdamore@opensolaris.org 	emu10k_write_reg(devc, VEDS, 0, 0x7f7f);
68810913Sgdamore@opensolaris.org 	emu10k_write_reg(devc, VEDS, 1, 0x7f7f);
68910913Sgdamore@opensolaris.org 	emu10k_write_reg(devc, VEDS, 2, 0x7f7f);
69010913Sgdamore@opensolaris.org 	emu10k_write_reg(devc, VEDS, 3, 0x7f7f);
69110913Sgdamore@opensolaris.org 	emu10k_write_reg(devc, VEDS, 4, 0x7f7f);
69210913Sgdamore@opensolaris.org 	emu10k_write_reg(devc, VEDS, 5, 0x7f7f);
69310913Sgdamore@opensolaris.org 	emu10k_write_reg(devc, VEDS, 6, 0x7f7f);
69410913Sgdamore@opensolaris.org 	emu10k_write_reg(devc, VEDS, 7, 0x7f7f);
69510913Sgdamore@opensolaris.org 
69610913Sgdamore@opensolaris.org 	portc->active = B_TRUE;
69710913Sgdamore@opensolaris.org }
69810913Sgdamore@opensolaris.org 
69910913Sgdamore@opensolaris.org void
emu10k_stop_play(emu10k_portc_t * portc)70010913Sgdamore@opensolaris.org emu10k_stop_play(emu10k_portc_t *portc)
70110913Sgdamore@opensolaris.org {
70210913Sgdamore@opensolaris.org 	emu10k_devc_t *devc = portc->devc;
70310913Sgdamore@opensolaris.org 
70410913Sgdamore@opensolaris.org 	emu10k_stop_voice(devc, 0);
70510913Sgdamore@opensolaris.org 	emu10k_stop_voice(devc, 1);
70610913Sgdamore@opensolaris.org 	emu10k_stop_voice(devc, 2);
70710913Sgdamore@opensolaris.org 	emu10k_stop_voice(devc, 3);
70810913Sgdamore@opensolaris.org 	emu10k_stop_voice(devc, 4);
70910913Sgdamore@opensolaris.org 	emu10k_stop_voice(devc, 5);
71010913Sgdamore@opensolaris.org 	emu10k_stop_voice(devc, 6);
71110913Sgdamore@opensolaris.org 	emu10k_stop_voice(devc, 7);
71210913Sgdamore@opensolaris.org 
71310913Sgdamore@opensolaris.org 	portc->active = B_FALSE;
71410913Sgdamore@opensolaris.org }
71510913Sgdamore@opensolaris.org 
71610913Sgdamore@opensolaris.org void
emu10k_reset_play(emu10k_portc_t * portc)71710913Sgdamore@opensolaris.org emu10k_reset_play(emu10k_portc_t *portc)
71810913Sgdamore@opensolaris.org {
71910913Sgdamore@opensolaris.org 	emu10k_devc_t *devc = portc->devc;
72010913Sgdamore@opensolaris.org 	uint32_t offs;
72110913Sgdamore@opensolaris.org 
72210913Sgdamore@opensolaris.org 	offs = (portc->buf_size / portc->channels) * 2;
72310913Sgdamore@opensolaris.org 
72410913Sgdamore@opensolaris.org 	if (devc->feature_mask & SB_71) {
72510913Sgdamore@opensolaris.org 		emu10k_reset_pair(portc, 0, front_routing, 0);
72610913Sgdamore@opensolaris.org 		emu10k_reset_pair(portc, 2, clfe_routing, offs);
72710913Sgdamore@opensolaris.org 		emu10k_reset_pair(portc, 4, surr_routing, 2 * offs);
72810913Sgdamore@opensolaris.org 		emu10k_reset_pair(portc, 6, side_routing, 3 * offs);
72910913Sgdamore@opensolaris.org 	} else if (devc->feature_mask & SB_51) {
73010913Sgdamore@opensolaris.org 		emu10k_reset_pair(portc, 0, front_routing, 0);
73110913Sgdamore@opensolaris.org 		emu10k_reset_pair(portc, 2, clfe_routing, offs);
73210913Sgdamore@opensolaris.org 		emu10k_reset_pair(portc, 4, surr_routing, 2 * offs);
73310913Sgdamore@opensolaris.org 	} else {
73410913Sgdamore@opensolaris.org 		emu10k_reset_pair(portc, 0, front_routing, 0);
73510913Sgdamore@opensolaris.org 		emu10k_reset_pair(portc, 2, surr_routing, offs);
73610913Sgdamore@opensolaris.org 	}
73710913Sgdamore@opensolaris.org 
73810913Sgdamore@opensolaris.org 	portc->pos = 0;
73910913Sgdamore@opensolaris.org }
74010913Sgdamore@opensolaris.org 
74110913Sgdamore@opensolaris.org uint32_t emu10k_vars[5];
74210913Sgdamore@opensolaris.org 
74310913Sgdamore@opensolaris.org void
emu10k_update_play(emu10k_portc_t * portc)74410913Sgdamore@opensolaris.org emu10k_update_play(emu10k_portc_t *portc)
74510913Sgdamore@opensolaris.org {
74610913Sgdamore@opensolaris.org 	emu10k_devc_t *devc = portc->devc;
74710913Sgdamore@opensolaris.org 	uint32_t cnt, pos;
74810913Sgdamore@opensolaris.org 
74910913Sgdamore@opensolaris.org 	/*
75010913Sgdamore@opensolaris.org 	 * Note: position is given as stereo samples, i.e. frames.
75110913Sgdamore@opensolaris.org 	 */
75210913Sgdamore@opensolaris.org 	pos = emu10k_read_reg(devc, QKBCA, 0) & 0xffffff;
75310913Sgdamore@opensolaris.org 	pos -= (portc->memptr >> 2);
754*11936Sgdamore@opensolaris.org 	if (pos > portc->nframes) {
755*11936Sgdamore@opensolaris.org 		/*
756*11936Sgdamore@opensolaris.org 		 * This should never happen!  If it happens, we should
757*11936Sgdamore@opensolaris.org 		 * throw an FMA fault.  (When we support FMA.)  For now
758*11936Sgdamore@opensolaris.org 		 * we just assume the device is stuck, and report no
759*11936Sgdamore@opensolaris.org 		 * change in position.
760*11936Sgdamore@opensolaris.org 		 */
761*11936Sgdamore@opensolaris.org 		pos = portc->pos;
762*11936Sgdamore@opensolaris.org 	}
763*11936Sgdamore@opensolaris.org 	ASSERT(pos <= portc->nframes);
76410913Sgdamore@opensolaris.org 
765*11936Sgdamore@opensolaris.org 	if (pos < portc->pos) {
766*11936Sgdamore@opensolaris.org 		cnt = (portc->nframes - portc->pos) + pos;
76710913Sgdamore@opensolaris.org 	} else {
76810913Sgdamore@opensolaris.org 		cnt = (pos - portc->pos);
76910913Sgdamore@opensolaris.org 	}
770*11936Sgdamore@opensolaris.org 	ASSERT(cnt <= portc->nframes);
77110913Sgdamore@opensolaris.org 	if (portc->dopos) {
77210913Sgdamore@opensolaris.org 		emu10k_vars[0] = portc->pos;
77310913Sgdamore@opensolaris.org 		emu10k_vars[1] = pos;
77410913Sgdamore@opensolaris.org 		emu10k_vars[2] = (uint32_t)portc->count;
77510913Sgdamore@opensolaris.org 		emu10k_vars[3] = cnt;
77610913Sgdamore@opensolaris.org 		portc->dopos = 0;
77710913Sgdamore@opensolaris.org 	}
77810913Sgdamore@opensolaris.org 	portc->count += cnt;
77910913Sgdamore@opensolaris.org 	portc->pos = pos;
78010913Sgdamore@opensolaris.org }
78110913Sgdamore@opensolaris.org 
78210913Sgdamore@opensolaris.org void
emu10k_start_rec(emu10k_portc_t * portc)78310913Sgdamore@opensolaris.org emu10k_start_rec(emu10k_portc_t *portc)
78410913Sgdamore@opensolaris.org {
78510913Sgdamore@opensolaris.org 	emu10k_devc_t *devc = portc->devc;
78610913Sgdamore@opensolaris.org 	uint32_t tmp;
78710913Sgdamore@opensolaris.org 
78810913Sgdamore@opensolaris.org 	tmp = 0;			/* setup 48Kz */
78910913Sgdamore@opensolaris.org 	if (devc->feature_mask & (SB_AUDIGY|SB_AUDIGY2|SB_AUDIGY2VAL))
79010913Sgdamore@opensolaris.org 		tmp |= 0x30;		/* Left/right channel enable */
79110913Sgdamore@opensolaris.org 	else
79210913Sgdamore@opensolaris.org 		tmp |= 0x18;		/* Left/right channel enable */
79310913Sgdamore@opensolaris.org 	emu10k_write_reg(devc, ADCSR, 0, tmp);	/* GO */
79410913Sgdamore@opensolaris.org 
79510913Sgdamore@opensolaris.org 	portc->active = B_TRUE;
79610913Sgdamore@opensolaris.org }
79710913Sgdamore@opensolaris.org 
79810913Sgdamore@opensolaris.org void
emu10k_stop_rec(emu10k_portc_t * portc)79910913Sgdamore@opensolaris.org emu10k_stop_rec(emu10k_portc_t *portc)
80010913Sgdamore@opensolaris.org {
80110913Sgdamore@opensolaris.org 	emu10k_devc_t *devc = portc->devc;
80210913Sgdamore@opensolaris.org 
80310913Sgdamore@opensolaris.org 	ASSERT(mutex_owned(&devc->mutex));
80410913Sgdamore@opensolaris.org 	emu10k_write_reg(devc, ADCSR, 0, 0);
80510913Sgdamore@opensolaris.org 
80610913Sgdamore@opensolaris.org 	portc->active = B_FALSE;
80710913Sgdamore@opensolaris.org }
80810913Sgdamore@opensolaris.org void
emu10k_reset_rec(emu10k_portc_t * portc)80910913Sgdamore@opensolaris.org emu10k_reset_rec(emu10k_portc_t *portc)
81010913Sgdamore@opensolaris.org {
81110913Sgdamore@opensolaris.org 	emu10k_devc_t *devc = portc->devc;
81210913Sgdamore@opensolaris.org 	uint32_t sz;
81310913Sgdamore@opensolaris.org 
81410913Sgdamore@opensolaris.org 	switch (portc->buf_size) {
81510913Sgdamore@opensolaris.org 	case 4096:
81610913Sgdamore@opensolaris.org 		sz = 15;
81710913Sgdamore@opensolaris.org 		break;
81810913Sgdamore@opensolaris.org 	case 8192:
81910913Sgdamore@opensolaris.org 		sz = 19;
82010913Sgdamore@opensolaris.org 		break;
82110913Sgdamore@opensolaris.org 	case 16384:
82210913Sgdamore@opensolaris.org 		sz = 23;
82310913Sgdamore@opensolaris.org 		break;
82410913Sgdamore@opensolaris.org 	case 32768:
82510913Sgdamore@opensolaris.org 		sz = 27;
82610913Sgdamore@opensolaris.org 		break;
82710913Sgdamore@opensolaris.org 	case 65536:
82810913Sgdamore@opensolaris.org 		sz = 31;
82910913Sgdamore@opensolaris.org 		break;
83010913Sgdamore@opensolaris.org 	}
83110913Sgdamore@opensolaris.org 	emu10k_write_reg(devc, ADCBA, 0, portc->buf_paddr);
83210913Sgdamore@opensolaris.org 	emu10k_write_reg(devc, ADCBS, 0, sz);
83310913Sgdamore@opensolaris.org 	emu10k_write_reg(devc, ADCSR, 0, 0);	/* reset for phase */
83410913Sgdamore@opensolaris.org 	portc->pos = 0;
83510913Sgdamore@opensolaris.org }
83610913Sgdamore@opensolaris.org 
83710913Sgdamore@opensolaris.org void
emu10k_update_rec(emu10k_portc_t * portc)83810913Sgdamore@opensolaris.org emu10k_update_rec(emu10k_portc_t *portc)
83910913Sgdamore@opensolaris.org {
84010913Sgdamore@opensolaris.org 	emu10k_devc_t *devc = portc->devc;
84110913Sgdamore@opensolaris.org 	uint32_t cnt, pos;
84210913Sgdamore@opensolaris.org 
84310913Sgdamore@opensolaris.org 	/* given in bytes, we divide all counts by 4 to get samples */
84410913Sgdamore@opensolaris.org 	pos = emu10k_read_reg(devc,
84510913Sgdamore@opensolaris.org 	    (devc->feature_mask & SB_LIVE) ? MIDX : ADCIDX, 0);
84610913Sgdamore@opensolaris.org 	if (pos <= portc->pos) {
84710913Sgdamore@opensolaris.org 		cnt = ((portc->buf_size) - portc->pos) >> 2;
84810913Sgdamore@opensolaris.org 		cnt += (pos >> 2);
84910913Sgdamore@opensolaris.org 	} else {
85010913Sgdamore@opensolaris.org 		cnt = ((pos - portc->pos) >> 2);
85110913Sgdamore@opensolaris.org 	}
85210913Sgdamore@opensolaris.org 	portc->count += cnt;
85310913Sgdamore@opensolaris.org 	portc->pos = pos;
85410913Sgdamore@opensolaris.org }
85510913Sgdamore@opensolaris.org 
85610913Sgdamore@opensolaris.org int
emu10k_alloc_port(emu10k_devc_t * devc,int num)85710913Sgdamore@opensolaris.org emu10k_alloc_port(emu10k_devc_t *devc, int num)
85810913Sgdamore@opensolaris.org {
85910913Sgdamore@opensolaris.org 	emu10k_portc_t *portc;
86010913Sgdamore@opensolaris.org 	size_t len;
86110913Sgdamore@opensolaris.org 	ddi_dma_cookie_t cookie;
86210913Sgdamore@opensolaris.org 	uint_t count;
86310913Sgdamore@opensolaris.org 	int dir;
86410913Sgdamore@opensolaris.org 	unsigned caps;
86510913Sgdamore@opensolaris.org 	audio_dev_t *adev;
86610913Sgdamore@opensolaris.org 	int i, n;
86710913Sgdamore@opensolaris.org 
86810913Sgdamore@opensolaris.org 	adev = devc->adev;
86910913Sgdamore@opensolaris.org 	portc = kmem_zalloc(sizeof (*portc), KM_SLEEP);
87010913Sgdamore@opensolaris.org 	devc->portc[num] = portc;
87110913Sgdamore@opensolaris.org 	portc->devc = devc;
87210913Sgdamore@opensolaris.org 
87310913Sgdamore@opensolaris.org 	portc->memptr = devc->audio_memptr;
87410913Sgdamore@opensolaris.org 	devc->audio_memptr += (DMABUF_SIZE + 4095) & ~4095;
87510913Sgdamore@opensolaris.org 
87610913Sgdamore@opensolaris.org 	switch (num) {
87710913Sgdamore@opensolaris.org 	case EMU10K_REC:
87810913Sgdamore@opensolaris.org 		portc->syncdir = DDI_DMA_SYNC_FORKERNEL;
87910913Sgdamore@opensolaris.org 		caps = ENGINE_INPUT_CAP;
88010913Sgdamore@opensolaris.org 		dir = DDI_DMA_READ;
88110913Sgdamore@opensolaris.org 		portc->channels = 2;
88210913Sgdamore@opensolaris.org 		portc->start_port = emu10k_start_rec;
88310913Sgdamore@opensolaris.org 		portc->stop_port = emu10k_stop_rec;
88410913Sgdamore@opensolaris.org 		portc->reset_port = emu10k_reset_rec;
88510913Sgdamore@opensolaris.org 		portc->update_port = emu10k_update_rec;
88610913Sgdamore@opensolaris.org 		/* This is the minimum record buffer size. */
88710913Sgdamore@opensolaris.org 		portc->buf_size = 4096;
888*11936Sgdamore@opensolaris.org 		portc->nframes = portc->buf_size / 4;
88910913Sgdamore@opensolaris.org 		break;
89010913Sgdamore@opensolaris.org 	case EMU10K_PLAY:
89110913Sgdamore@opensolaris.org 		portc->syncdir = DDI_DMA_SYNC_FORDEV;
89210913Sgdamore@opensolaris.org 		caps = ENGINE_OUTPUT_CAP;
89310913Sgdamore@opensolaris.org 		dir = DDI_DMA_WRITE;
89410913Sgdamore@opensolaris.org 		portc->channels = 8;
89510913Sgdamore@opensolaris.org 		portc->start_port = emu10k_start_play;
89610913Sgdamore@opensolaris.org 		portc->stop_port = emu10k_stop_play;
89710913Sgdamore@opensolaris.org 		portc->reset_port = emu10k_reset_play;
89810913Sgdamore@opensolaris.org 		portc->update_port = emu10k_update_play;
899*11936Sgdamore@opensolaris.org 		/* This could probably be tunable. */
900*11936Sgdamore@opensolaris.org 		portc->nframes = 2048;
90110913Sgdamore@opensolaris.org 		portc->buf_size = portc->nframes * portc->channels * 2;
90210913Sgdamore@opensolaris.org 		break;
90310913Sgdamore@opensolaris.org 	default:
90410913Sgdamore@opensolaris.org 		return (DDI_FAILURE);
90510913Sgdamore@opensolaris.org 	}
90610913Sgdamore@opensolaris.org 
90710913Sgdamore@opensolaris.org 	/*
90810913Sgdamore@opensolaris.org 	 * Fragments that are not powers of two don't seem to work
90910913Sgdamore@opensolaris.org 	 * at all with EMU10K.  For simplicity's sake, we eliminate
91010913Sgdamore@opensolaris.org 	 * the question and fix the interrupt rate.  This is also the
91110913Sgdamore@opensolaris.org 	 * logical minimum for record, which requires at least 4K for
91210913Sgdamore@opensolaris.org 	 * the record size.
91310913Sgdamore@opensolaris.org 	 */
91410913Sgdamore@opensolaris.org 
91510913Sgdamore@opensolaris.org 	if (portc->buf_size > DMABUF_SIZE) {
91610913Sgdamore@opensolaris.org 		cmn_err(CE_NOTE, "Buffer size %d is too large (max %d)",
91710913Sgdamore@opensolaris.org 		    (int)portc->buf_size, DMABUF_SIZE);
91810913Sgdamore@opensolaris.org 		portc->buf_size = DMABUF_SIZE;
91910913Sgdamore@opensolaris.org 	}
92010913Sgdamore@opensolaris.org 
92110913Sgdamore@opensolaris.org 	/* Alloc buffers */
92210913Sgdamore@opensolaris.org 	if (ddi_dma_alloc_handle(devc->dip, &dma_attr_buf, DDI_DMA_SLEEP, NULL,
92310913Sgdamore@opensolaris.org 	    &portc->buf_dmah) != DDI_SUCCESS) {
92410913Sgdamore@opensolaris.org 		audio_dev_warn(adev, "failed to allocate BUF handle");
92510913Sgdamore@opensolaris.org 		return (DDI_FAILURE);
92610913Sgdamore@opensolaris.org 	}
92710913Sgdamore@opensolaris.org 
92810913Sgdamore@opensolaris.org 	if (ddi_dma_mem_alloc(portc->buf_dmah, portc->buf_size,
92910913Sgdamore@opensolaris.org 	    &dev_attr, DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL,
93010913Sgdamore@opensolaris.org 	    &portc->buf_kaddr, &len, &portc->buf_acch) != DDI_SUCCESS) {
93110913Sgdamore@opensolaris.org 		audio_dev_warn(adev, "failed to allocate BUF memory");
93210913Sgdamore@opensolaris.org 		return (DDI_FAILURE);
93310913Sgdamore@opensolaris.org 	}
93410913Sgdamore@opensolaris.org 
93510913Sgdamore@opensolaris.org 	if (ddi_dma_addr_bind_handle(portc->buf_dmah, NULL, portc->buf_kaddr,
93610913Sgdamore@opensolaris.org 	    len, DDI_DMA_CONSISTENT | dir, DDI_DMA_SLEEP,
93710913Sgdamore@opensolaris.org 	    NULL, &cookie, &count) != DDI_SUCCESS) {
93810913Sgdamore@opensolaris.org 		audio_dev_warn(adev, "failed binding BUF DMA handle");
93910913Sgdamore@opensolaris.org 		return (DDI_FAILURE);
94010913Sgdamore@opensolaris.org 	}
94110913Sgdamore@opensolaris.org 	portc->buf_paddr = cookie.dmac_address;
94210913Sgdamore@opensolaris.org 
94310913Sgdamore@opensolaris.org 	if ((devc->feature_mask & SB_LIVE) &&
94410913Sgdamore@opensolaris.org 	    (portc->buf_paddr & 0x80000000)) {
94510913Sgdamore@opensolaris.org 		audio_dev_warn(adev, "Got DMA buffer beyond 2G limit.");
94610913Sgdamore@opensolaris.org 		return (DDI_FAILURE);
94710913Sgdamore@opensolaris.org 	}
94810913Sgdamore@opensolaris.org 
94910913Sgdamore@opensolaris.org 	if (num == EMU10K_PLAY) {	/* Output device */
95010913Sgdamore@opensolaris.org 		n = portc->memptr / 4096;
95110913Sgdamore@opensolaris.org 		/*
95210913Sgdamore@opensolaris.org 		 * Fill the page table
95310913Sgdamore@opensolaris.org 		 */
95410913Sgdamore@opensolaris.org 		for (i = 0; i < portc->buf_size / 4096; i++) {
95510913Sgdamore@opensolaris.org 			FILL_PAGE_MAP_ENTRY(n + i,
95610913Sgdamore@opensolaris.org 			    portc->buf_paddr + i * 4096);
95710913Sgdamore@opensolaris.org 		}
95810913Sgdamore@opensolaris.org 
95910913Sgdamore@opensolaris.org 		(void) ddi_dma_sync(devc->pt_dmah, 0, 0, DDI_DMA_SYNC_FORDEV);
96010913Sgdamore@opensolaris.org 	}
96110913Sgdamore@opensolaris.org 
96210913Sgdamore@opensolaris.org 	portc->engine = audio_engine_alloc(&emu10k_engine_ops, caps);
96310913Sgdamore@opensolaris.org 	if (portc->engine == NULL) {
96410913Sgdamore@opensolaris.org 		audio_dev_warn(adev, "audio_engine_alloc failed");
96510913Sgdamore@opensolaris.org 		return (DDI_FAILURE);
96610913Sgdamore@opensolaris.org 	}
96710913Sgdamore@opensolaris.org 
96810913Sgdamore@opensolaris.org 	audio_engine_set_private(portc->engine, portc);
96910913Sgdamore@opensolaris.org 	audio_dev_add_engine(adev, portc->engine);
97010913Sgdamore@opensolaris.org 
97110913Sgdamore@opensolaris.org 	return (DDI_SUCCESS);
97210913Sgdamore@opensolaris.org }
97310913Sgdamore@opensolaris.org 
97410913Sgdamore@opensolaris.org void
emu10k_destroy(emu10k_devc_t * devc)97510913Sgdamore@opensolaris.org emu10k_destroy(emu10k_devc_t *devc)
97610913Sgdamore@opensolaris.org {
977*11936Sgdamore@opensolaris.org 	mutex_destroy(&devc->mutex);
97810913Sgdamore@opensolaris.org 
97910913Sgdamore@opensolaris.org 	if (devc->silence_paddr) {
98010913Sgdamore@opensolaris.org 		(void) ddi_dma_unbind_handle(devc->silence_dmah);
98110913Sgdamore@opensolaris.org 	}
98210913Sgdamore@opensolaris.org 	if (devc->silence_acch) {
98310913Sgdamore@opensolaris.org 		ddi_dma_mem_free(&devc->silence_acch);
98410913Sgdamore@opensolaris.org 	}
98510913Sgdamore@opensolaris.org 	if (devc->silence_dmah) {
98610913Sgdamore@opensolaris.org 		ddi_dma_free_handle(&devc->silence_dmah);
98710913Sgdamore@opensolaris.org 	}
98810913Sgdamore@opensolaris.org 
98910913Sgdamore@opensolaris.org 	if (devc->pt_paddr) {
99010913Sgdamore@opensolaris.org 		(void) ddi_dma_unbind_handle(devc->pt_dmah);
99110913Sgdamore@opensolaris.org 	}
99210913Sgdamore@opensolaris.org 	if (devc->pt_acch) {
99310913Sgdamore@opensolaris.org 		ddi_dma_mem_free(&devc->pt_acch);
99410913Sgdamore@opensolaris.org 	}
99510913Sgdamore@opensolaris.org 	if (devc->pt_dmah) {
99610913Sgdamore@opensolaris.org 		ddi_dma_free_handle(&devc->pt_dmah);
99710913Sgdamore@opensolaris.org 	}
99810913Sgdamore@opensolaris.org 
99910913Sgdamore@opensolaris.org 
100010913Sgdamore@opensolaris.org 	for (int i = 0; i < CTL_MAX; i++) {
100110913Sgdamore@opensolaris.org 		emu10k_ctrl_t *ec = &devc->ctrls[i];
100210913Sgdamore@opensolaris.org 		if (ec->ctrl != NULL) {
100310913Sgdamore@opensolaris.org 			audio_dev_del_control(ec->ctrl);
100410913Sgdamore@opensolaris.org 			ec->ctrl = NULL;
100510913Sgdamore@opensolaris.org 		}
100610913Sgdamore@opensolaris.org 	}
100710913Sgdamore@opensolaris.org 
100810913Sgdamore@opensolaris.org 	for (int i = 0; i < EMU10K_NUM_PORTC; i++) {
100910913Sgdamore@opensolaris.org 		emu10k_portc_t *portc = devc->portc[i];
101010913Sgdamore@opensolaris.org 		if (!portc)
101110913Sgdamore@opensolaris.org 			continue;
101210913Sgdamore@opensolaris.org 		if (portc->engine) {
101310913Sgdamore@opensolaris.org 			audio_dev_remove_engine(devc->adev, portc->engine);
101410913Sgdamore@opensolaris.org 			audio_engine_free(portc->engine);
101510913Sgdamore@opensolaris.org 		}
101610913Sgdamore@opensolaris.org 		if (portc->buf_paddr) {
101710913Sgdamore@opensolaris.org 			(void) ddi_dma_unbind_handle(portc->buf_dmah);
101810913Sgdamore@opensolaris.org 		}
101910913Sgdamore@opensolaris.org 		if (portc->buf_acch) {
102010913Sgdamore@opensolaris.org 			ddi_dma_mem_free(&portc->buf_acch);
102110913Sgdamore@opensolaris.org 		}
102210913Sgdamore@opensolaris.org 		if (portc->buf_dmah) {
102310913Sgdamore@opensolaris.org 			ddi_dma_free_handle(&portc->buf_dmah);
102410913Sgdamore@opensolaris.org 		}
102510913Sgdamore@opensolaris.org 		kmem_free(portc, sizeof (*portc));
102610913Sgdamore@opensolaris.org 	}
102710913Sgdamore@opensolaris.org 
102810913Sgdamore@opensolaris.org 	if (devc->ac97 != NULL) {
102910913Sgdamore@opensolaris.org 		ac97_free(devc->ac97);
103010913Sgdamore@opensolaris.org 	}
103110913Sgdamore@opensolaris.org 	if (devc->adev != NULL) {
103210913Sgdamore@opensolaris.org 		audio_dev_free(devc->adev);
103310913Sgdamore@opensolaris.org 	}
103410913Sgdamore@opensolaris.org 	if (devc->regsh != NULL) {
103510913Sgdamore@opensolaris.org 		ddi_regs_map_free(&devc->regsh);
103610913Sgdamore@opensolaris.org 	}
103710913Sgdamore@opensolaris.org 	if (devc->pcih != NULL) {
103810913Sgdamore@opensolaris.org 		pci_config_teardown(&devc->pcih);
103910913Sgdamore@opensolaris.org 	}
104010913Sgdamore@opensolaris.org 
104110913Sgdamore@opensolaris.org 	kmem_free(devc, sizeof (*devc));
104210913Sgdamore@opensolaris.org }
104310913Sgdamore@opensolaris.org 
104410913Sgdamore@opensolaris.org static void
emu10k_init_voice(emu10k_devc_t * devc,int voice)104510913Sgdamore@opensolaris.org emu10k_init_voice(emu10k_devc_t *devc, int voice)
104610913Sgdamore@opensolaris.org {
104710913Sgdamore@opensolaris.org 	emu10k_set_loop_stop(devc, voice, 1);
104810913Sgdamore@opensolaris.org 
104910913Sgdamore@opensolaris.org 	emu10k_write_reg(devc, VEDS, voice, 0x0);
105010913Sgdamore@opensolaris.org 	emu10k_write_reg(devc, IP, voice, 0x0);
105110913Sgdamore@opensolaris.org 	emu10k_write_reg(devc, VTFT, voice, 0xffff);
105210913Sgdamore@opensolaris.org 	emu10k_write_reg(devc, CVCF, voice, 0xffff);
105310913Sgdamore@opensolaris.org 	emu10k_write_reg(devc, PTAB, voice, 0x0);
105410913Sgdamore@opensolaris.org 	emu10k_write_reg(devc, CPF, voice, 0x0);
105510913Sgdamore@opensolaris.org 	emu10k_write_reg(devc, CCR, voice, 0x0);
105610913Sgdamore@opensolaris.org 	emu10k_write_reg(devc, SCSA, voice, 0x0);
105710913Sgdamore@opensolaris.org 	emu10k_write_reg(devc, SDL, voice, 0x10);
105810913Sgdamore@opensolaris.org 	emu10k_write_reg(devc, QKBCA, voice, 0x0);
105910913Sgdamore@opensolaris.org 	emu10k_write_reg(devc, Z1, voice, 0x0);
106010913Sgdamore@opensolaris.org 	emu10k_write_reg(devc, Z2, voice, 0x0);
106110913Sgdamore@opensolaris.org 
106210913Sgdamore@opensolaris.org 	if (devc->feature_mask & (SB_AUDIGY|SB_AUDIGY2|SB_AUDIGY2VAL))
106310913Sgdamore@opensolaris.org 		emu10k_write_reg(devc, SRDA, voice, 0x03020100);
106410913Sgdamore@opensolaris.org 	else
106510913Sgdamore@opensolaris.org 		emu10k_write_reg(devc, FXRT, voice, 0x32100000);
106610913Sgdamore@opensolaris.org 
106710913Sgdamore@opensolaris.org 	emu10k_write_reg(devc, MEHA, voice, 0x0);
106810913Sgdamore@opensolaris.org 	emu10k_write_reg(devc, MEDS, voice, 0x0);
106910913Sgdamore@opensolaris.org 	emu10k_write_reg(devc, IFA, voice, 0xffff);
107010913Sgdamore@opensolaris.org 	emu10k_write_reg(devc, PEFE, voice, 0x0);
107110913Sgdamore@opensolaris.org 	emu10k_write_reg(devc, VFM, voice, 0x0);
107210913Sgdamore@opensolaris.org 	emu10k_write_reg(devc, TMFQ, voice, 24);
107310913Sgdamore@opensolaris.org 	emu10k_write_reg(devc, VVFQ, voice, 24);
107410913Sgdamore@opensolaris.org 	emu10k_write_reg(devc, TMPE, voice, 0x0);
107510913Sgdamore@opensolaris.org 	emu10k_write_reg(devc, VLV, voice, 0x0);
107610913Sgdamore@opensolaris.org 	emu10k_write_reg(devc, MLV, voice, 0x0);
107710913Sgdamore@opensolaris.org 	emu10k_write_reg(devc, VEHA, voice, 0x0);
107810913Sgdamore@opensolaris.org 	emu10k_write_reg(devc, VEV, voice, 0x0);
107910913Sgdamore@opensolaris.org 	emu10k_write_reg(devc, MEV, voice, 0x0);
108010913Sgdamore@opensolaris.org 
108110913Sgdamore@opensolaris.org 	if (devc->feature_mask & (SB_AUDIGY|SB_AUDIGY2|SB_AUDIGY2VAL)) {
108210913Sgdamore@opensolaris.org 		emu10k_write_reg(devc, CSBA, voice, 0x0);
108310913Sgdamore@opensolaris.org 		emu10k_write_reg(devc, CSDC, voice, 0x0);
108410913Sgdamore@opensolaris.org 		emu10k_write_reg(devc, CSFE, voice, 0x0);
108510913Sgdamore@opensolaris.org 		emu10k_write_reg(devc, CSHG, voice, 0x0);
108610913Sgdamore@opensolaris.org 		emu10k_write_reg(devc, SRHE, voice, 0x3f3f3f3f);
108710913Sgdamore@opensolaris.org 	}
108810913Sgdamore@opensolaris.org }
108910913Sgdamore@opensolaris.org 
109010913Sgdamore@opensolaris.org int
emu10k_hwinit(emu10k_devc_t * devc)109110913Sgdamore@opensolaris.org emu10k_hwinit(emu10k_devc_t *devc)
109210913Sgdamore@opensolaris.org {
109310913Sgdamore@opensolaris.org 
109410913Sgdamore@opensolaris.org 	unsigned int tmp, i;
109510913Sgdamore@opensolaris.org 	unsigned int reg;
109610913Sgdamore@opensolaris.org 
109710913Sgdamore@opensolaris.org 	ASSERT(mutex_owned(&devc->mutex));
109810913Sgdamore@opensolaris.org 
109910913Sgdamore@opensolaris.org 	emu10k_write_reg(devc, AC97SLOT, 0, AC97SLOT_CENTER | AC97SLOT_LFE);
110010913Sgdamore@opensolaris.org 
110110913Sgdamore@opensolaris.org 	OUTL(devc, 0x00000000, devc->regs + 0x0c);	/* Intr disable */
110210913Sgdamore@opensolaris.org 	OUTL(devc, HCFG_LOCKSOUNDCACHE | HCFG_LOCKTANKCACHE_MASK |
110310913Sgdamore@opensolaris.org 	    HCFG_MUTEBUTTONENABLE,
110410913Sgdamore@opensolaris.org 	    devc->regs + HCFG);
110510913Sgdamore@opensolaris.org 
110610913Sgdamore@opensolaris.org 	emu10k_write_reg(devc, MBS, 0, 0x0);
110710913Sgdamore@opensolaris.org 	emu10k_write_reg(devc, MBA, 0, 0x0);
110810913Sgdamore@opensolaris.org 	emu10k_write_reg(devc, FXBS, 0, 0x0);
110910913Sgdamore@opensolaris.org 	emu10k_write_reg(devc, FXBA, 0, 0x0);
111010913Sgdamore@opensolaris.org 	emu10k_write_reg(devc, ADCBS, 0, 0x0);
111110913Sgdamore@opensolaris.org 	emu10k_write_reg(devc, ADCBA, 0, 0x0);
111210913Sgdamore@opensolaris.org 
1113*11936Sgdamore@opensolaris.org 	/* Ensure all interrupts are disabled */
111410913Sgdamore@opensolaris.org 	OUTL(devc, 0, devc->regs + IE);
111510913Sgdamore@opensolaris.org 	emu10k_write_reg(devc, CLIEL, 0, 0x0);
111610913Sgdamore@opensolaris.org 	emu10k_write_reg(devc, CLIEH, 0, 0x0);
111710913Sgdamore@opensolaris.org 	if (!(devc->feature_mask & SB_LIVE)) {
111810913Sgdamore@opensolaris.org 		emu10k_write_reg(devc, HLIEL, 0, 0x0);
111910913Sgdamore@opensolaris.org 		emu10k_write_reg(devc, HLIEH, 0, 0x0);
112010913Sgdamore@opensolaris.org 	}
112110913Sgdamore@opensolaris.org 	emu10k_write_reg(devc, CLIPL, 0, 0xffffffff);
112210913Sgdamore@opensolaris.org 	emu10k_write_reg(devc, CLIPH, 0, 0xffffffff);
112310913Sgdamore@opensolaris.org 	emu10k_write_reg(devc, SOLL, 0, 0xffffffff);
112410913Sgdamore@opensolaris.org 	emu10k_write_reg(devc, SOLH, 0, 0xffffffff);
112510913Sgdamore@opensolaris.org 
112610913Sgdamore@opensolaris.org 
112710913Sgdamore@opensolaris.org 	if (devc->feature_mask & (SB_AUDIGY|SB_AUDIGY2|SB_AUDIGY2VAL)) {
112810913Sgdamore@opensolaris.org 		emu10k_write_reg(devc, SOC, 0, 0xf00);	/* ?? */
112910913Sgdamore@opensolaris.org 		emu10k_write_reg(devc, AC97SLOT, 0, 0x3);	/* ?? */
113010913Sgdamore@opensolaris.org 	}
113110913Sgdamore@opensolaris.org 
113210913Sgdamore@opensolaris.org 	for (i = 0; i < 64; i++)
113310913Sgdamore@opensolaris.org 		emu10k_init_voice(devc, i);
113410913Sgdamore@opensolaris.org 
113510913Sgdamore@opensolaris.org 	emu10k_write_reg(devc, SCS0, 0, 0x2109204);
113610913Sgdamore@opensolaris.org 	emu10k_write_reg(devc, SCS1, 0, 0x2109204);
113710913Sgdamore@opensolaris.org 	emu10k_write_reg(devc, SCS2, 0, 0x2109204);
113810913Sgdamore@opensolaris.org 
113910913Sgdamore@opensolaris.org 	emu10k_write_reg(devc, PTBA, 0, devc->pt_paddr);
114010913Sgdamore@opensolaris.org 	tmp = emu10k_read_reg(devc, PTBA, 0);
114110913Sgdamore@opensolaris.org 
114210913Sgdamore@opensolaris.org 	emu10k_write_reg(devc, TCBA, 0, 0x0);
114310913Sgdamore@opensolaris.org 	emu10k_write_reg(devc, TCBS, 0, 0x4);
114410913Sgdamore@opensolaris.org 
114510913Sgdamore@opensolaris.org 	reg = 0;
114610913Sgdamore@opensolaris.org 	if (devc->feature_mask & SB_71) {
114710913Sgdamore@opensolaris.org 		reg = AC97SLOT_CENTER | AC97SLOT_LFE | AC97SLOT_REAR_LEFT |
114810913Sgdamore@opensolaris.org 		    AC97SLOT_REAR_RIGHT;
114910913Sgdamore@opensolaris.org 	} else if (devc->feature_mask & SB_51) {
115010913Sgdamore@opensolaris.org 		reg = AC97SLOT_CENTER | AC97SLOT_LFE;
115110913Sgdamore@opensolaris.org 	}
115210913Sgdamore@opensolaris.org 	if (devc->feature_mask & (SB_AUDIGY|SB_AUDIGY2|SB_AUDIGY2VAL))
115310913Sgdamore@opensolaris.org 		reg |= 0x40;
115410913Sgdamore@opensolaris.org 	emu10k_write_reg(devc, AC97SLOT, 0, reg);
115510913Sgdamore@opensolaris.org 
115610913Sgdamore@opensolaris.org 	if (devc->feature_mask & SB_AUDIGY2) {
115710913Sgdamore@opensolaris.org 		/* Enable analog outputs on Audigy2 */
115810913Sgdamore@opensolaris.org 		int tmp;
115910913Sgdamore@opensolaris.org 
116010913Sgdamore@opensolaris.org 		/* Setup SRCMulti_I2S SamplingRate */
116110913Sgdamore@opensolaris.org 		tmp = emu10k_read_reg(devc, EHC, 0);
116210913Sgdamore@opensolaris.org 		tmp &= 0xfffff1ff;
116310913Sgdamore@opensolaris.org 		tmp |= (0x2 << 9);
116410913Sgdamore@opensolaris.org 		emu10k_write_reg(devc, EHC, 0, tmp);
116510913Sgdamore@opensolaris.org 		/* emu10k_write_reg (devc, SOC, 0, 0x00000000); */
116610913Sgdamore@opensolaris.org 
116710913Sgdamore@opensolaris.org 		/* Setup SRCSel (Enable Spdif,I2S SRCMulti) */
116810913Sgdamore@opensolaris.org 		OUTL(devc, 0x600000, devc->regs + 0x20);
116910913Sgdamore@opensolaris.org 		OUTL(devc, 0x14, devc->regs + 0x24);
117010913Sgdamore@opensolaris.org 
117110913Sgdamore@opensolaris.org 		/* Setup SRCMulti Input Audio Enable */
117210913Sgdamore@opensolaris.org 		OUTL(devc, 0x6E0000, devc->regs + 0x20);
117310913Sgdamore@opensolaris.org 
117410913Sgdamore@opensolaris.org 		OUTL(devc, 0xFF00FF00, devc->regs + 0x24);
117510913Sgdamore@opensolaris.org 
117610913Sgdamore@opensolaris.org 		/* Setup I2S ASRC Enable  (HC register) */
117710913Sgdamore@opensolaris.org 		tmp = INL(devc, devc->regs + HCFG);
117810913Sgdamore@opensolaris.org 		tmp |= 0x00000070;
117910913Sgdamore@opensolaris.org 		OUTL(devc, tmp, devc->regs + HCFG);
118010913Sgdamore@opensolaris.org 
118110913Sgdamore@opensolaris.org 		/*
118210913Sgdamore@opensolaris.org 		 * Unmute Analog now.  Set GPO6 to 1 for Apollo.
118310913Sgdamore@opensolaris.org 		 * This has to be done after init ALice3 I2SOut beyond 48KHz.
118410913Sgdamore@opensolaris.org 		 * So, sequence is important
118510913Sgdamore@opensolaris.org 		 */
118610913Sgdamore@opensolaris.org 		tmp = INL(devc, devc->regs + 0x18);
118710913Sgdamore@opensolaris.org 		tmp |= 0x0040;
118810913Sgdamore@opensolaris.org 
118910913Sgdamore@opensolaris.org 		OUTL(devc, tmp, devc->regs + 0x18);
119010913Sgdamore@opensolaris.org 	}
119110913Sgdamore@opensolaris.org 
119210913Sgdamore@opensolaris.org 	if (devc->feature_mask & SB_AUDIGY2VAL) {
119310913Sgdamore@opensolaris.org 		/* Enable analog outputs on Audigy2 */
119410913Sgdamore@opensolaris.org 		int tmp;
119510913Sgdamore@opensolaris.org 
119610913Sgdamore@opensolaris.org 		/* Setup SRCMulti_I2S SamplingRate */
119710913Sgdamore@opensolaris.org 		tmp = emu10k_read_reg(devc, EHC, 0);
119810913Sgdamore@opensolaris.org 		tmp &= 0xfffff1ff;
119910913Sgdamore@opensolaris.org 		tmp |= (0x2 << 9);
120010913Sgdamore@opensolaris.org 		emu10k_write_reg(devc, EHC, 0, tmp);
120110913Sgdamore@opensolaris.org 
120210913Sgdamore@opensolaris.org 		/* Setup SRCSel (Enable Spdif,I2S SRCMulti) */
120310913Sgdamore@opensolaris.org 		OUTL(devc, 0x600000, devc->regs + 0x20);
120410913Sgdamore@opensolaris.org 		OUTL(devc, 0x14, devc->regs + 0x24);
120510913Sgdamore@opensolaris.org 
120610913Sgdamore@opensolaris.org 		/* Setup SRCMulti Input Audio Enable */
120710913Sgdamore@opensolaris.org 		OUTL(devc, 0x7B0000, devc->regs + 0x20);
120810913Sgdamore@opensolaris.org 		OUTL(devc, 0xFF000000, devc->regs + 0x24);
120910913Sgdamore@opensolaris.org 
121010913Sgdamore@opensolaris.org 		/* SPDIF output enable */
121110913Sgdamore@opensolaris.org 		OUTL(devc, 0x7A0000, devc->regs + 0x20);
121210913Sgdamore@opensolaris.org 		OUTL(devc, 0xFF000000, devc->regs + 0x24);
121310913Sgdamore@opensolaris.org 
121410913Sgdamore@opensolaris.org 		tmp = INL(devc, devc->regs + 0x18) & ~0x8;
121510913Sgdamore@opensolaris.org 		OUTL(devc, tmp, devc->regs + 0x18);
121610913Sgdamore@opensolaris.org 	}
121710913Sgdamore@opensolaris.org 
121810913Sgdamore@opensolaris.org 	emu10k_write_reg(devc, SOLL, 0, 0xffffffff);
121910913Sgdamore@opensolaris.org 	emu10k_write_reg(devc, SOLH, 0, 0xffffffff);
122010913Sgdamore@opensolaris.org 
122110913Sgdamore@opensolaris.org 	if (devc->feature_mask & (SB_AUDIGY|SB_AUDIGY2|SB_AUDIGY2VAL)) {
122210913Sgdamore@opensolaris.org 		unsigned int mode = 0;
122310913Sgdamore@opensolaris.org 
122410913Sgdamore@opensolaris.org 		if (devc->feature_mask & (SB_AUDIGY2|SB_AUDIGY2VAL))
122510913Sgdamore@opensolaris.org 			mode |= HCFG_AC3ENABLE_GPSPDIF | HCFG_AC3ENABLE_CDSPDIF;
122610913Sgdamore@opensolaris.org 		OUTL(devc,
122710913Sgdamore@opensolaris.org 		    HCFG_AUDIOENABLE | HCFG_AUTOMUTE |
122810913Sgdamore@opensolaris.org 		    HCFG_JOYENABLE | A_HCFG_VMUTE |
122910913Sgdamore@opensolaris.org 		    A_HCFG_AUTOMUTE | mode, devc->regs + HCFG);
123010913Sgdamore@opensolaris.org 
123110913Sgdamore@opensolaris.org 		OUTL(devc, INL(devc, devc->regs + 0x18) |
123210913Sgdamore@opensolaris.org 		    0x0004, devc->regs + 0x18);	/* GPIO (S/PDIF enable) */
123310913Sgdamore@opensolaris.org 
123410913Sgdamore@opensolaris.org 
123510913Sgdamore@opensolaris.org 		/* enable IR port */
123610913Sgdamore@opensolaris.org 		tmp = INL(devc, devc->regs + 0x18);
123710913Sgdamore@opensolaris.org 		OUTL(devc, tmp | A_IOCFG_GPOUT2, devc->regs + 0x18);
123810913Sgdamore@opensolaris.org 		drv_usecwait(500);
123910913Sgdamore@opensolaris.org 		OUTL(devc, tmp | A_IOCFG_GPOUT1 | A_IOCFG_GPOUT2,
124010913Sgdamore@opensolaris.org 		    devc->regs + 0x18);
124110913Sgdamore@opensolaris.org 		drv_usecwait(100);
124210913Sgdamore@opensolaris.org 		OUTL(devc, tmp, devc->regs + 0x18);
124310913Sgdamore@opensolaris.org 	} else {
124410913Sgdamore@opensolaris.org 		OUTL(devc,
124510913Sgdamore@opensolaris.org 		    HCFG_AUDIOENABLE | HCFG_LOCKTANKCACHE_MASK |
124610913Sgdamore@opensolaris.org 		    HCFG_AUTOMUTE | HCFG_JOYENABLE, devc->regs + HCFG);
124710913Sgdamore@opensolaris.org 	}
124810913Sgdamore@opensolaris.org 
124910913Sgdamore@opensolaris.org 
125010913Sgdamore@opensolaris.org 	/* enable IR port */
125110913Sgdamore@opensolaris.org 	tmp = INL(devc, devc->regs + HCFG);
125210913Sgdamore@opensolaris.org 	OUTL(devc, tmp | HCFG_GPOUT2, devc->regs + HCFG);
125310913Sgdamore@opensolaris.org 	drv_usecwait(500);
125410913Sgdamore@opensolaris.org 	OUTL(devc, tmp | HCFG_GPOUT1 | HCFG_GPOUT2, devc->regs + HCFG);
125510913Sgdamore@opensolaris.org 	drv_usecwait(100);
125610913Sgdamore@opensolaris.org 	OUTL(devc, tmp, devc->regs + HCFG);
125710913Sgdamore@opensolaris.org 
125810913Sgdamore@opensolaris.org 
125910913Sgdamore@opensolaris.org 	/*
126010913Sgdamore@opensolaris.org 	 * Start by configuring for analog mode.
126110913Sgdamore@opensolaris.org 	 */
126210913Sgdamore@opensolaris.org 	if (devc->feature_mask & (SB_AUDIGY|SB_AUDIGY2|SB_AUDIGY2VAL)) {
126310913Sgdamore@opensolaris.org 		reg = INL(devc, devc->regs + 0x18) & ~A_IOCFG_GPOUT0;
126410913Sgdamore@opensolaris.org 		reg |= ((devc->feature_mask & SB_INVSP) ? 0x4 : 0);
126510913Sgdamore@opensolaris.org 		OUTL(devc, reg, devc->regs + 0x18);
126610913Sgdamore@opensolaris.org 	}
126710913Sgdamore@opensolaris.org 	if (devc->feature_mask & SB_LIVE) {	/* SBLIVE */
126810913Sgdamore@opensolaris.org 		reg = INL(devc, devc->regs + HCFG) & ~HCFG_GPOUT0;
126910913Sgdamore@opensolaris.org 		reg |= ((devc->feature_mask & SB_INVSP) ? HCFG_GPOUT0 : 0);
127010913Sgdamore@opensolaris.org 		OUTL(devc, reg, devc->regs + HCFG);
127110913Sgdamore@opensolaris.org 	}
127210913Sgdamore@opensolaris.org 
127310913Sgdamore@opensolaris.org 	if (devc->feature_mask & SB_AUDIGY2VAL) {
127410913Sgdamore@opensolaris.org 		OUTL(devc, INL(devc, devc->regs + 0x18) | 0x0060,
127510913Sgdamore@opensolaris.org 		    devc->regs + 0x18);
127610913Sgdamore@opensolaris.org 	} else if (devc->feature_mask & SB_AUDIGY2) {
127710913Sgdamore@opensolaris.org 		OUTL(devc, INL(devc, devc->regs + 0x18) | 0x0040,
127810913Sgdamore@opensolaris.org 		    devc->regs + 0x18);
127910913Sgdamore@opensolaris.org 	} else if (devc->feature_mask & SB_AUDIGY) {
128010913Sgdamore@opensolaris.org 		OUTL(devc, INL(devc, devc->regs + 0x18) | 0x0080,
128110913Sgdamore@opensolaris.org 		    devc->regs + 0x18);
128210913Sgdamore@opensolaris.org 	}
128310913Sgdamore@opensolaris.org 
128410913Sgdamore@opensolaris.org 	emu10k_init_effects(devc);
128510913Sgdamore@opensolaris.org 
128610913Sgdamore@opensolaris.org 	return (DDI_SUCCESS);
128710913Sgdamore@opensolaris.org }
128810913Sgdamore@opensolaris.org 
128910913Sgdamore@opensolaris.org static const int db2lin_101[101] = {
129010913Sgdamore@opensolaris.org 	0x00000000,
129110913Sgdamore@opensolaris.org 	0x0024B53A, 0x002750CA, 0x002A1BC6, 0x002D198D, 0x00304DBA, 0x0033BC2A,
129210913Sgdamore@opensolaris.org 	0x00376901, 0x003B58AF, 0x003F8FF1, 0x004413DF, 0x0048E9EA, 0x004E17E9,
129310913Sgdamore@opensolaris.org 	0x0053A419, 0x0059952C, 0x005FF24E, 0x0066C32A, 0x006E0FFB, 0x0075E18D,
129410913Sgdamore@opensolaris.org 	0x007E414F, 0x0087395B, 0x0090D482, 0x009B1E5B, 0x00A6234F, 0x00B1F0A7,
129510913Sgdamore@opensolaris.org 	0x00BE94A1, 0x00CC1E7C, 0x00DA9E8D, 0x00EA2650, 0x00FAC881, 0x010C9931,
129610913Sgdamore@opensolaris.org 	0x011FADDC, 0x01341D87, 0x014A00D8, 0x01617235, 0x017A8DE6, 0x01957233,
129710913Sgdamore@opensolaris.org 	0x01B23F8D, 0x01D118B1, 0x01F222D4, 0x021585D1, 0x023B6C57, 0x0264041D,
129810913Sgdamore@opensolaris.org 	0x028F7E19, 0x02BE0EBD, 0x02EFEE33, 0x032558A2, 0x035E8E7A, 0x039BD4BC,
129910913Sgdamore@opensolaris.org 	0x03DD7551, 0x0423BF61, 0x046F07B5, 0x04BFA91B, 0x051604D5, 0x0572830D,
130010913Sgdamore@opensolaris.org 	0x05D59354, 0x063FAD27, 0x06B15080, 0x072B0673, 0x07AD61CD, 0x0838FFCA,
130110913Sgdamore@opensolaris.org 	0x08CE88D3, 0x096EB147, 0x0A1A3A53, 0x0AD1F2E0, 0x0B96B889, 0x0C6978A5,
130210913Sgdamore@opensolaris.org 	0x0D4B316A, 0x0E3CF31B, 0x0F3FE155, 0x10553469, 0x117E3AD9, 0x12BC5AEA,
130310913Sgdamore@opensolaris.org 	0x14111457, 0x157E0219, 0x1704DC5E, 0x18A77A97, 0x1A67D5B6, 0x1C480A87,
130410913Sgdamore@opensolaris.org 	0x1E4A5C45, 0x2071374D, 0x22BF3412, 0x25371A37, 0x27DBE3EF, 0x2AB0C18F,
130510913Sgdamore@opensolaris.org 	0x2DB91D6F, 0x30F89FFD, 0x34733433, 0x382D0C46, 0x3C2AA6BD, 0x4070D3D9,
130610913Sgdamore@opensolaris.org 	0x4504BB66, 0x49EBE2F1, 0x4F2C346F, 0x54CC0565, 0x5AD21E86, 0x6145C3E7,
130710913Sgdamore@opensolaris.org 	0x682EBDBD, 0x6F9561C4, 0x77829D4D,
130810913Sgdamore@opensolaris.org 	0x7fffffff
130910913Sgdamore@opensolaris.org };
131010913Sgdamore@opensolaris.org 
131110913Sgdamore@opensolaris.org static int
emu10k_convert_fixpoint(int val)131210913Sgdamore@opensolaris.org emu10k_convert_fixpoint(int val)
131310913Sgdamore@opensolaris.org {
131410913Sgdamore@opensolaris.org 	if (val < 0)
131510913Sgdamore@opensolaris.org 		val = 0;
131610913Sgdamore@opensolaris.org 	if (val > 100)
131710913Sgdamore@opensolaris.org 		val = 100;
131810913Sgdamore@opensolaris.org 	return (db2lin_101[val]);
131910913Sgdamore@opensolaris.org }
132010913Sgdamore@opensolaris.org 
132110913Sgdamore@opensolaris.org static void
emu10k_write_gpr(emu10k_devc_t * devc,int gpr,uint32_t value)132210913Sgdamore@opensolaris.org emu10k_write_gpr(emu10k_devc_t *devc, int gpr, uint32_t value)
132310913Sgdamore@opensolaris.org {
132410913Sgdamore@opensolaris.org 	ASSERT(gpr < MAX_GPR);
132510913Sgdamore@opensolaris.org 	devc->gpr_shadow[gpr].valid = B_TRUE;
132610913Sgdamore@opensolaris.org 	devc->gpr_shadow[gpr].value = value;
1327*11936Sgdamore@opensolaris.org 	emu10k_write_reg(devc, gpr + GPR0, 0, value);
132810913Sgdamore@opensolaris.org }
132910913Sgdamore@opensolaris.org 
133010913Sgdamore@opensolaris.org static int
emu10k_set_stereo(void * arg,uint64_t val)133110913Sgdamore@opensolaris.org emu10k_set_stereo(void *arg, uint64_t val)
133210913Sgdamore@opensolaris.org {
133310913Sgdamore@opensolaris.org 	emu10k_ctrl_t *ec = arg;
133410913Sgdamore@opensolaris.org 	emu10k_devc_t *devc = ec->devc;
133510913Sgdamore@opensolaris.org 	uint32_t left, right;
133610913Sgdamore@opensolaris.org 
133710913Sgdamore@opensolaris.org 	left = (val >> 8) & 0xff;
133810913Sgdamore@opensolaris.org 	right = val & 0xff;
133910913Sgdamore@opensolaris.org 	if ((left > 100) || (right > 100) || (val & ~(0xffff)))
134010913Sgdamore@opensolaris.org 		return (EINVAL);
134110913Sgdamore@opensolaris.org 
134210913Sgdamore@opensolaris.org 	left = emu10k_convert_fixpoint(left);
134310913Sgdamore@opensolaris.org 	right = emu10k_convert_fixpoint(right);
134410913Sgdamore@opensolaris.org 
134510913Sgdamore@opensolaris.org 	mutex_enter(&devc->mutex);
134610913Sgdamore@opensolaris.org 	ec->val = val;
134710913Sgdamore@opensolaris.org 
134810913Sgdamore@opensolaris.org 	emu10k_write_gpr(devc, ec->gpr_num, left);
134910913Sgdamore@opensolaris.org 	emu10k_write_gpr(devc, ec->gpr_num + 1, right);
135010913Sgdamore@opensolaris.org 
135110913Sgdamore@opensolaris.org 	mutex_exit(&devc->mutex);
135210913Sgdamore@opensolaris.org 	return (0);
135310913Sgdamore@opensolaris.org }
135410913Sgdamore@opensolaris.org 
135510913Sgdamore@opensolaris.org static int
emu10k_set_mono(void * arg,uint64_t val)135610913Sgdamore@opensolaris.org emu10k_set_mono(void *arg, uint64_t val)
135710913Sgdamore@opensolaris.org {
135810913Sgdamore@opensolaris.org 	emu10k_ctrl_t *ec = arg;
135910913Sgdamore@opensolaris.org 	emu10k_devc_t *devc = ec->devc;
136010913Sgdamore@opensolaris.org 	uint32_t v;
136110913Sgdamore@opensolaris.org 
136210913Sgdamore@opensolaris.org 	if (val > 100)
136310913Sgdamore@opensolaris.org 		return (EINVAL);
136410913Sgdamore@opensolaris.org 
136510913Sgdamore@opensolaris.org 	v = emu10k_convert_fixpoint(val & 0xff);
136610913Sgdamore@opensolaris.org 
136710913Sgdamore@opensolaris.org 	mutex_enter(&devc->mutex);
136810913Sgdamore@opensolaris.org 	ec->val = val;
136910913Sgdamore@opensolaris.org 	emu10k_write_gpr(devc, ec->gpr_num, v);
137010913Sgdamore@opensolaris.org 	mutex_exit(&devc->mutex);
137110913Sgdamore@opensolaris.org 	return (0);
137210913Sgdamore@opensolaris.org }
137310913Sgdamore@opensolaris.org 
137410913Sgdamore@opensolaris.org static int
emu10k_get_control(void * arg,uint64_t * val)137510913Sgdamore@opensolaris.org emu10k_get_control(void *arg, uint64_t *val)
137610913Sgdamore@opensolaris.org {
137710913Sgdamore@opensolaris.org 	emu10k_ctrl_t *ec = arg;
137810913Sgdamore@opensolaris.org 	emu10k_devc_t *devc = ec->devc;
137910913Sgdamore@opensolaris.org 
138010913Sgdamore@opensolaris.org 	mutex_enter(&devc->mutex);
138110913Sgdamore@opensolaris.org 	*val = ec->val;
138210913Sgdamore@opensolaris.org 	mutex_exit(&devc->mutex);
138310913Sgdamore@opensolaris.org 	return (0);
138410913Sgdamore@opensolaris.org }
138510913Sgdamore@opensolaris.org 
138610913Sgdamore@opensolaris.org #define	PLAYCTL	(AUDIO_CTRL_FLAG_RW | AUDIO_CTRL_FLAG_PLAY)
138710913Sgdamore@opensolaris.org #define	RECCTL	(AUDIO_CTRL_FLAG_RW | AUDIO_CTRL_FLAG_REC)
138810913Sgdamore@opensolaris.org #define	MONCTL	(AUDIO_CTRL_FLAG_RW | AUDIO_CTRL_FLAG_MONITOR)
138910913Sgdamore@opensolaris.org #define	MAINVOL	(PLAYCTL | AUDIO_CTRL_FLAG_MAINVOL)
139010913Sgdamore@opensolaris.org #define	PCMVOL	(PLAYCTL | AUDIO_CTRL_FLAG_PCMVOL)
139110913Sgdamore@opensolaris.org #define	RECVOL	(RECCTL | AUDIO_CTRL_FLAG_RECVOL)
139210913Sgdamore@opensolaris.org #define	MONVOL	(MONCTL | AUDIO_CTRL_FLAG_MONVOL)
139310913Sgdamore@opensolaris.org 
139410913Sgdamore@opensolaris.org static int
emu10k_get_ac97src(void * arg,uint64_t * valp)139510913Sgdamore@opensolaris.org emu10k_get_ac97src(void *arg, uint64_t *valp)
139610913Sgdamore@opensolaris.org {
139710913Sgdamore@opensolaris.org 	ac97_ctrl_t *ctrl = arg;
139810913Sgdamore@opensolaris.org 
139910913Sgdamore@opensolaris.org 	return (ac97_control_get(ctrl, valp));
140010913Sgdamore@opensolaris.org }
140110913Sgdamore@opensolaris.org 
140210913Sgdamore@opensolaris.org static int
emu10k_set_ac97src(void * arg,uint64_t value)140310913Sgdamore@opensolaris.org emu10k_set_ac97src(void *arg, uint64_t value)
140410913Sgdamore@opensolaris.org {
140510913Sgdamore@opensolaris.org 	ac97_ctrl_t	*ctrl = arg;
140610913Sgdamore@opensolaris.org 
140710913Sgdamore@opensolaris.org 	return (ac97_control_set(ctrl, value));
140810913Sgdamore@opensolaris.org }
140910913Sgdamore@opensolaris.org 
141010913Sgdamore@opensolaris.org static int
emu10k_set_jack3(void * arg,uint64_t value)141110913Sgdamore@opensolaris.org emu10k_set_jack3(void *arg, uint64_t value)
141210913Sgdamore@opensolaris.org {
141310913Sgdamore@opensolaris.org 	emu10k_ctrl_t	*ec = arg;
141410913Sgdamore@opensolaris.org 	emu10k_devc_t	*devc = ec->devc;
141510913Sgdamore@opensolaris.org 	uint32_t	set_val;
141610913Sgdamore@opensolaris.org 	uint32_t	val;
141710913Sgdamore@opensolaris.org 
141810913Sgdamore@opensolaris.org 	set_val = ddi_ffs(value & 0xffffffffU);
141910913Sgdamore@opensolaris.org 	set_val--;
142010913Sgdamore@opensolaris.org 	mutex_enter(&devc->mutex);
142110913Sgdamore@opensolaris.org 	switch (set_val) {
142210913Sgdamore@opensolaris.org 	case 0:
142310913Sgdamore@opensolaris.org 	case 1:
142410913Sgdamore@opensolaris.org 		break;
142510913Sgdamore@opensolaris.org 	default:
142610913Sgdamore@opensolaris.org 		mutex_exit(&devc->mutex);
142710913Sgdamore@opensolaris.org 		return (EINVAL);
142810913Sgdamore@opensolaris.org 	}
142910913Sgdamore@opensolaris.org 	ec->val = value;
143010913Sgdamore@opensolaris.org 	/* center/lfe */
143110913Sgdamore@opensolaris.org 	if (devc->feature_mask & SB_INVSP) {
143210913Sgdamore@opensolaris.org 		set_val = !set_val;
143310913Sgdamore@opensolaris.org 	}
1434*11936Sgdamore@opensolaris.org 	if (devc->feature_mask & (SB_AUDIGY|SB_AUDIGY2|SB_AUDIGY2VAL)) {
1435*11936Sgdamore@opensolaris.org 		val = INL(devc, devc->regs + 0x18);
1436*11936Sgdamore@opensolaris.org 		val &= ~A_IOCFG_GPOUT0;
1437*11936Sgdamore@opensolaris.org 		val |= set_val ? 0x44 : 0x40;
1438*11936Sgdamore@opensolaris.org 		OUTL(devc, val, devc->regs + 0x18);
143910913Sgdamore@opensolaris.org 
1440*11936Sgdamore@opensolaris.org 	} else if (devc->feature_mask & SB_LIVE) {
1441*11936Sgdamore@opensolaris.org 		val = INL(devc, devc->regs + HCFG);
1442*11936Sgdamore@opensolaris.org 		val &= ~HCFG_GPOUT0;
1443*11936Sgdamore@opensolaris.org 		val |= set_val ? HCFG_GPOUT0 : 0;
1444*11936Sgdamore@opensolaris.org 		OUTL(devc, val, devc->regs + HCFG);
144510913Sgdamore@opensolaris.org 	}
144610913Sgdamore@opensolaris.org 	mutex_exit(&devc->mutex);
144710913Sgdamore@opensolaris.org 	return (0);
144810913Sgdamore@opensolaris.org }
144910913Sgdamore@opensolaris.org 
145010913Sgdamore@opensolaris.org static int
emu10k_set_recsrc(void * arg,uint64_t value)145110913Sgdamore@opensolaris.org emu10k_set_recsrc(void *arg, uint64_t value)
145210913Sgdamore@opensolaris.org {
145310913Sgdamore@opensolaris.org 	emu10k_ctrl_t	*ec = arg;
145410913Sgdamore@opensolaris.org 	emu10k_devc_t	*devc = ec->devc;
145510913Sgdamore@opensolaris.org 	uint32_t	set_val;
145610913Sgdamore@opensolaris.org 
145710913Sgdamore@opensolaris.org 	set_val = ddi_ffs(value & 0xffffffffU);
145810913Sgdamore@opensolaris.org 	set_val--;
145910913Sgdamore@opensolaris.org 
146010913Sgdamore@opensolaris.org 	/*
146110913Sgdamore@opensolaris.org 	 * We start assuming well set up AC'97 for stereomix recording.
146210913Sgdamore@opensolaris.org 	 */
146310913Sgdamore@opensolaris.org 	switch (set_val) {
146410913Sgdamore@opensolaris.org 	case INPUT_AC97:
146510913Sgdamore@opensolaris.org 	case INPUT_SPD1:
146610913Sgdamore@opensolaris.org 	case INPUT_SPD2:
146710913Sgdamore@opensolaris.org 	case INPUT_DIGCD:
146810913Sgdamore@opensolaris.org 	case INPUT_AUX2:
146910913Sgdamore@opensolaris.org 	case INPUT_LINE2:
147010913Sgdamore@opensolaris.org 	case INPUT_STEREOMIX:
147110913Sgdamore@opensolaris.org 		break;
147210913Sgdamore@opensolaris.org 	default:
147310913Sgdamore@opensolaris.org 		return (EINVAL);
147410913Sgdamore@opensolaris.org 	}
147510913Sgdamore@opensolaris.org 
147610913Sgdamore@opensolaris.org 	mutex_enter(&devc->mutex);
147710913Sgdamore@opensolaris.org 	ec->val = value;
147810913Sgdamore@opensolaris.org 
147910913Sgdamore@opensolaris.org 	emu10k_write_gpr(devc, GPR_REC_AC97, (set_val == INPUT_AC97));
148010913Sgdamore@opensolaris.org 	emu10k_write_gpr(devc, GPR_REC_SPDIF1, (set_val == INPUT_SPD1));
148110913Sgdamore@opensolaris.org 	emu10k_write_gpr(devc, GPR_REC_SPDIF2, (set_val == INPUT_SPD2));
148210913Sgdamore@opensolaris.org 	emu10k_write_gpr(devc, GPR_REC_DIGCD, (set_val == INPUT_DIGCD));
148310913Sgdamore@opensolaris.org 	emu10k_write_gpr(devc, GPR_REC_AUX2, (set_val == INPUT_AUX2));
148410913Sgdamore@opensolaris.org 	emu10k_write_gpr(devc, GPR_REC_LINE2, (set_val == INPUT_LINE2));
148510913Sgdamore@opensolaris.org 	emu10k_write_gpr(devc, GPR_REC_PCM, (set_val == INPUT_STEREOMIX));
148610913Sgdamore@opensolaris.org 
148710913Sgdamore@opensolaris.org 	mutex_exit(&devc->mutex);
148810913Sgdamore@opensolaris.org 
148910913Sgdamore@opensolaris.org 	return (0);
149010913Sgdamore@opensolaris.org }
149110913Sgdamore@opensolaris.org 
149210913Sgdamore@opensolaris.org static void
emu10k_create_stereo(emu10k_devc_t * devc,int ctl,int gpr,const char * id,int flags,int defval)149310913Sgdamore@opensolaris.org emu10k_create_stereo(emu10k_devc_t *devc, int ctl, int gpr,
149410913Sgdamore@opensolaris.org     const char *id, int flags, int defval)
149510913Sgdamore@opensolaris.org {
149610913Sgdamore@opensolaris.org 	emu10k_ctrl_t *ec;
149710913Sgdamore@opensolaris.org 	audio_ctrl_desc_t desc;
149810913Sgdamore@opensolaris.org 
149910913Sgdamore@opensolaris.org 	bzero(&desc, sizeof (desc));
150010913Sgdamore@opensolaris.org 
150110913Sgdamore@opensolaris.org 	ec = &devc->ctrls[ctl];
150210913Sgdamore@opensolaris.org 	ec->devc = devc;
150310913Sgdamore@opensolaris.org 	ec->gpr_num = gpr;
150410913Sgdamore@opensolaris.org 
150510913Sgdamore@opensolaris.org 	desc.acd_name = id;
150610913Sgdamore@opensolaris.org 	desc.acd_type = AUDIO_CTRL_TYPE_STEREO;
150710913Sgdamore@opensolaris.org 	desc.acd_minvalue = 0;
150810913Sgdamore@opensolaris.org 	desc.acd_maxvalue = 100;
150910913Sgdamore@opensolaris.org 	desc.acd_flags = flags;
151010913Sgdamore@opensolaris.org 
151110913Sgdamore@opensolaris.org 	ec->val = (defval << 8) | defval;
151210913Sgdamore@opensolaris.org 	ec->ctrl = audio_dev_add_control(devc->adev, &desc,
151310913Sgdamore@opensolaris.org 	    emu10k_get_control, emu10k_set_stereo, ec);
151410913Sgdamore@opensolaris.org 
151510913Sgdamore@opensolaris.org 	mutex_enter(&devc->mutex);
151610913Sgdamore@opensolaris.org 	emu10k_write_gpr(devc, gpr, emu10k_convert_fixpoint(defval));
151710913Sgdamore@opensolaris.org 	emu10k_write_gpr(devc, gpr + 1, emu10k_convert_fixpoint(defval));
151810913Sgdamore@opensolaris.org 	mutex_exit(&devc->mutex);
151910913Sgdamore@opensolaris.org }
152010913Sgdamore@opensolaris.org 
152110913Sgdamore@opensolaris.org static void
emu10k_create_mono(emu10k_devc_t * devc,int ctl,int gpr,const char * id,int flags,int defval)152210913Sgdamore@opensolaris.org emu10k_create_mono(emu10k_devc_t *devc, int ctl, int gpr,
152310913Sgdamore@opensolaris.org     const char *id, int flags, int defval)
152410913Sgdamore@opensolaris.org {
152510913Sgdamore@opensolaris.org 	emu10k_ctrl_t *ec;
152610913Sgdamore@opensolaris.org 	audio_ctrl_desc_t desc;
152710913Sgdamore@opensolaris.org 
152810913Sgdamore@opensolaris.org 	bzero(&desc, sizeof (desc));
152910913Sgdamore@opensolaris.org 
153010913Sgdamore@opensolaris.org 	ec = &devc->ctrls[ctl];
153110913Sgdamore@opensolaris.org 	ec->devc = devc;
153210913Sgdamore@opensolaris.org 	ec->gpr_num = gpr;
153310913Sgdamore@opensolaris.org 
153410913Sgdamore@opensolaris.org 	desc.acd_name = id;
153510913Sgdamore@opensolaris.org 	desc.acd_type = AUDIO_CTRL_TYPE_MONO;
153610913Sgdamore@opensolaris.org 	desc.acd_minvalue = 0;
153710913Sgdamore@opensolaris.org 	desc.acd_maxvalue = 100;
153810913Sgdamore@opensolaris.org 	desc.acd_flags = flags;
153910913Sgdamore@opensolaris.org 
154010913Sgdamore@opensolaris.org 	ec->val = defval;
154110913Sgdamore@opensolaris.org 	ec->ctrl = audio_dev_add_control(devc->adev, &desc,
154210913Sgdamore@opensolaris.org 	    emu10k_get_control, emu10k_set_mono, ec);
154310913Sgdamore@opensolaris.org 
154410913Sgdamore@opensolaris.org 	mutex_enter(&devc->mutex);
154510913Sgdamore@opensolaris.org 	emu10k_write_gpr(devc, gpr, emu10k_convert_fixpoint(defval));
154610913Sgdamore@opensolaris.org 	mutex_exit(&devc->mutex);
154710913Sgdamore@opensolaris.org }
154810913Sgdamore@opensolaris.org 
154910913Sgdamore@opensolaris.org /*
155010913Sgdamore@opensolaris.org  * AC'97 source.  The AC'97 PCM record channel is routed to our
155110913Sgdamore@opensolaris.org  * mixer.  While we could support the direct monitoring capability of
155210913Sgdamore@opensolaris.org  * the AC'97 part itself, this would not work correctly with outputs
155310913Sgdamore@opensolaris.org  * that are not routed via AC'97 (such as the Live Drive headphones
155410913Sgdamore@opensolaris.org  * or digital outputs.)  So we just offer the ability to select one
155510913Sgdamore@opensolaris.org  * AC'97 source, and then offer independent ability to either monitor
155610913Sgdamore@opensolaris.org  * or record from the AC'97 mixer's PCM record channel.
155710913Sgdamore@opensolaris.org  */
155810913Sgdamore@opensolaris.org static void
emu10k_create_ac97src(emu10k_devc_t * devc)155910913Sgdamore@opensolaris.org emu10k_create_ac97src(emu10k_devc_t *devc)
156010913Sgdamore@opensolaris.org {
156110913Sgdamore@opensolaris.org 	emu10k_ctrl_t *ec;
156210913Sgdamore@opensolaris.org 	audio_ctrl_desc_t desc;
156310913Sgdamore@opensolaris.org 	ac97_ctrl_t *ac;
156410913Sgdamore@opensolaris.org 	const audio_ctrl_desc_t *acd;
156510913Sgdamore@opensolaris.org 
156610913Sgdamore@opensolaris.org 	bzero(&desc, sizeof (desc));
156710913Sgdamore@opensolaris.org 
156810913Sgdamore@opensolaris.org 	ec = &devc->ctrls[CTL_AC97SRC];
156910913Sgdamore@opensolaris.org 	desc.acd_name = "ac97-source";
157010913Sgdamore@opensolaris.org 	desc.acd_type = AUDIO_CTRL_TYPE_ENUM;
157110913Sgdamore@opensolaris.org 	desc.acd_flags = RECCTL;
157210913Sgdamore@opensolaris.org 	ec->devc = devc;
157310913Sgdamore@opensolaris.org 	ac = ac97_control_find(devc->ac97, AUDIO_CTRL_ID_RECSRC);
157410913Sgdamore@opensolaris.org 	if (ac == NULL) {
157510913Sgdamore@opensolaris.org 		return;
157610913Sgdamore@opensolaris.org 	}
157710913Sgdamore@opensolaris.org 
157810913Sgdamore@opensolaris.org 	acd = ac97_control_desc(ac);
157910913Sgdamore@opensolaris.org 
158010913Sgdamore@opensolaris.org 	for (int i = 0; i < 64; i++) {
158110913Sgdamore@opensolaris.org 		const char *n;
158210913Sgdamore@opensolaris.org 		if (((acd->acd_minvalue & (1ULL << i)) == 0) ||
158310913Sgdamore@opensolaris.org 		    ((n = acd->acd_enum[i]) == NULL)) {
158410913Sgdamore@opensolaris.org 			continue;
158510913Sgdamore@opensolaris.org 		}
158610913Sgdamore@opensolaris.org 		desc.acd_enum[i] = acd->acd_enum[i];
158710913Sgdamore@opensolaris.org 		/* we suppress some port options */
158810913Sgdamore@opensolaris.org 		if ((strcmp(n, AUDIO_PORT_STEREOMIX) == 0) ||
158910913Sgdamore@opensolaris.org 		    (strcmp(n, AUDIO_PORT_MONOMIX) == 0) ||
159010913Sgdamore@opensolaris.org 		    (strcmp(n, AUDIO_PORT_VIDEO) == 0)) {
159110913Sgdamore@opensolaris.org 			continue;
159210913Sgdamore@opensolaris.org 		}
159310913Sgdamore@opensolaris.org 		desc.acd_minvalue |= (1ULL << i);
159410913Sgdamore@opensolaris.org 		desc.acd_maxvalue |= (1ULL << i);
159510913Sgdamore@opensolaris.org 	}
159610913Sgdamore@opensolaris.org 
159710913Sgdamore@opensolaris.org 	ec->ctrl = audio_dev_add_control(devc->adev, &desc,
159810913Sgdamore@opensolaris.org 	    emu10k_get_ac97src, emu10k_set_ac97src, ac);
159910913Sgdamore@opensolaris.org }
160010913Sgdamore@opensolaris.org 
160110913Sgdamore@opensolaris.org /*
160210913Sgdamore@opensolaris.org  * Record source... this one is tricky.  While the chip will
160310913Sgdamore@opensolaris.org  * conceivably let us *mix* some of the audio streams for recording,
160410913Sgdamore@opensolaris.org  * the AC'97 inputs don't have this capability.  Offering it to users
160510913Sgdamore@opensolaris.org  * is likely to be confusing, so we offer a single record source
160610913Sgdamore@opensolaris.org  * selection option.  Its not ideal, but it ought to be good enough
160710913Sgdamore@opensolaris.org  * for the vast majority of users.
160810913Sgdamore@opensolaris.org  */
160910913Sgdamore@opensolaris.org static void
emu10k_create_recsrc(emu10k_devc_t * devc)161010913Sgdamore@opensolaris.org emu10k_create_recsrc(emu10k_devc_t *devc)
161110913Sgdamore@opensolaris.org {
161210913Sgdamore@opensolaris.org 	emu10k_ctrl_t *ec;
161310913Sgdamore@opensolaris.org 	audio_ctrl_desc_t desc;
161410913Sgdamore@opensolaris.org 	ac97_ctrl_t *ac;
161510913Sgdamore@opensolaris.org 
161610913Sgdamore@opensolaris.org 	bzero(&desc, sizeof (desc));
161710913Sgdamore@opensolaris.org 
161810913Sgdamore@opensolaris.org 	ec = &devc->ctrls[CTL_RECSRC];
161910913Sgdamore@opensolaris.org 	desc.acd_name = AUDIO_CTRL_ID_RECSRC;
162010913Sgdamore@opensolaris.org 	desc.acd_type = AUDIO_CTRL_TYPE_ENUM;
162110913Sgdamore@opensolaris.org 	desc.acd_flags = RECCTL;
162210913Sgdamore@opensolaris.org 	desc.acd_minvalue = 0;
162310913Sgdamore@opensolaris.org 	desc.acd_maxvalue = 0;
162410913Sgdamore@opensolaris.org 	bzero(desc.acd_enum, sizeof (desc.acd_enum));
162510913Sgdamore@opensolaris.org 	ec->devc = devc;
162610913Sgdamore@opensolaris.org 	ac = ac97_control_find(devc->ac97, AUDIO_CTRL_ID_RECSRC);
162710913Sgdamore@opensolaris.org 
162810913Sgdamore@opensolaris.org 	/* only low order bits set by AC'97 */
162910913Sgdamore@opensolaris.org 	ASSERT(desc.acd_minvalue == desc.acd_maxvalue);
163010913Sgdamore@opensolaris.org 	ASSERT((desc.acd_minvalue & ~0xffff) == 0);
163110913Sgdamore@opensolaris.org 
163210913Sgdamore@opensolaris.org 	/*
163310913Sgdamore@opensolaris.org 	 * It would be really cool if we could detect whether these
163410913Sgdamore@opensolaris.org 	 * options are all sensible on a given configuration.  Units
163510913Sgdamore@opensolaris.org 	 * without live-drive support, and units without a physical
163610913Sgdamore@opensolaris.org 	 * live-drive, simply can't do all these.
163710913Sgdamore@opensolaris.org 	 */
163810913Sgdamore@opensolaris.org 	if (ac != NULL) {
163910913Sgdamore@opensolaris.org 		desc.acd_minvalue |= (1 << INPUT_AC97);
164010913Sgdamore@opensolaris.org 		desc.acd_maxvalue |= (1 << INPUT_AC97);
164110913Sgdamore@opensolaris.org 		desc.acd_enum[INPUT_AC97] = "ac97";
164210913Sgdamore@opensolaris.org 		ec->val = (1 << INPUT_AC97);
164310913Sgdamore@opensolaris.org 	} else {
164410913Sgdamore@opensolaris.org 		/* next best guess */
164510913Sgdamore@opensolaris.org 		ec->val = (1 << INPUT_LINE2);
164610913Sgdamore@opensolaris.org 	}
164710913Sgdamore@opensolaris.org 
164810913Sgdamore@opensolaris.org 	desc.acd_minvalue |= (1 << INPUT_SPD1);
164910913Sgdamore@opensolaris.org 	desc.acd_maxvalue |= (1 << INPUT_SPD1);
165010913Sgdamore@opensolaris.org 	desc.acd_enum[INPUT_SPD1] = AUDIO_PORT_SPDIFIN;
165110913Sgdamore@opensolaris.org 
165210913Sgdamore@opensolaris.org 	desc.acd_minvalue |= (1 << INPUT_SPD2);
165310913Sgdamore@opensolaris.org 	desc.acd_maxvalue |= (1 << INPUT_SPD2);
165410913Sgdamore@opensolaris.org 	desc.acd_enum[INPUT_SPD2] = "spdif2-in";
165510913Sgdamore@opensolaris.org 
165610913Sgdamore@opensolaris.org 	desc.acd_minvalue |= (1 << INPUT_DIGCD);
165710913Sgdamore@opensolaris.org 	desc.acd_maxvalue |= (1 << INPUT_DIGCD);
165810913Sgdamore@opensolaris.org 	desc.acd_enum[INPUT_DIGCD] = "digital-cd";
165910913Sgdamore@opensolaris.org 
166010913Sgdamore@opensolaris.org 	desc.acd_minvalue |= (1 << INPUT_AUX2);
166110913Sgdamore@opensolaris.org 	desc.acd_maxvalue |= (1 << INPUT_AUX2);
166210913Sgdamore@opensolaris.org 	desc.acd_enum[INPUT_AUX2] = AUDIO_PORT_AUX2IN;
166310913Sgdamore@opensolaris.org 
166410913Sgdamore@opensolaris.org 	desc.acd_minvalue |= (1 << INPUT_LINE2);
166510913Sgdamore@opensolaris.org 	desc.acd_maxvalue |= (1 << INPUT_LINE2);
166610913Sgdamore@opensolaris.org 	desc.acd_enum[INPUT_LINE2] = "line2-in";
166710913Sgdamore@opensolaris.org 
166810913Sgdamore@opensolaris.org 	desc.acd_minvalue |= (1 << INPUT_STEREOMIX);
166910913Sgdamore@opensolaris.org 	desc.acd_maxvalue |= (1 << INPUT_STEREOMIX);
167010913Sgdamore@opensolaris.org 	desc.acd_enum[INPUT_STEREOMIX] = AUDIO_PORT_STEREOMIX;
167110913Sgdamore@opensolaris.org 
167210913Sgdamore@opensolaris.org 	emu10k_write_gpr(devc, GPR_REC_SPDIF1, 0);
167310913Sgdamore@opensolaris.org 	emu10k_write_gpr(devc, GPR_REC_SPDIF2, 0);
167410913Sgdamore@opensolaris.org 	emu10k_write_gpr(devc, GPR_REC_DIGCD, 0);
167510913Sgdamore@opensolaris.org 	emu10k_write_gpr(devc, GPR_REC_AUX2, 0);
167610913Sgdamore@opensolaris.org 	emu10k_write_gpr(devc, GPR_REC_LINE2, 0);
167710913Sgdamore@opensolaris.org 	emu10k_write_gpr(devc, GPR_REC_PCM, 0);
167810913Sgdamore@opensolaris.org 	emu10k_write_gpr(devc, GPR_REC_AC97, 1);
167910913Sgdamore@opensolaris.org 
168010913Sgdamore@opensolaris.org 	ec->ctrl = audio_dev_add_control(devc->adev, &desc,
168110913Sgdamore@opensolaris.org 	    emu10k_get_control, emu10k_set_recsrc, ec);
168210913Sgdamore@opensolaris.org }
168310913Sgdamore@opensolaris.org 
168410913Sgdamore@opensolaris.org static void
emu10k_create_jack3(emu10k_devc_t * devc)168510913Sgdamore@opensolaris.org emu10k_create_jack3(emu10k_devc_t *devc)
168610913Sgdamore@opensolaris.org {
168710913Sgdamore@opensolaris.org 	emu10k_ctrl_t *ec;
168810913Sgdamore@opensolaris.org 	audio_ctrl_desc_t desc;
168910913Sgdamore@opensolaris.org 
169010913Sgdamore@opensolaris.org 	bzero(&desc, sizeof (desc));
169110913Sgdamore@opensolaris.org 
169210913Sgdamore@opensolaris.org 	ec = &devc->ctrls[CTL_JACK3];
169310913Sgdamore@opensolaris.org 	desc.acd_name = AUDIO_CTRL_ID_JACK3;
169410913Sgdamore@opensolaris.org 	desc.acd_type = AUDIO_CTRL_TYPE_ENUM;
169510913Sgdamore@opensolaris.org 	desc.acd_flags = AUDIO_CTRL_FLAG_RW;
169610913Sgdamore@opensolaris.org 	desc.acd_minvalue = 0x3;
169710913Sgdamore@opensolaris.org 	desc.acd_maxvalue = 0x3;
169810913Sgdamore@opensolaris.org 	bzero(desc.acd_enum, sizeof (desc.acd_enum));
169910913Sgdamore@opensolaris.org 	ec->devc = devc;
170010913Sgdamore@opensolaris.org 	ec->val = 0x1;
170110913Sgdamore@opensolaris.org 
170210913Sgdamore@opensolaris.org 	desc.acd_enum[0] = AUDIO_PORT_CENLFE;
170310913Sgdamore@opensolaris.org 	desc.acd_enum[1] = AUDIO_PORT_SPDIFOUT;
170410913Sgdamore@opensolaris.org 
170510913Sgdamore@opensolaris.org 	ec->ctrl = audio_dev_add_control(devc->adev, &desc,
170610913Sgdamore@opensolaris.org 	    emu10k_get_control, emu10k_set_jack3, ec);
170710913Sgdamore@opensolaris.org }
170810913Sgdamore@opensolaris.org 
170910913Sgdamore@opensolaris.org 
171010913Sgdamore@opensolaris.org static void
emu10k_create_controls(emu10k_devc_t * devc)171110913Sgdamore@opensolaris.org emu10k_create_controls(emu10k_devc_t *devc)
171210913Sgdamore@opensolaris.org {
171310913Sgdamore@opensolaris.org 	ac97_t		*ac97;
171410913Sgdamore@opensolaris.org 	ac97_ctrl_t	*ac;
171510913Sgdamore@opensolaris.org 
171610913Sgdamore@opensolaris.org 	emu10k_create_mono(devc, CTL_VOLUME, GPR_VOL_PCM,
171710913Sgdamore@opensolaris.org 	    AUDIO_CTRL_ID_VOLUME, PCMVOL, 75);
171810913Sgdamore@opensolaris.org 
171910913Sgdamore@opensolaris.org 	emu10k_create_stereo(devc, CTL_FRONT, GPR_VOL_FRONT,
172010913Sgdamore@opensolaris.org 	    AUDIO_CTRL_ID_FRONT, MAINVOL, 100);
172110913Sgdamore@opensolaris.org 	emu10k_create_stereo(devc, CTL_SURROUND, GPR_VOL_SURR,
172210913Sgdamore@opensolaris.org 	    AUDIO_CTRL_ID_SURROUND, MAINVOL, 100);
172310913Sgdamore@opensolaris.org 	if (devc->feature_mask & (SB_51 | SB_71)) {
172410913Sgdamore@opensolaris.org 		emu10k_create_mono(devc, CTL_CENTER, GPR_VOL_CEN,
172510913Sgdamore@opensolaris.org 		    AUDIO_CTRL_ID_CENTER, MAINVOL, 100);
172610913Sgdamore@opensolaris.org 		emu10k_create_mono(devc, CTL_LFE, GPR_VOL_LFE,
172710913Sgdamore@opensolaris.org 		    AUDIO_CTRL_ID_LFE, MAINVOL, 100);
172810913Sgdamore@opensolaris.org 	}
172910913Sgdamore@opensolaris.org 	if (devc->feature_mask & SB_71) {
173010913Sgdamore@opensolaris.org 		emu10k_create_stereo(devc, CTL_SIDE, GPR_VOL_SIDE,
173110913Sgdamore@opensolaris.org 		    "side", MAINVOL, 100);
173210913Sgdamore@opensolaris.org 	}
173310913Sgdamore@opensolaris.org 
173410913Sgdamore@opensolaris.org 	emu10k_create_stereo(devc, CTL_RECGAIN, GPR_VOL_REC,
173510913Sgdamore@opensolaris.org 	    AUDIO_CTRL_ID_RECGAIN, RECVOL, 50);
173610913Sgdamore@opensolaris.org 
173710913Sgdamore@opensolaris.org 	emu10k_create_ac97src(devc);
173810913Sgdamore@opensolaris.org 	emu10k_create_recsrc(devc);
173910913Sgdamore@opensolaris.org 	/*
174010913Sgdamore@opensolaris.org 	 * 5.1 devices have versa jack.  Note that from what we can
174110913Sgdamore@opensolaris.org 	 * tell, none of the 7.1 devices have or need this versa jack,
174210913Sgdamore@opensolaris.org 	 * as they all seem to have a dedicated digital I/O port.
174310913Sgdamore@opensolaris.org 	 */
174410913Sgdamore@opensolaris.org 	if ((devc->feature_mask & SB_51) &&
174510913Sgdamore@opensolaris.org 	    !(devc->feature_mask & SB_AUDIGY2VAL)) {
174610913Sgdamore@opensolaris.org 		emu10k_create_jack3(devc);
174710913Sgdamore@opensolaris.org 	}
174810913Sgdamore@opensolaris.org 
174910913Sgdamore@opensolaris.org 	/* these ones AC'97 can manage directly */
175010913Sgdamore@opensolaris.org 	ac97 = devc->ac97;
175110913Sgdamore@opensolaris.org 
175210913Sgdamore@opensolaris.org 	if ((ac = ac97_control_find(ac97, AUDIO_CTRL_ID_MICBOOST)) != NULL)
175310913Sgdamore@opensolaris.org 		ac97_control_register(ac);
175410913Sgdamore@opensolaris.org 	if ((ac = ac97_control_find(ac97, AUDIO_CTRL_ID_MICGAIN)) != NULL)
175510913Sgdamore@opensolaris.org 		ac97_control_register(ac);
175610913Sgdamore@opensolaris.org 
175710913Sgdamore@opensolaris.org 	/* set any AC'97 analog outputs to full volume (no attenuation) */
175810913Sgdamore@opensolaris.org 	if ((ac = ac97_control_find(ac97, AUDIO_CTRL_ID_FRONT)) != NULL)
175910919SGarrett.Damore@Sun.COM 		(void) ac97_control_set(ac, (100 << 8) | 100);
176010913Sgdamore@opensolaris.org 	if ((ac = ac97_control_find(ac97, AUDIO_CTRL_ID_LINEOUT)) != NULL)
176110919SGarrett.Damore@Sun.COM 		(void) ac97_control_set(ac, (100 << 8) | 100);
176210913Sgdamore@opensolaris.org 	if ((ac = ac97_control_find(ac97, AUDIO_CTRL_ID_SURROUND)) != NULL)
176310919SGarrett.Damore@Sun.COM 		(void) ac97_control_set(ac, (100 << 8) | 100);
176410913Sgdamore@opensolaris.org 	if ((ac = ac97_control_find(ac97, AUDIO_CTRL_ID_CENTER)) != NULL)
176510919SGarrett.Damore@Sun.COM 		(void) ac97_control_set(ac, 100);
176610913Sgdamore@opensolaris.org 	if ((ac = ac97_control_find(ac97, AUDIO_CTRL_ID_LFE)) != NULL)
176710919SGarrett.Damore@Sun.COM 		(void) ac97_control_set(ac, 100);
176810913Sgdamore@opensolaris.org 
176910913Sgdamore@opensolaris.org 	/* Monitor sources */
177010913Sgdamore@opensolaris.org 	emu10k_create_stereo(devc, CTL_AC97, GPR_MON_AC97,
177110913Sgdamore@opensolaris.org 	    "ac97-monitor", MONVOL, 0);
177210913Sgdamore@opensolaris.org 	emu10k_create_stereo(devc, CTL_SPD1, GPR_MON_SPDIF1,
177310913Sgdamore@opensolaris.org 	    AUDIO_PORT_SPDIFIN, MONVOL, 0);
177410913Sgdamore@opensolaris.org 	emu10k_create_stereo(devc, CTL_DIGCD, GPR_MON_DIGCD,
177510913Sgdamore@opensolaris.org 	    "digital-cd", MONVOL, 0);
177610913Sgdamore@opensolaris.org 	emu10k_create_stereo(devc, CTL_SPD1, GPR_MON_SPDIF1,
177710913Sgdamore@opensolaris.org 	    AUDIO_PORT_SPDIFIN, MONVOL, 0);
177810913Sgdamore@opensolaris.org 
177910913Sgdamore@opensolaris.org 	if ((devc->feature_mask & SB_NOEXP) == 0) {
178010913Sgdamore@opensolaris.org 		/*
178110913Sgdamore@opensolaris.org 		 * These ports are only available via an external
178210913Sgdamore@opensolaris.org 		 * expansion box.  Don't expose them for cards  that
178310913Sgdamore@opensolaris.org 		 * don't have support for it.
178410913Sgdamore@opensolaris.org 		 */
178510913Sgdamore@opensolaris.org 		emu10k_create_stereo(devc, CTL_HEADPH, GPR_VOL_HEADPH,
178610913Sgdamore@opensolaris.org 		    AUDIO_CTRL_ID_HEADPHONE, MAINVOL, 100);
178710913Sgdamore@opensolaris.org 		emu10k_create_stereo(devc, CTL_SPD2, GPR_MON_SPDIF2,
178810913Sgdamore@opensolaris.org 		    "spdif2-in", MONVOL, 0);
178910913Sgdamore@opensolaris.org 		emu10k_create_stereo(devc, CTL_LINE2, GPR_MON_LINE2,
179010913Sgdamore@opensolaris.org 		    "line2-in", MONVOL, 0);
179110913Sgdamore@opensolaris.org 		emu10k_create_stereo(devc, CTL_AUX2, GPR_MON_AUX2,
179210913Sgdamore@opensolaris.org 		    AUDIO_PORT_AUX2IN, MONVOL, 0);
179310913Sgdamore@opensolaris.org 	}
179410913Sgdamore@opensolaris.org }
179510913Sgdamore@opensolaris.org 
179610913Sgdamore@opensolaris.org static void
emu10k_load_dsp(emu10k_devc_t * devc,uint32_t * code,int ncode,uint32_t * init,int ninit)179710913Sgdamore@opensolaris.org emu10k_load_dsp(emu10k_devc_t *devc, uint32_t *code, int ncode,
179810913Sgdamore@opensolaris.org     uint32_t *init, int ninit)
179910913Sgdamore@opensolaris.org {
180010913Sgdamore@opensolaris.org 	int i;
180110913Sgdamore@opensolaris.org 
180210913Sgdamore@opensolaris.org 	if (ncode > 1024) {
180310913Sgdamore@opensolaris.org 		audio_dev_warn(devc->adev, "DSP file size too big");
180410913Sgdamore@opensolaris.org 		return;
180510913Sgdamore@opensolaris.org 	}
180610913Sgdamore@opensolaris.org 	if (ninit > MAX_GPR) {
180710913Sgdamore@opensolaris.org 		audio_dev_warn(devc->adev, "Too many inits");
180810913Sgdamore@opensolaris.org 		return;
180910913Sgdamore@opensolaris.org 	}
181010913Sgdamore@opensolaris.org 
181110913Sgdamore@opensolaris.org 	/* Upload our DSP code */
181210913Sgdamore@opensolaris.org 	for (i = 0; i < ncode; i++) {
181310913Sgdamore@opensolaris.org 		emu10k_write_efx(devc, UC0 + i, code[i]);
181410913Sgdamore@opensolaris.org 	}
181510913Sgdamore@opensolaris.org 
181610913Sgdamore@opensolaris.org 	/* Upload the initialization settings */
181710913Sgdamore@opensolaris.org 	for (i = 0; i < ninit; i += 2) {
181810913Sgdamore@opensolaris.org 		emu10k_write_reg(devc, init[i] + GPR0, 0, init[i + 1]);
181910913Sgdamore@opensolaris.org 	}
182010913Sgdamore@opensolaris.org }
182110913Sgdamore@opensolaris.org 
182210913Sgdamore@opensolaris.org #define	LIVE_NOP()					\
182310913Sgdamore@opensolaris.org 	emu10k_write_efx(devc, UC0 + (pc * 2), 0x10040);	\
182410913Sgdamore@opensolaris.org 	emu10k_write_efx(devc, UC0 + (pc * 2 + 1), 0x610040);	\
182510913Sgdamore@opensolaris.org 	pc++
182610913Sgdamore@opensolaris.org #define	LIVE_ACC3(r, a, x, y) /* z=w+x+y */				\
182710913Sgdamore@opensolaris.org 	emu10k_write_efx(devc, UC0 + (pc * 2), (x << 10) | y);		\
182810913Sgdamore@opensolaris.org 	emu10k_write_efx(devc, UC0 + (pc * 2 + 1), (6 << 20) | (r << 10) | a); \
182910913Sgdamore@opensolaris.org 	pc++
183010913Sgdamore@opensolaris.org 
183110913Sgdamore@opensolaris.org #define	AUDIGY_ACC3(r, a, x, y) /* z=w+x+y */				\
183210913Sgdamore@opensolaris.org 	emu10k_write_efx(devc, UC0 + (pc * 2), (x << 12) | y);		\
183310913Sgdamore@opensolaris.org 	emu10k_write_efx(devc, UC0 + (pc * 2+1), (6 << 24) | (r << 12) | a); \
183410913Sgdamore@opensolaris.org 	pc++
183510913Sgdamore@opensolaris.org #define	AUDIGY_NOP() AUDIGY_ACC3(0xc0, 0xc0, 0xc0, 0xc0)
183610913Sgdamore@opensolaris.org 
183710913Sgdamore@opensolaris.org static void
emu10k_init_effects(emu10k_devc_t * devc)183810913Sgdamore@opensolaris.org emu10k_init_effects(emu10k_devc_t *devc)
183910913Sgdamore@opensolaris.org {
184010913Sgdamore@opensolaris.org 	int i;
184110913Sgdamore@opensolaris.org 	unsigned short pc;
184210913Sgdamore@opensolaris.org 
184310913Sgdamore@opensolaris.org 	ASSERT(mutex_owned(&devc->mutex));
184410913Sgdamore@opensolaris.org 
184510913Sgdamore@opensolaris.org 	if (devc->feature_mask & (SB_AUDIGY|SB_AUDIGY2|SB_AUDIGY2VAL)) {
184610913Sgdamore@opensolaris.org 		pc = 0;
184710913Sgdamore@opensolaris.org 		for (i = 0; i < 512; i++) {
184810913Sgdamore@opensolaris.org 			AUDIGY_NOP();
184910913Sgdamore@opensolaris.org 		}
185010913Sgdamore@opensolaris.org 
185110913Sgdamore@opensolaris.org 		for (i = 0; i < 256; i++)
185210913Sgdamore@opensolaris.org 			emu10k_write_efx(devc, GPR0 + i, 0);
185310913Sgdamore@opensolaris.org 		emu10k_write_reg(devc, AUDIGY_DBG, 0, 0);
185410913Sgdamore@opensolaris.org 		emu10k_load_dsp(devc,
185510913Sgdamore@opensolaris.org 		    emu10k2_code,
185610913Sgdamore@opensolaris.org 		    sizeof (emu10k2_code) / sizeof (emu10k2_code[0]),
185710913Sgdamore@opensolaris.org 		    emu10k2_init,
185810913Sgdamore@opensolaris.org 		    sizeof (emu10k2_init) / sizeof (emu10k2_init[0]));
185910913Sgdamore@opensolaris.org 
186010913Sgdamore@opensolaris.org 	} else {
186110913Sgdamore@opensolaris.org 		pc = 0;
186210913Sgdamore@opensolaris.org 		for (i = 0; i < 512; i++) {
186310913Sgdamore@opensolaris.org 			LIVE_NOP();
186410913Sgdamore@opensolaris.org 		}
186510913Sgdamore@opensolaris.org 
186610913Sgdamore@opensolaris.org 		for (i = 0; i < 256; i++)
186710913Sgdamore@opensolaris.org 			emu10k_write_efx(devc, GPR0 + i, 0);
186810913Sgdamore@opensolaris.org 		emu10k_write_reg(devc, DBG, 0, 0);
186910913Sgdamore@opensolaris.org 		emu10k_load_dsp(devc,
187010913Sgdamore@opensolaris.org 		    emu10k1_code,
187110913Sgdamore@opensolaris.org 		    sizeof (emu10k1_code) / sizeof (emu10k1_code[0]),
187210913Sgdamore@opensolaris.org 		    emu10k1_init,
187310913Sgdamore@opensolaris.org 		    sizeof (emu10k1_init) / sizeof (emu10k1_init[0]));
187410913Sgdamore@opensolaris.org 	}
187510913Sgdamore@opensolaris.org }
187610913Sgdamore@opensolaris.org 
187710913Sgdamore@opensolaris.org /* mixer */
187810913Sgdamore@opensolaris.org 
187910913Sgdamore@opensolaris.org static struct {
188010913Sgdamore@opensolaris.org 	uint16_t	devid;
188110913Sgdamore@opensolaris.org 	uint16_t	subid;
188210913Sgdamore@opensolaris.org 	const char	*model;
188310913Sgdamore@opensolaris.org 	const char	*prod;
188410913Sgdamore@opensolaris.org 	unsigned	feature_mask;
188510913Sgdamore@opensolaris.org } emu10k_cards[] = {
188610913Sgdamore@opensolaris.org 	{ 0x2, 0x0020, "CT4670", "Live! Value", SB_LIVE | SB_NOEXP },
188710913Sgdamore@opensolaris.org 	{ 0x2, 0x0021, "CT4621", "Live!", SB_LIVE },
188810913Sgdamore@opensolaris.org 	{ 0x2, 0x100a, "SB0220", "Live! 5.1 Digital",
188910913Sgdamore@opensolaris.org 	    SB_LIVE | SB_51 | SB_NOEXP },
189010913Sgdamore@opensolaris.org 	{ 0x2, 0x8022, "CT4780", "Live! Value", SB_LIVE },
189110913Sgdamore@opensolaris.org 	{ 0x2, 0x8023, "CT4790", "PCI512", SB_LIVE | SB_NOEXP },
189210913Sgdamore@opensolaris.org 	{ 0x2, 0x8026, "CT4830", "Live! Value", SB_LIVE },
189310913Sgdamore@opensolaris.org 	{ 0x2, 0x8028, "CT4870", "Live! Value", SB_LIVE },
189410913Sgdamore@opensolaris.org 	{ 0x2, 0x8031, "CT4831", "Live! Value", SB_LIVE },
189510913Sgdamore@opensolaris.org 	{ 0x2, 0x8040, "CT4760", "Live!", SB_LIVE },
189610913Sgdamore@opensolaris.org 	{ 0x2, 0x8051, "CT4850", "Live! Value", SB_LIVE },
189710913Sgdamore@opensolaris.org 	{ 0x2, 0x8061, "SB0060", "Live! 5.1", SB_LIVE | SB_51 },
189810913Sgdamore@opensolaris.org 	{ 0x2, 0x8064, "SB0100", "Live! 5.1", SB_LIVE | SB_51 },
189910913Sgdamore@opensolaris.org 	{ 0x2, 0x8065, "SB0220", "Live! 5.1", SB_LIVE | SB_51 },
190010913Sgdamore@opensolaris.org 	{ 0x2, 0x8066, "SB0228", "Live! 5.1", SB_LIVE | SB_51 },
190110913Sgdamore@opensolaris.org 	{ 0x4, 0x0051, "SB0090", "Audigy", SB_AUDIGY | SB_51 },
190210913Sgdamore@opensolaris.org 	{ 0x4, 0x0052, "SB0160", "Audigy ES", SB_AUDIGY | SB_51 },
190310913Sgdamore@opensolaris.org 	{ 0x4, 0x0053, "SB0092", "Audigy", SB_AUDIGY | SB_51 },
190410913Sgdamore@opensolaris.org 	{ 0x4, 0x1002, "SB0240P", "Audigy 2 Platinum",
190510913Sgdamore@opensolaris.org 	    SB_AUDIGY2 | SB_71 | SB_INVSP },
190610913Sgdamore@opensolaris.org 	{ 0x4, 0x1003, "SB0353", "Audigy 2 ZS", SB_AUDIGY2 | SB_71 | SB_INVSP },
190710913Sgdamore@opensolaris.org 	{ 0x4, 0x1005, "SB0280", "Audigy 2 Platinum EX", SB_AUDIGY2 | SB_71 },
190810913Sgdamore@opensolaris.org 	{ 0x4, 0x1007, "SB0240", "Audigy 2", SB_AUDIGY2 | SB_71 },
190910913Sgdamore@opensolaris.org 	{ 0x4, 0x2001, "SB0360", "Audigy 2 ZS", SB_AUDIGY2 | SB_71 | SB_INVSP },
191010913Sgdamore@opensolaris.org 	{ 0x4, 0x2002, "SB0350", "Audigy 2 ZS", SB_AUDIGY2 | SB_71 | SB_INVSP },
191110913Sgdamore@opensolaris.org 	{ 0x4, 0x2006, "SB0350", "Audigy 2", SB_AUDIGY2 | SB_71 | SB_INVSP },
191210913Sgdamore@opensolaris.org 	{ 0x4, 0x2007, "SB0380", "Audigy 4 Pro", SB_AUDIGY2 | SB_71 },
191310913Sgdamore@opensolaris.org 	{ 0x8, 0x1001, "SB0400", "Audigy 2 Value",
191410913Sgdamore@opensolaris.org 	    SB_AUDIGY2VAL | SB_71 | SB_NOEXP },
191510913Sgdamore@opensolaris.org 	{ 0x8, 0x1021, "SB0610", "Audigy 4",
191610913Sgdamore@opensolaris.org 	    SB_AUDIGY2VAL | SB_71 | SB_NOEXP },
191710913Sgdamore@opensolaris.org 	{ 0x8, 0x2001, "SB0530", "Audigy 2 ZS Notebook",
191810913Sgdamore@opensolaris.org 	    SB_AUDIGY2VAL | SB_71 },
191910913Sgdamore@opensolaris.org 	{ 0, 0, NULL, NULL, 0 },
192010913Sgdamore@opensolaris.org };
192110913Sgdamore@opensolaris.org 
192210913Sgdamore@opensolaris.org int
emu10k_attach(dev_info_t * dip)192310913Sgdamore@opensolaris.org emu10k_attach(dev_info_t *dip)
192410913Sgdamore@opensolaris.org {
192510913Sgdamore@opensolaris.org 	uint16_t pci_command;
192610913Sgdamore@opensolaris.org 	uint16_t subid;
192710913Sgdamore@opensolaris.org 	uint16_t devid;
192810913Sgdamore@opensolaris.org 	emu10k_devc_t *devc;
192910913Sgdamore@opensolaris.org 	ddi_acc_handle_t pcih;
193010913Sgdamore@opensolaris.org 	ddi_dma_cookie_t cookie;
193110913Sgdamore@opensolaris.org 	uint_t count;
193210913Sgdamore@opensolaris.org 	ulong_t len;
193310913Sgdamore@opensolaris.org 	int i;
193410913Sgdamore@opensolaris.org 	const char *name;
193510913Sgdamore@opensolaris.org 	const char *model;
193610913Sgdamore@opensolaris.org 	char namebuf[64];
193710913Sgdamore@opensolaris.org 	int feature_mask;
193810913Sgdamore@opensolaris.org 
193910913Sgdamore@opensolaris.org 	devc = kmem_zalloc(sizeof (*devc), KM_SLEEP);
194010913Sgdamore@opensolaris.org 	devc->dip = dip;
194110913Sgdamore@opensolaris.org 	ddi_set_driver_private(dip, devc);
194210913Sgdamore@opensolaris.org 
194310913Sgdamore@opensolaris.org 	if ((devc->adev = audio_dev_alloc(dip, 0)) == NULL) {
194410913Sgdamore@opensolaris.org 		cmn_err(CE_WARN, "audio_dev_alloc failed");
194510913Sgdamore@opensolaris.org 		goto error;
194610913Sgdamore@opensolaris.org 	}
194710913Sgdamore@opensolaris.org 
194810913Sgdamore@opensolaris.org 	if (pci_config_setup(dip, &pcih) != DDI_SUCCESS) {
194910913Sgdamore@opensolaris.org 		audio_dev_warn(devc->adev, "pci_config_setup failed");
195010913Sgdamore@opensolaris.org 		goto error;
195110913Sgdamore@opensolaris.org 	}
195210913Sgdamore@opensolaris.org 	devc->pcih = pcih;
195310913Sgdamore@opensolaris.org 
195410913Sgdamore@opensolaris.org 	devid = pci_config_get16(pcih, PCI_CONF_DEVID);
195510913Sgdamore@opensolaris.org 	subid = pci_config_get16(pcih, PCI_CONF_SUBSYSID);
195610913Sgdamore@opensolaris.org 
195710913Sgdamore@opensolaris.org 	pci_command = pci_config_get16(pcih, PCI_CONF_COMM);
195810913Sgdamore@opensolaris.org 	pci_command |= PCI_COMM_ME | PCI_COMM_IO;
195910913Sgdamore@opensolaris.org 	pci_config_put16(pcih, PCI_CONF_COMM, pci_command);
196010913Sgdamore@opensolaris.org 
196110913Sgdamore@opensolaris.org 	if ((ddi_regs_map_setup(dip, 1, &devc->regs, 0, 0, &dev_attr,
196210913Sgdamore@opensolaris.org 	    &devc->regsh)) != DDI_SUCCESS) {
196310913Sgdamore@opensolaris.org 		audio_dev_warn(devc->adev, "failed to map registers");
196410913Sgdamore@opensolaris.org 		goto error;
196510913Sgdamore@opensolaris.org 	}
196610913Sgdamore@opensolaris.org 
196710913Sgdamore@opensolaris.org 	switch (devid) {
196810913Sgdamore@opensolaris.org 	case PCI_DEVICE_ID_SBLIVE:
196910913Sgdamore@opensolaris.org 		name = "Live!";
197010913Sgdamore@opensolaris.org 		model = "CT????";
197110913Sgdamore@opensolaris.org 		feature_mask = SB_LIVE;
197210913Sgdamore@opensolaris.org 		break;
197310913Sgdamore@opensolaris.org 
197410913Sgdamore@opensolaris.org 	case PCI_DEVICE_ID_AUDIGYVALUE:
197510913Sgdamore@opensolaris.org 		name = "Audigy 2 Value";
197610913Sgdamore@opensolaris.org 		model = "SB????";
197710913Sgdamore@opensolaris.org 		feature_mask = SB_AUDIGY2VAL;
197810913Sgdamore@opensolaris.org 		break;
197910913Sgdamore@opensolaris.org 
198010913Sgdamore@opensolaris.org 	case PCI_DEVICE_ID_AUDIGY:
198110913Sgdamore@opensolaris.org 		if (subid >= 0x1002 && subid <= 0x2005) {
198210913Sgdamore@opensolaris.org 			name = "Audigy 2";
198310913Sgdamore@opensolaris.org 			model = "SB????";
198410913Sgdamore@opensolaris.org 			feature_mask = SB_AUDIGY2;
198510913Sgdamore@opensolaris.org 		} else {
198610913Sgdamore@opensolaris.org 			name = "Audigy";
198710913Sgdamore@opensolaris.org 			model = "SB????";
198810913Sgdamore@opensolaris.org 			feature_mask = SB_AUDIGY;
198910913Sgdamore@opensolaris.org 		}
199010913Sgdamore@opensolaris.org 		break;
199110913Sgdamore@opensolaris.org 
199210913Sgdamore@opensolaris.org 	default:
199310913Sgdamore@opensolaris.org 		audio_dev_warn(devc->adev, "Unrecognized device");
199410913Sgdamore@opensolaris.org 		goto error;
199510913Sgdamore@opensolaris.org 	}
199610913Sgdamore@opensolaris.org 
199710913Sgdamore@opensolaris.org 	for (i = 0; emu10k_cards[i].prod; i++) {
199810913Sgdamore@opensolaris.org 		if ((devid == emu10k_cards[i].devid) &&
199910913Sgdamore@opensolaris.org 		    (subid == emu10k_cards[i].subid)) {
200010913Sgdamore@opensolaris.org 			name = emu10k_cards[i].prod;
200110913Sgdamore@opensolaris.org 			model = emu10k_cards[i].model;
200210913Sgdamore@opensolaris.org 			feature_mask = emu10k_cards[i].feature_mask;
200310913Sgdamore@opensolaris.org 			break;
200410913Sgdamore@opensolaris.org 		}
200510913Sgdamore@opensolaris.org 	}
200610913Sgdamore@opensolaris.org 	devc->feature_mask = feature_mask;
200710913Sgdamore@opensolaris.org 
200810913Sgdamore@opensolaris.org 	(void) snprintf(namebuf, sizeof (namebuf), "Sound Blaster %s", name);
200910913Sgdamore@opensolaris.org 
201010913Sgdamore@opensolaris.org 	audio_dev_set_description(devc->adev, namebuf);
201110913Sgdamore@opensolaris.org 	audio_dev_set_version(devc->adev, model);
201210913Sgdamore@opensolaris.org 
2013*11936Sgdamore@opensolaris.org 	mutex_init(&devc->mutex, NULL, MUTEX_DRIVER, 0);
201410913Sgdamore@opensolaris.org 
201510913Sgdamore@opensolaris.org 	/* allocate static page table memory */
201610913Sgdamore@opensolaris.org 
201710913Sgdamore@opensolaris.org 	devc->max_mem = AUDIO_MEMSIZE;
201810913Sgdamore@opensolaris.org 
201910913Sgdamore@opensolaris.org 	/* SB Live/Audigy supports at most 32M of memory) */
202010913Sgdamore@opensolaris.org 	if (devc->max_mem > 32 * 1024 * 1024)
202110913Sgdamore@opensolaris.org 		devc->max_mem = 32 * 1024 * 1024;
202210913Sgdamore@opensolaris.org 
202310913Sgdamore@opensolaris.org 	devc->max_pages = devc->max_mem / 4096;
202410913Sgdamore@opensolaris.org 	if (devc->max_pages < 1024)
202510913Sgdamore@opensolaris.org 		devc->max_pages = 1024;
202610913Sgdamore@opensolaris.org 
202710913Sgdamore@opensolaris.org 	/* Allocate page table */
202810913Sgdamore@opensolaris.org 	if (ddi_dma_alloc_handle(devc->dip, &dma_attr_buf, DDI_DMA_SLEEP, NULL,
202910913Sgdamore@opensolaris.org 	    &devc->pt_dmah) != DDI_SUCCESS) {
203010913Sgdamore@opensolaris.org 		audio_dev_warn(devc->adev,
203110913Sgdamore@opensolaris.org 		    "failed to allocate page table handle");
2032*11936Sgdamore@opensolaris.org 		goto error;
203310913Sgdamore@opensolaris.org 	}
203410913Sgdamore@opensolaris.org 
203510913Sgdamore@opensolaris.org 	if (ddi_dma_mem_alloc(devc->pt_dmah, devc->max_pages * 4,
203610913Sgdamore@opensolaris.org 	    &dev_attr, DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL,
203710913Sgdamore@opensolaris.org 	    &devc->pt_kaddr, &len, &devc->pt_acch) !=
203810913Sgdamore@opensolaris.org 	    DDI_SUCCESS) {
203910913Sgdamore@opensolaris.org 		audio_dev_warn(devc->adev,
204010913Sgdamore@opensolaris.org 		    "failed to allocate memory for page table");
2041*11936Sgdamore@opensolaris.org 		goto error;
204210913Sgdamore@opensolaris.org 	}
204310913Sgdamore@opensolaris.org 
204410913Sgdamore@opensolaris.org 	if (ddi_dma_addr_bind_handle(devc->pt_dmah, NULL,
204510913Sgdamore@opensolaris.org 	    devc->pt_kaddr, len, DDI_DMA_CONSISTENT | DDI_DMA_WRITE,
204610913Sgdamore@opensolaris.org 	    DDI_DMA_SLEEP, NULL, &cookie, &count) != DDI_SUCCESS) {
204710913Sgdamore@opensolaris.org 		audio_dev_warn(devc->adev,
204810913Sgdamore@opensolaris.org 		    "failed binding page table DMA handle");
2049*11936Sgdamore@opensolaris.org 		goto error;
205010913Sgdamore@opensolaris.org 	}
205110913Sgdamore@opensolaris.org 
205210913Sgdamore@opensolaris.org 	devc->page_map = (void *)devc->pt_kaddr;
205310913Sgdamore@opensolaris.org 	devc->pt_paddr = cookie.dmac_address;
205410913Sgdamore@opensolaris.org 	bzero(devc->pt_kaddr, devc->max_pages * 4);
205510913Sgdamore@opensolaris.org 
205610913Sgdamore@opensolaris.org 	/* Allocate silent page */
205710913Sgdamore@opensolaris.org 	if (ddi_dma_alloc_handle(devc->dip, &dma_attr_buf, DDI_DMA_SLEEP, NULL,
205810913Sgdamore@opensolaris.org 	    &devc->silence_dmah) != DDI_SUCCESS) {
205910913Sgdamore@opensolaris.org 		audio_dev_warn(devc->adev,
206010913Sgdamore@opensolaris.org 		    "failed to allocate silent page handle");
2061*11936Sgdamore@opensolaris.org 		goto error;
206210913Sgdamore@opensolaris.org 	}
206310913Sgdamore@opensolaris.org 
206410913Sgdamore@opensolaris.org 	if (ddi_dma_mem_alloc(devc->silence_dmah, 4096,
206510913Sgdamore@opensolaris.org 	    &buf_attr, DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL,
206610913Sgdamore@opensolaris.org 	    &devc->silence_kaddr, &len,
206710913Sgdamore@opensolaris.org 	    &devc->silence_acch) != DDI_SUCCESS) {
206810913Sgdamore@opensolaris.org 		audio_dev_warn(devc->adev,
206910913Sgdamore@opensolaris.org 		    "failed to allocate silent page memory");
2070*11936Sgdamore@opensolaris.org 		goto error;
207110913Sgdamore@opensolaris.org 	}
207210913Sgdamore@opensolaris.org 
207310913Sgdamore@opensolaris.org 	(void) ddi_dma_sync(devc->silence_dmah, 0, 0, DDI_DMA_SYNC_FORDEV);
207410913Sgdamore@opensolaris.org 
207510913Sgdamore@opensolaris.org 	if (ddi_dma_addr_bind_handle(devc->silence_dmah, NULL,
207610913Sgdamore@opensolaris.org 	    devc->silence_kaddr, len, DDI_DMA_CONSISTENT | DDI_DMA_WRITE,
207710913Sgdamore@opensolaris.org 	    DDI_DMA_SLEEP, NULL, &cookie, &count) != DDI_SUCCESS) {
207810913Sgdamore@opensolaris.org 		audio_dev_warn(devc->adev,
207910913Sgdamore@opensolaris.org 		    "failed binding silent page DMA handle");
2080*11936Sgdamore@opensolaris.org 		goto error;
208110913Sgdamore@opensolaris.org 	}
208210913Sgdamore@opensolaris.org 
208310913Sgdamore@opensolaris.org 	devc->silence_paddr = cookie.dmac_address;
208410913Sgdamore@opensolaris.org 	bzero(devc->silence_kaddr, 4096);
208510913Sgdamore@opensolaris.org 	devc->audio_memptr = 4096;	/* Skip the silence page */
208610913Sgdamore@opensolaris.org 
208710913Sgdamore@opensolaris.org 	for (i = 0; i < devc->max_pages; i++)
208810913Sgdamore@opensolaris.org 		FILL_PAGE_MAP_ENTRY(i, devc->silence_paddr);
208910913Sgdamore@opensolaris.org 
209010913Sgdamore@opensolaris.org 	(void) ddi_dma_sync(devc->pt_dmah, 0, 0, DDI_DMA_SYNC_FORDEV);
209110913Sgdamore@opensolaris.org 
209210913Sgdamore@opensolaris.org 	devc->ac97 = ac97_allocate(devc->adev, dip,
209310913Sgdamore@opensolaris.org 	    emu10k_read_ac97, emu10k_write_ac97, devc);
209410913Sgdamore@opensolaris.org 	if (devc->ac97 == NULL) {
209510913Sgdamore@opensolaris.org 		audio_dev_warn(devc->adev, "failed to allocate ac97 handle");
209610913Sgdamore@opensolaris.org 		goto error;
209710913Sgdamore@opensolaris.org 	}
209810913Sgdamore@opensolaris.org 
209910913Sgdamore@opensolaris.org 	ac97_probe_controls(devc->ac97);
210010913Sgdamore@opensolaris.org 
210110913Sgdamore@opensolaris.org 	/* allocate voice 0 for play */
210210913Sgdamore@opensolaris.org 	if (emu10k_alloc_port(devc, EMU10K_REC) != DDI_SUCCESS)
210310913Sgdamore@opensolaris.org 		goto error;
210410913Sgdamore@opensolaris.org 
210510913Sgdamore@opensolaris.org 	if (emu10k_alloc_port(devc, EMU10K_PLAY) != DDI_SUCCESS)
210610913Sgdamore@opensolaris.org 		goto error;
210710913Sgdamore@opensolaris.org 
210810913Sgdamore@opensolaris.org 	/* now initialize the hardware */
210910913Sgdamore@opensolaris.org 	mutex_enter(&devc->mutex);
211010913Sgdamore@opensolaris.org 	if (emu10k_hwinit(devc) != DDI_SUCCESS) {
211110913Sgdamore@opensolaris.org 		mutex_exit(&devc->mutex);
211210913Sgdamore@opensolaris.org 		goto error;
211310913Sgdamore@opensolaris.org 	}
211410913Sgdamore@opensolaris.org 	mutex_exit(&devc->mutex);
211510913Sgdamore@opensolaris.org 
211610913Sgdamore@opensolaris.org 	emu10k_create_controls(devc);
211710913Sgdamore@opensolaris.org 
211810913Sgdamore@opensolaris.org 	if (audio_dev_register(devc->adev) != DDI_SUCCESS) {
211910913Sgdamore@opensolaris.org 		audio_dev_warn(devc->adev, "unable to register audio device");
212010913Sgdamore@opensolaris.org 		goto error;
212110913Sgdamore@opensolaris.org 	}
212210913Sgdamore@opensolaris.org 
212310913Sgdamore@opensolaris.org 	ddi_report_dev(dip);
212410913Sgdamore@opensolaris.org 
212510913Sgdamore@opensolaris.org 	return (DDI_SUCCESS);
212610913Sgdamore@opensolaris.org 
212710913Sgdamore@opensolaris.org error:
212810913Sgdamore@opensolaris.org 	emu10k_destroy(devc);
212910913Sgdamore@opensolaris.org 	return (DDI_FAILURE);
213010913Sgdamore@opensolaris.org }
213110913Sgdamore@opensolaris.org 
213210913Sgdamore@opensolaris.org int
emu10k_resume(dev_info_t * dip)213310913Sgdamore@opensolaris.org emu10k_resume(dev_info_t *dip)
213410913Sgdamore@opensolaris.org {
213510913Sgdamore@opensolaris.org 	emu10k_devc_t *devc;
213610913Sgdamore@opensolaris.org 
213710913Sgdamore@opensolaris.org 	devc = ddi_get_driver_private(dip);
213810913Sgdamore@opensolaris.org 
213910913Sgdamore@opensolaris.org 	mutex_enter(&devc->mutex);
214010913Sgdamore@opensolaris.org 	if (emu10k_hwinit(devc) != DDI_SUCCESS) {
214110913Sgdamore@opensolaris.org 		mutex_exit(&devc->mutex);
214210913Sgdamore@opensolaris.org 		/*
214310913Sgdamore@opensolaris.org 		 * In case of failure, we leave the chip suspended,
214410913Sgdamore@opensolaris.org 		 * but don't panic.  Audio service is not normally a a
214510913Sgdamore@opensolaris.org 		 * critical service.
214610913Sgdamore@opensolaris.org 		 */
214710913Sgdamore@opensolaris.org 		audio_dev_warn(devc->adev, "FAILED to RESUME device");
214810913Sgdamore@opensolaris.org 		return (DDI_SUCCESS);
214910913Sgdamore@opensolaris.org 	}
215010913Sgdamore@opensolaris.org 
215110913Sgdamore@opensolaris.org 	mutex_exit(&devc->mutex);
215210913Sgdamore@opensolaris.org 
215310913Sgdamore@opensolaris.org 	/* resume ac97 */
2154*11936Sgdamore@opensolaris.org 	ac97_reset(devc->ac97);
2155*11936Sgdamore@opensolaris.org 
2156*11936Sgdamore@opensolaris.org 	audio_dev_resume(devc->adev);
215710913Sgdamore@opensolaris.org 
215810913Sgdamore@opensolaris.org 	return (DDI_SUCCESS);
215910913Sgdamore@opensolaris.org }
216010913Sgdamore@opensolaris.org 
216110913Sgdamore@opensolaris.org int
emu10k_detach(emu10k_devc_t * devc)216210913Sgdamore@opensolaris.org emu10k_detach(emu10k_devc_t *devc)
216310913Sgdamore@opensolaris.org {
216410913Sgdamore@opensolaris.org 	if (audio_dev_unregister(devc->adev) != DDI_SUCCESS)
216510913Sgdamore@opensolaris.org 		return (DDI_FAILURE);
216610913Sgdamore@opensolaris.org 
216710913Sgdamore@opensolaris.org 	emu10k_destroy(devc);
216810913Sgdamore@opensolaris.org 	return (DDI_SUCCESS);
216910913Sgdamore@opensolaris.org }
217010913Sgdamore@opensolaris.org 
217110913Sgdamore@opensolaris.org int
emu10k_suspend(emu10k_devc_t * devc)217210913Sgdamore@opensolaris.org emu10k_suspend(emu10k_devc_t *devc)
217310913Sgdamore@opensolaris.org {
2174*11936Sgdamore@opensolaris.org 	audio_dev_suspend(devc->adev);
217510913Sgdamore@opensolaris.org 
217610913Sgdamore@opensolaris.org 	return (DDI_SUCCESS);
217710913Sgdamore@opensolaris.org }
217810913Sgdamore@opensolaris.org 
217910913Sgdamore@opensolaris.org static int emu10k_ddi_attach(dev_info_t *, ddi_attach_cmd_t);
218010913Sgdamore@opensolaris.org static int emu10k_ddi_detach(dev_info_t *, ddi_detach_cmd_t);
218110913Sgdamore@opensolaris.org static int emu10k_ddi_quiesce(dev_info_t *);
218210913Sgdamore@opensolaris.org 
218310913Sgdamore@opensolaris.org static struct dev_ops emu10k_dev_ops = {
218410913Sgdamore@opensolaris.org 	DEVO_REV,			/* rev */
218510913Sgdamore@opensolaris.org 	0,				/* refcnt */
218610913Sgdamore@opensolaris.org 	NULL,				/* getinfo */
218710913Sgdamore@opensolaris.org 	nulldev,			/* identify */
218810913Sgdamore@opensolaris.org 	nulldev,			/* probe */
218910913Sgdamore@opensolaris.org 	emu10k_ddi_attach,		/* attach */
219010913Sgdamore@opensolaris.org 	emu10k_ddi_detach,		/* detach */
219110913Sgdamore@opensolaris.org 	nodev,				/* reset */
219210913Sgdamore@opensolaris.org 	NULL,				/* cb_ops */
219310913Sgdamore@opensolaris.org 	NULL,				/* bus_ops */
219410913Sgdamore@opensolaris.org 	NULL,				/* power */
219510913Sgdamore@opensolaris.org 	emu10k_ddi_quiesce,		/* quiesce */
219610913Sgdamore@opensolaris.org };
219710913Sgdamore@opensolaris.org 
219810913Sgdamore@opensolaris.org static struct modldrv emu10k_modldrv = {
219910913Sgdamore@opensolaris.org 	&mod_driverops,			/* drv_modops */
220010913Sgdamore@opensolaris.org 	"Creative EMU10K Audio",	/* linkinfo */
220110913Sgdamore@opensolaris.org 	&emu10k_dev_ops,		/* dev_ops */
220210913Sgdamore@opensolaris.org };
220310913Sgdamore@opensolaris.org 
220410913Sgdamore@opensolaris.org static struct modlinkage modlinkage = {
220510913Sgdamore@opensolaris.org 	MODREV_1,
220610913Sgdamore@opensolaris.org 	{ &emu10k_modldrv, NULL }
220710913Sgdamore@opensolaris.org };
220810913Sgdamore@opensolaris.org 
220910913Sgdamore@opensolaris.org int
_init(void)221010913Sgdamore@opensolaris.org _init(void)
221110913Sgdamore@opensolaris.org {
221210913Sgdamore@opensolaris.org 	int rv;
221310913Sgdamore@opensolaris.org 
221410913Sgdamore@opensolaris.org 	audio_init_ops(&emu10k_dev_ops, EMU10K_NAME);
221510913Sgdamore@opensolaris.org 	if ((rv = mod_install(&modlinkage)) != 0) {
221610913Sgdamore@opensolaris.org 		audio_fini_ops(&emu10k_dev_ops);
221710913Sgdamore@opensolaris.org 	}
221810913Sgdamore@opensolaris.org 	return (rv);
221910913Sgdamore@opensolaris.org }
222010913Sgdamore@opensolaris.org 
222110913Sgdamore@opensolaris.org int
_fini(void)222210913Sgdamore@opensolaris.org _fini(void)
222310913Sgdamore@opensolaris.org {
222410913Sgdamore@opensolaris.org 	int rv;
222510913Sgdamore@opensolaris.org 
222610913Sgdamore@opensolaris.org 	if ((rv = mod_remove(&modlinkage)) == 0) {
222710913Sgdamore@opensolaris.org 		audio_fini_ops(&emu10k_dev_ops);
222810913Sgdamore@opensolaris.org 	}
222910913Sgdamore@opensolaris.org 	return (rv);
223010913Sgdamore@opensolaris.org }
223110913Sgdamore@opensolaris.org 
223210913Sgdamore@opensolaris.org int
_info(struct modinfo * modinfop)223310913Sgdamore@opensolaris.org _info(struct modinfo *modinfop)
223410913Sgdamore@opensolaris.org {
223510913Sgdamore@opensolaris.org 	return (mod_info(&modlinkage, modinfop));
223610913Sgdamore@opensolaris.org }
223710913Sgdamore@opensolaris.org 
223810913Sgdamore@opensolaris.org int
emu10k_ddi_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)223910913Sgdamore@opensolaris.org emu10k_ddi_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
224010913Sgdamore@opensolaris.org {
224110913Sgdamore@opensolaris.org 	switch (cmd) {
224210913Sgdamore@opensolaris.org 	case DDI_ATTACH:
224310913Sgdamore@opensolaris.org 		return (emu10k_attach(dip));
224410913Sgdamore@opensolaris.org 
224510913Sgdamore@opensolaris.org 	case DDI_RESUME:
224610913Sgdamore@opensolaris.org 		return (emu10k_resume(dip));
224710913Sgdamore@opensolaris.org 
224810913Sgdamore@opensolaris.org 	default:
224910913Sgdamore@opensolaris.org 		return (DDI_FAILURE);
225010913Sgdamore@opensolaris.org 	}
225110913Sgdamore@opensolaris.org }
225210913Sgdamore@opensolaris.org 
225310913Sgdamore@opensolaris.org int
emu10k_ddi_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)225410913Sgdamore@opensolaris.org emu10k_ddi_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
225510913Sgdamore@opensolaris.org {
225610913Sgdamore@opensolaris.org 	emu10k_devc_t *devc;
225710913Sgdamore@opensolaris.org 
225810913Sgdamore@opensolaris.org 	devc = ddi_get_driver_private(dip);
225910913Sgdamore@opensolaris.org 
226010913Sgdamore@opensolaris.org 	switch (cmd) {
226110913Sgdamore@opensolaris.org 	case DDI_DETACH:
226210913Sgdamore@opensolaris.org 		return (emu10k_detach(devc));
226310913Sgdamore@opensolaris.org 
226410913Sgdamore@opensolaris.org 	case DDI_SUSPEND:
226510913Sgdamore@opensolaris.org 		return (emu10k_suspend(devc));
226610913Sgdamore@opensolaris.org 
226710913Sgdamore@opensolaris.org 	default:
226810913Sgdamore@opensolaris.org 		return (DDI_FAILURE);
226910913Sgdamore@opensolaris.org 	}
227010913Sgdamore@opensolaris.org }
227110913Sgdamore@opensolaris.org 
227210913Sgdamore@opensolaris.org int
emu10k_ddi_quiesce(dev_info_t * dip)227310913Sgdamore@opensolaris.org emu10k_ddi_quiesce(dev_info_t *dip)
227410913Sgdamore@opensolaris.org {
227510913Sgdamore@opensolaris.org 	emu10k_devc_t *devc;
227610913Sgdamore@opensolaris.org 
227710913Sgdamore@opensolaris.org 	devc = ddi_get_driver_private(dip);
227810913Sgdamore@opensolaris.org 
227910913Sgdamore@opensolaris.org 	/* stop all voices */
228010913Sgdamore@opensolaris.org 	for (int i = 0; i < 64; i++) {
228110913Sgdamore@opensolaris.org 		emu10k_write_reg(devc, VEDS, i, 0);
228210913Sgdamore@opensolaris.org 	}
228310913Sgdamore@opensolaris.org 	for (int i = 0; i < 64; i++) {
228410913Sgdamore@opensolaris.org 		emu10k_write_reg(devc, VTFT, i, 0);
228510913Sgdamore@opensolaris.org 		emu10k_write_reg(devc, CVCF, i, 0);
228610913Sgdamore@opensolaris.org 		emu10k_write_reg(devc, PTAB, i, 0);
228710913Sgdamore@opensolaris.org 		emu10k_write_reg(devc, CPF, i, 0);
228810913Sgdamore@opensolaris.org 	}
228910913Sgdamore@opensolaris.org 
229010913Sgdamore@opensolaris.org 	/*
229110913Sgdamore@opensolaris.org 	 * Turn off the hardware
229210913Sgdamore@opensolaris.org 	 */
229310913Sgdamore@opensolaris.org 	OUTL(devc,
229410913Sgdamore@opensolaris.org 	    HCFG_LOCKSOUNDCACHE | HCFG_LOCKTANKCACHE_MASK |
229510913Sgdamore@opensolaris.org 	    HCFG_MUTEBUTTONENABLE, devc->regs + HCFG);
229610913Sgdamore@opensolaris.org 
229710913Sgdamore@opensolaris.org 	/* stop ADC recording */
229810913Sgdamore@opensolaris.org 	emu10k_write_reg(devc, ADCSR, 0, 0x0);
229910913Sgdamore@opensolaris.org 	emu10k_write_reg(devc, ADCBA, 0, 0x0);
230010913Sgdamore@opensolaris.org 	emu10k_write_reg(devc, ADCBA, 0, 0x0);
230110913Sgdamore@opensolaris.org 
230210913Sgdamore@opensolaris.org 	emu10k_write_reg(devc, PTBA, 0, 0);
230310913Sgdamore@opensolaris.org 
230410913Sgdamore@opensolaris.org 	return (DDI_SUCCESS);
230510913Sgdamore@opensolaris.org }
2306