xref: /illumos-gate/usr/src/uts/common/io/audio/drv/audioens/audioens.c (revision 239924d360a544a40689b6b360d1183a0a936d97)
188447a05SGarrett D'Amore /*
288447a05SGarrett D'Amore  * CDDL HEADER START
388447a05SGarrett D'Amore  *
488447a05SGarrett D'Amore  * The contents of this file are subject to the terms of the
588447a05SGarrett D'Amore  * Common Development and Distribution License (the "License").
688447a05SGarrett D'Amore  * You may not use this file except in compliance with the License.
788447a05SGarrett D'Amore  *
888447a05SGarrett D'Amore  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
988447a05SGarrett D'Amore  * or http://www.opensolaris.org/os/licensing.
1088447a05SGarrett D'Amore  * See the License for the specific language governing permissions
1188447a05SGarrett D'Amore  * and limitations under the License.
1288447a05SGarrett D'Amore  *
1388447a05SGarrett D'Amore  * When distributing Covered Code, include this CDDL HEADER in each
1488447a05SGarrett D'Amore  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1588447a05SGarrett D'Amore  * If applicable, add the following below this CDDL HEADER, with the
1688447a05SGarrett D'Amore  * fields enclosed by brackets "[]" replaced with your own identifying
1788447a05SGarrett D'Amore  * information: Portions Copyright [yyyy] [name of copyright owner]
1888447a05SGarrett D'Amore  *
1988447a05SGarrett D'Amore  * CDDL HEADER END
2088447a05SGarrett D'Amore  */
2188447a05SGarrett D'Amore /*
22*239924d3SGarrett D'Amore  * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
2388447a05SGarrett D'Amore  */
2488447a05SGarrett D'Amore /*
2588447a05SGarrett D'Amore  * Purpose: Creative/Ensoniq AudioPCI97  driver (ES1371/ES1373)
2688447a05SGarrett D'Amore  *
2788447a05SGarrett D'Amore  * This driver is used with the original Ensoniq AudioPCI97 card and many
2888447a05SGarrett D'Amore  * PCI based Sound Blaster cards by Creative Technologies. For example
2988447a05SGarrett D'Amore  * Sound Blaster PCI128 and Creative/Ectiva EV1938.
3088447a05SGarrett D'Amore  */
3188447a05SGarrett D'Amore 
3288447a05SGarrett D'Amore /*
3388447a05SGarrett D'Amore  * This file is part of Open Sound System
3488447a05SGarrett D'Amore  *
3588447a05SGarrett D'Amore  * Copyright (C) 4Front Technologies 1996-2008.
3688447a05SGarrett D'Amore  *
3788447a05SGarrett D'Amore  * This software is released under CDDL 1.0 source license.
3888447a05SGarrett D'Amore  * See the COPYING file included in the main directory of this source
3988447a05SGarrett D'Amore  * distribution for the license terms and conditions.
4088447a05SGarrett D'Amore  */
4188447a05SGarrett D'Amore 
4288447a05SGarrett D'Amore #include <sys/audio/audio_driver.h>
4388447a05SGarrett D'Amore #include <sys/audio/ac97.h>
4488447a05SGarrett D'Amore #include <sys/note.h>
4588447a05SGarrett D'Amore #include <sys/pci.h>
4688447a05SGarrett D'Amore #include "audioens.h"
4788447a05SGarrett D'Amore 
4888447a05SGarrett D'Amore /*
4988447a05SGarrett D'Amore  * Set the latency to 32, 64, 96, 128 clocks - some APCI97 devices exhibit
5088447a05SGarrett D'Amore  * garbled audio in some cases and setting the latency to higer values fixes it
5188447a05SGarrett D'Amore  * Values: 32, 64, 96, 128 - Default: 64 (or defined by bios)
5288447a05SGarrett D'Amore  */
5388447a05SGarrett D'Amore int audioens_latency = 0;
5488447a05SGarrett D'Amore 
5588447a05SGarrett D'Amore /*
5688447a05SGarrett D'Amore  * Enable SPDIF port on SoundBlaster 128D or Sound Blaster Digital-4.1 models
5788447a05SGarrett D'Amore  * Values: 1=Enable 0=Disable Default: 0
5888447a05SGarrett D'Amore  */
5988447a05SGarrett D'Amore int audioens_spdif = 0;
6088447a05SGarrett D'Amore 
6188447a05SGarrett D'Amore /*
6288447a05SGarrett D'Amore  * Note: Latest devices can support SPDIF with AC3 pass thru.
6388447a05SGarrett D'Amore  * However, in order to do this, one of the two DMA engines must be
6488447a05SGarrett D'Amore  * dedicated to this, which would prevent the card from supporting 4
6588447a05SGarrett D'Amore  * channel audio.  For now we don't bother with the AC3 pass through
6688447a05SGarrett D'Amore  * mode, and instead just focus on 4 channel support.  In the future,
6788447a05SGarrett D'Amore  * this could be selectable via a property.
6888447a05SGarrett D'Amore  */
6988447a05SGarrett D'Amore 
7088447a05SGarrett D'Amore #define	ENSONIQ_VENDOR_ID	0x1274
7188447a05SGarrett D'Amore #define	CREATIVE_VENDOR_ID	0x1102
7288447a05SGarrett D'Amore #define	ECTIVA_VENDOR_ID	0x1102
7388447a05SGarrett D'Amore #define	ENSONIQ_ES1371		0x1371
7488447a05SGarrett D'Amore #define	ENSONIQ_ES5880		0x8001
7588447a05SGarrett D'Amore #define	ENSONIQ_ES5880A		0x8002
7688447a05SGarrett D'Amore #define	ENSONIQ_ES5880B		0x5880
7788447a05SGarrett D'Amore #define	ECTIVA_ES1938		0x8938
7888447a05SGarrett D'Amore 
7988447a05SGarrett D'Amore #define	DEFRATE			48000
8088447a05SGarrett D'Amore #define	DRVNAME			"audioens"
8188447a05SGarrett D'Amore 
8288447a05SGarrett D'Amore typedef struct audioens_port
8388447a05SGarrett D'Amore {
8488447a05SGarrett D'Amore 	/* Audio parameters */
8588447a05SGarrett D'Amore 	int			speed;
8688447a05SGarrett D'Amore 
8788447a05SGarrett D'Amore 	int			num;
8888447a05SGarrett D'Amore #define	PORT_DAC		0
8988447a05SGarrett D'Amore #define	PORT_ADC		1
9088447a05SGarrett D'Amore #define	PORT_MAX		PORT_ADC
9188447a05SGarrett D'Amore 
9288447a05SGarrett D'Amore 	caddr_t			kaddr;
9388447a05SGarrett D'Amore 	uint32_t		paddr;
9488447a05SGarrett D'Amore 	ddi_acc_handle_t	acch;
9588447a05SGarrett D'Amore 	ddi_dma_handle_t	dmah;
9688447a05SGarrett D'Amore 	int			nchan;
9788447a05SGarrett D'Amore 	unsigned		nframes;
9888447a05SGarrett D'Amore 	unsigned		frameno;
9988447a05SGarrett D'Amore 	uint64_t		count;
10088447a05SGarrett D'Amore 
10188447a05SGarrett D'Amore 	struct audioens_dev	*dev;
10288447a05SGarrett D'Amore 	audio_engine_t	*engine;
10388447a05SGarrett D'Amore } audioens_port_t;
10488447a05SGarrett D'Amore 
10588447a05SGarrett D'Amore typedef struct audioens_dev
10688447a05SGarrett D'Amore {
10788447a05SGarrett D'Amore 	audio_dev_t		*osdev;
10888447a05SGarrett D'Amore 	kmutex_t		mutex;
10988447a05SGarrett D'Amore 	uint16_t		devid;
11088447a05SGarrett D'Amore 	uint8_t			revision;
11188447a05SGarrett D'Amore 	dev_info_t		*dip;
11288447a05SGarrett D'Amore 
11388447a05SGarrett D'Amore 	audioens_port_t		port[PORT_MAX + 1];
11488447a05SGarrett D'Amore 
11588447a05SGarrett D'Amore 	ac97_t			*ac97;
11688447a05SGarrett D'Amore 
11788447a05SGarrett D'Amore 	caddr_t			regs;
11888447a05SGarrett D'Amore 	ddi_acc_handle_t	acch;
11988447a05SGarrett D'Amore } audioens_dev_t;
12088447a05SGarrett D'Amore 
12188447a05SGarrett D'Amore static ddi_device_acc_attr_t acc_attr = {
12288447a05SGarrett D'Amore 	DDI_DEVICE_ATTR_V0,
12388447a05SGarrett D'Amore 	DDI_STRUCTURE_LE_ACC,
12488447a05SGarrett D'Amore 	DDI_STRICTORDER_ACC
12588447a05SGarrett D'Amore };
12688447a05SGarrett D'Amore 
12788447a05SGarrett D'Amore static ddi_device_acc_attr_t buf_attr = {
12888447a05SGarrett D'Amore 	DDI_DEVICE_ATTR_V0,
12988447a05SGarrett D'Amore 	DDI_NEVERSWAP_ACC,
13088447a05SGarrett D'Amore 	DDI_STRICTORDER_ACC
13188447a05SGarrett D'Amore };
13288447a05SGarrett D'Amore 
13388447a05SGarrett D'Amore /*
13488447a05SGarrett D'Amore  * The hardware appears to be able to address up to 16-bits worth of longwords,
13568c47f65SGarrett D'Amore  * giving a total address space of 256K.  But we need substantially less.
13688447a05SGarrett D'Amore  */
13768c47f65SGarrett D'Amore #define	AUDIOENS_BUF_LEN	(16384)
13888447a05SGarrett D'Amore 
13988447a05SGarrett D'Amore static ddi_dma_attr_t dma_attr = {
14088447a05SGarrett D'Amore 	DMA_ATTR_VERSION,	/* dma_attr_version */
14188447a05SGarrett D'Amore 	0x0,			/* dma_attr_addr_lo */
14288447a05SGarrett D'Amore 	0xffffffffU,		/* dma_attr_addr_hi */
14388447a05SGarrett D'Amore 	0x3ffff,		/* dma_attr_count_max */
14488447a05SGarrett D'Amore 	0x8,			/* dma_attr_align */
14588447a05SGarrett D'Amore 	0x7f,			/* dma_attr_burstsizes */
14688447a05SGarrett D'Amore 	0x1,			/* dma_attr_minxfer */
14788447a05SGarrett D'Amore 	0x3ffff,		/* dma_attr_maxxfer */
14888447a05SGarrett D'Amore 	0x3ffff,		/* dma_attr_seg */
14988447a05SGarrett D'Amore 	0x1,			/* dma_attr_sgllen */
15088447a05SGarrett D'Amore 	0x1,			/* dma_attr_granular */
15188447a05SGarrett D'Amore 	0			/* dma_attr_flags */
15288447a05SGarrett D'Amore };
15388447a05SGarrett D'Amore 
15488447a05SGarrett D'Amore #define	GET8(dev, offset)	\
15588447a05SGarrett D'Amore 	ddi_get8(dev->acch, (uint8_t *)(dev->regs + (offset)))
15688447a05SGarrett D'Amore #define	GET16(dev, offset)	\
15788447a05SGarrett D'Amore 	ddi_get16(dev->acch, (uint16_t *)(void *)(dev->regs + (offset)))
15888447a05SGarrett D'Amore #define	GET32(dev, offset)	\
15988447a05SGarrett D'Amore 	ddi_get32(dev->acch, (uint32_t *)(void *)(dev->regs + (offset)))
16088447a05SGarrett D'Amore #define	PUT8(dev, offset, v)	\
16188447a05SGarrett D'Amore 	ddi_put8(dev->acch, (uint8_t *)(dev->regs + (offset)), v)
16288447a05SGarrett D'Amore #define	PUT16(dev, offset, v)	\
16388447a05SGarrett D'Amore 	ddi_put16(dev->acch, (uint16_t *)(void *)(dev->regs + (offset)), v)
16488447a05SGarrett D'Amore #define	PUT32(dev, offset, v)	\
16588447a05SGarrett D'Amore 	ddi_put32(dev->acch, (uint32_t *)(void *)(dev->regs + (offset)), v)
16688447a05SGarrett D'Amore 
16788447a05SGarrett D'Amore #define	CLR8(dev, offset, v)	PUT8(dev, offset, GET8(dev, offset) & ~(v))
16888447a05SGarrett D'Amore #define	SET8(dev, offset, v)	PUT8(dev, offset, GET8(dev, offset) | (v))
16988447a05SGarrett D'Amore #define	CLR32(dev, offset, v)	PUT32(dev, offset, GET32(dev, offset) & ~(v))
17088447a05SGarrett D'Amore #define	SET32(dev, offset, v)	PUT32(dev, offset, GET32(dev, offset) | (v))
17188447a05SGarrett D'Amore 
17288447a05SGarrett D'Amore static void audioens_init_hw(audioens_dev_t *);
17388447a05SGarrett D'Amore 
17488447a05SGarrett D'Amore static uint16_t
17588447a05SGarrett D'Amore audioens_rd97(void *dev_, uint8_t wAddr)
17688447a05SGarrett D'Amore {
17788447a05SGarrett D'Amore 	audioens_dev_t *dev = dev_;
17888447a05SGarrett D'Amore 	int i, dtemp;
17988447a05SGarrett D'Amore 
18088447a05SGarrett D'Amore 	mutex_enter(&dev->mutex);
18188447a05SGarrett D'Amore 	dtemp = GET32(dev, CONC_dCODECCTL_OFF);
18288447a05SGarrett D'Amore 	/* wait for WIP to go away saving the current state for later */
18388447a05SGarrett D'Amore 	for (i = 0; i < 0x100UL; ++i) {
18488447a05SGarrett D'Amore 		dtemp = GET32(dev, CONC_dCODECCTL_OFF);
18588447a05SGarrett D'Amore 		if ((dtemp & (1UL << 30)) == 0)
18688447a05SGarrett D'Amore 			break;
18788447a05SGarrett D'Amore 	}
18888447a05SGarrett D'Amore 
18988447a05SGarrett D'Amore 	/* write addr w/data=0 and assert read request ... */
19088447a05SGarrett D'Amore 	PUT32(dev, CONC_dCODECCTL_OFF, ((int)wAddr << 16) | (1UL << 23));
19188447a05SGarrett D'Amore 
19288447a05SGarrett D'Amore 	/* now wait for the data (RDY) */
19388447a05SGarrett D'Amore 	for (i = 0; i < 0x100UL; ++i) {
19488447a05SGarrett D'Amore 		dtemp = GET32(dev, CONC_dCODECCTL_OFF);
19588447a05SGarrett D'Amore 		if (dtemp & (1UL << 31))
19688447a05SGarrett D'Amore 			break;
19788447a05SGarrett D'Amore 	}
19888447a05SGarrett D'Amore 	dtemp = GET32(dev, CONC_dCODECCTL_OFF);
19988447a05SGarrett D'Amore 	mutex_exit(&dev->mutex);
20088447a05SGarrett D'Amore 
20188447a05SGarrett D'Amore 	return (dtemp & 0xffff);
20288447a05SGarrett D'Amore }
20388447a05SGarrett D'Amore 
20488447a05SGarrett D'Amore static void
20588447a05SGarrett D'Amore audioens_wr97(void *dev_, uint8_t wAddr, uint16_t wData)
20688447a05SGarrett D'Amore {
20788447a05SGarrett D'Amore 	audioens_dev_t *dev = dev_;
20888447a05SGarrett D'Amore 	int i, dtemp;
20988447a05SGarrett D'Amore 
21088447a05SGarrett D'Amore 	mutex_enter(&dev->mutex);
21188447a05SGarrett D'Amore 	/* wait for WIP to go away */
21288447a05SGarrett D'Amore 	for (i = 0; i < 0x100UL; ++i) {
21388447a05SGarrett D'Amore 		dtemp = GET32(dev, CONC_dCODECCTL_OFF);
21488447a05SGarrett D'Amore 		if ((dtemp & (1UL << 30)) == 0)
21588447a05SGarrett D'Amore 			break;
21688447a05SGarrett D'Amore 	}
21788447a05SGarrett D'Amore 
21888447a05SGarrett D'Amore 	PUT32(dev, CONC_dCODECCTL_OFF, ((int)wAddr << 16) | wData);
21988447a05SGarrett D'Amore 
22088447a05SGarrett D'Amore 	mutex_exit(&dev->mutex);
22188447a05SGarrett D'Amore }
22288447a05SGarrett D'Amore 
22388447a05SGarrett D'Amore static unsigned short
22488447a05SGarrett D'Amore SRCRegRead(audioens_dev_t *dev, unsigned short reg)
22588447a05SGarrett D'Amore {
22688447a05SGarrett D'Amore 	int i, dtemp;
22788447a05SGarrett D'Amore 
22888447a05SGarrett D'Amore 	dtemp = GET32(dev, CONC_dSRCIO_OFF);
22988447a05SGarrett D'Amore 	/* wait for ready */
23088447a05SGarrett D'Amore 	for (i = 0; i < SRC_IOPOLL_COUNT; ++i) {
23188447a05SGarrett D'Amore 		dtemp = GET32(dev, CONC_dSRCIO_OFF);
23288447a05SGarrett D'Amore 		if ((dtemp & SRC_BUSY) == 0)
23388447a05SGarrett D'Amore 			break;
23488447a05SGarrett D'Amore 	}
23588447a05SGarrett D'Amore 
23688447a05SGarrett D'Amore 	/* assert a read request */
23788447a05SGarrett D'Amore 	PUT32(dev, CONC_dSRCIO_OFF, (dtemp & SRC_CTLMASK) | ((int)reg << 25));
23888447a05SGarrett D'Amore 
23988447a05SGarrett D'Amore 	/* now wait for the data */
24088447a05SGarrett D'Amore 	for (i = 0; i < SRC_IOPOLL_COUNT; ++i) {
24188447a05SGarrett D'Amore 		dtemp = GET32(dev, CONC_dSRCIO_OFF);
24288447a05SGarrett D'Amore 		if ((dtemp & SRC_BUSY) == 0)
24388447a05SGarrett D'Amore 			break;
24488447a05SGarrett D'Amore 	}
24588447a05SGarrett D'Amore 
24688447a05SGarrett D'Amore 	return ((unsigned short) dtemp);
24788447a05SGarrett D'Amore }
24888447a05SGarrett D'Amore 
24988447a05SGarrett D'Amore static void
25088447a05SGarrett D'Amore SRCRegWrite(audioens_dev_t *dev, unsigned short reg, unsigned short val)
25188447a05SGarrett D'Amore {
25288447a05SGarrett D'Amore 	int i, dtemp;
25388447a05SGarrett D'Amore 	int writeval;
25488447a05SGarrett D'Amore 
25588447a05SGarrett D'Amore 	dtemp = GET32(dev, CONC_dSRCIO_OFF);
25688447a05SGarrett D'Amore 	/* wait for ready */
25788447a05SGarrett D'Amore 	for (i = 0; i < SRC_IOPOLL_COUNT; ++i) {
25888447a05SGarrett D'Amore 		dtemp = GET32(dev, CONC_dSRCIO_OFF);
25988447a05SGarrett D'Amore 		if ((dtemp & SRC_BUSY) == 0)
26088447a05SGarrett D'Amore 			break;
26188447a05SGarrett D'Amore 	}
26288447a05SGarrett D'Amore 
26388447a05SGarrett D'Amore 	/* assert the write request */
26488447a05SGarrett D'Amore 	writeval = (dtemp & SRC_CTLMASK) | SRC_WENABLE |
26588447a05SGarrett D'Amore 	    ((int)reg << 25) | val;
26688447a05SGarrett D'Amore 	PUT32(dev, CONC_dSRCIO_OFF, writeval);
26788447a05SGarrett D'Amore }
26888447a05SGarrett D'Amore 
26988447a05SGarrett D'Amore static void
27088447a05SGarrett D'Amore SRCSetRate(audioens_dev_t *dev, unsigned char base, unsigned short rate)
27188447a05SGarrett D'Amore {
27288447a05SGarrett D'Amore 	int i, freq, dtemp;
27388447a05SGarrett D'Amore 	unsigned short N, truncM, truncStart;
27488447a05SGarrett D'Amore 
27588447a05SGarrett D'Amore 	if (base != SRC_ADC_BASE) {
27688447a05SGarrett D'Amore 		/* freeze the channel */
27788447a05SGarrett D'Amore 		dtemp = (base == SRC_DAC1_BASE) ?
27888447a05SGarrett D'Amore 		    SRC_DAC1FREEZE : SRC_DAC2FREEZE;
27988447a05SGarrett D'Amore 		for (i = 0; i < SRC_IOPOLL_COUNT; ++i) {
28088447a05SGarrett D'Amore 			if (!(GET32(dev, CONC_dSRCIO_OFF) & SRC_BUSY))
28188447a05SGarrett D'Amore 				break;
28288447a05SGarrett D'Amore 		}
28388447a05SGarrett D'Amore 		PUT32(dev, CONC_dSRCIO_OFF,
28488447a05SGarrett D'Amore 		    (GET32(dev, CONC_dSRCIO_OFF) & SRC_CTLMASK) | dtemp);
28588447a05SGarrett D'Amore 
28688447a05SGarrett D'Amore 		/* calculate new frequency and write it - preserve accum */
28788447a05SGarrett D'Amore 		freq = ((int)rate << 16) / 3000U;
28888447a05SGarrett D'Amore 		SRCRegWrite(dev, (unsigned short) base + SRC_INT_REGS_OFF,
28988447a05SGarrett D'Amore 		    (SRCRegRead(dev, (unsigned short) base + SRC_INT_REGS_OFF)
29088447a05SGarrett D'Amore 		    & 0x00ffU) | ((unsigned short) (freq >> 6) & 0xfc00));
29188447a05SGarrett D'Amore 		SRCRegWrite(dev, (unsigned short) base + SRC_VFREQ_FRAC_OFF,
29288447a05SGarrett D'Amore 		    (unsigned short) freq >> 1);
29388447a05SGarrett D'Amore 
29488447a05SGarrett D'Amore 		/* un-freeze the channel */
29588447a05SGarrett D'Amore 		for (i = 0; i < SRC_IOPOLL_COUNT; ++i)
29688447a05SGarrett D'Amore 			if (!(GET32(dev, CONC_dSRCIO_OFF) & SRC_BUSY))
29788447a05SGarrett D'Amore 				break;
29888447a05SGarrett D'Amore 		PUT32(dev, CONC_dSRCIO_OFF,
29988447a05SGarrett D'Amore 		    (GET32(dev, CONC_dSRCIO_OFF) & SRC_CTLMASK) & ~dtemp);
30088447a05SGarrett D'Amore 	} else {
30188447a05SGarrett D'Amore 		/* derive oversample ratio */
30288447a05SGarrett D'Amore 		N = rate / 3000U;
30388447a05SGarrett D'Amore 		if (N == 15 || N == 13 || N == 11 || N == 9)
30488447a05SGarrett D'Amore 			--N;
30588447a05SGarrett D'Amore 
30688447a05SGarrett D'Amore 		/* truncate the filter and write n/trunc_start */
30788447a05SGarrett D'Amore 		truncM = (21 * N - 1) | 1;
30888447a05SGarrett D'Amore 		if (rate >= 24000U) {
30988447a05SGarrett D'Amore 			if (truncM > 239)
31088447a05SGarrett D'Amore 				truncM = 239;
31188447a05SGarrett D'Amore 			truncStart = (239 - truncM) >> 1;
31288447a05SGarrett D'Amore 
31388447a05SGarrett D'Amore 			SRCRegWrite(dev, base + SRC_TRUNC_N_OFF,
31488447a05SGarrett D'Amore 			    (truncStart << 9) | (N << 4));
31588447a05SGarrett D'Amore 		} else {
31688447a05SGarrett D'Amore 			if (truncM > 119)
31788447a05SGarrett D'Amore 				truncM = 119;
31888447a05SGarrett D'Amore 			truncStart = (119 - truncM) >> 1;
31988447a05SGarrett D'Amore 
32088447a05SGarrett D'Amore 			SRCRegWrite(dev, base + SRC_TRUNC_N_OFF,
32188447a05SGarrett D'Amore 			    0x8000U | (truncStart << 9) | (N << 4));
32288447a05SGarrett D'Amore 		}
32388447a05SGarrett D'Amore 
32488447a05SGarrett D'Amore 		/* calculate new frequency and write it - preserve accum */
32588447a05SGarrett D'Amore 		freq = ((48000UL << 16) / rate) * N;
32688447a05SGarrett D'Amore 		SRCRegWrite(dev, base + SRC_INT_REGS_OFF,
32788447a05SGarrett D'Amore 		    (SRCRegRead(dev, (unsigned short) base + SRC_INT_REGS_OFF)
32888447a05SGarrett D'Amore 		    & 0x00ff) | ((unsigned short) (freq >> 6) & 0xfc00));
32988447a05SGarrett D'Amore 		SRCRegWrite(dev, base + SRC_VFREQ_FRAC_OFF,
33088447a05SGarrett D'Amore 		    (unsigned short) freq >> 1);
33188447a05SGarrett D'Amore 
33288447a05SGarrett D'Amore 		SRCRegWrite(dev, SRC_ADC_VOL_L, N << 8);
33388447a05SGarrett D'Amore 		SRCRegWrite(dev, SRC_ADC_VOL_R, N << 8);
33488447a05SGarrett D'Amore 	}
33588447a05SGarrett D'Amore }
33688447a05SGarrett D'Amore 
33788447a05SGarrett D'Amore static void
33888447a05SGarrett D'Amore SRCInit(audioens_dev_t *dev)
33988447a05SGarrett D'Amore {
34088447a05SGarrett D'Amore 	int i;
34188447a05SGarrett D'Amore 
34288447a05SGarrett D'Amore 	/* Clear all SRC RAM then init - keep SRC disabled until done */
34388447a05SGarrett D'Amore 	for (i = 0; i < SRC_IOPOLL_COUNT; ++i) {
34488447a05SGarrett D'Amore 		if (!(GET32(dev, CONC_dSRCIO_OFF) & SRC_BUSY))
34588447a05SGarrett D'Amore 			break;
34688447a05SGarrett D'Amore 	}
34788447a05SGarrett D'Amore 	PUT32(dev, CONC_dSRCIO_OFF, SRC_DISABLE);
34888447a05SGarrett D'Amore 
34988447a05SGarrett D'Amore 	for (i = 0; i < 0x80; ++i)
35088447a05SGarrett D'Amore 		SRCRegWrite(dev, (unsigned short) i, 0U);
35188447a05SGarrett D'Amore 
35288447a05SGarrett D'Amore 	SRCRegWrite(dev, SRC_DAC1_BASE + SRC_TRUNC_N_OFF, 16 << 4);
35388447a05SGarrett D'Amore 	SRCRegWrite(dev, SRC_DAC1_BASE + SRC_INT_REGS_OFF, 16 << 10);
35488447a05SGarrett D'Amore 	SRCRegWrite(dev, SRC_DAC2_BASE + SRC_TRUNC_N_OFF, 16 << 4);
35588447a05SGarrett D'Amore 	SRCRegWrite(dev, SRC_DAC2_BASE + SRC_INT_REGS_OFF, 16 << 10);
35688447a05SGarrett D'Amore 	SRCRegWrite(dev, SRC_DAC1_VOL_L, 1 << 12);
35788447a05SGarrett D'Amore 	SRCRegWrite(dev, SRC_DAC1_VOL_R, 1 << 12);
35888447a05SGarrett D'Amore 	SRCRegWrite(dev, SRC_DAC2_VOL_L, 1 << 12);
35988447a05SGarrett D'Amore 	SRCRegWrite(dev, SRC_DAC2_VOL_R, 1 << 12);
36088447a05SGarrett D'Amore 	SRCRegWrite(dev, SRC_ADC_VOL_L, 1 << 12);
36188447a05SGarrett D'Amore 	SRCRegWrite(dev, SRC_ADC_VOL_R, 1 << 12);
36288447a05SGarrett D'Amore 
36388447a05SGarrett D'Amore 	/* default some rates */
36488447a05SGarrett D'Amore 	SRCSetRate(dev, SRC_DAC1_BASE, 48000);
36588447a05SGarrett D'Amore 	SRCSetRate(dev, SRC_DAC2_BASE, 48000);
36688447a05SGarrett D'Amore 	SRCSetRate(dev, SRC_ADC_BASE, 48000);
36788447a05SGarrett D'Amore 
36888447a05SGarrett D'Amore 	/* now enable the whole deal */
36988447a05SGarrett D'Amore 	for (i = 0; i < SRC_IOPOLL_COUNT; ++i) {
37088447a05SGarrett D'Amore 		if (!(GET32(dev, CONC_dSRCIO_OFF) & SRC_BUSY))
37188447a05SGarrett D'Amore 			break;
37288447a05SGarrett D'Amore 	}
37388447a05SGarrett D'Amore 	PUT32(dev, CONC_dSRCIO_OFF, 0);
37488447a05SGarrett D'Amore }
37588447a05SGarrett D'Amore 
37688447a05SGarrett D'Amore static void
37788447a05SGarrett D'Amore audioens_writemem(audioens_dev_t *dev, uint32_t page, uint32_t offs,
37888447a05SGarrett D'Amore     uint32_t data)
37988447a05SGarrett D'Amore {
38088447a05SGarrett D'Amore 	/* Select memory page */
38188447a05SGarrett D'Amore 	PUT32(dev, CONC_bMEMPAGE_OFF, page);
38288447a05SGarrett D'Amore 	PUT32(dev, offs, data);
38388447a05SGarrett D'Amore }
38488447a05SGarrett D'Amore 
38588447a05SGarrett D'Amore static uint32_t
38688447a05SGarrett D'Amore audioens_readmem(audioens_dev_t *dev, uint32_t page, uint32_t offs)
38788447a05SGarrett D'Amore {
38888447a05SGarrett D'Amore 	PUT32(dev, CONC_bMEMPAGE_OFF, page);	/* Select memory page */
38988447a05SGarrett D'Amore 	return (GET32(dev, offs));
39088447a05SGarrett D'Amore }
39188447a05SGarrett D'Amore 
39288447a05SGarrett D'Amore /*
39388447a05SGarrett D'Amore  * Audio routines
39488447a05SGarrett D'Amore  */
39588447a05SGarrett D'Amore static int
39688447a05SGarrett D'Amore audioens_format(void *arg)
39788447a05SGarrett D'Amore {
39888447a05SGarrett D'Amore 	_NOTE(ARGUNUSED(arg));
39988447a05SGarrett D'Amore 
40088447a05SGarrett D'Amore 	/* hardware can also do AUDIO_FORMAT_U8, but no need for it */
40188447a05SGarrett D'Amore 	return (AUDIO_FORMAT_S16_LE);
40288447a05SGarrett D'Amore }
40388447a05SGarrett D'Amore 
40488447a05SGarrett D'Amore static int
40588447a05SGarrett D'Amore audioens_channels(void *arg)
40688447a05SGarrett D'Amore {
40788447a05SGarrett D'Amore 	audioens_port_t *port = arg;
40888447a05SGarrett D'Amore 
40988447a05SGarrett D'Amore 	return (port->nchan);
41088447a05SGarrett D'Amore }
41188447a05SGarrett D'Amore 
41288447a05SGarrett D'Amore static int
41388447a05SGarrett D'Amore audioens_rate(void *arg)
41488447a05SGarrett D'Amore {
41588447a05SGarrett D'Amore 	audioens_port_t *port = arg;
41688447a05SGarrett D'Amore 
41788447a05SGarrett D'Amore 	return (port->speed);
41888447a05SGarrett D'Amore }
41988447a05SGarrett D'Amore 
42068c47f65SGarrett D'Amore static int
42168c47f65SGarrett D'Amore audioens_open(void *arg, int flag, unsigned *nframes, caddr_t *bufp)
42288447a05SGarrett D'Amore {
42368c47f65SGarrett D'Amore 	audioens_port_t	*port = arg;
42488447a05SGarrett D'Amore 	audioens_dev_t	*dev = port->dev;
42588447a05SGarrett D'Amore 
42668c47f65SGarrett D'Amore 	_NOTE(ARGUNUSED(flag));
42768c47f65SGarrett D'Amore 
42868c47f65SGarrett D'Amore 	mutex_enter(&dev->mutex);
42968c47f65SGarrett D'Amore 
43068c47f65SGarrett D'Amore 	port->nframes = AUDIOENS_BUF_LEN / (port->nchan * sizeof (int16_t));
43168c47f65SGarrett D'Amore 	port->count = 0;
43268c47f65SGarrett D'Amore 
43368c47f65SGarrett D'Amore 	*nframes = port->nframes;
43468c47f65SGarrett D'Amore 	*bufp = port->kaddr;
43568c47f65SGarrett D'Amore 	mutex_exit(&dev->mutex);
43668c47f65SGarrett D'Amore 
43768c47f65SGarrett D'Amore 	return (0);
43868c47f65SGarrett D'Amore }
43968c47f65SGarrett D'Amore 
44068c47f65SGarrett D'Amore static int
44168c47f65SGarrett D'Amore audioens_start(void *arg)
44268c47f65SGarrett D'Amore {
44368c47f65SGarrett D'Amore 	audioens_port_t *port = arg;
44468c47f65SGarrett D'Amore 	audioens_dev_t *dev = port->dev;
44568c47f65SGarrett D'Amore 	uint32_t tmp;
44668c47f65SGarrett D'Amore 
44768c47f65SGarrett D'Amore 	mutex_enter(&dev->mutex);
44888447a05SGarrett D'Amore 
44988447a05SGarrett D'Amore 	switch (port->num) {
45088447a05SGarrett D'Amore 	case PORT_DAC:
45188447a05SGarrett D'Amore 		/* Set physical address of the DMA buffer */
45288447a05SGarrett D'Amore 		audioens_writemem(dev, CONC_DAC1CTL_PAGE, CONC_dDAC1PADDR_OFF,
45388447a05SGarrett D'Amore 		    port->paddr);
45488447a05SGarrett D'Amore 		audioens_writemem(dev, CONC_DAC2CTL_PAGE, CONC_dDAC2PADDR_OFF,
45588447a05SGarrett D'Amore 		    port->paddr + (port->nframes * sizeof (int16_t) * 2));
45688447a05SGarrett D'Amore 
45788447a05SGarrett D'Amore 		/* Set DAC rate */
45888447a05SGarrett D'Amore 		SRCSetRate(dev, SRC_DAC1_BASE, port->speed);
45988447a05SGarrett D'Amore 		SRCSetRate(dev, SRC_DAC2_BASE, port->speed);
46088447a05SGarrett D'Amore 
46188447a05SGarrett D'Amore 		/* Configure the channel setup - SPDIF only uses front */
46288447a05SGarrett D'Amore 		tmp = GET32(dev, CONC_dSTATUS_OFF);
46388447a05SGarrett D'Amore 		tmp &= ~(CONC_STATUS_SPKR_MASK | CONC_STATUS_SPDIF_MASK);
46488447a05SGarrett D'Amore 		tmp |= CONC_STATUS_SPKR_4CH | CONC_STATUS_SPDIF_P1;
46588447a05SGarrett D'Amore 		PUT32(dev, CONC_dSTATUS_OFF, tmp);
46688447a05SGarrett D'Amore 
46788447a05SGarrett D'Amore 		/* Set format */
46888447a05SGarrett D'Amore 		PUT8(dev, CONC_bSKIPC_OFF, 0x10);
46988447a05SGarrett D'Amore 		SET8(dev, CONC_bSERFMT_OFF,
47088447a05SGarrett D'Amore 		    CONC_PCM_DAC1_16BIT | CONC_PCM_DAC2_16BIT |
47188447a05SGarrett D'Amore 		    CONC_PCM_DAC1_STEREO | CONC_PCM_DAC2_STEREO);
47288447a05SGarrett D'Amore 
47388447a05SGarrett D'Amore 		/* Set the frame count */
47488447a05SGarrett D'Amore 		audioens_writemem(dev, CONC_DAC1CTL_PAGE, CONC_wDAC1FC_OFF,
47588447a05SGarrett D'Amore 		    port->nframes - 1);
47688447a05SGarrett D'Amore 		audioens_writemem(dev, CONC_DAC2CTL_PAGE, CONC_wDAC2FC_OFF,
47788447a05SGarrett D'Amore 		    port->nframes - 1);
47888447a05SGarrett D'Amore 
47988447a05SGarrett D'Amore 		/* Set # of frames between interrupts */
48068c47f65SGarrett D'Amore 		PUT16(dev, CONC_wDAC1IC_OFF, port->nframes - 1);
48168c47f65SGarrett D'Amore 		PUT16(dev, CONC_wDAC2IC_OFF, port->nframes - 1);
48268c47f65SGarrett D'Amore 
48368c47f65SGarrett D'Amore 		SET8(dev, CONC_bDEVCTL_OFF,
48468c47f65SGarrett D'Amore 		    CONC_DEVCTL_DAC2_EN | CONC_DEVCTL_DAC1_EN);
48568c47f65SGarrett D'Amore 
48688447a05SGarrett D'Amore 		break;
48788447a05SGarrett D'Amore 
48888447a05SGarrett D'Amore 	case PORT_ADC:
48988447a05SGarrett D'Amore 		/* Set physical address of the DMA buffer */
49088447a05SGarrett D'Amore 		audioens_writemem(dev, CONC_ADCCTL_PAGE, CONC_dADCPADDR_OFF,
49188447a05SGarrett D'Amore 		    port->paddr);
49288447a05SGarrett D'Amore 
49388447a05SGarrett D'Amore 		/* Set ADC rate */
49488447a05SGarrett D'Amore 		SRCSetRate(dev, SRC_ADC_BASE, port->speed);
49588447a05SGarrett D'Amore 
49688447a05SGarrett D'Amore 		/* Set format - for input we only support 16 bit input */
49788447a05SGarrett D'Amore 		tmp = GET8(dev, CONC_bSERFMT_OFF);
49888447a05SGarrett D'Amore 		tmp |= CONC_PCM_ADC_16BIT;
49988447a05SGarrett D'Amore 		tmp |= CONC_PCM_ADC_STEREO;
50088447a05SGarrett D'Amore 
50188447a05SGarrett D'Amore 		PUT8(dev, CONC_bSKIPC_OFF, 0x10);
50288447a05SGarrett D'Amore 
50388447a05SGarrett D'Amore 		PUT8(dev, CONC_bSERFMT_OFF, tmp);
50488447a05SGarrett D'Amore 
50588447a05SGarrett D'Amore 		/* Set the frame count */
50688447a05SGarrett D'Amore 		audioens_writemem(dev, CONC_ADCCTL_PAGE, CONC_wADCFC_OFF,
50788447a05SGarrett D'Amore 		    port->nframes - 1);
50888447a05SGarrett D'Amore 
50988447a05SGarrett D'Amore 		/* Set # of frames between interrupts */
51068c47f65SGarrett D'Amore 		PUT16(dev, CONC_wADCIC_OFF, port->nframes - 1);
51188447a05SGarrett D'Amore 
51268c47f65SGarrett D'Amore 		SET8(dev, CONC_bDEVCTL_OFF, CONC_DEVCTL_ADC_EN);
51388447a05SGarrett D'Amore 		break;
51488447a05SGarrett D'Amore 	}
51588447a05SGarrett D'Amore 
51688447a05SGarrett D'Amore 	port->frameno = 0;
51788447a05SGarrett D'Amore 	mutex_exit(&dev->mutex);
51888447a05SGarrett D'Amore 
51988447a05SGarrett D'Amore 	return (0);
52088447a05SGarrett D'Amore }
52188447a05SGarrett D'Amore 
52288447a05SGarrett D'Amore static void
52368c47f65SGarrett D'Amore audioens_stop(void *arg)
52488447a05SGarrett D'Amore {
52588447a05SGarrett D'Amore 	audioens_port_t *port = arg;
52688447a05SGarrett D'Amore 	audioens_dev_t *dev = port->dev;
52788447a05SGarrett D'Amore 
52888447a05SGarrett D'Amore 	mutex_enter(&dev->mutex);
52968c47f65SGarrett D'Amore 	switch (port->num) {
53068c47f65SGarrett D'Amore 	case PORT_DAC:
53168c47f65SGarrett D'Amore 		CLR8(dev, CONC_bDEVCTL_OFF,
53268c47f65SGarrett D'Amore 		    CONC_DEVCTL_DAC2_EN | CONC_DEVCTL_DAC1_EN);
53368c47f65SGarrett D'Amore 		break;
53468c47f65SGarrett D'Amore 	case PORT_ADC:
53568c47f65SGarrett D'Amore 		CLR8(dev, CONC_bDEVCTL_OFF, CONC_DEVCTL_ADC_EN);
53668c47f65SGarrett D'Amore 		break;
53788447a05SGarrett D'Amore 	}
53888447a05SGarrett D'Amore 	mutex_exit(&dev->mutex);
53988447a05SGarrett D'Amore }
54088447a05SGarrett D'Amore 
54168c47f65SGarrett D'Amore static uint64_t
54268c47f65SGarrett D'Amore audioens_count(void *arg)
54388447a05SGarrett D'Amore {
54488447a05SGarrett D'Amore 	audioens_port_t *port = arg;
54588447a05SGarrett D'Amore 	audioens_dev_t *dev = port->dev;
54668c47f65SGarrett D'Amore 	uint64_t val;
54788447a05SGarrett D'Amore 	uint32_t page, offs;
54888447a05SGarrett D'Amore 	int frameno, n;
54988447a05SGarrett D'Amore 
55088447a05SGarrett D'Amore 	switch (port->num) {
55188447a05SGarrett D'Amore 	case PORT_DAC:
55288447a05SGarrett D'Amore 		page = CONC_DAC1CTL_PAGE;
55388447a05SGarrett D'Amore 		offs = CONC_wDAC1FC_OFF;
55488447a05SGarrett D'Amore 		break;
55588447a05SGarrett D'Amore 
55688447a05SGarrett D'Amore 	case PORT_ADC:
55788447a05SGarrett D'Amore 		page = CONC_ADCCTL_PAGE;
55888447a05SGarrett D'Amore 		offs = CONC_wADCFC_OFF;
55988447a05SGarrett D'Amore 		break;
56088447a05SGarrett D'Amore 	}
56188447a05SGarrett D'Amore 
56268c47f65SGarrett D'Amore 	mutex_enter(&dev->mutex);
56388447a05SGarrett D'Amore 	/*
56488447a05SGarrett D'Amore 	 * Note that the current frame counter is in the high nybble.
56588447a05SGarrett D'Amore 	 */
56688447a05SGarrett D'Amore 	frameno = audioens_readmem(port->dev, page, offs) >> 16;
56788447a05SGarrett D'Amore 	n = frameno >= port->frameno ?
56888447a05SGarrett D'Amore 	    frameno - port->frameno :
56988447a05SGarrett D'Amore 	    frameno + port->nframes - port->frameno;
57088447a05SGarrett D'Amore 	port->frameno = frameno;
57188447a05SGarrett D'Amore 	port->count += n;
57288447a05SGarrett D'Amore 
57388447a05SGarrett D'Amore 	val = port->count;
57488447a05SGarrett D'Amore 	mutex_exit(&dev->mutex);
57568c47f65SGarrett D'Amore 
57688447a05SGarrett D'Amore 	return (val);
57788447a05SGarrett D'Amore }
57888447a05SGarrett D'Amore 
57988447a05SGarrett D'Amore static void
58088447a05SGarrett D'Amore audioens_close(void *arg)
58188447a05SGarrett D'Amore {
58268c47f65SGarrett D'Amore 	_NOTE(ARGUNUSED(arg));
58388447a05SGarrett D'Amore }
58488447a05SGarrett D'Amore 
58588447a05SGarrett D'Amore static void
58688447a05SGarrett D'Amore audioens_sync(void *arg, unsigned nframes)
58788447a05SGarrett D'Amore {
58888447a05SGarrett D'Amore 	audioens_port_t *port = arg;
58988447a05SGarrett D'Amore 
59088447a05SGarrett D'Amore 	_NOTE(ARGUNUSED(nframes));
59188447a05SGarrett D'Amore 
59288447a05SGarrett D'Amore 	if (port->num == PORT_ADC) {
59368c47f65SGarrett D'Amore 		(void) ddi_dma_sync(port->dmah, 0, 0, DDI_DMA_SYNC_FORKERNEL);
59488447a05SGarrett D'Amore 	} else {
59588447a05SGarrett D'Amore 		(void) ddi_dma_sync(port->dmah, 0, 0, DDI_DMA_SYNC_FORDEV);
59688447a05SGarrett D'Amore 	}
59788447a05SGarrett D'Amore }
59888447a05SGarrett D'Amore 
59988447a05SGarrett D'Amore static void
60088447a05SGarrett D'Amore audioens_chinfo(void *arg, int chan, unsigned *offset, unsigned *incr)
60188447a05SGarrett D'Amore {
60288447a05SGarrett D'Amore 	audioens_port_t *port = arg;
60388447a05SGarrett D'Amore 
60488447a05SGarrett D'Amore 	if ((port->num == PORT_DAC) && (chan >= 2)) {
60588447a05SGarrett D'Amore 		*offset = (port->nframes * 2) + (chan % 2);
60688447a05SGarrett D'Amore 		*incr = 2;
60788447a05SGarrett D'Amore 	} else {
60888447a05SGarrett D'Amore 		*offset = chan;
60988447a05SGarrett D'Amore 		*incr = 2;
61088447a05SGarrett D'Amore 	}
61188447a05SGarrett D'Amore }
61288447a05SGarrett D'Amore 
61388447a05SGarrett D'Amore audio_engine_ops_t audioens_engine_ops = {
61488447a05SGarrett D'Amore 	AUDIO_ENGINE_VERSION,		/* version number */
61588447a05SGarrett D'Amore 	audioens_open,
61688447a05SGarrett D'Amore 	audioens_close,
61788447a05SGarrett D'Amore 	audioens_start,
61888447a05SGarrett D'Amore 	audioens_stop,
61988447a05SGarrett D'Amore 	audioens_count,
62088447a05SGarrett D'Amore 	audioens_format,
62188447a05SGarrett D'Amore 	audioens_channels,
62288447a05SGarrett D'Amore 	audioens_rate,
62388447a05SGarrett D'Amore 	audioens_sync,
624f9ead4a5SGarrett D'Amore 	NULL,
625f9ead4a5SGarrett D'Amore 	audioens_chinfo,
626f9ead4a5SGarrett D'Amore 	NULL,
62788447a05SGarrett D'Amore };
62888447a05SGarrett D'Amore 
62988447a05SGarrett D'Amore void
63088447a05SGarrett D'Amore audioens_init_hw(audioens_dev_t *dev)
63188447a05SGarrett D'Amore {
63288447a05SGarrett D'Amore 	int tmp;
63388447a05SGarrett D'Amore 
63488447a05SGarrett D'Amore 	if ((dev->devid == ENSONIQ_ES5880) ||
63588447a05SGarrett D'Amore 	    (dev->devid == ENSONIQ_ES5880A) ||
63688447a05SGarrett D'Amore 	    (dev->devid == ENSONIQ_ES5880B) ||
63788447a05SGarrett D'Amore 	    (dev->devid == 0x1371 && dev->revision == 7) ||
63888447a05SGarrett D'Amore 	    (dev->devid == 0x1371 && dev->revision >= 9)) {
63988447a05SGarrett D'Amore 
64088447a05SGarrett D'Amore 		/* Have a ES5880 so enable the codec manually */
64188447a05SGarrett D'Amore 		tmp = GET8(dev, CONC_bINTSUMM_OFF) & 0xff;
64288447a05SGarrett D'Amore 		tmp |= 0x20;
64388447a05SGarrett D'Amore 		PUT8(dev, CONC_bINTSUMM_OFF, tmp);
64488447a05SGarrett D'Amore 		for (int i = 0; i < 2000; i++)
64588447a05SGarrett D'Amore 			drv_usecwait(10);
64688447a05SGarrett D'Amore 	}
64788447a05SGarrett D'Amore 
64888447a05SGarrett D'Amore 	SRCInit(dev);
64988447a05SGarrett D'Amore 
65088447a05SGarrett D'Amore 	/*
65188447a05SGarrett D'Amore 	 * Turn on CODEC (UART and joystick left disabled)
65288447a05SGarrett D'Amore 	 */
65388447a05SGarrett D'Amore 	tmp = GET32(dev, CONC_bDEVCTL_OFF) & 0xff;
65488447a05SGarrett D'Amore 	tmp &= ~(CONC_DEVCTL_PCICLK_DS | CONC_DEVCTL_XTALCLK_DS);
65588447a05SGarrett D'Amore 	PUT8(dev, CONC_bDEVCTL_OFF, tmp);
65688447a05SGarrett D'Amore 	PUT8(dev, CONC_bUARTCSTAT_OFF, 0x00);
65788447a05SGarrett D'Amore 
65888447a05SGarrett D'Amore 	/* Perform AC97 codec warm reset */
65988447a05SGarrett D'Amore 	tmp = GET8(dev, CONC_bMISCCTL_OFF) & 0xff;
66088447a05SGarrett D'Amore 	PUT8(dev, CONC_bMISCCTL_OFF, tmp | CONC_MISCCTL_SYNC_RES);
66188447a05SGarrett D'Amore 	drv_usecwait(200);
66288447a05SGarrett D'Amore 	PUT8(dev, CONC_bMISCCTL_OFF, tmp);
66388447a05SGarrett D'Amore 	drv_usecwait(200);
66488447a05SGarrett D'Amore 
66588447a05SGarrett D'Amore 	if (dev->revision >= 4) {
66688447a05SGarrett D'Amore 		/* XXX: enable SPDIF - PCM only for now */
66788447a05SGarrett D'Amore 		if (audioens_spdif) {
66888447a05SGarrett D'Amore 			/* enable SPDIF */
66988447a05SGarrett D'Amore 			PUT32(dev, 0x04, GET32(dev, 0x04) | (1 << 18));
67088447a05SGarrett D'Amore 			/* SPDIF out = data from DAC */
67188447a05SGarrett D'Amore 			PUT32(dev, 0x00, GET32(dev, 0x00) | (1 << 26));
67288447a05SGarrett D'Amore 			CLR32(dev, CONC_dSPDIF_OFF, CONC_SPDIF_AC3);
67388447a05SGarrett D'Amore 
67488447a05SGarrett D'Amore 		} else {
67588447a05SGarrett D'Amore 			/* disable spdif out */
67688447a05SGarrett D'Amore 			PUT32(dev, 0x04, GET32(dev, 0x04) & ~(1 << 18));
67788447a05SGarrett D'Amore 			PUT32(dev, 0x00, GET32(dev, 0x00) & ~(1 << 26));
67888447a05SGarrett D'Amore 		}
67988447a05SGarrett D'Amore 
68088447a05SGarrett D'Amore 		/* we want to run each channel independently */
68188447a05SGarrett D'Amore 		CLR32(dev, CONC_dSTATUS_OFF, CONC_STATUS_ECHO);
68288447a05SGarrett D'Amore 	}
68388447a05SGarrett D'Amore }
68488447a05SGarrett D'Amore 
68588447a05SGarrett D'Amore static int
68688447a05SGarrett D'Amore audioens_init(audioens_dev_t *dev)
68788447a05SGarrett D'Amore {
68888447a05SGarrett D'Amore 
68988447a05SGarrett D'Amore 	audioens_init_hw(dev);
69088447a05SGarrett D'Amore 
69188447a05SGarrett D'Amore 	/*
69288447a05SGarrett D'Amore 	 * On this hardware, we want to disable the internal speaker by
69388447a05SGarrett D'Amore 	 * default, if it exists.  (We don't have a speakerphone on any
69488447a05SGarrett D'Amore 	 * of these cards, and no SPARC hardware uses it either!)
69588447a05SGarrett D'Amore 	 */
696505c7a69SGarrett D'Amore 	(void) ddi_prop_update_int(DDI_DEV_T_NONE, dev->dip, AC97_PROP_SPEAKER,
697505c7a69SGarrett D'Amore 	    0);
69888447a05SGarrett D'Amore 
69988447a05SGarrett D'Amore 	/*
70088447a05SGarrett D'Amore 	 * Init mixer
70188447a05SGarrett D'Amore 	 */
70288447a05SGarrett D'Amore 
70388447a05SGarrett D'Amore 	dev->ac97 = ac97_alloc(dev->dip, audioens_rd97, audioens_wr97, dev);
70488447a05SGarrett D'Amore 	if (dev->ac97 == NULL)
70588447a05SGarrett D'Amore 		return (DDI_FAILURE);
70688447a05SGarrett D'Amore 
70788447a05SGarrett D'Amore 	if (ac97_init(dev->ac97, dev->osdev) != 0) {
70888447a05SGarrett D'Amore 		return (DDI_FAILURE);
70988447a05SGarrett D'Amore 	}
71088447a05SGarrett D'Amore 
71188447a05SGarrett D'Amore 	for (int i = 0; i <= PORT_MAX; i++) {
71288447a05SGarrett D'Amore 		audioens_port_t *port;
71388447a05SGarrett D'Amore 		unsigned caps;
71488447a05SGarrett D'Amore 		unsigned dmaflags;
71588447a05SGarrett D'Amore 		size_t rlen;
71688447a05SGarrett D'Amore 		ddi_dma_cookie_t c;
71788447a05SGarrett D'Amore 		unsigned ccnt;
71888447a05SGarrett D'Amore 
71988447a05SGarrett D'Amore 		port = &dev->port[i];
72088447a05SGarrett D'Amore 		port->dev = dev;
72188447a05SGarrett D'Amore 
72288447a05SGarrett D'Amore 		switch (i) {
72388447a05SGarrett D'Amore 		case PORT_DAC:
72488447a05SGarrett D'Amore 			port->nchan = 4;
72588447a05SGarrett D'Amore 			port->speed = 48000;
72688447a05SGarrett D'Amore 			caps = ENGINE_OUTPUT_CAP;
72788447a05SGarrett D'Amore 			dmaflags = DDI_DMA_WRITE | DDI_DMA_CONSISTENT;
72888447a05SGarrett D'Amore 			break;
72988447a05SGarrett D'Amore 
73088447a05SGarrett D'Amore 		case PORT_ADC:
73188447a05SGarrett D'Amore 			port->nchan = 2;
73288447a05SGarrett D'Amore 			port->speed = 48000;
73388447a05SGarrett D'Amore 			caps = ENGINE_INPUT_CAP;
73488447a05SGarrett D'Amore 			dmaflags = DDI_DMA_READ | DDI_DMA_CONSISTENT;
73588447a05SGarrett D'Amore 			break;
73688447a05SGarrett D'Amore 		}
73788447a05SGarrett D'Amore 
73888447a05SGarrett D'Amore 		port->num = i;
73988447a05SGarrett D'Amore 
74088447a05SGarrett D'Amore 		/*
74188447a05SGarrett D'Amore 		 * Allocate DMA resources.
74288447a05SGarrett D'Amore 		 */
74388447a05SGarrett D'Amore 
74488447a05SGarrett D'Amore 		if (ddi_dma_alloc_handle(dev->dip, &dma_attr, DDI_DMA_SLEEP,
74588447a05SGarrett D'Amore 		    NULL, &port->dmah) != DDI_SUCCESS) {
74688447a05SGarrett D'Amore 			audio_dev_warn(dev->osdev,
74788447a05SGarrett D'Amore 			    "port %d: dma handle allocation failed", i);
74888447a05SGarrett D'Amore 			return (DDI_FAILURE);
74988447a05SGarrett D'Amore 		}
75088447a05SGarrett D'Amore 		if (ddi_dma_mem_alloc(port->dmah, AUDIOENS_BUF_LEN, &buf_attr,
75188447a05SGarrett D'Amore 		    DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL, &port->kaddr,
75288447a05SGarrett D'Amore 		    &rlen, &port->acch) != DDI_SUCCESS) {
75388447a05SGarrett D'Amore 			audio_dev_warn(dev->osdev,
75488447a05SGarrett D'Amore 			    "port %d: dma memory allocation failed", i);
75588447a05SGarrett D'Amore 			return (DDI_FAILURE);
75688447a05SGarrett D'Amore 		}
75788447a05SGarrett D'Amore 		/* ensure that the buffer is zeroed out properly */
75888447a05SGarrett D'Amore 		bzero(port->kaddr, rlen);
75988447a05SGarrett D'Amore 		if (ddi_dma_addr_bind_handle(port->dmah, NULL, port->kaddr,
76088447a05SGarrett D'Amore 		    AUDIOENS_BUF_LEN, dmaflags, DDI_DMA_SLEEP, NULL,
76188447a05SGarrett D'Amore 		    &c, &ccnt) != DDI_DMA_MAPPED) {
76288447a05SGarrett D'Amore 			audio_dev_warn(dev->osdev,
76388447a05SGarrett D'Amore 			    "port %d: dma binding failed", i);
76488447a05SGarrett D'Amore 			return (DDI_FAILURE);
76588447a05SGarrett D'Amore 		}
76688447a05SGarrett D'Amore 		port->paddr = c.dmac_address;
76788447a05SGarrett D'Amore 
76888447a05SGarrett D'Amore 		/*
76988447a05SGarrett D'Amore 		 * Allocate and configure audio engine.
77088447a05SGarrett D'Amore 		 */
77188447a05SGarrett D'Amore 		port->engine = audio_engine_alloc(&audioens_engine_ops, caps);
77288447a05SGarrett D'Amore 		if (port->engine == NULL) {
77388447a05SGarrett D'Amore 			audio_dev_warn(dev->osdev,
77488447a05SGarrett D'Amore 			    "port %d: audio_engine_alloc failed", i);
77588447a05SGarrett D'Amore 			return (DDI_FAILURE);
77688447a05SGarrett D'Amore 		}
77788447a05SGarrett D'Amore 
77888447a05SGarrett D'Amore 		audio_engine_set_private(port->engine, port);
77988447a05SGarrett D'Amore 		audio_dev_add_engine(dev->osdev, port->engine);
78088447a05SGarrett D'Amore 	}
78188447a05SGarrett D'Amore 
78288447a05SGarrett D'Amore 	/*
78388447a05SGarrett D'Amore 	 * Set up kstats for interrupt reporting.
78488447a05SGarrett D'Amore 	 */
78588447a05SGarrett D'Amore 	if (audio_dev_register(dev->osdev) != DDI_SUCCESS) {
78688447a05SGarrett D'Amore 		audio_dev_warn(dev->osdev,
78788447a05SGarrett D'Amore 		    "unable to register with audio framework");
78888447a05SGarrett D'Amore 		return (DDI_FAILURE);
78988447a05SGarrett D'Amore 	}
79088447a05SGarrett D'Amore 
79188447a05SGarrett D'Amore 	return (DDI_SUCCESS);
79288447a05SGarrett D'Amore }
79388447a05SGarrett D'Amore 
79488447a05SGarrett D'Amore void
79588447a05SGarrett D'Amore audioens_destroy(audioens_dev_t *dev)
79688447a05SGarrett D'Amore {
79788447a05SGarrett D'Amore 	int	i;
79888447a05SGarrett D'Amore 
79968c47f65SGarrett D'Amore 	mutex_destroy(&dev->mutex);
80088447a05SGarrett D'Amore 
80188447a05SGarrett D'Amore 	/* free up ports, including DMA resources for ports */
80288447a05SGarrett D'Amore 	for (i = 0; i <= PORT_MAX; i++) {
80388447a05SGarrett D'Amore 		audioens_port_t	*port = &dev->port[i];
80488447a05SGarrett D'Amore 
80588447a05SGarrett D'Amore 		if (port->paddr != 0)
80688447a05SGarrett D'Amore 			(void) ddi_dma_unbind_handle(port->dmah);
80788447a05SGarrett D'Amore 		if (port->acch != NULL)
80888447a05SGarrett D'Amore 			ddi_dma_mem_free(&port->acch);
80988447a05SGarrett D'Amore 		if (port->dmah != NULL)
81088447a05SGarrett D'Amore 			ddi_dma_free_handle(&port->dmah);
81188447a05SGarrett D'Amore 
81288447a05SGarrett D'Amore 		if (port->engine != NULL) {
81388447a05SGarrett D'Amore 			audio_dev_remove_engine(dev->osdev, port->engine);
81488447a05SGarrett D'Amore 			audio_engine_free(port->engine);
81588447a05SGarrett D'Amore 		}
81688447a05SGarrett D'Amore 	}
81788447a05SGarrett D'Amore 
81888447a05SGarrett D'Amore 	if (dev->acch != NULL) {
81988447a05SGarrett D'Amore 		ddi_regs_map_free(&dev->acch);
82088447a05SGarrett D'Amore 	}
82188447a05SGarrett D'Amore 
82288447a05SGarrett D'Amore 	if (dev->ac97) {
82388447a05SGarrett D'Amore 		ac97_free(dev->ac97);
82488447a05SGarrett D'Amore 	}
82588447a05SGarrett D'Amore 
82688447a05SGarrett D'Amore 	if (dev->osdev != NULL) {
82788447a05SGarrett D'Amore 		audio_dev_free(dev->osdev);
82888447a05SGarrett D'Amore 	}
82988447a05SGarrett D'Amore 
83088447a05SGarrett D'Amore 	kmem_free(dev, sizeof (*dev));
83188447a05SGarrett D'Amore }
83288447a05SGarrett D'Amore 
83388447a05SGarrett D'Amore int
83488447a05SGarrett D'Amore audioens_attach(dev_info_t *dip)
83588447a05SGarrett D'Amore {
83688447a05SGarrett D'Amore 	uint16_t pci_command, vendor, device;
83788447a05SGarrett D'Amore 	uint8_t revision;
83888447a05SGarrett D'Amore 	audioens_dev_t *dev;
83988447a05SGarrett D'Amore 	ddi_acc_handle_t pcih;
84088447a05SGarrett D'Amore 	const char *chip_name;
84188447a05SGarrett D'Amore 	const char *chip_vers;
84288447a05SGarrett D'Amore 
84388447a05SGarrett D'Amore 	dev = kmem_zalloc(sizeof (*dev), KM_SLEEP);
84488447a05SGarrett D'Amore 	dev->dip = dip;
84588447a05SGarrett D'Amore 	ddi_set_driver_private(dip, dev);
84668c47f65SGarrett D'Amore 	mutex_init(&dev->mutex, NULL, MUTEX_DRIVER, NULL);
84788447a05SGarrett D'Amore 
84888447a05SGarrett D'Amore 	if (pci_config_setup(dip, &pcih) != DDI_SUCCESS) {
84988447a05SGarrett D'Amore 		audio_dev_warn(dev->osdev, "pci_config_setup failed");
85068c47f65SGarrett D'Amore 		mutex_destroy(&dev->mutex);
851*239924d3SGarrett D'Amore 		kmem_free(dev, sizeof (*dev));
85288447a05SGarrett D'Amore 		return (DDI_FAILURE);
85388447a05SGarrett D'Amore 	}
85488447a05SGarrett D'Amore 
85588447a05SGarrett D'Amore 	vendor = pci_config_get16(pcih, PCI_CONF_VENID);
85688447a05SGarrett D'Amore 	device = pci_config_get16(pcih, PCI_CONF_DEVID);
85788447a05SGarrett D'Amore 	revision = pci_config_get8(pcih, PCI_CONF_REVID);
85888447a05SGarrett D'Amore 
85988447a05SGarrett D'Amore 	if ((vendor != ENSONIQ_VENDOR_ID && vendor != CREATIVE_VENDOR_ID) ||
86088447a05SGarrett D'Amore 	    (device != ENSONIQ_ES1371 && device != ENSONIQ_ES5880 &&
86188447a05SGarrett D'Amore 	    device != ENSONIQ_ES5880A && device != ECTIVA_ES1938 &&
86288447a05SGarrett D'Amore 	    device != ENSONIQ_ES5880B))
86388447a05SGarrett D'Amore 		goto err_exit;
86488447a05SGarrett D'Amore 
86588447a05SGarrett D'Amore 	chip_name = "AudioPCI97";
86688447a05SGarrett D'Amore 	chip_vers = "unknown";
86788447a05SGarrett D'Amore 
86888447a05SGarrett D'Amore 	switch (device) {
86988447a05SGarrett D'Amore 	case ENSONIQ_ES1371:
87088447a05SGarrett D'Amore 		chip_name = "AudioPCI97";
87188447a05SGarrett D'Amore 		switch (revision) {
87288447a05SGarrett D'Amore 		case 0x02:
87388447a05SGarrett D'Amore 		case 0x09:
87488447a05SGarrett D'Amore 		default:
87588447a05SGarrett D'Amore 			chip_vers = "ES1371";
87688447a05SGarrett D'Amore 			break;
87788447a05SGarrett D'Amore 		case 0x04:
87888447a05SGarrett D'Amore 		case 0x06:
87988447a05SGarrett D'Amore 		case 0x08:
88088447a05SGarrett D'Amore 			chip_vers = "ES1373";
88188447a05SGarrett D'Amore 			break;
88288447a05SGarrett D'Amore 		case 0x07:
88388447a05SGarrett D'Amore 			chip_vers = "ES5880";
88488447a05SGarrett D'Amore 			break;
88588447a05SGarrett D'Amore 		}
88688447a05SGarrett D'Amore 		break;
88788447a05SGarrett D'Amore 
88888447a05SGarrett D'Amore 	case ENSONIQ_ES5880:
88988447a05SGarrett D'Amore 		chip_name = "SB PCI128";
89088447a05SGarrett D'Amore 		chip_vers = "ES5880";
89188447a05SGarrett D'Amore 		break;
89288447a05SGarrett D'Amore 	case ENSONIQ_ES5880A:
89388447a05SGarrett D'Amore 		chip_name = "SB PCI128";
89488447a05SGarrett D'Amore 		chip_vers = "ES5880A";
89588447a05SGarrett D'Amore 		break;
89688447a05SGarrett D'Amore 	case ENSONIQ_ES5880B:
89788447a05SGarrett D'Amore 		chip_name = "SB PCI128";
89888447a05SGarrett D'Amore 		chip_vers = "ES5880B";
89988447a05SGarrett D'Amore 		break;
90088447a05SGarrett D'Amore 
90188447a05SGarrett D'Amore 	case ECTIVA_ES1938:
90288447a05SGarrett D'Amore 		chip_name = "AudioPCI";
90388447a05SGarrett D'Amore 		chip_vers = "ES1938";
90488447a05SGarrett D'Amore 		break;
90588447a05SGarrett D'Amore 	}
90688447a05SGarrett D'Amore 
90788447a05SGarrett D'Amore 	dev->revision = revision;
90888447a05SGarrett D'Amore 	dev->devid = device;
90988447a05SGarrett D'Amore 
91088447a05SGarrett D'Amore 	dev->osdev = audio_dev_alloc(dip, 0);
91188447a05SGarrett D'Amore 	if (dev->osdev == NULL) {
91288447a05SGarrett D'Amore 		goto err_exit;
91388447a05SGarrett D'Amore 	}
91488447a05SGarrett D'Amore 
91588447a05SGarrett D'Amore 	audio_dev_set_description(dev->osdev, chip_name);
91688447a05SGarrett D'Amore 	audio_dev_set_version(dev->osdev, chip_vers);
91788447a05SGarrett D'Amore 
91888447a05SGarrett D'Amore 	/* set the PCI latency */
91988447a05SGarrett D'Amore 	if ((audioens_latency == 32) || (audioens_latency == 64) ||
92088447a05SGarrett D'Amore 	    (audioens_latency == 96))
92188447a05SGarrett D'Amore 		pci_config_put8(pcih, PCI_CONF_LATENCY_TIMER,
92288447a05SGarrett D'Amore 		    audioens_latency);
92388447a05SGarrett D'Amore 
92488447a05SGarrett D'Amore 	/* activate the device */
92588447a05SGarrett D'Amore 	pci_command = pci_config_get16(pcih, PCI_CONF_COMM);
92688447a05SGarrett D'Amore 	pci_command |= PCI_COMM_ME | PCI_COMM_IO;
92788447a05SGarrett D'Amore 	pci_config_put16(pcih, PCI_CONF_COMM, pci_command);
92888447a05SGarrett D'Amore 
92988447a05SGarrett D'Amore 	/* map registers */
93088447a05SGarrett D'Amore 	if (ddi_regs_map_setup(dip, 1, &dev->regs, 0, 0, &acc_attr,
93188447a05SGarrett D'Amore 	    &dev->acch) != DDI_SUCCESS) {
93288447a05SGarrett D'Amore 		audio_dev_warn(dev->osdev, "can't map registers");
93388447a05SGarrett D'Amore 		goto err_exit;
93488447a05SGarrett D'Amore 	}
93588447a05SGarrett D'Amore 
93688447a05SGarrett D'Amore 	/* This allocates and configures the engines */
93788447a05SGarrett D'Amore 	if (audioens_init(dev) != DDI_SUCCESS) {
93888447a05SGarrett D'Amore 		audio_dev_warn(dev->osdev, "can't init device");
93988447a05SGarrett D'Amore 		goto err_exit;
94088447a05SGarrett D'Amore 	}
94188447a05SGarrett D'Amore 
94288447a05SGarrett D'Amore 	pci_config_teardown(&pcih);
94388447a05SGarrett D'Amore 
94488447a05SGarrett D'Amore 	ddi_report_dev(dip);
94588447a05SGarrett D'Amore 
94688447a05SGarrett D'Amore 	return (DDI_SUCCESS);
94788447a05SGarrett D'Amore 
94888447a05SGarrett D'Amore err_exit:
94988447a05SGarrett D'Amore 	pci_config_teardown(&pcih);
95088447a05SGarrett D'Amore 
95188447a05SGarrett D'Amore 	audioens_destroy(dev);
95288447a05SGarrett D'Amore 
95388447a05SGarrett D'Amore 	return (DDI_FAILURE);
95488447a05SGarrett D'Amore }
95588447a05SGarrett D'Amore 
95688447a05SGarrett D'Amore int
95788447a05SGarrett D'Amore audioens_detach(audioens_dev_t *dev)
95888447a05SGarrett D'Amore {
95988447a05SGarrett D'Amore 	int tmp;
96088447a05SGarrett D'Amore 
96188447a05SGarrett D'Amore 	/* first unregister us from the DDI framework, might be busy */
96288447a05SGarrett D'Amore 	if (audio_dev_unregister(dev->osdev) != DDI_SUCCESS)
96388447a05SGarrett D'Amore 		return (DDI_FAILURE);
96488447a05SGarrett D'Amore 
96588447a05SGarrett D'Amore 	mutex_enter(&dev->mutex);
96688447a05SGarrett D'Amore 
96788447a05SGarrett D'Amore 	tmp = GET8(dev, CONC_bSERCTL_OFF) &
96888447a05SGarrett D'Amore 	    ~(CONC_SERCTL_DAC2IE | CONC_SERCTL_DAC1IE | CONC_SERCTL_ADCIE);
96988447a05SGarrett D'Amore 	PUT8(dev, CONC_bSERCTL_OFF, tmp);
97088447a05SGarrett D'Amore 	PUT8(dev, CONC_bSERCTL_OFF, tmp);
97188447a05SGarrett D'Amore 	PUT8(dev, CONC_bSERCTL_OFF, tmp);
97288447a05SGarrett D'Amore 	PUT8(dev, CONC_bSERCTL_OFF, tmp);
97388447a05SGarrett D'Amore 
97488447a05SGarrett D'Amore 	tmp = GET8(dev, CONC_bDEVCTL_OFF) &
97588447a05SGarrett D'Amore 	    ~(CONC_DEVCTL_DAC2_EN | CONC_DEVCTL_ADC_EN | CONC_DEVCTL_DAC1_EN);
97688447a05SGarrett D'Amore 	PUT8(dev, CONC_bDEVCTL_OFF, tmp);
97788447a05SGarrett D'Amore 	PUT8(dev, CONC_bDEVCTL_OFF, tmp);
97888447a05SGarrett D'Amore 	PUT8(dev, CONC_bDEVCTL_OFF, tmp);
97988447a05SGarrett D'Amore 	PUT8(dev, CONC_bDEVCTL_OFF, tmp);
98088447a05SGarrett D'Amore 
98188447a05SGarrett D'Amore 	mutex_exit(&dev->mutex);
98288447a05SGarrett D'Amore 
98388447a05SGarrett D'Amore 	audioens_destroy(dev);
98488447a05SGarrett D'Amore 
98588447a05SGarrett D'Amore 	return (DDI_SUCCESS);
98688447a05SGarrett D'Amore }
98788447a05SGarrett D'Amore 
98888447a05SGarrett D'Amore static int
98988447a05SGarrett D'Amore audioens_resume(audioens_dev_t *dev)
99088447a05SGarrett D'Amore {
99188447a05SGarrett D'Amore 	/* reinitialize hardware */
99288447a05SGarrett D'Amore 	audioens_init_hw(dev);
99388447a05SGarrett D'Amore 
99488447a05SGarrett D'Amore 	/* restore AC97 state */
99568c47f65SGarrett D'Amore 	ac97_reset(dev->ac97);
99688447a05SGarrett D'Amore 
99768c47f65SGarrett D'Amore 	audio_dev_resume(dev->osdev);
99888447a05SGarrett D'Amore 
99988447a05SGarrett D'Amore 	return (DDI_SUCCESS);
100088447a05SGarrett D'Amore }
100188447a05SGarrett D'Amore 
100288447a05SGarrett D'Amore static int
100388447a05SGarrett D'Amore audioens_suspend(audioens_dev_t *dev)
100488447a05SGarrett D'Amore {
100568c47f65SGarrett D'Amore 	audio_dev_suspend(dev->osdev);
100688447a05SGarrett D'Amore 
100788447a05SGarrett D'Amore 	return (DDI_SUCCESS);
100888447a05SGarrett D'Amore }
100988447a05SGarrett D'Amore 
101088447a05SGarrett D'Amore static int
101188447a05SGarrett D'Amore audioens_quiesce(dev_info_t *dip)
101288447a05SGarrett D'Amore {
101388447a05SGarrett D'Amore 	audioens_dev_t	*dev;
101488447a05SGarrett D'Amore 	uint8_t		tmp;
101588447a05SGarrett D'Amore 
101688447a05SGarrett D'Amore 	if ((dev = ddi_get_driver_private(dip)) == NULL) {
101788447a05SGarrett D'Amore 		return (DDI_FAILURE);
101888447a05SGarrett D'Amore 	}
101988447a05SGarrett D'Amore 
102088447a05SGarrett D'Amore 	/* This disables all DMA engines and interrupts */
102188447a05SGarrett D'Amore 	tmp = GET8(dev, CONC_bSERCTL_OFF) &
102288447a05SGarrett D'Amore 	    ~(CONC_SERCTL_DAC2IE | CONC_SERCTL_DAC1IE | CONC_SERCTL_ADCIE);
102388447a05SGarrett D'Amore 	PUT8(dev, CONC_bSERCTL_OFF, tmp);
102488447a05SGarrett D'Amore 	PUT8(dev, CONC_bSERCTL_OFF, tmp);
102588447a05SGarrett D'Amore 	PUT8(dev, CONC_bSERCTL_OFF, tmp);
102688447a05SGarrett D'Amore 	PUT8(dev, CONC_bSERCTL_OFF, tmp);
102788447a05SGarrett D'Amore 
102888447a05SGarrett D'Amore 	tmp = GET8(dev, CONC_bDEVCTL_OFF) &
102988447a05SGarrett D'Amore 	    ~(CONC_DEVCTL_DAC2_EN | CONC_DEVCTL_ADC_EN | CONC_DEVCTL_DAC1_EN);
103088447a05SGarrett D'Amore 	PUT8(dev, CONC_bDEVCTL_OFF, tmp);
103188447a05SGarrett D'Amore 	PUT8(dev, CONC_bDEVCTL_OFF, tmp);
103288447a05SGarrett D'Amore 	PUT8(dev, CONC_bDEVCTL_OFF, tmp);
103388447a05SGarrett D'Amore 	PUT8(dev, CONC_bDEVCTL_OFF, tmp);
103488447a05SGarrett D'Amore 
103588447a05SGarrett D'Amore 	return (DDI_SUCCESS);
103688447a05SGarrett D'Amore }
103788447a05SGarrett D'Amore 
103888447a05SGarrett D'Amore 
103988447a05SGarrett D'Amore static int
104088447a05SGarrett D'Amore audioens_ddi_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
104188447a05SGarrett D'Amore {
104288447a05SGarrett D'Amore 	audioens_dev_t *dev;
104388447a05SGarrett D'Amore 
104488447a05SGarrett D'Amore 	switch (cmd) {
104588447a05SGarrett D'Amore 	case DDI_ATTACH:
104688447a05SGarrett D'Amore 		return (audioens_attach(dip));
104788447a05SGarrett D'Amore 
104888447a05SGarrett D'Amore 	case DDI_RESUME:
104988447a05SGarrett D'Amore 		if ((dev = ddi_get_driver_private(dip)) == NULL) {
105088447a05SGarrett D'Amore 			return (DDI_FAILURE);
105188447a05SGarrett D'Amore 		}
105288447a05SGarrett D'Amore 		return (audioens_resume(dev));
105388447a05SGarrett D'Amore 
105488447a05SGarrett D'Amore 	default:
105588447a05SGarrett D'Amore 		return (DDI_FAILURE);
105688447a05SGarrett D'Amore 	}
105788447a05SGarrett D'Amore }
105888447a05SGarrett D'Amore 
105988447a05SGarrett D'Amore static int
106088447a05SGarrett D'Amore audioens_ddi_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
106188447a05SGarrett D'Amore {
106288447a05SGarrett D'Amore 	audioens_dev_t *dev;
106388447a05SGarrett D'Amore 
106488447a05SGarrett D'Amore 	if ((dev = ddi_get_driver_private(dip)) == NULL) {
106588447a05SGarrett D'Amore 		return (DDI_FAILURE);
106688447a05SGarrett D'Amore 	}
106788447a05SGarrett D'Amore 
106888447a05SGarrett D'Amore 	switch (cmd) {
106988447a05SGarrett D'Amore 	case DDI_DETACH:
107088447a05SGarrett D'Amore 		return (audioens_detach(dev));
107188447a05SGarrett D'Amore 
107288447a05SGarrett D'Amore 	case DDI_SUSPEND:
107388447a05SGarrett D'Amore 		return (audioens_suspend(dev));
107488447a05SGarrett D'Amore 	default:
107588447a05SGarrett D'Amore 		return (DDI_FAILURE);
107688447a05SGarrett D'Amore 	}
107788447a05SGarrett D'Amore }
107888447a05SGarrett D'Amore 
107988447a05SGarrett D'Amore static int audioens_ddi_attach(dev_info_t *, ddi_attach_cmd_t);
108088447a05SGarrett D'Amore static int audioens_ddi_detach(dev_info_t *, ddi_detach_cmd_t);
108188447a05SGarrett D'Amore 
108288447a05SGarrett D'Amore static struct dev_ops audioens_dev_ops = {
108388447a05SGarrett D'Amore 	DEVO_REV,		/* rev */
108488447a05SGarrett D'Amore 	0,			/* refcnt */
108588447a05SGarrett D'Amore 	NULL,			/* getinfo */
108688447a05SGarrett D'Amore 	nulldev,		/* identify */
108788447a05SGarrett D'Amore 	nulldev,		/* probe */
108888447a05SGarrett D'Amore 	audioens_ddi_attach,	/* attach */
108988447a05SGarrett D'Amore 	audioens_ddi_detach,	/* detach */
109088447a05SGarrett D'Amore 	nodev,			/* reset */
109188447a05SGarrett D'Amore 	NULL,			/* cb_ops */
109288447a05SGarrett D'Amore 	NULL,			/* bus_ops */
109388447a05SGarrett D'Amore 	NULL,			/* power */
109488447a05SGarrett D'Amore 	audioens_quiesce,	/* quiesce */
109588447a05SGarrett D'Amore };
109688447a05SGarrett D'Amore 
109788447a05SGarrett D'Amore static struct modldrv audioens_modldrv = {
109888447a05SGarrett D'Amore 	&mod_driverops,			/* drv_modops */
109988447a05SGarrett D'Amore 	"Ensoniq 1371/1373 Audio",	/* linkinfo */
110088447a05SGarrett D'Amore 	&audioens_dev_ops,		/* dev_ops */
110188447a05SGarrett D'Amore };
110288447a05SGarrett D'Amore 
110388447a05SGarrett D'Amore static struct modlinkage modlinkage = {
110488447a05SGarrett D'Amore 	MODREV_1,
110588447a05SGarrett D'Amore 	{ &audioens_modldrv, NULL }
110688447a05SGarrett D'Amore };
110788447a05SGarrett D'Amore 
110888447a05SGarrett D'Amore int
110988447a05SGarrett D'Amore _init(void)
111088447a05SGarrett D'Amore {
111188447a05SGarrett D'Amore 	int	rv;
111288447a05SGarrett D'Amore 
111388447a05SGarrett D'Amore 	audio_init_ops(&audioens_dev_ops, DRVNAME);
111488447a05SGarrett D'Amore 	if ((rv = mod_install(&modlinkage)) != 0) {
111588447a05SGarrett D'Amore 		audio_fini_ops(&audioens_dev_ops);
111688447a05SGarrett D'Amore 	}
111788447a05SGarrett D'Amore 	return (rv);
111888447a05SGarrett D'Amore }
111988447a05SGarrett D'Amore 
112088447a05SGarrett D'Amore int
112188447a05SGarrett D'Amore _fini(void)
112288447a05SGarrett D'Amore {
112388447a05SGarrett D'Amore 	int	rv;
112488447a05SGarrett D'Amore 
112588447a05SGarrett D'Amore 	if ((rv = mod_remove(&modlinkage)) == 0) {
112688447a05SGarrett D'Amore 		audio_fini_ops(&audioens_dev_ops);
112788447a05SGarrett D'Amore 	}
112888447a05SGarrett D'Amore 	return (rv);
112988447a05SGarrett D'Amore }
113088447a05SGarrett D'Amore 
113188447a05SGarrett D'Amore int
113288447a05SGarrett D'Amore _info(struct modinfo *modinfop)
113388447a05SGarrett D'Amore {
113488447a05SGarrett D'Amore 	return (mod_info(&modlinkage, modinfop));
113588447a05SGarrett D'Amore }
1136