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 /*
22239924d3SGarrett D'Amore  * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
235b162753SGarrett D'Amore  * Copyright 2016 Garrett D'Amore <garrett@damore.org>
2488447a05SGarrett D'Amore  */
2588447a05SGarrett D'Amore /*
2688447a05SGarrett D'Amore  * Purpose: Creative/Ensoniq AudioPCI97  driver (ES1371/ES1373)
2788447a05SGarrett D'Amore  *
2888447a05SGarrett D'Amore  * This driver is used with the original Ensoniq AudioPCI97 card and many
2988447a05SGarrett D'Amore  * PCI based Sound Blaster cards by Creative Technologies. For example
3088447a05SGarrett D'Amore  * Sound Blaster PCI128 and Creative/Ectiva EV1938.
3188447a05SGarrett D'Amore  */
3288447a05SGarrett D'Amore 
3388447a05SGarrett D'Amore /*
3488447a05SGarrett D'Amore  * This file is part of Open Sound System
3588447a05SGarrett D'Amore  *
3688447a05SGarrett D'Amore  * Copyright (C) 4Front Technologies 1996-2008.
3788447a05SGarrett D'Amore  *
3888447a05SGarrett D'Amore  * This software is released under CDDL 1.0 source license.
3988447a05SGarrett D'Amore  * See the COPYING file included in the main directory of this source
4088447a05SGarrett D'Amore  * distribution for the license terms and conditions.
4188447a05SGarrett D'Amore  */
4288447a05SGarrett D'Amore 
4388447a05SGarrett D'Amore #include <sys/audio/audio_driver.h>
4488447a05SGarrett D'Amore #include <sys/audio/ac97.h>
4588447a05SGarrett D'Amore #include <sys/note.h>
4688447a05SGarrett D'Amore #include <sys/pci.h>
475b162753SGarrett D'Amore 
485b162753SGarrett D'Amore /*
495b162753SGarrett D'Amore  * For VMWare platforms, we have to utilize the (emulated) hardware interrupts
505b162753SGarrett D'Amore  * of the device.  This is necessary for audio playback to function, as
515b162753SGarrett D'Amore  * the toggling of the interrupt bits apparently triggers logic inside the
525b162753SGarrett D'Amore  * emulated device.  So we need to detect this platform, and conditionally
535b162753SGarrett D'Amore  * wire up the interrupt handler.
545b162753SGarrett D'Amore  */
555b162753SGarrett D'Amore #ifdef __x86
565b162753SGarrett D'Amore #include <sys/x86_archext.h>
575b162753SGarrett D'Amore #endif
585b162753SGarrett D'Amore 
5988447a05SGarrett D'Amore #include "audioens.h"
6088447a05SGarrett D'Amore 
6188447a05SGarrett D'Amore /*
6288447a05SGarrett D'Amore  * Set the latency to 32, 64, 96, 128 clocks - some APCI97 devices exhibit
6388447a05SGarrett D'Amore  * garbled audio in some cases and setting the latency to higer values fixes it
6488447a05SGarrett D'Amore  * Values: 32, 64, 96, 128 - Default: 64 (or defined by bios)
6588447a05SGarrett D'Amore  */
6688447a05SGarrett D'Amore int audioens_latency = 0;
6788447a05SGarrett D'Amore 
6888447a05SGarrett D'Amore /*
6988447a05SGarrett D'Amore  * Enable SPDIF port on SoundBlaster 128D or Sound Blaster Digital-4.1 models
7088447a05SGarrett D'Amore  * Values: 1=Enable 0=Disable Default: 0
7188447a05SGarrett D'Amore  */
7288447a05SGarrett D'Amore int audioens_spdif = 0;
7388447a05SGarrett D'Amore 
7488447a05SGarrett D'Amore /*
7588447a05SGarrett D'Amore  * Note: Latest devices can support SPDIF with AC3 pass thru.
7688447a05SGarrett D'Amore  * However, in order to do this, one of the two DMA engines must be
7788447a05SGarrett D'Amore  * dedicated to this, which would prevent the card from supporting 4
7888447a05SGarrett D'Amore  * channel audio.  For now we don't bother with the AC3 pass through
7988447a05SGarrett D'Amore  * mode, and instead just focus on 4 channel support.  In the future,
8088447a05SGarrett D'Amore  * this could be selectable via a property.
8188447a05SGarrett D'Amore  */
8288447a05SGarrett D'Amore 
8388447a05SGarrett D'Amore #define	ENSONIQ_VENDOR_ID	0x1274
8488447a05SGarrett D'Amore #define	CREATIVE_VENDOR_ID	0x1102
8588447a05SGarrett D'Amore #define	ECTIVA_VENDOR_ID	0x1102
8688447a05SGarrett D'Amore #define	ENSONIQ_ES1371		0x1371
8788447a05SGarrett D'Amore #define	ENSONIQ_ES5880		0x8001
8888447a05SGarrett D'Amore #define	ENSONIQ_ES5880A		0x8002
8988447a05SGarrett D'Amore #define	ENSONIQ_ES5880B		0x5880
9088447a05SGarrett D'Amore #define	ECTIVA_ES1938		0x8938
9188447a05SGarrett D'Amore 
9288447a05SGarrett D'Amore #define	DEFRATE			48000
9388447a05SGarrett D'Amore #define	DRVNAME			"audioens"
9488447a05SGarrett D'Amore 
9588447a05SGarrett D'Amore typedef struct audioens_port
9688447a05SGarrett D'Amore {
9788447a05SGarrett D'Amore 	/* Audio parameters */
9888447a05SGarrett D'Amore 	int			speed;
9988447a05SGarrett D'Amore 
10088447a05SGarrett D'Amore 	int			num;
10188447a05SGarrett D'Amore #define	PORT_DAC		0
10288447a05SGarrett D'Amore #define	PORT_ADC		1
10388447a05SGarrett D'Amore #define	PORT_MAX		PORT_ADC
10488447a05SGarrett D'Amore 
10588447a05SGarrett D'Amore 	caddr_t			kaddr;
10688447a05SGarrett D'Amore 	uint32_t		paddr;
10788447a05SGarrett D'Amore 	ddi_acc_handle_t	acch;
10888447a05SGarrett D'Amore 	ddi_dma_handle_t	dmah;
10988447a05SGarrett D'Amore 	int			nchan;
11088447a05SGarrett D'Amore 	unsigned		nframes;
1115b162753SGarrett D'Amore 	unsigned		iframes;
11288447a05SGarrett D'Amore 	unsigned		frameno;
11388447a05SGarrett D'Amore 	uint64_t		count;
11488447a05SGarrett D'Amore 
11588447a05SGarrett D'Amore 	struct audioens_dev	*dev;
1165b162753SGarrett D'Amore 	audio_engine_t		*engine;
11788447a05SGarrett D'Amore } audioens_port_t;
11888447a05SGarrett D'Amore 
11988447a05SGarrett D'Amore typedef struct audioens_dev
12088447a05SGarrett D'Amore {
12188447a05SGarrett D'Amore 	audio_dev_t		*osdev;
12288447a05SGarrett D'Amore 	kmutex_t		mutex;
12388447a05SGarrett D'Amore 	uint16_t		devid;
12488447a05SGarrett D'Amore 	uint8_t			revision;
12588447a05SGarrett D'Amore 	dev_info_t		*dip;
12688447a05SGarrett D'Amore 
12788447a05SGarrett D'Amore 	audioens_port_t		port[PORT_MAX + 1];
12888447a05SGarrett D'Amore 
12988447a05SGarrett D'Amore 	ac97_t			*ac97;
13088447a05SGarrett D'Amore 
13188447a05SGarrett D'Amore 	caddr_t			regs;
13288447a05SGarrett D'Amore 	ddi_acc_handle_t	acch;
1335b162753SGarrett D'Amore 
1345b162753SGarrett D'Amore 	boolean_t		suspended;
1355b162753SGarrett D'Amore 
1365b162753SGarrett D'Amore #ifdef __x86
1375b162753SGarrett D'Amore 	boolean_t		useintr;
1385b162753SGarrett D'Amore 	ddi_intr_handle_t	intrh;
1395b162753SGarrett D'Amore 	uint_t			intrpri;
1405b162753SGarrett D'Amore #endif
14188447a05SGarrett D'Amore } audioens_dev_t;
14288447a05SGarrett D'Amore 
14388447a05SGarrett D'Amore static ddi_device_acc_attr_t acc_attr = {
14488447a05SGarrett D'Amore 	DDI_DEVICE_ATTR_V0,
14588447a05SGarrett D'Amore 	DDI_STRUCTURE_LE_ACC,
14688447a05SGarrett D'Amore 	DDI_STRICTORDER_ACC
14788447a05SGarrett D'Amore };
14888447a05SGarrett D'Amore 
14988447a05SGarrett D'Amore static ddi_device_acc_attr_t buf_attr = {
15088447a05SGarrett D'Amore 	DDI_DEVICE_ATTR_V0,
15188447a05SGarrett D'Amore 	DDI_NEVERSWAP_ACC,
15288447a05SGarrett D'Amore 	DDI_STRICTORDER_ACC
15388447a05SGarrett D'Amore };
15488447a05SGarrett D'Amore 
15588447a05SGarrett D'Amore static ddi_dma_attr_t dma_attr = {
15688447a05SGarrett D'Amore 	DMA_ATTR_VERSION,	/* dma_attr_version */
15788447a05SGarrett D'Amore 	0x0,			/* dma_attr_addr_lo */
15888447a05SGarrett D'Amore 	0xffffffffU,		/* dma_attr_addr_hi */
15988447a05SGarrett D'Amore 	0x3ffff,		/* dma_attr_count_max */
16088447a05SGarrett D'Amore 	0x8,			/* dma_attr_align */
16188447a05SGarrett D'Amore 	0x7f,			/* dma_attr_burstsizes */
16288447a05SGarrett D'Amore 	0x1,			/* dma_attr_minxfer */
16388447a05SGarrett D'Amore 	0x3ffff,		/* dma_attr_maxxfer */
16488447a05SGarrett D'Amore 	0x3ffff,		/* dma_attr_seg */
16588447a05SGarrett D'Amore 	0x1,			/* dma_attr_sgllen */
16688447a05SGarrett D'Amore 	0x1,			/* dma_attr_granular */
16788447a05SGarrett D'Amore 	0			/* dma_attr_flags */
16888447a05SGarrett D'Amore };
16988447a05SGarrett D'Amore 
17088447a05SGarrett D'Amore #define	GET8(dev, offset)	\
17188447a05SGarrett D'Amore 	ddi_get8(dev->acch, (uint8_t *)(dev->regs + (offset)))
17288447a05SGarrett D'Amore #define	GET16(dev, offset)	\
17388447a05SGarrett D'Amore 	ddi_get16(dev->acch, (uint16_t *)(void *)(dev->regs + (offset)))
17488447a05SGarrett D'Amore #define	GET32(dev, offset)	\
17588447a05SGarrett D'Amore 	ddi_get32(dev->acch, (uint32_t *)(void *)(dev->regs + (offset)))
17688447a05SGarrett D'Amore #define	PUT8(dev, offset, v)	\
17788447a05SGarrett D'Amore 	ddi_put8(dev->acch, (uint8_t *)(dev->regs + (offset)), v)
17888447a05SGarrett D'Amore #define	PUT16(dev, offset, v)	\
17988447a05SGarrett D'Amore 	ddi_put16(dev->acch, (uint16_t *)(void *)(dev->regs + (offset)), v)
18088447a05SGarrett D'Amore #define	PUT32(dev, offset, v)	\
18188447a05SGarrett D'Amore 	ddi_put32(dev->acch, (uint32_t *)(void *)(dev->regs + (offset)), v)
18288447a05SGarrett D'Amore 
18388447a05SGarrett D'Amore #define	CLR8(dev, offset, v)	PUT8(dev, offset, GET8(dev, offset) & ~(v))
18488447a05SGarrett D'Amore #define	SET8(dev, offset, v)	PUT8(dev, offset, GET8(dev, offset) | (v))
18588447a05SGarrett D'Amore #define	CLR32(dev, offset, v)	PUT32(dev, offset, GET32(dev, offset) & ~(v))
18688447a05SGarrett D'Amore #define	SET32(dev, offset, v)	PUT32(dev, offset, GET32(dev, offset) | (v))
18788447a05SGarrett D'Amore 
18888447a05SGarrett D'Amore static void audioens_init_hw(audioens_dev_t *);
18988447a05SGarrett D'Amore 
19088447a05SGarrett D'Amore static uint16_t
audioens_rd97(void * dev_,uint8_t wAddr)19188447a05SGarrett D'Amore audioens_rd97(void *dev_, uint8_t wAddr)
19288447a05SGarrett D'Amore {
19388447a05SGarrett D'Amore 	audioens_dev_t *dev = dev_;
19488447a05SGarrett D'Amore 	int i, dtemp;
19588447a05SGarrett D'Amore 
19688447a05SGarrett D'Amore 	mutex_enter(&dev->mutex);
19788447a05SGarrett D'Amore 	dtemp = GET32(dev, CONC_dCODECCTL_OFF);
19888447a05SGarrett D'Amore 	/* wait for WIP to go away saving the current state for later */
19988447a05SGarrett D'Amore 	for (i = 0; i < 0x100UL; ++i) {
20088447a05SGarrett D'Amore 		dtemp = GET32(dev, CONC_dCODECCTL_OFF);
20188447a05SGarrett D'Amore 		if ((dtemp & (1UL << 30)) == 0)
20288447a05SGarrett D'Amore 			break;
20388447a05SGarrett D'Amore 	}
20488447a05SGarrett D'Amore 
20588447a05SGarrett D'Amore 	/* write addr w/data=0 and assert read request ... */
20688447a05SGarrett D'Amore 	PUT32(dev, CONC_dCODECCTL_OFF, ((int)wAddr << 16) | (1UL << 23));
20788447a05SGarrett D'Amore 
20888447a05SGarrett D'Amore 	/* now wait for the data (RDY) */
20988447a05SGarrett D'Amore 	for (i = 0; i < 0x100UL; ++i) {
21088447a05SGarrett D'Amore 		dtemp = GET32(dev, CONC_dCODECCTL_OFF);
21188447a05SGarrett D'Amore 		if (dtemp & (1UL << 31))
21288447a05SGarrett D'Amore 			break;
21388447a05SGarrett D'Amore 	}
21488447a05SGarrett D'Amore 	dtemp = GET32(dev, CONC_dCODECCTL_OFF);
21588447a05SGarrett D'Amore 	mutex_exit(&dev->mutex);
21688447a05SGarrett D'Amore 
21788447a05SGarrett D'Amore 	return (dtemp & 0xffff);
21888447a05SGarrett D'Amore }
21988447a05SGarrett D'Amore 
22088447a05SGarrett D'Amore static void
audioens_wr97(void * dev_,uint8_t wAddr,uint16_t wData)22188447a05SGarrett D'Amore audioens_wr97(void *dev_, uint8_t wAddr, uint16_t wData)
22288447a05SGarrett D'Amore {
22388447a05SGarrett D'Amore 	audioens_dev_t *dev = dev_;
22488447a05SGarrett D'Amore 	int i, dtemp;
22588447a05SGarrett D'Amore 
22688447a05SGarrett D'Amore 	mutex_enter(&dev->mutex);
22788447a05SGarrett D'Amore 	/* wait for WIP to go away */
22888447a05SGarrett D'Amore 	for (i = 0; i < 0x100UL; ++i) {
22988447a05SGarrett D'Amore 		dtemp = GET32(dev, CONC_dCODECCTL_OFF);
23088447a05SGarrett D'Amore 		if ((dtemp & (1UL << 30)) == 0)
23188447a05SGarrett D'Amore 			break;
23288447a05SGarrett D'Amore 	}
23388447a05SGarrett D'Amore 
23488447a05SGarrett D'Amore 	PUT32(dev, CONC_dCODECCTL_OFF, ((int)wAddr << 16) | wData);
23588447a05SGarrett D'Amore 
23688447a05SGarrett D'Amore 	mutex_exit(&dev->mutex);
23788447a05SGarrett D'Amore }
23888447a05SGarrett D'Amore 
23988447a05SGarrett D'Amore static unsigned short
SRCRegRead(audioens_dev_t * dev,unsigned short reg)24088447a05SGarrett D'Amore SRCRegRead(audioens_dev_t *dev, unsigned short reg)
24188447a05SGarrett D'Amore {
24288447a05SGarrett D'Amore 	int i, dtemp;
24388447a05SGarrett D'Amore 
24488447a05SGarrett D'Amore 	dtemp = GET32(dev, CONC_dSRCIO_OFF);
24588447a05SGarrett D'Amore 	/* wait for ready */
24688447a05SGarrett D'Amore 	for (i = 0; i < SRC_IOPOLL_COUNT; ++i) {
24788447a05SGarrett D'Amore 		dtemp = GET32(dev, CONC_dSRCIO_OFF);
24888447a05SGarrett D'Amore 		if ((dtemp & SRC_BUSY) == 0)
24988447a05SGarrett D'Amore 			break;
25088447a05SGarrett D'Amore 	}
25188447a05SGarrett D'Amore 
25288447a05SGarrett D'Amore 	/* assert a read request */
25388447a05SGarrett D'Amore 	PUT32(dev, CONC_dSRCIO_OFF, (dtemp & SRC_CTLMASK) | ((int)reg << 25));
25488447a05SGarrett D'Amore 
25588447a05SGarrett D'Amore 	/* now wait for the data */
25688447a05SGarrett D'Amore 	for (i = 0; i < SRC_IOPOLL_COUNT; ++i) {
25788447a05SGarrett D'Amore 		dtemp = GET32(dev, CONC_dSRCIO_OFF);
25888447a05SGarrett D'Amore 		if ((dtemp & SRC_BUSY) == 0)
25988447a05SGarrett D'Amore 			break;
26088447a05SGarrett D'Amore 	}
26188447a05SGarrett D'Amore 
26288447a05SGarrett D'Amore 	return ((unsigned short) dtemp);
26388447a05SGarrett D'Amore }
26488447a05SGarrett D'Amore 
26588447a05SGarrett D'Amore static void
SRCRegWrite(audioens_dev_t * dev,unsigned short reg,unsigned short val)26688447a05SGarrett D'Amore SRCRegWrite(audioens_dev_t *dev, unsigned short reg, unsigned short val)
26788447a05SGarrett D'Amore {
26888447a05SGarrett D'Amore 	int i, dtemp;
26988447a05SGarrett D'Amore 	int writeval;
27088447a05SGarrett D'Amore 
27188447a05SGarrett D'Amore 	dtemp = GET32(dev, CONC_dSRCIO_OFF);
27288447a05SGarrett D'Amore 	/* wait for ready */
27388447a05SGarrett D'Amore 	for (i = 0; i < SRC_IOPOLL_COUNT; ++i) {
27488447a05SGarrett D'Amore 		dtemp = GET32(dev, CONC_dSRCIO_OFF);
27588447a05SGarrett D'Amore 		if ((dtemp & SRC_BUSY) == 0)
27688447a05SGarrett D'Amore 			break;
27788447a05SGarrett D'Amore 	}
27888447a05SGarrett D'Amore 
27988447a05SGarrett D'Amore 	/* assert the write request */
28088447a05SGarrett D'Amore 	writeval = (dtemp & SRC_CTLMASK) | SRC_WENABLE |
28188447a05SGarrett D'Amore 	    ((int)reg << 25) | val;
28288447a05SGarrett D'Amore 	PUT32(dev, CONC_dSRCIO_OFF, writeval);
28388447a05SGarrett D'Amore }
28488447a05SGarrett D'Amore 
28588447a05SGarrett D'Amore static void
SRCSetRate(audioens_dev_t * dev,unsigned char base,unsigned short rate)28688447a05SGarrett D'Amore SRCSetRate(audioens_dev_t *dev, unsigned char base, unsigned short rate)
28788447a05SGarrett D'Amore {
28888447a05SGarrett D'Amore 	int i, freq, dtemp;
28988447a05SGarrett D'Amore 	unsigned short N, truncM, truncStart;
29088447a05SGarrett D'Amore 
29188447a05SGarrett D'Amore 	if (base != SRC_ADC_BASE) {
29288447a05SGarrett D'Amore 		/* freeze the channel */
29388447a05SGarrett D'Amore 		dtemp = (base == SRC_DAC1_BASE) ?
29488447a05SGarrett D'Amore 		    SRC_DAC1FREEZE : SRC_DAC2FREEZE;
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 		}
29988447a05SGarrett D'Amore 		PUT32(dev, CONC_dSRCIO_OFF,
30088447a05SGarrett D'Amore 		    (GET32(dev, CONC_dSRCIO_OFF) & SRC_CTLMASK) | dtemp);
30188447a05SGarrett D'Amore 
30288447a05SGarrett D'Amore 		/* calculate new frequency and write it - preserve accum */
30388447a05SGarrett D'Amore 		freq = ((int)rate << 16) / 3000U;
30488447a05SGarrett D'Amore 		SRCRegWrite(dev, (unsigned short) base + SRC_INT_REGS_OFF,
30588447a05SGarrett D'Amore 		    (SRCRegRead(dev, (unsigned short) base + SRC_INT_REGS_OFF)
30688447a05SGarrett D'Amore 		    & 0x00ffU) | ((unsigned short) (freq >> 6) & 0xfc00));
30788447a05SGarrett D'Amore 		SRCRegWrite(dev, (unsigned short) base + SRC_VFREQ_FRAC_OFF,
30888447a05SGarrett D'Amore 		    (unsigned short) freq >> 1);
30988447a05SGarrett D'Amore 
31088447a05SGarrett D'Amore 		/* un-freeze the channel */
31188447a05SGarrett D'Amore 		for (i = 0; i < SRC_IOPOLL_COUNT; ++i)
31288447a05SGarrett D'Amore 			if (!(GET32(dev, CONC_dSRCIO_OFF) & SRC_BUSY))
31388447a05SGarrett D'Amore 				break;
31488447a05SGarrett D'Amore 		PUT32(dev, CONC_dSRCIO_OFF,
31588447a05SGarrett D'Amore 		    (GET32(dev, CONC_dSRCIO_OFF) & SRC_CTLMASK) & ~dtemp);
31688447a05SGarrett D'Amore 	} else {
31788447a05SGarrett D'Amore 		/* derive oversample ratio */
31888447a05SGarrett D'Amore 		N = rate / 3000U;
31988447a05SGarrett D'Amore 		if (N == 15 || N == 13 || N == 11 || N == 9)
32088447a05SGarrett D'Amore 			--N;
32188447a05SGarrett D'Amore 
32288447a05SGarrett D'Amore 		/* truncate the filter and write n/trunc_start */
32388447a05SGarrett D'Amore 		truncM = (21 * N - 1) | 1;
32488447a05SGarrett D'Amore 		if (rate >= 24000U) {
32588447a05SGarrett D'Amore 			if (truncM > 239)
32688447a05SGarrett D'Amore 				truncM = 239;
32788447a05SGarrett D'Amore 			truncStart = (239 - truncM) >> 1;
32888447a05SGarrett D'Amore 
32988447a05SGarrett D'Amore 			SRCRegWrite(dev, base + SRC_TRUNC_N_OFF,
33088447a05SGarrett D'Amore 			    (truncStart << 9) | (N << 4));
33188447a05SGarrett D'Amore 		} else {
33288447a05SGarrett D'Amore 			if (truncM > 119)
33388447a05SGarrett D'Amore 				truncM = 119;
33488447a05SGarrett D'Amore 			truncStart = (119 - truncM) >> 1;
33588447a05SGarrett D'Amore 
33688447a05SGarrett D'Amore 			SRCRegWrite(dev, base + SRC_TRUNC_N_OFF,
33788447a05SGarrett D'Amore 			    0x8000U | (truncStart << 9) | (N << 4));
33888447a05SGarrett D'Amore 		}
33988447a05SGarrett D'Amore 
34088447a05SGarrett D'Amore 		/* calculate new frequency and write it - preserve accum */
34188447a05SGarrett D'Amore 		freq = ((48000UL << 16) / rate) * N;
34288447a05SGarrett D'Amore 		SRCRegWrite(dev, base + SRC_INT_REGS_OFF,
34388447a05SGarrett D'Amore 		    (SRCRegRead(dev, (unsigned short) base + SRC_INT_REGS_OFF)
34488447a05SGarrett D'Amore 		    & 0x00ff) | ((unsigned short) (freq >> 6) & 0xfc00));
34588447a05SGarrett D'Amore 		SRCRegWrite(dev, base + SRC_VFREQ_FRAC_OFF,
34688447a05SGarrett D'Amore 		    (unsigned short) freq >> 1);
34788447a05SGarrett D'Amore 
34888447a05SGarrett D'Amore 		SRCRegWrite(dev, SRC_ADC_VOL_L, N << 8);
34988447a05SGarrett D'Amore 		SRCRegWrite(dev, SRC_ADC_VOL_R, N << 8);
35088447a05SGarrett D'Amore 	}
35188447a05SGarrett D'Amore }
35288447a05SGarrett D'Amore 
35388447a05SGarrett D'Amore static void
SRCInit(audioens_dev_t * dev)35488447a05SGarrett D'Amore SRCInit(audioens_dev_t *dev)
35588447a05SGarrett D'Amore {
35688447a05SGarrett D'Amore 	int i;
35788447a05SGarrett D'Amore 
35888447a05SGarrett D'Amore 	/* Clear all SRC RAM then init - keep SRC disabled until done */
35988447a05SGarrett D'Amore 	for (i = 0; i < SRC_IOPOLL_COUNT; ++i) {
36088447a05SGarrett D'Amore 		if (!(GET32(dev, CONC_dSRCIO_OFF) & SRC_BUSY))
36188447a05SGarrett D'Amore 			break;
36288447a05SGarrett D'Amore 	}
36388447a05SGarrett D'Amore 	PUT32(dev, CONC_dSRCIO_OFF, SRC_DISABLE);
36488447a05SGarrett D'Amore 
36588447a05SGarrett D'Amore 	for (i = 0; i < 0x80; ++i)
36688447a05SGarrett D'Amore 		SRCRegWrite(dev, (unsigned short) i, 0U);
36788447a05SGarrett D'Amore 
36888447a05SGarrett D'Amore 	SRCRegWrite(dev, SRC_DAC1_BASE + SRC_TRUNC_N_OFF, 16 << 4);
36988447a05SGarrett D'Amore 	SRCRegWrite(dev, SRC_DAC1_BASE + SRC_INT_REGS_OFF, 16 << 10);
37088447a05SGarrett D'Amore 	SRCRegWrite(dev, SRC_DAC2_BASE + SRC_TRUNC_N_OFF, 16 << 4);
37188447a05SGarrett D'Amore 	SRCRegWrite(dev, SRC_DAC2_BASE + SRC_INT_REGS_OFF, 16 << 10);
37288447a05SGarrett D'Amore 	SRCRegWrite(dev, SRC_DAC1_VOL_L, 1 << 12);
37388447a05SGarrett D'Amore 	SRCRegWrite(dev, SRC_DAC1_VOL_R, 1 << 12);
37488447a05SGarrett D'Amore 	SRCRegWrite(dev, SRC_DAC2_VOL_L, 1 << 12);
37588447a05SGarrett D'Amore 	SRCRegWrite(dev, SRC_DAC2_VOL_R, 1 << 12);
37688447a05SGarrett D'Amore 	SRCRegWrite(dev, SRC_ADC_VOL_L, 1 << 12);
37788447a05SGarrett D'Amore 	SRCRegWrite(dev, SRC_ADC_VOL_R, 1 << 12);
37888447a05SGarrett D'Amore 
37988447a05SGarrett D'Amore 	/* default some rates */
38088447a05SGarrett D'Amore 	SRCSetRate(dev, SRC_DAC1_BASE, 48000);
38188447a05SGarrett D'Amore 	SRCSetRate(dev, SRC_DAC2_BASE, 48000);
38288447a05SGarrett D'Amore 	SRCSetRate(dev, SRC_ADC_BASE, 48000);
38388447a05SGarrett D'Amore 
38488447a05SGarrett D'Amore 	/* now enable the whole deal */
38588447a05SGarrett D'Amore 	for (i = 0; i < SRC_IOPOLL_COUNT; ++i) {
38688447a05SGarrett D'Amore 		if (!(GET32(dev, CONC_dSRCIO_OFF) & SRC_BUSY))
38788447a05SGarrett D'Amore 			break;
38888447a05SGarrett D'Amore 	}
38988447a05SGarrett D'Amore 	PUT32(dev, CONC_dSRCIO_OFF, 0);
39088447a05SGarrett D'Amore }
39188447a05SGarrett D'Amore 
39288447a05SGarrett D'Amore static void
audioens_writemem(audioens_dev_t * dev,uint32_t page,uint32_t offs,uint32_t data)39388447a05SGarrett D'Amore audioens_writemem(audioens_dev_t *dev, uint32_t page, uint32_t offs,
39488447a05SGarrett D'Amore     uint32_t data)
39588447a05SGarrett D'Amore {
39688447a05SGarrett D'Amore 	/* Select memory page */
39788447a05SGarrett D'Amore 	PUT32(dev, CONC_bMEMPAGE_OFF, page);
39888447a05SGarrett D'Amore 	PUT32(dev, offs, data);
39988447a05SGarrett D'Amore }
40088447a05SGarrett D'Amore 
40188447a05SGarrett D'Amore static uint32_t
audioens_readmem(audioens_dev_t * dev,uint32_t page,uint32_t offs)40288447a05SGarrett D'Amore audioens_readmem(audioens_dev_t *dev, uint32_t page, uint32_t offs)
40388447a05SGarrett D'Amore {
40488447a05SGarrett D'Amore 	PUT32(dev, CONC_bMEMPAGE_OFF, page);	/* Select memory page */
40588447a05SGarrett D'Amore 	return (GET32(dev, offs));
40688447a05SGarrett D'Amore }
40788447a05SGarrett D'Amore 
4085b162753SGarrett D'Amore #ifdef __x86
4095b162753SGarrett D'Amore static unsigned
audioens_intr(caddr_t arg1,caddr_t arg2)4105b162753SGarrett D'Amore audioens_intr(caddr_t arg1, caddr_t arg2)
4115b162753SGarrett D'Amore {
4125b162753SGarrett D'Amore 	audioens_dev_t *dev = (void *)arg1;
4135b162753SGarrett D'Amore 	uint32_t status;
4145b162753SGarrett D'Amore 	uint32_t frameno;
4155b162753SGarrett D'Amore 	uint32_t n;
4165b162753SGarrett D'Amore 	audioens_port_t *port;
4175b162753SGarrett D'Amore 
4185b162753SGarrett D'Amore 	_NOTE(ARGUNUSED(arg2));
4195b162753SGarrett D'Amore 
4205b162753SGarrett D'Amore 	mutex_enter(&dev->mutex);
4215b162753SGarrett D'Amore 	if (dev->suspended || !dev->useintr) {
4225b162753SGarrett D'Amore 		mutex_exit(&dev->mutex);
4235b162753SGarrett D'Amore 		return (DDI_INTR_UNCLAIMED);
4245b162753SGarrett D'Amore 	}
4255b162753SGarrett D'Amore 
4265b162753SGarrett D'Amore 	status = GET32(dev, CONC_dSTATUS_OFF);
4275b162753SGarrett D'Amore 	if ((status & CONC_STATUS_PENDING) == 0) {
4285b162753SGarrett D'Amore 		mutex_exit(&dev->mutex);
4295b162753SGarrett D'Amore 		return (DDI_INTR_UNCLAIMED);
4305b162753SGarrett D'Amore 	}
4315b162753SGarrett D'Amore 
4325b162753SGarrett D'Amore 	/* Three interrupts, DAC1, DAC2, and ADC.  The UART we just toss. */
4335b162753SGarrett D'Amore 
4345b162753SGarrett D'Amore 	if (status & CONC_STATUS_DAC1INT) {
4355b162753SGarrett D'Amore 		port = &dev->port[PORT_DAC];
4365b162753SGarrett D'Amore 
4375b162753SGarrett D'Amore 		/* current frame counter is in high nybble */
4385b162753SGarrett D'Amore 		frameno = audioens_readmem(dev,
4395b162753SGarrett D'Amore 		    CONC_DAC1CTL_PAGE, CONC_wDAC1FC_OFF) >> 16;
4405b162753SGarrett D'Amore 		n = frameno >= port->frameno ?
4415b162753SGarrett D'Amore 		    frameno - port->frameno :
4425b162753SGarrett D'Amore 		    frameno + port->nframes - port->frameno;
4435b162753SGarrett D'Amore 		port->frameno = frameno;
4445b162753SGarrett D'Amore 		port->count += n;
4455b162753SGarrett D'Amore 		CLR8(dev, CONC_bSERCTL_OFF, CONC_SERCTL_DAC1IE);
4465b162753SGarrett D'Amore 		SET8(dev, CONC_bSERCTL_OFF, CONC_SERCTL_DAC1IE);
4475b162753SGarrett D'Amore 	}
4485b162753SGarrett D'Amore 	if (status & CONC_STATUS_ADCINT) {
4495b162753SGarrett D'Amore 		port = &dev->port[PORT_ADC];
4505b162753SGarrett D'Amore 
4515b162753SGarrett D'Amore 		/* current frame counter is in high nybble */
4525b162753SGarrett D'Amore 		frameno = audioens_readmem(dev,
4535b162753SGarrett D'Amore 		    CONC_ADCCTL_PAGE, CONC_wADCFC_OFF) >> 16;
4545b162753SGarrett D'Amore 		n = frameno >= port->frameno ?
4555b162753SGarrett D'Amore 		    frameno - port->frameno :
4565b162753SGarrett D'Amore 		    frameno + port->nframes - port->frameno;
4575b162753SGarrett D'Amore 		port->frameno = frameno;
4585b162753SGarrett D'Amore 		port->count += n;
4595b162753SGarrett D'Amore 		CLR8(dev, CONC_bSERCTL_OFF, CONC_SERCTL_ADCIE);
4605b162753SGarrett D'Amore 		SET8(dev, CONC_bSERCTL_OFF, CONC_SERCTL_ADCIE);
4615b162753SGarrett D'Amore 	}
4625b162753SGarrett D'Amore 	if (status & CONC_STATUS_DAC2INT) {
4635b162753SGarrett D'Amore 		CLR8(dev, CONC_bSERCTL_OFF, CONC_SERCTL_DAC2IE);
4645b162753SGarrett D'Amore 		SET8(dev, CONC_bSERCTL_OFF, CONC_SERCTL_DAC2IE);
4655b162753SGarrett D'Amore 	}
4665b162753SGarrett D'Amore 	if (status & CONC_STATUS_UARTINT) {
4675b162753SGarrett D'Amore 		/*
4685b162753SGarrett D'Amore 		 * Consume data in the UART RX FIFO.  We don't support
4695b162753SGarrett D'Amore 		 * the UART for now, so just eat it.
4705b162753SGarrett D'Amore 		 */
4715b162753SGarrett D'Amore 		while (GET8(dev, CONC_bUARTCSTAT_OFF) & CONC_UART_RXRDY)
4725b162753SGarrett D'Amore 			continue;
4735b162753SGarrett D'Amore 	}
4745b162753SGarrett D'Amore 	mutex_exit(&dev->mutex);
4755b162753SGarrett D'Amore 
4765b162753SGarrett D'Amore 	return (DDI_INTR_CLAIMED);
4775b162753SGarrett D'Amore }
4785b162753SGarrett D'Amore 
4795b162753SGarrett D'Amore static int
audioens_setup_intr(audioens_dev_t * dev)4805b162753SGarrett D'Amore audioens_setup_intr(audioens_dev_t *dev)
4815b162753SGarrett D'Amore {
4825b162753SGarrett D'Amore 	int	act;
4835b162753SGarrett D'Amore 	uint_t	ipri;
4845b162753SGarrett D'Amore 
4855b162753SGarrett D'Amore 	if ((ddi_intr_alloc(dev->dip, &dev->intrh, DDI_INTR_TYPE_FIXED, 0, 1,
4865b162753SGarrett D'Amore 	    &act, DDI_INTR_ALLOC_NORMAL) != DDI_SUCCESS) || (act != 1)) {
4875b162753SGarrett D'Amore 		audio_dev_warn(dev->osdev, "can't alloc intr handle");
4885b162753SGarrett D'Amore 		goto fail;
4895b162753SGarrett D'Amore 	}
4905b162753SGarrett D'Amore 
4915b162753SGarrett D'Amore 	if (ddi_intr_get_pri(dev->intrh, &ipri) != DDI_SUCCESS) {
4925b162753SGarrett D'Amore 		audio_dev_warn(dev->osdev, "can't get interrupt priority");
4935b162753SGarrett D'Amore 		goto fail;
4945b162753SGarrett D'Amore 	}
4955b162753SGarrett D'Amore 	if (ddi_intr_add_handler(dev->intrh, audioens_intr, dev, NULL) !=
4965b162753SGarrett D'Amore 	    DDI_SUCCESS) {
4975b162753SGarrett D'Amore 		audio_dev_warn(dev->osdev, "cannot add interrupt handler");
4985b162753SGarrett D'Amore 		goto fail;
4995b162753SGarrett D'Amore 	}
5005b162753SGarrett D'Amore 	dev->intrpri = ipri;
5015b162753SGarrett D'Amore 	return (DDI_SUCCESS);
5025b162753SGarrett D'Amore 
5035b162753SGarrett D'Amore fail:
5045b162753SGarrett D'Amore 	if (dev->intrh != NULL) {
5055b162753SGarrett D'Amore 		(void) ddi_intr_free(dev->intrh);
5065b162753SGarrett D'Amore 		dev->intrh = NULL;
5075b162753SGarrett D'Amore 	}
5085b162753SGarrett D'Amore 	return (DDI_FAILURE);
5095b162753SGarrett D'Amore }
5105b162753SGarrett D'Amore 
5115b162753SGarrett D'Amore #endif	/* __x86 */
5125b162753SGarrett D'Amore 
51388447a05SGarrett D'Amore /*
51488447a05SGarrett D'Amore  * Audio routines
51588447a05SGarrett D'Amore  */
51688447a05SGarrett D'Amore static int
audioens_format(void * arg)51788447a05SGarrett D'Amore audioens_format(void *arg)
51888447a05SGarrett D'Amore {
51988447a05SGarrett D'Amore 	_NOTE(ARGUNUSED(arg));
52088447a05SGarrett D'Amore 
52188447a05SGarrett D'Amore 	/* hardware can also do AUDIO_FORMAT_U8, but no need for it */
52288447a05SGarrett D'Amore 	return (AUDIO_FORMAT_S16_LE);
52388447a05SGarrett D'Amore }
52488447a05SGarrett D'Amore 
52588447a05SGarrett D'Amore static int
audioens_channels(void * arg)52688447a05SGarrett D'Amore audioens_channels(void *arg)
52788447a05SGarrett D'Amore {
52888447a05SGarrett D'Amore 	audioens_port_t *port = arg;
52988447a05SGarrett D'Amore 
53088447a05SGarrett D'Amore 	return (port->nchan);
53188447a05SGarrett D'Amore }
53288447a05SGarrett D'Amore 
53388447a05SGarrett D'Amore static int
audioens_rate(void * arg)53488447a05SGarrett D'Amore audioens_rate(void *arg)
53588447a05SGarrett D'Amore {
53688447a05SGarrett D'Amore 	audioens_port_t *port = arg;
53788447a05SGarrett D'Amore 
53888447a05SGarrett D'Amore 	return (port->speed);
53988447a05SGarrett D'Amore }
54088447a05SGarrett D'Amore 
54168c47f65SGarrett D'Amore static int
audioens_open(void * arg,int flag,unsigned * nframes,caddr_t * bufp)54268c47f65SGarrett D'Amore audioens_open(void *arg, int flag, unsigned *nframes, caddr_t *bufp)
54388447a05SGarrett D'Amore {
54468c47f65SGarrett D'Amore 	audioens_port_t	*port = arg;
54588447a05SGarrett D'Amore 	audioens_dev_t	*dev = port->dev;
54688447a05SGarrett D'Amore 
54768c47f65SGarrett D'Amore 	_NOTE(ARGUNUSED(flag));
54868c47f65SGarrett D'Amore 
54968c47f65SGarrett D'Amore 	mutex_enter(&dev->mutex);
55068c47f65SGarrett D'Amore 
55168c47f65SGarrett D'Amore 	port->count = 0;
55268c47f65SGarrett D'Amore 
55368c47f65SGarrett D'Amore 	*nframes = port->nframes;
55468c47f65SGarrett D'Amore 	*bufp = port->kaddr;
55568c47f65SGarrett D'Amore 	mutex_exit(&dev->mutex);
55668c47f65SGarrett D'Amore 
55768c47f65SGarrett D'Amore 	return (0);
55868c47f65SGarrett D'Amore }
55968c47f65SGarrett D'Amore 
56068c47f65SGarrett D'Amore static int
audioens_start(void * arg)56168c47f65SGarrett D'Amore audioens_start(void *arg)
56268c47f65SGarrett D'Amore {
56368c47f65SGarrett D'Amore 	audioens_port_t *port = arg;
56468c47f65SGarrett D'Amore 	audioens_dev_t *dev = port->dev;
56568c47f65SGarrett D'Amore 	uint32_t tmp;
56668c47f65SGarrett D'Amore 
56768c47f65SGarrett D'Amore 	mutex_enter(&dev->mutex);
56888447a05SGarrett D'Amore 
56988447a05SGarrett D'Amore 	switch (port->num) {
57088447a05SGarrett D'Amore 	case PORT_DAC:
57188447a05SGarrett D'Amore 		/* Set physical address of the DMA buffer */
57288447a05SGarrett D'Amore 		audioens_writemem(dev, CONC_DAC1CTL_PAGE, CONC_dDAC1PADDR_OFF,
57388447a05SGarrett D'Amore 		    port->paddr);
57488447a05SGarrett D'Amore 		audioens_writemem(dev, CONC_DAC2CTL_PAGE, CONC_dDAC2PADDR_OFF,
57588447a05SGarrett D'Amore 		    port->paddr + (port->nframes * sizeof (int16_t) * 2));
57688447a05SGarrett D'Amore 
57788447a05SGarrett D'Amore 		/* Set DAC rate */
57888447a05SGarrett D'Amore 		SRCSetRate(dev, SRC_DAC1_BASE, port->speed);
57988447a05SGarrett D'Amore 		SRCSetRate(dev, SRC_DAC2_BASE, port->speed);
58088447a05SGarrett D'Amore 
58188447a05SGarrett D'Amore 		/* Configure the channel setup - SPDIF only uses front */
58288447a05SGarrett D'Amore 		tmp = GET32(dev, CONC_dSTATUS_OFF);
58388447a05SGarrett D'Amore 		tmp &= ~(CONC_STATUS_SPKR_MASK | CONC_STATUS_SPDIF_MASK);
58488447a05SGarrett D'Amore 		tmp |= CONC_STATUS_SPKR_4CH | CONC_STATUS_SPDIF_P1;
58588447a05SGarrett D'Amore 		PUT32(dev, CONC_dSTATUS_OFF, tmp);
58688447a05SGarrett D'Amore 
58788447a05SGarrett D'Amore 		/* Set format */
58888447a05SGarrett D'Amore 		PUT8(dev, CONC_bSKIPC_OFF, 0x10);
58988447a05SGarrett D'Amore 		SET8(dev, CONC_bSERFMT_OFF,
59088447a05SGarrett D'Amore 		    CONC_PCM_DAC1_16BIT | CONC_PCM_DAC2_16BIT |
59188447a05SGarrett D'Amore 		    CONC_PCM_DAC1_STEREO | CONC_PCM_DAC2_STEREO);
59288447a05SGarrett D'Amore 
59388447a05SGarrett D'Amore 		/* Set the frame count */
59488447a05SGarrett D'Amore 		audioens_writemem(dev, CONC_DAC1CTL_PAGE, CONC_wDAC1FC_OFF,
59588447a05SGarrett D'Amore 		    port->nframes - 1);
59688447a05SGarrett D'Amore 		audioens_writemem(dev, CONC_DAC2CTL_PAGE, CONC_wDAC2FC_OFF,
59788447a05SGarrett D'Amore 		    port->nframes - 1);
59888447a05SGarrett D'Amore 
5995b162753SGarrett D'Amore 		PUT16(dev, CONC_wDAC1IC_OFF, port->iframes - 1);
6005b162753SGarrett D'Amore 		PUT16(dev, CONC_wDAC2IC_OFF, port->iframes - 1);
60168c47f65SGarrett D'Amore 		SET8(dev, CONC_bDEVCTL_OFF,
60268c47f65SGarrett D'Amore 		    CONC_DEVCTL_DAC2_EN | CONC_DEVCTL_DAC1_EN);
6035b162753SGarrett D'Amore #ifdef __x86
6045b162753SGarrett D'Amore 		if (dev->useintr) {
6055b162753SGarrett D'Amore 			SET8(dev, CONC_bSERCTL_OFF, CONC_SERCTL_DAC1IE);
6065b162753SGarrett D'Amore 		}
6075b162753SGarrett D'Amore #endif
60868c47f65SGarrett D'Amore 
60988447a05SGarrett D'Amore 		break;
61088447a05SGarrett D'Amore 
61188447a05SGarrett D'Amore 	case PORT_ADC:
61288447a05SGarrett D'Amore 		/* Set physical address of the DMA buffer */
61388447a05SGarrett D'Amore 		audioens_writemem(dev, CONC_ADCCTL_PAGE, CONC_dADCPADDR_OFF,
61488447a05SGarrett D'Amore 		    port->paddr);
61588447a05SGarrett D'Amore 
61688447a05SGarrett D'Amore 		/* Set ADC rate */
61788447a05SGarrett D'Amore 		SRCSetRate(dev, SRC_ADC_BASE, port->speed);
61888447a05SGarrett D'Amore 
61988447a05SGarrett D'Amore 		/* Set format - for input we only support 16 bit input */
62088447a05SGarrett D'Amore 		tmp = GET8(dev, CONC_bSERFMT_OFF);
62188447a05SGarrett D'Amore 		tmp |= CONC_PCM_ADC_16BIT;
62288447a05SGarrett D'Amore 		tmp |= CONC_PCM_ADC_STEREO;
62388447a05SGarrett D'Amore 
62488447a05SGarrett D'Amore 		PUT8(dev, CONC_bSKIPC_OFF, 0x10);
62588447a05SGarrett D'Amore 
62688447a05SGarrett D'Amore 		PUT8(dev, CONC_bSERFMT_OFF, tmp);
62788447a05SGarrett D'Amore 
62888447a05SGarrett D'Amore 		/* Set the frame count */
62988447a05SGarrett D'Amore 		audioens_writemem(dev, CONC_ADCCTL_PAGE, CONC_wADCFC_OFF,
63088447a05SGarrett D'Amore 		    port->nframes - 1);
63188447a05SGarrett D'Amore 
63288447a05SGarrett D'Amore 		/* Set # of frames between interrupts */
6335b162753SGarrett D'Amore 		PUT16(dev, CONC_wADCIC_OFF, port->iframes - 1);
63488447a05SGarrett D'Amore 
63568c47f65SGarrett D'Amore 		SET8(dev, CONC_bDEVCTL_OFF, CONC_DEVCTL_ADC_EN);
6365b162753SGarrett D'Amore #ifdef __x86
6375b162753SGarrett D'Amore 		if (dev->useintr) {
6385b162753SGarrett D'Amore 			SET8(dev, CONC_bSERCTL_OFF, CONC_SERCTL_ADCIE);
6395b162753SGarrett D'Amore 		}
6405b162753SGarrett D'Amore #endif
64188447a05SGarrett D'Amore 		break;
64288447a05SGarrett D'Amore 	}
64388447a05SGarrett D'Amore 
64488447a05SGarrett D'Amore 	port->frameno = 0;
64588447a05SGarrett D'Amore 	mutex_exit(&dev->mutex);
64688447a05SGarrett D'Amore 
64788447a05SGarrett D'Amore 	return (0);
64888447a05SGarrett D'Amore }
64988447a05SGarrett D'Amore 
65088447a05SGarrett D'Amore static void
audioens_stop(void * arg)65168c47f65SGarrett D'Amore audioens_stop(void *arg)
65288447a05SGarrett D'Amore {
65388447a05SGarrett D'Amore 	audioens_port_t *port = arg;
65488447a05SGarrett D'Amore 	audioens_dev_t *dev = port->dev;
65588447a05SGarrett D'Amore 
65688447a05SGarrett D'Amore 	mutex_enter(&dev->mutex);
65768c47f65SGarrett D'Amore 	switch (port->num) {
65868c47f65SGarrett D'Amore 	case PORT_DAC:
65968c47f65SGarrett D'Amore 		CLR8(dev, CONC_bDEVCTL_OFF,
66068c47f65SGarrett D'Amore 		    CONC_DEVCTL_DAC2_EN | CONC_DEVCTL_DAC1_EN);
66168c47f65SGarrett D'Amore 		break;
66268c47f65SGarrett D'Amore 	case PORT_ADC:
66368c47f65SGarrett D'Amore 		CLR8(dev, CONC_bDEVCTL_OFF, CONC_DEVCTL_ADC_EN);
66468c47f65SGarrett D'Amore 		break;
66588447a05SGarrett D'Amore 	}
66688447a05SGarrett D'Amore 	mutex_exit(&dev->mutex);
66788447a05SGarrett D'Amore }
66888447a05SGarrett D'Amore 
66968c47f65SGarrett D'Amore static uint64_t
audioens_count(void * arg)67068c47f65SGarrett D'Amore audioens_count(void *arg)
67188447a05SGarrett D'Amore {
67288447a05SGarrett D'Amore 	audioens_port_t *port = arg;
67388447a05SGarrett D'Amore 	audioens_dev_t *dev = port->dev;
67468c47f65SGarrett D'Amore 	uint64_t val;
67588447a05SGarrett D'Amore 	uint32_t page, offs;
67688447a05SGarrett D'Amore 	int frameno, n;
67788447a05SGarrett D'Amore 
67888447a05SGarrett D'Amore 	switch (port->num) {
67988447a05SGarrett D'Amore 	case PORT_DAC:
68088447a05SGarrett D'Amore 		page = CONC_DAC1CTL_PAGE;
68188447a05SGarrett D'Amore 		offs = CONC_wDAC1FC_OFF;
68288447a05SGarrett D'Amore 		break;
68388447a05SGarrett D'Amore 
68488447a05SGarrett D'Amore 	case PORT_ADC:
68588447a05SGarrett D'Amore 		page = CONC_ADCCTL_PAGE;
68688447a05SGarrett D'Amore 		offs = CONC_wADCFC_OFF;
68788447a05SGarrett D'Amore 		break;
688*001481f6SToomas Soome 	default:
689*001481f6SToomas Soome 		panic("Unknown port number: %d\n", port->num);
69088447a05SGarrett D'Amore 	}
69188447a05SGarrett D'Amore 
69268c47f65SGarrett D'Amore 	mutex_enter(&dev->mutex);
6935b162753SGarrett D'Amore #ifdef __x86
6945b162753SGarrett D'Amore 	if (!dev->useintr) {
6955b162753SGarrett D'Amore #endif
6965b162753SGarrett D'Amore 
69788447a05SGarrett D'Amore 	/*
69888447a05SGarrett D'Amore 	 * Note that the current frame counter is in the high nybble.
69988447a05SGarrett D'Amore 	 */
70088447a05SGarrett D'Amore 	frameno = audioens_readmem(port->dev, page, offs) >> 16;
70188447a05SGarrett D'Amore 	n = frameno >= port->frameno ?
70288447a05SGarrett D'Amore 	    frameno - port->frameno :
70388447a05SGarrett D'Amore 	    frameno + port->nframes - port->frameno;
70488447a05SGarrett D'Amore 	port->frameno = frameno;
70588447a05SGarrett D'Amore 	port->count += n;
70688447a05SGarrett D'Amore 
7075b162753SGarrett D'Amore #ifdef __x86
7085b162753SGarrett D'Amore 	}
7095b162753SGarrett D'Amore #endif
7105b162753SGarrett D'Amore 
71188447a05SGarrett D'Amore 	val = port->count;
71288447a05SGarrett D'Amore 	mutex_exit(&dev->mutex);
71368c47f65SGarrett D'Amore 
71488447a05SGarrett D'Amore 	return (val);
71588447a05SGarrett D'Amore }
71688447a05SGarrett D'Amore 
71788447a05SGarrett D'Amore static void
audioens_close(void * arg)71888447a05SGarrett D'Amore audioens_close(void *arg)
71988447a05SGarrett D'Amore {
72068c47f65SGarrett D'Amore 	_NOTE(ARGUNUSED(arg));
72188447a05SGarrett D'Amore }
72288447a05SGarrett D'Amore 
72388447a05SGarrett D'Amore static void
audioens_sync(void * arg,unsigned nframes)72488447a05SGarrett D'Amore audioens_sync(void *arg, unsigned nframes)
72588447a05SGarrett D'Amore {
72688447a05SGarrett D'Amore 	audioens_port_t *port = arg;
72788447a05SGarrett D'Amore 
72888447a05SGarrett D'Amore 	_NOTE(ARGUNUSED(nframes));
72988447a05SGarrett D'Amore 
73088447a05SGarrett D'Amore 	if (port->num == PORT_ADC) {
73168c47f65SGarrett D'Amore 		(void) ddi_dma_sync(port->dmah, 0, 0, DDI_DMA_SYNC_FORKERNEL);
73288447a05SGarrett D'Amore 	} else {
73388447a05SGarrett D'Amore 		(void) ddi_dma_sync(port->dmah, 0, 0, DDI_DMA_SYNC_FORDEV);
73488447a05SGarrett D'Amore 	}
73588447a05SGarrett D'Amore }
73688447a05SGarrett D'Amore 
73788447a05SGarrett D'Amore static void
audioens_chinfo(void * arg,int chan,unsigned * offset,unsigned * incr)73888447a05SGarrett D'Amore audioens_chinfo(void *arg, int chan, unsigned *offset, unsigned *incr)
73988447a05SGarrett D'Amore {
74088447a05SGarrett D'Amore 	audioens_port_t *port = arg;
74188447a05SGarrett D'Amore 
74288447a05SGarrett D'Amore 	if ((port->num == PORT_DAC) && (chan >= 2)) {
74388447a05SGarrett D'Amore 		*offset = (port->nframes * 2) + (chan % 2);
74488447a05SGarrett D'Amore 		*incr = 2;
74588447a05SGarrett D'Amore 	} else {
74688447a05SGarrett D'Amore 		*offset = chan;
74788447a05SGarrett D'Amore 		*incr = 2;
74888447a05SGarrett D'Amore 	}
74988447a05SGarrett D'Amore }
75088447a05SGarrett D'Amore 
75188447a05SGarrett D'Amore audio_engine_ops_t audioens_engine_ops = {
75288447a05SGarrett D'Amore 	AUDIO_ENGINE_VERSION,		/* version number */
75388447a05SGarrett D'Amore 	audioens_open,
75488447a05SGarrett D'Amore 	audioens_close,
75588447a05SGarrett D'Amore 	audioens_start,
75688447a05SGarrett D'Amore 	audioens_stop,
75788447a05SGarrett D'Amore 	audioens_count,
75888447a05SGarrett D'Amore 	audioens_format,
75988447a05SGarrett D'Amore 	audioens_channels,
76088447a05SGarrett D'Amore 	audioens_rate,
76188447a05SGarrett D'Amore 	audioens_sync,
762f9ead4a5SGarrett D'Amore 	NULL,
763f9ead4a5SGarrett D'Amore 	audioens_chinfo,
764f9ead4a5SGarrett D'Amore 	NULL,
76588447a05SGarrett D'Amore };
76688447a05SGarrett D'Amore 
76788447a05SGarrett D'Amore void
audioens_init_hw(audioens_dev_t * dev)76888447a05SGarrett D'Amore audioens_init_hw(audioens_dev_t *dev)
76988447a05SGarrett D'Amore {
77088447a05SGarrett D'Amore 	int tmp;
77188447a05SGarrett D'Amore 
77288447a05SGarrett D'Amore 	if ((dev->devid == ENSONIQ_ES5880) ||
77388447a05SGarrett D'Amore 	    (dev->devid == ENSONIQ_ES5880A) ||
77488447a05SGarrett D'Amore 	    (dev->devid == ENSONIQ_ES5880B) ||
77588447a05SGarrett D'Amore 	    (dev->devid == 0x1371 && dev->revision == 7) ||
77688447a05SGarrett D'Amore 	    (dev->devid == 0x1371 && dev->revision >= 9)) {
77788447a05SGarrett D'Amore 
77888447a05SGarrett D'Amore 		/* Have a ES5880 so enable the codec manually */
77988447a05SGarrett D'Amore 		tmp = GET8(dev, CONC_bINTSUMM_OFF) & 0xff;
78088447a05SGarrett D'Amore 		tmp |= 0x20;
78188447a05SGarrett D'Amore 		PUT8(dev, CONC_bINTSUMM_OFF, tmp);
78288447a05SGarrett D'Amore 		for (int i = 0; i < 2000; i++)
78388447a05SGarrett D'Amore 			drv_usecwait(10);
78488447a05SGarrett D'Amore 	}
78588447a05SGarrett D'Amore 
78688447a05SGarrett D'Amore 	SRCInit(dev);
78788447a05SGarrett D'Amore 
78888447a05SGarrett D'Amore 	/*
78988447a05SGarrett D'Amore 	 * Turn on CODEC (UART and joystick left disabled)
79088447a05SGarrett D'Amore 	 */
79188447a05SGarrett D'Amore 	tmp = GET32(dev, CONC_bDEVCTL_OFF) & 0xff;
79288447a05SGarrett D'Amore 	tmp &= ~(CONC_DEVCTL_PCICLK_DS | CONC_DEVCTL_XTALCLK_DS);
79388447a05SGarrett D'Amore 	PUT8(dev, CONC_bDEVCTL_OFF, tmp);
79488447a05SGarrett D'Amore 	PUT8(dev, CONC_bUARTCSTAT_OFF, 0x00);
79588447a05SGarrett D'Amore 
79688447a05SGarrett D'Amore 	/* Perform AC97 codec warm reset */
79788447a05SGarrett D'Amore 	tmp = GET8(dev, CONC_bMISCCTL_OFF) & 0xff;
79888447a05SGarrett D'Amore 	PUT8(dev, CONC_bMISCCTL_OFF, tmp | CONC_MISCCTL_SYNC_RES);
79988447a05SGarrett D'Amore 	drv_usecwait(200);
80088447a05SGarrett D'Amore 	PUT8(dev, CONC_bMISCCTL_OFF, tmp);
80188447a05SGarrett D'Amore 	drv_usecwait(200);
80288447a05SGarrett D'Amore 
80388447a05SGarrett D'Amore 	if (dev->revision >= 4) {
80488447a05SGarrett D'Amore 		/* XXX: enable SPDIF - PCM only for now */
80588447a05SGarrett D'Amore 		if (audioens_spdif) {
80688447a05SGarrett D'Amore 			/* enable SPDIF */
80788447a05SGarrett D'Amore 			PUT32(dev, 0x04, GET32(dev, 0x04) | (1 << 18));
80888447a05SGarrett D'Amore 			/* SPDIF out = data from DAC */
80988447a05SGarrett D'Amore 			PUT32(dev, 0x00, GET32(dev, 0x00) | (1 << 26));
81088447a05SGarrett D'Amore 			CLR32(dev, CONC_dSPDIF_OFF, CONC_SPDIF_AC3);
81188447a05SGarrett D'Amore 
81288447a05SGarrett D'Amore 		} else {
81388447a05SGarrett D'Amore 			/* disable spdif out */
81488447a05SGarrett D'Amore 			PUT32(dev, 0x04, GET32(dev, 0x04) & ~(1 << 18));
81588447a05SGarrett D'Amore 			PUT32(dev, 0x00, GET32(dev, 0x00) & ~(1 << 26));
81688447a05SGarrett D'Amore 		}
81788447a05SGarrett D'Amore 
81888447a05SGarrett D'Amore 		/* we want to run each channel independently */
81988447a05SGarrett D'Amore 		CLR32(dev, CONC_dSTATUS_OFF, CONC_STATUS_ECHO);
82088447a05SGarrett D'Amore 	}
82188447a05SGarrett D'Amore }
82288447a05SGarrett D'Amore 
82388447a05SGarrett D'Amore static int
audioens_init(audioens_dev_t * dev)82488447a05SGarrett D'Amore audioens_init(audioens_dev_t *dev)
82588447a05SGarrett D'Amore {
82688447a05SGarrett D'Amore 
82788447a05SGarrett D'Amore 	audioens_init_hw(dev);
82888447a05SGarrett D'Amore 
82988447a05SGarrett D'Amore 	/*
83088447a05SGarrett D'Amore 	 * On this hardware, we want to disable the internal speaker by
83188447a05SGarrett D'Amore 	 * default, if it exists.  (We don't have a speakerphone on any
83288447a05SGarrett D'Amore 	 * of these cards, and no SPARC hardware uses it either!)
83388447a05SGarrett D'Amore 	 */
834505c7a69SGarrett D'Amore 	(void) ddi_prop_update_int(DDI_DEV_T_NONE, dev->dip, AC97_PROP_SPEAKER,
835505c7a69SGarrett D'Amore 	    0);
83688447a05SGarrett D'Amore 
83788447a05SGarrett D'Amore 	/*
83888447a05SGarrett D'Amore 	 * Init mixer
83988447a05SGarrett D'Amore 	 */
84088447a05SGarrett D'Amore 
84188447a05SGarrett D'Amore 	dev->ac97 = ac97_alloc(dev->dip, audioens_rd97, audioens_wr97, dev);
84288447a05SGarrett D'Amore 	if (dev->ac97 == NULL)
84388447a05SGarrett D'Amore 		return (DDI_FAILURE);
84488447a05SGarrett D'Amore 
84588447a05SGarrett D'Amore 	if (ac97_init(dev->ac97, dev->osdev) != 0) {
84688447a05SGarrett D'Amore 		return (DDI_FAILURE);
84788447a05SGarrett D'Amore 	}
84888447a05SGarrett D'Amore 
84988447a05SGarrett D'Amore 	for (int i = 0; i <= PORT_MAX; i++) {
85088447a05SGarrett D'Amore 		audioens_port_t *port;
85188447a05SGarrett D'Amore 		unsigned caps;
85288447a05SGarrett D'Amore 		unsigned dmaflags;
85388447a05SGarrett D'Amore 		size_t rlen;
85488447a05SGarrett D'Amore 		ddi_dma_cookie_t c;
85588447a05SGarrett D'Amore 		unsigned ccnt;
8565b162753SGarrett D'Amore 		size_t bufsz;
85788447a05SGarrett D'Amore 
85888447a05SGarrett D'Amore 		port = &dev->port[i];
85988447a05SGarrett D'Amore 		port->dev = dev;
86088447a05SGarrett D'Amore 
8615b162753SGarrett D'Amore 		/*
8625b162753SGarrett D'Amore 		 * We have 48000Hz.  At that rate, 128 frames will give
8635b162753SGarrett D'Amore 		 * us an interrupt rate of 375Hz.  2048 frames buys about
8645b162753SGarrett D'Amore 		 * 42ms of buffer.  Note that interrupts are only enabled
8655b162753SGarrett D'Amore 		 * for platforms which need them (i.e. VMWare).
8665b162753SGarrett D'Amore 		 */
8675b162753SGarrett D'Amore 
86888447a05SGarrett D'Amore 		switch (i) {
86988447a05SGarrett D'Amore 		case PORT_DAC:
87088447a05SGarrett D'Amore 			port->nchan = 4;
87188447a05SGarrett D'Amore 			port->speed = 48000;
8725b162753SGarrett D'Amore 			port->iframes = 128;
8735b162753SGarrett D'Amore 			port->nframes = 2048;
87488447a05SGarrett D'Amore 			caps = ENGINE_OUTPUT_CAP;
87588447a05SGarrett D'Amore 			dmaflags = DDI_DMA_WRITE | DDI_DMA_CONSISTENT;
87688447a05SGarrett D'Amore 			break;
87788447a05SGarrett D'Amore 
87888447a05SGarrett D'Amore 		case PORT_ADC:
87988447a05SGarrett D'Amore 			port->nchan = 2;
88088447a05SGarrett D'Amore 			port->speed = 48000;
8815b162753SGarrett D'Amore 			port->iframes = 128;
8825b162753SGarrett D'Amore 			port->nframes = 2048;
88388447a05SGarrett D'Amore 			caps = ENGINE_INPUT_CAP;
88488447a05SGarrett D'Amore 			dmaflags = DDI_DMA_READ | DDI_DMA_CONSISTENT;
88588447a05SGarrett D'Amore 			break;
88688447a05SGarrett D'Amore 		}
88788447a05SGarrett D'Amore 
88888447a05SGarrett D'Amore 		port->num = i;
8895b162753SGarrett D'Amore 		bufsz = port->nframes * port->nchan * sizeof (uint16_t);
89088447a05SGarrett D'Amore 
89188447a05SGarrett D'Amore 		/*
89288447a05SGarrett D'Amore 		 * Allocate DMA resources.
89388447a05SGarrett D'Amore 		 */
89488447a05SGarrett D'Amore 
89588447a05SGarrett D'Amore 		if (ddi_dma_alloc_handle(dev->dip, &dma_attr, DDI_DMA_SLEEP,
89688447a05SGarrett D'Amore 		    NULL, &port->dmah) != DDI_SUCCESS) {
89788447a05SGarrett D'Amore 			audio_dev_warn(dev->osdev,
89888447a05SGarrett D'Amore 			    "port %d: dma handle allocation failed", i);
89988447a05SGarrett D'Amore 			return (DDI_FAILURE);
90088447a05SGarrett D'Amore 		}
9015b162753SGarrett D'Amore 		if (ddi_dma_mem_alloc(port->dmah, bufsz, &buf_attr,
90288447a05SGarrett D'Amore 		    DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL, &port->kaddr,
90388447a05SGarrett D'Amore 		    &rlen, &port->acch) != DDI_SUCCESS) {
90488447a05SGarrett D'Amore 			audio_dev_warn(dev->osdev,
90588447a05SGarrett D'Amore 			    "port %d: dma memory allocation failed", i);
90688447a05SGarrett D'Amore 			return (DDI_FAILURE);
90788447a05SGarrett D'Amore 		}
90888447a05SGarrett D'Amore 		/* ensure that the buffer is zeroed out properly */
90988447a05SGarrett D'Amore 		bzero(port->kaddr, rlen);
91088447a05SGarrett D'Amore 		if (ddi_dma_addr_bind_handle(port->dmah, NULL, port->kaddr,
9115b162753SGarrett D'Amore 		    bufsz, dmaflags, DDI_DMA_SLEEP, NULL,
91288447a05SGarrett D'Amore 		    &c, &ccnt) != DDI_DMA_MAPPED) {
91388447a05SGarrett D'Amore 			audio_dev_warn(dev->osdev,
91488447a05SGarrett D'Amore 			    "port %d: dma binding failed", i);
91588447a05SGarrett D'Amore 			return (DDI_FAILURE);
91688447a05SGarrett D'Amore 		}
91788447a05SGarrett D'Amore 		port->paddr = c.dmac_address;
91888447a05SGarrett D'Amore 
91988447a05SGarrett D'Amore 		/*
92088447a05SGarrett D'Amore 		 * Allocate and configure audio engine.
92188447a05SGarrett D'Amore 		 */
92288447a05SGarrett D'Amore 		port->engine = audio_engine_alloc(&audioens_engine_ops, caps);
92388447a05SGarrett D'Amore 		if (port->engine == NULL) {
92488447a05SGarrett D'Amore 			audio_dev_warn(dev->osdev,
92588447a05SGarrett D'Amore 			    "port %d: audio_engine_alloc failed", i);
92688447a05SGarrett D'Amore 			return (DDI_FAILURE);
92788447a05SGarrett D'Amore 		}
92888447a05SGarrett D'Amore 
92988447a05SGarrett D'Amore 		audio_engine_set_private(port->engine, port);
93088447a05SGarrett D'Amore 		audio_dev_add_engine(dev->osdev, port->engine);
93188447a05SGarrett D'Amore 	}
93288447a05SGarrett D'Amore 
93388447a05SGarrett D'Amore 	if (audio_dev_register(dev->osdev) != DDI_SUCCESS) {
93488447a05SGarrett D'Amore 		audio_dev_warn(dev->osdev,
93588447a05SGarrett D'Amore 		    "unable to register with audio framework");
93688447a05SGarrett D'Amore 		return (DDI_FAILURE);
93788447a05SGarrett D'Amore 	}
93888447a05SGarrett D'Amore 
93988447a05SGarrett D'Amore 	return (DDI_SUCCESS);
94088447a05SGarrett D'Amore }
94188447a05SGarrett D'Amore 
94288447a05SGarrett D'Amore void
audioens_destroy(audioens_dev_t * dev)94388447a05SGarrett D'Amore audioens_destroy(audioens_dev_t *dev)
94488447a05SGarrett D'Amore {
94588447a05SGarrett D'Amore 	int	i;
94688447a05SGarrett D'Amore 
9475b162753SGarrett D'Amore #ifdef __x86
9485b162753SGarrett D'Amore 	if (dev->useintr && dev->intrh != NULL) {
9495b162753SGarrett D'Amore 		(void) ddi_intr_disable(dev->intrh);
9505b162753SGarrett D'Amore 		(void) ddi_intr_remove_handler(dev->intrh);
9515b162753SGarrett D'Amore 		(void) ddi_intr_free(dev->intrh);
9525b162753SGarrett D'Amore 		dev->intrh = NULL;
9535b162753SGarrett D'Amore 	}
9545b162753SGarrett D'Amore #endif
9555b162753SGarrett D'Amore 
95668c47f65SGarrett D'Amore 	mutex_destroy(&dev->mutex);
95788447a05SGarrett D'Amore 
95888447a05SGarrett D'Amore 	/* free up ports, including DMA resources for ports */
95988447a05SGarrett D'Amore 	for (i = 0; i <= PORT_MAX; i++) {
96088447a05SGarrett D'Amore 		audioens_port_t	*port = &dev->port[i];
96188447a05SGarrett D'Amore 
96288447a05SGarrett D'Amore 		if (port->paddr != 0)
96388447a05SGarrett D'Amore 			(void) ddi_dma_unbind_handle(port->dmah);
96488447a05SGarrett D'Amore 		if (port->acch != NULL)
96588447a05SGarrett D'Amore 			ddi_dma_mem_free(&port->acch);
96688447a05SGarrett D'Amore 		if (port->dmah != NULL)
96788447a05SGarrett D'Amore 			ddi_dma_free_handle(&port->dmah);
96888447a05SGarrett D'Amore 
96988447a05SGarrett D'Amore 		if (port->engine != NULL) {
97088447a05SGarrett D'Amore 			audio_dev_remove_engine(dev->osdev, port->engine);
97188447a05SGarrett D'Amore 			audio_engine_free(port->engine);
97288447a05SGarrett D'Amore 		}
97388447a05SGarrett D'Amore 	}
97488447a05SGarrett D'Amore 
97588447a05SGarrett D'Amore 	if (dev->acch != NULL) {
97688447a05SGarrett D'Amore 		ddi_regs_map_free(&dev->acch);
97788447a05SGarrett D'Amore 	}
97888447a05SGarrett D'Amore 
97988447a05SGarrett D'Amore 	if (dev->ac97) {
98088447a05SGarrett D'Amore 		ac97_free(dev->ac97);
98188447a05SGarrett D'Amore 	}
98288447a05SGarrett D'Amore 
98388447a05SGarrett D'Amore 	if (dev->osdev != NULL) {
98488447a05SGarrett D'Amore 		audio_dev_free(dev->osdev);
98588447a05SGarrett D'Amore 	}
98688447a05SGarrett D'Amore 
98788447a05SGarrett D'Amore 	kmem_free(dev, sizeof (*dev));
98888447a05SGarrett D'Amore }
98988447a05SGarrett D'Amore 
99088447a05SGarrett D'Amore int
audioens_attach(dev_info_t * dip)99188447a05SGarrett D'Amore audioens_attach(dev_info_t *dip)
99288447a05SGarrett D'Amore {
99388447a05SGarrett D'Amore 	uint16_t pci_command, vendor, device;
99488447a05SGarrett D'Amore 	uint8_t revision;
99588447a05SGarrett D'Amore 	audioens_dev_t *dev;
99688447a05SGarrett D'Amore 	ddi_acc_handle_t pcih;
99788447a05SGarrett D'Amore 	const char *chip_name;
99888447a05SGarrett D'Amore 	const char *chip_vers;
99988447a05SGarrett D'Amore 
100088447a05SGarrett D'Amore 	dev = kmem_zalloc(sizeof (*dev), KM_SLEEP);
100188447a05SGarrett D'Amore 	dev->dip = dip;
100288447a05SGarrett D'Amore 	ddi_set_driver_private(dip, dev);
100368c47f65SGarrett D'Amore 	mutex_init(&dev->mutex, NULL, MUTEX_DRIVER, NULL);
100488447a05SGarrett D'Amore 
100588447a05SGarrett D'Amore 	if (pci_config_setup(dip, &pcih) != DDI_SUCCESS) {
100688447a05SGarrett D'Amore 		audio_dev_warn(dev->osdev, "pci_config_setup failed");
10075b162753SGarrett D'Amore 		goto err_exit;
100888447a05SGarrett D'Amore 	}
100988447a05SGarrett D'Amore 
101088447a05SGarrett D'Amore 	vendor = pci_config_get16(pcih, PCI_CONF_VENID);
101188447a05SGarrett D'Amore 	device = pci_config_get16(pcih, PCI_CONF_DEVID);
101288447a05SGarrett D'Amore 	revision = pci_config_get8(pcih, PCI_CONF_REVID);
101388447a05SGarrett D'Amore 
101488447a05SGarrett D'Amore 	if ((vendor != ENSONIQ_VENDOR_ID && vendor != CREATIVE_VENDOR_ID) ||
101588447a05SGarrett D'Amore 	    (device != ENSONIQ_ES1371 && device != ENSONIQ_ES5880 &&
101688447a05SGarrett D'Amore 	    device != ENSONIQ_ES5880A && device != ECTIVA_ES1938 &&
10175b162753SGarrett D'Amore 	    device != ENSONIQ_ES5880B)) {
10185b162753SGarrett D'Amore 		audio_dev_warn(dev->osdev, "unrecognized device");
101988447a05SGarrett D'Amore 		goto err_exit;
10205b162753SGarrett D'Amore 	}
102188447a05SGarrett D'Amore 
102288447a05SGarrett D'Amore 	chip_name = "AudioPCI97";
102388447a05SGarrett D'Amore 	chip_vers = "unknown";
102488447a05SGarrett D'Amore 
102588447a05SGarrett D'Amore 	switch (device) {
102688447a05SGarrett D'Amore 	case ENSONIQ_ES1371:
102788447a05SGarrett D'Amore 		chip_name = "AudioPCI97";
102888447a05SGarrett D'Amore 		switch (revision) {
102988447a05SGarrett D'Amore 		case 0x02:
103088447a05SGarrett D'Amore 		case 0x09:
103188447a05SGarrett D'Amore 		default:
103288447a05SGarrett D'Amore 			chip_vers = "ES1371";
103388447a05SGarrett D'Amore 			break;
103488447a05SGarrett D'Amore 		case 0x04:
103588447a05SGarrett D'Amore 		case 0x06:
103688447a05SGarrett D'Amore 		case 0x08:
103788447a05SGarrett D'Amore 			chip_vers = "ES1373";
103888447a05SGarrett D'Amore 			break;
103988447a05SGarrett D'Amore 		case 0x07:
104088447a05SGarrett D'Amore 			chip_vers = "ES5880";
104188447a05SGarrett D'Amore 			break;
104288447a05SGarrett D'Amore 		}
104388447a05SGarrett D'Amore 		break;
104488447a05SGarrett D'Amore 
104588447a05SGarrett D'Amore 	case ENSONIQ_ES5880:
104688447a05SGarrett D'Amore 		chip_name = "SB PCI128";
104788447a05SGarrett D'Amore 		chip_vers = "ES5880";
104888447a05SGarrett D'Amore 		break;
104988447a05SGarrett D'Amore 	case ENSONIQ_ES5880A:
105088447a05SGarrett D'Amore 		chip_name = "SB PCI128";
105188447a05SGarrett D'Amore 		chip_vers = "ES5880A";
105288447a05SGarrett D'Amore 		break;
105388447a05SGarrett D'Amore 	case ENSONIQ_ES5880B:
105488447a05SGarrett D'Amore 		chip_name = "SB PCI128";
105588447a05SGarrett D'Amore 		chip_vers = "ES5880B";
105688447a05SGarrett D'Amore 		break;
105788447a05SGarrett D'Amore 
105888447a05SGarrett D'Amore 	case ECTIVA_ES1938:
105988447a05SGarrett D'Amore 		chip_name = "AudioPCI";
106088447a05SGarrett D'Amore 		chip_vers = "ES1938";
106188447a05SGarrett D'Amore 		break;
106288447a05SGarrett D'Amore 	}
106388447a05SGarrett D'Amore 
106488447a05SGarrett D'Amore 	dev->revision = revision;
106588447a05SGarrett D'Amore 	dev->devid = device;
106688447a05SGarrett D'Amore 
106788447a05SGarrett D'Amore 	dev->osdev = audio_dev_alloc(dip, 0);
106888447a05SGarrett D'Amore 	if (dev->osdev == NULL) {
106988447a05SGarrett D'Amore 		goto err_exit;
107088447a05SGarrett D'Amore 	}
107188447a05SGarrett D'Amore 
107288447a05SGarrett D'Amore 	audio_dev_set_description(dev->osdev, chip_name);
107388447a05SGarrett D'Amore 	audio_dev_set_version(dev->osdev, chip_vers);
107488447a05SGarrett D'Amore 
107588447a05SGarrett D'Amore 	/* set the PCI latency */
107688447a05SGarrett D'Amore 	if ((audioens_latency == 32) || (audioens_latency == 64) ||
107788447a05SGarrett D'Amore 	    (audioens_latency == 96))
107888447a05SGarrett D'Amore 		pci_config_put8(pcih, PCI_CONF_LATENCY_TIMER,
107988447a05SGarrett D'Amore 		    audioens_latency);
108088447a05SGarrett D'Amore 
108188447a05SGarrett D'Amore 	/* activate the device */
108288447a05SGarrett D'Amore 	pci_command = pci_config_get16(pcih, PCI_CONF_COMM);
108388447a05SGarrett D'Amore 	pci_command |= PCI_COMM_ME | PCI_COMM_IO;
108488447a05SGarrett D'Amore 	pci_config_put16(pcih, PCI_CONF_COMM, pci_command);
108588447a05SGarrett D'Amore 
108688447a05SGarrett D'Amore 	/* map registers */
108788447a05SGarrett D'Amore 	if (ddi_regs_map_setup(dip, 1, &dev->regs, 0, 0, &acc_attr,
108888447a05SGarrett D'Amore 	    &dev->acch) != DDI_SUCCESS) {
108988447a05SGarrett D'Amore 		audio_dev_warn(dev->osdev, "can't map registers");
109088447a05SGarrett D'Amore 		goto err_exit;
109188447a05SGarrett D'Amore 	}
109288447a05SGarrett D'Amore 
10935b162753SGarrett D'Amore #ifdef __x86
10945b162753SGarrett D'Amore 	/*
10955b162753SGarrett D'Amore 	 * Virtual platforms (mostly VMWare!) seem to need us to pulse
10965b162753SGarrett D'Amore 	 * the interrupt enables to make progress.  So enable (emulated)
10975b162753SGarrett D'Amore 	 * hardware interrupts.
10985b162753SGarrett D'Amore 	 */
10995b162753SGarrett D'Amore 	dev->useintr = B_FALSE;
11005b162753SGarrett D'Amore 	if (get_hwenv() & HW_VIRTUAL) {
11015b162753SGarrett D'Amore 		dev->useintr = B_TRUE;
11025b162753SGarrett D'Amore 		if (audioens_setup_intr(dev) != DDI_SUCCESS) {
11035b162753SGarrett D'Amore 			goto err_exit;
11045b162753SGarrett D'Amore 		}
11055b162753SGarrett D'Amore 		/* Reinitialize the mutex with interrupt priority. */
11065b162753SGarrett D'Amore 		mutex_destroy(&dev->mutex);
11075b162753SGarrett D'Amore 		mutex_init(&dev->mutex, NULL, MUTEX_DRIVER,
11085b162753SGarrett D'Amore 		    DDI_INTR_PRI(dev->intrpri));
11095b162753SGarrett D'Amore 	}
11105b162753SGarrett D'Amore #endif
11115b162753SGarrett D'Amore 
111288447a05SGarrett D'Amore 	/* This allocates and configures the engines */
111388447a05SGarrett D'Amore 	if (audioens_init(dev) != DDI_SUCCESS) {
111488447a05SGarrett D'Amore 		audio_dev_warn(dev->osdev, "can't init device");
111588447a05SGarrett D'Amore 		goto err_exit;
111688447a05SGarrett D'Amore 	}
111788447a05SGarrett D'Amore 
11185b162753SGarrett D'Amore #ifdef __x86
11195b162753SGarrett D'Amore 	if (dev->useintr) {
11205b162753SGarrett D'Amore 		(void) ddi_intr_enable(dev->intrh);
11215b162753SGarrett D'Amore 	}
11225b162753SGarrett D'Amore #endif
112388447a05SGarrett D'Amore 	pci_config_teardown(&pcih);
112488447a05SGarrett D'Amore 
112588447a05SGarrett D'Amore 	ddi_report_dev(dip);
112688447a05SGarrett D'Amore 
112788447a05SGarrett D'Amore 	return (DDI_SUCCESS);
112888447a05SGarrett D'Amore 
112988447a05SGarrett D'Amore err_exit:
113088447a05SGarrett D'Amore 	pci_config_teardown(&pcih);
113188447a05SGarrett D'Amore 
113288447a05SGarrett D'Amore 	audioens_destroy(dev);
113388447a05SGarrett D'Amore 
113488447a05SGarrett D'Amore 	return (DDI_FAILURE);
113588447a05SGarrett D'Amore }
113688447a05SGarrett D'Amore 
113788447a05SGarrett D'Amore int
audioens_detach(audioens_dev_t * dev)113888447a05SGarrett D'Amore audioens_detach(audioens_dev_t *dev)
113988447a05SGarrett D'Amore {
114088447a05SGarrett D'Amore 	int tmp;
114188447a05SGarrett D'Amore 
114288447a05SGarrett D'Amore 	/* first unregister us from the DDI framework, might be busy */
114388447a05SGarrett D'Amore 	if (audio_dev_unregister(dev->osdev) != DDI_SUCCESS)
114488447a05SGarrett D'Amore 		return (DDI_FAILURE);
114588447a05SGarrett D'Amore 
114688447a05SGarrett D'Amore 	mutex_enter(&dev->mutex);
114788447a05SGarrett D'Amore 
114888447a05SGarrett D'Amore 	tmp = GET8(dev, CONC_bSERCTL_OFF) &
114988447a05SGarrett D'Amore 	    ~(CONC_SERCTL_DAC2IE | CONC_SERCTL_DAC1IE | CONC_SERCTL_ADCIE);
115088447a05SGarrett D'Amore 	PUT8(dev, CONC_bSERCTL_OFF, tmp);
115188447a05SGarrett D'Amore 	PUT8(dev, CONC_bSERCTL_OFF, tmp);
115288447a05SGarrett D'Amore 	PUT8(dev, CONC_bSERCTL_OFF, tmp);
115388447a05SGarrett D'Amore 	PUT8(dev, CONC_bSERCTL_OFF, tmp);
115488447a05SGarrett D'Amore 
115588447a05SGarrett D'Amore 	tmp = GET8(dev, CONC_bDEVCTL_OFF) &
115688447a05SGarrett D'Amore 	    ~(CONC_DEVCTL_DAC2_EN | CONC_DEVCTL_ADC_EN | CONC_DEVCTL_DAC1_EN);
115788447a05SGarrett D'Amore 	PUT8(dev, CONC_bDEVCTL_OFF, tmp);
115888447a05SGarrett D'Amore 	PUT8(dev, CONC_bDEVCTL_OFF, tmp);
115988447a05SGarrett D'Amore 	PUT8(dev, CONC_bDEVCTL_OFF, tmp);
116088447a05SGarrett D'Amore 	PUT8(dev, CONC_bDEVCTL_OFF, tmp);
116188447a05SGarrett D'Amore 
116288447a05SGarrett D'Amore 	mutex_exit(&dev->mutex);
116388447a05SGarrett D'Amore 
116488447a05SGarrett D'Amore 	audioens_destroy(dev);
116588447a05SGarrett D'Amore 
116688447a05SGarrett D'Amore 	return (DDI_SUCCESS);
116788447a05SGarrett D'Amore }
116888447a05SGarrett D'Amore 
116988447a05SGarrett D'Amore static int
audioens_resume(audioens_dev_t * dev)117088447a05SGarrett D'Amore audioens_resume(audioens_dev_t *dev)
117188447a05SGarrett D'Amore {
11725b162753SGarrett D'Amore 	mutex_enter(&dev->mutex);
11735b162753SGarrett D'Amore 	dev->suspended = B_FALSE;
11745b162753SGarrett D'Amore 	mutex_exit(&dev->mutex);
11755b162753SGarrett D'Amore 
117688447a05SGarrett D'Amore 	/* reinitialize hardware */
117788447a05SGarrett D'Amore 	audioens_init_hw(dev);
117888447a05SGarrett D'Amore 
117988447a05SGarrett D'Amore 	/* restore AC97 state */
118068c47f65SGarrett D'Amore 	ac97_reset(dev->ac97);
118188447a05SGarrett D'Amore 
118268c47f65SGarrett D'Amore 	audio_dev_resume(dev->osdev);
118388447a05SGarrett D'Amore 
118488447a05SGarrett D'Amore 	return (DDI_SUCCESS);
118588447a05SGarrett D'Amore }
118688447a05SGarrett D'Amore 
118788447a05SGarrett D'Amore static int
audioens_suspend(audioens_dev_t * dev)118888447a05SGarrett D'Amore audioens_suspend(audioens_dev_t *dev)
118988447a05SGarrett D'Amore {
119068c47f65SGarrett D'Amore 	audio_dev_suspend(dev->osdev);
119188447a05SGarrett D'Amore 
11925b162753SGarrett D'Amore 	mutex_enter(&dev->mutex);
11935b162753SGarrett D'Amore 	CLR8(dev, CONC_bDEVCTL_OFF,
11945b162753SGarrett D'Amore 	    CONC_DEVCTL_DAC2_EN | CONC_DEVCTL_DAC1_EN | CONC_DEVCTL_ADC_EN);
11955b162753SGarrett D'Amore 	dev->suspended = B_TRUE;
11965b162753SGarrett D'Amore 	mutex_exit(&dev->mutex);
11975b162753SGarrett D'Amore 
119888447a05SGarrett D'Amore 	return (DDI_SUCCESS);
119988447a05SGarrett D'Amore }
120088447a05SGarrett D'Amore 
120188447a05SGarrett D'Amore static int
audioens_quiesce(dev_info_t * dip)120288447a05SGarrett D'Amore audioens_quiesce(dev_info_t *dip)
120388447a05SGarrett D'Amore {
120488447a05SGarrett D'Amore 	audioens_dev_t	*dev;
120588447a05SGarrett D'Amore 	uint8_t		tmp;
120688447a05SGarrett D'Amore 
120788447a05SGarrett D'Amore 	if ((dev = ddi_get_driver_private(dip)) == NULL) {
120888447a05SGarrett D'Amore 		return (DDI_FAILURE);
120988447a05SGarrett D'Amore 	}
121088447a05SGarrett D'Amore 
121188447a05SGarrett D'Amore 	/* This disables all DMA engines and interrupts */
121288447a05SGarrett D'Amore 	tmp = GET8(dev, CONC_bSERCTL_OFF) &
121388447a05SGarrett D'Amore 	    ~(CONC_SERCTL_DAC2IE | CONC_SERCTL_DAC1IE | CONC_SERCTL_ADCIE);
121488447a05SGarrett D'Amore 	PUT8(dev, CONC_bSERCTL_OFF, tmp);
121588447a05SGarrett D'Amore 	PUT8(dev, CONC_bSERCTL_OFF, tmp);
121688447a05SGarrett D'Amore 	PUT8(dev, CONC_bSERCTL_OFF, tmp);
121788447a05SGarrett D'Amore 	PUT8(dev, CONC_bSERCTL_OFF, tmp);
121888447a05SGarrett D'Amore 
121988447a05SGarrett D'Amore 	tmp = GET8(dev, CONC_bDEVCTL_OFF) &
122088447a05SGarrett D'Amore 	    ~(CONC_DEVCTL_DAC2_EN | CONC_DEVCTL_ADC_EN | CONC_DEVCTL_DAC1_EN);
122188447a05SGarrett D'Amore 	PUT8(dev, CONC_bDEVCTL_OFF, tmp);
122288447a05SGarrett D'Amore 	PUT8(dev, CONC_bDEVCTL_OFF, tmp);
122388447a05SGarrett D'Amore 	PUT8(dev, CONC_bDEVCTL_OFF, tmp);
122488447a05SGarrett D'Amore 	PUT8(dev, CONC_bDEVCTL_OFF, tmp);
122588447a05SGarrett D'Amore 
122688447a05SGarrett D'Amore 	return (DDI_SUCCESS);
122788447a05SGarrett D'Amore }
122888447a05SGarrett D'Amore 
122988447a05SGarrett D'Amore 
123088447a05SGarrett D'Amore static int
audioens_ddi_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)123188447a05SGarrett D'Amore audioens_ddi_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
123288447a05SGarrett D'Amore {
123388447a05SGarrett D'Amore 	audioens_dev_t *dev;
123488447a05SGarrett D'Amore 
123588447a05SGarrett D'Amore 	switch (cmd) {
123688447a05SGarrett D'Amore 	case DDI_ATTACH:
123788447a05SGarrett D'Amore 		return (audioens_attach(dip));
123888447a05SGarrett D'Amore 
123988447a05SGarrett D'Amore 	case DDI_RESUME:
124088447a05SGarrett D'Amore 		if ((dev = ddi_get_driver_private(dip)) == NULL) {
124188447a05SGarrett D'Amore 			return (DDI_FAILURE);
124288447a05SGarrett D'Amore 		}
124388447a05SGarrett D'Amore 		return (audioens_resume(dev));
124488447a05SGarrett D'Amore 
124588447a05SGarrett D'Amore 	default:
124688447a05SGarrett D'Amore 		return (DDI_FAILURE);
124788447a05SGarrett D'Amore 	}
124888447a05SGarrett D'Amore }
124988447a05SGarrett D'Amore 
125088447a05SGarrett D'Amore static int
audioens_ddi_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)125188447a05SGarrett D'Amore audioens_ddi_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
125288447a05SGarrett D'Amore {
125388447a05SGarrett D'Amore 	audioens_dev_t *dev;
125488447a05SGarrett D'Amore 
125588447a05SGarrett D'Amore 	if ((dev = ddi_get_driver_private(dip)) == NULL) {
125688447a05SGarrett D'Amore 		return (DDI_FAILURE);
125788447a05SGarrett D'Amore 	}
125888447a05SGarrett D'Amore 
125988447a05SGarrett D'Amore 	switch (cmd) {
126088447a05SGarrett D'Amore 	case DDI_DETACH:
126188447a05SGarrett D'Amore 		return (audioens_detach(dev));
126288447a05SGarrett D'Amore 
126388447a05SGarrett D'Amore 	case DDI_SUSPEND:
126488447a05SGarrett D'Amore 		return (audioens_suspend(dev));
126588447a05SGarrett D'Amore 	default:
126688447a05SGarrett D'Amore 		return (DDI_FAILURE);
126788447a05SGarrett D'Amore 	}
126888447a05SGarrett D'Amore }
126988447a05SGarrett D'Amore 
127088447a05SGarrett D'Amore static int audioens_ddi_attach(dev_info_t *, ddi_attach_cmd_t);
127188447a05SGarrett D'Amore static int audioens_ddi_detach(dev_info_t *, ddi_detach_cmd_t);
127288447a05SGarrett D'Amore 
127388447a05SGarrett D'Amore static struct dev_ops audioens_dev_ops = {
127488447a05SGarrett D'Amore 	DEVO_REV,		/* rev */
127588447a05SGarrett D'Amore 	0,			/* refcnt */
127688447a05SGarrett D'Amore 	NULL,			/* getinfo */
127788447a05SGarrett D'Amore 	nulldev,		/* identify */
127888447a05SGarrett D'Amore 	nulldev,		/* probe */
127988447a05SGarrett D'Amore 	audioens_ddi_attach,	/* attach */
128088447a05SGarrett D'Amore 	audioens_ddi_detach,	/* detach */
128188447a05SGarrett D'Amore 	nodev,			/* reset */
128288447a05SGarrett D'Amore 	NULL,			/* cb_ops */
128388447a05SGarrett D'Amore 	NULL,			/* bus_ops */
128488447a05SGarrett D'Amore 	NULL,			/* power */
128588447a05SGarrett D'Amore 	audioens_quiesce,	/* quiesce */
128688447a05SGarrett D'Amore };
128788447a05SGarrett D'Amore 
128888447a05SGarrett D'Amore static struct modldrv audioens_modldrv = {
128988447a05SGarrett D'Amore 	&mod_driverops,			/* drv_modops */
129088447a05SGarrett D'Amore 	"Ensoniq 1371/1373 Audio",	/* linkinfo */
129188447a05SGarrett D'Amore 	&audioens_dev_ops,		/* dev_ops */
129288447a05SGarrett D'Amore };
129388447a05SGarrett D'Amore 
129488447a05SGarrett D'Amore static struct modlinkage modlinkage = {
129588447a05SGarrett D'Amore 	MODREV_1,
129688447a05SGarrett D'Amore 	{ &audioens_modldrv, NULL }
129788447a05SGarrett D'Amore };
129888447a05SGarrett D'Amore 
129988447a05SGarrett D'Amore int
_init(void)130088447a05SGarrett D'Amore _init(void)
130188447a05SGarrett D'Amore {
130288447a05SGarrett D'Amore 	int	rv;
130388447a05SGarrett D'Amore 
130488447a05SGarrett D'Amore 	audio_init_ops(&audioens_dev_ops, DRVNAME);
130588447a05SGarrett D'Amore 	if ((rv = mod_install(&modlinkage)) != 0) {
130688447a05SGarrett D'Amore 		audio_fini_ops(&audioens_dev_ops);
130788447a05SGarrett D'Amore 	}
130888447a05SGarrett D'Amore 	return (rv);
130988447a05SGarrett D'Amore }
131088447a05SGarrett D'Amore 
131188447a05SGarrett D'Amore int
_fini(void)131288447a05SGarrett D'Amore _fini(void)
131388447a05SGarrett D'Amore {
131488447a05SGarrett D'Amore 	int	rv;
131588447a05SGarrett D'Amore 
131688447a05SGarrett D'Amore 	if ((rv = mod_remove(&modlinkage)) == 0) {
131788447a05SGarrett D'Amore 		audio_fini_ops(&audioens_dev_ops);
131888447a05SGarrett D'Amore 	}
131988447a05SGarrett D'Amore 	return (rv);
132088447a05SGarrett D'Amore }
132188447a05SGarrett D'Amore 
132288447a05SGarrett D'Amore int
_info(struct modinfo * modinfop)132388447a05SGarrett D'Amore _info(struct modinfo *modinfop)
132488447a05SGarrett D'Amore {
132588447a05SGarrett D'Amore 	return (mod_info(&modlinkage, modinfop));
132688447a05SGarrett D'Amore }
1327