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*68c47f65SGarrett D'Amore  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
2388447a05SGarrett D'Amore  * Use is subject to license terms.
2488447a05SGarrett D'Amore  */
2588447a05SGarrett D'Amore /*
2688447a05SGarrett D'Amore  * Purpose: Creative/Ensoniq AudioPCI  driver (ES1370)
2788447a05SGarrett D'Amore  *
2888447a05SGarrett D'Amore  * This driver is used with the original Ensoniq AudioPCI.
2988447a05SGarrett D'Amore  */
3088447a05SGarrett D'Amore 
3188447a05SGarrett D'Amore /*
3288447a05SGarrett D'Amore  * This file is part of Open Sound System
3388447a05SGarrett D'Amore  *
3488447a05SGarrett D'Amore  * Copyright (C) 4Front Technologies 1996-2008.
3588447a05SGarrett D'Amore  *
3688447a05SGarrett D'Amore  * This software is released under CDDL 1.0 source license.
3788447a05SGarrett D'Amore  * See the COPYING file included in the main directory of this source
3888447a05SGarrett D'Amore  * distribution for the license terms and conditions.
3988447a05SGarrett D'Amore  */
4088447a05SGarrett D'Amore 
4188447a05SGarrett D'Amore #include <sys/audio/audio_driver.h>
4288447a05SGarrett D'Amore #include <sys/note.h>
4388447a05SGarrett D'Amore #include <sys/pci.h>
4488447a05SGarrett D'Amore #include "audiopci.h"
4588447a05SGarrett D'Amore 
4688447a05SGarrett D'Amore /*
4788447a05SGarrett D'Amore  * The original OSS driver used a single duplex engine and a separate
4888447a05SGarrett D'Amore  * playback only engine.  Instead, we expose three engines, one for input
4988447a05SGarrett D'Amore  * and two for output.
5088447a05SGarrett D'Amore  */
5188447a05SGarrett D'Amore 
5288447a05SGarrett D'Amore #define	ENSONIQ_VENDOR_ID	0x1274
5388447a05SGarrett D'Amore #define	CREATIVE_VENDOR_ID	0x1102
5488447a05SGarrett D'Amore #define	ENSONIQ_ES1370		0x5000
5588447a05SGarrett D'Amore 
5688447a05SGarrett D'Amore #define	DRVNAME			"audiopci"
5788447a05SGarrett D'Amore 
5888447a05SGarrett D'Amore #define	INPUT_MIC	0
5988447a05SGarrett D'Amore #define	INPUT_LINEIN	1
6088447a05SGarrett D'Amore #define	INPUT_CD	2
6188447a05SGarrett D'Amore #define	INPUT_VIDEO	3
6288447a05SGarrett D'Amore #define	INPUT_PHONE	4
6388447a05SGarrett D'Amore #define	INSRCS		0x1f		/* bits 0-4 */
6488447a05SGarrett D'Amore 
6588447a05SGarrett D'Amore static const char *audiopci_insrcs[] = {
6688447a05SGarrett D'Amore 	AUDIO_PORT_MIC,
6788447a05SGarrett D'Amore 	AUDIO_PORT_LINEIN,
6888447a05SGarrett D'Amore 	AUDIO_PORT_CD,
6988447a05SGarrett D'Amore 	AUDIO_PORT_VIDEO,
7088447a05SGarrett D'Amore 	AUDIO_PORT_PHONE,
7188447a05SGarrett D'Amore 	NULL
7288447a05SGarrett D'Amore };
7388447a05SGarrett D'Amore 
7488447a05SGarrett D'Amore typedef struct audiopci_port
7588447a05SGarrett D'Amore {
7688447a05SGarrett D'Amore 	/* Audio parameters */
7788447a05SGarrett D'Amore 	int			speed;
7888447a05SGarrett D'Amore 	int			fmt;
7988447a05SGarrett D'Amore 
8088447a05SGarrett D'Amore 	int			num;
8188447a05SGarrett D'Amore #define	PORT_DAC		0
8288447a05SGarrett D'Amore #define	PORT_SYN		1
8388447a05SGarrett D'Amore #define	PORT_ADC		2
8488447a05SGarrett D'Amore #define	PORT_MAX		PORT_ADC
8588447a05SGarrett D'Amore 
8688447a05SGarrett D'Amore 	caddr_t			kaddr;
8788447a05SGarrett D'Amore 	uint32_t		paddr;
8888447a05SGarrett D'Amore 	ddi_acc_handle_t	acch;
8988447a05SGarrett D'Amore 	ddi_dma_handle_t	dmah;
9088447a05SGarrett D'Amore 	unsigned		nframes;
9188447a05SGarrett D'Amore 	unsigned		frameno;
9288447a05SGarrett D'Amore 	uint64_t		count;
9388447a05SGarrett D'Amore 
9488447a05SGarrett D'Amore 	struct audiopci_dev	*dev;
95*68c47f65SGarrett D'Amore 	audio_engine_t		*engine;
9688447a05SGarrett D'Amore } audiopci_port_t;
9788447a05SGarrett D'Amore 
9888447a05SGarrett D'Amore typedef enum {
9988447a05SGarrett D'Amore 	CTL_VOLUME = 0,
10088447a05SGarrett D'Amore 	CTL_FRONT,
10188447a05SGarrett D'Amore 	CTL_MONO,
10288447a05SGarrett D'Amore 	CTL_MIC,
10388447a05SGarrett D'Amore 	CTL_LINE,
10488447a05SGarrett D'Amore 	CTL_CD,
10588447a05SGarrett D'Amore 	CTL_VID,
10688447a05SGarrett D'Amore 	CTL_PHONE,
10788447a05SGarrett D'Amore 	CTL_MICBOOST,
10888447a05SGarrett D'Amore 	CTL_RECSRC,
10988447a05SGarrett D'Amore 	CTL_MONSRC,
11088447a05SGarrett D'Amore 	CTL_NUM		/* must be last */
11188447a05SGarrett D'Amore } audiopci_ctrl_num_t;
11288447a05SGarrett D'Amore 
11388447a05SGarrett D'Amore typedef struct audiopci_ctrl
11488447a05SGarrett D'Amore {
11588447a05SGarrett D'Amore 	struct audiopci_dev	*dev;
11688447a05SGarrett D'Amore 	audio_ctrl_t		*ctrl;
11788447a05SGarrett D'Amore 	audiopci_ctrl_num_t	num;
11888447a05SGarrett D'Amore 	uint64_t		val;
11988447a05SGarrett D'Amore } audiopci_ctrl_t;
12088447a05SGarrett D'Amore 
12188447a05SGarrett D'Amore 
12288447a05SGarrett D'Amore typedef struct audiopci_dev
12388447a05SGarrett D'Amore {
12488447a05SGarrett D'Amore 	audio_dev_t		*adev;
12588447a05SGarrett D'Amore 	kmutex_t		mutex;
12688447a05SGarrett D'Amore 	uint16_t		devid;
12788447a05SGarrett D'Amore 	dev_info_t		*dip;
12888447a05SGarrett D'Amore 
12988447a05SGarrett D'Amore 	uint8_t			ak_regs[0x20];
13088447a05SGarrett D'Amore 	int			micbias;
13188447a05SGarrett D'Amore 
13288447a05SGarrett D'Amore 	/*
13388447a05SGarrett D'Amore 	 * Controls
13488447a05SGarrett D'Amore 	 */
13588447a05SGarrett D'Amore 	audiopci_ctrl_t		controls[CTL_NUM];
13688447a05SGarrett D'Amore #if 0
13788447a05SGarrett D'Amore 	audiopci_ctrl_t		*micbias;
13888447a05SGarrett D'Amore #endif
13988447a05SGarrett D'Amore 
14088447a05SGarrett D'Amore 	audiopci_port_t		port[PORT_MAX + 1];
14188447a05SGarrett D'Amore 
14288447a05SGarrett D'Amore 
14388447a05SGarrett D'Amore 	caddr_t			regs;
14488447a05SGarrett D'Amore 	ddi_acc_handle_t	acch;
14588447a05SGarrett D'Amore } audiopci_dev_t;
14688447a05SGarrett D'Amore 
14788447a05SGarrett D'Amore static ddi_device_acc_attr_t acc_attr = {
14888447a05SGarrett D'Amore 	DDI_DEVICE_ATTR_V0,
14988447a05SGarrett D'Amore 	DDI_STRUCTURE_LE_ACC,
15088447a05SGarrett D'Amore 	DDI_STRICTORDER_ACC
15188447a05SGarrett D'Amore };
15288447a05SGarrett D'Amore 
15388447a05SGarrett D'Amore static ddi_device_acc_attr_t buf_attr = {
15488447a05SGarrett D'Amore 	DDI_DEVICE_ATTR_V0,
15588447a05SGarrett D'Amore 	DDI_NEVERSWAP_ACC,
15688447a05SGarrett D'Amore 	DDI_STRICTORDER_ACC
15788447a05SGarrett D'Amore };
15888447a05SGarrett D'Amore 
15988447a05SGarrett D'Amore /*
16088447a05SGarrett D'Amore  * The hardware appears to be able to address up to 16-bits worth of longwords,
161*68c47f65SGarrett D'Amore  * giving a total address space of 256K.  But we need substantially less.
16288447a05SGarrett D'Amore  */
163*68c47f65SGarrett D'Amore #define	AUDIOPCI_BUF_LEN	(16384)
16488447a05SGarrett D'Amore 
16588447a05SGarrett D'Amore static ddi_dma_attr_t dma_attr = {
16688447a05SGarrett D'Amore 	DMA_ATTR_VERSION,	/* dma_attr_version */
16788447a05SGarrett D'Amore 	0x0,			/* dma_attr_addr_lo */
16888447a05SGarrett D'Amore 	0xffffffffU,		/* dma_attr_addr_hi */
16988447a05SGarrett D'Amore 	0x3ffff,		/* dma_attr_count_max */
17088447a05SGarrett D'Amore 	0x8,			/* dma_attr_align */
17188447a05SGarrett D'Amore 	0x7f,			/* dma_attr_burstsizes */
17288447a05SGarrett D'Amore 	0x1,			/* dma_attr_minxfer */
17388447a05SGarrett D'Amore 	0x3ffff,		/* dma_attr_maxxfer */
17488447a05SGarrett D'Amore 	0x3ffff,		/* dma_attr_seg */
17588447a05SGarrett D'Amore 	0x1,			/* dma_attr_sgllen */
17688447a05SGarrett D'Amore 	0x1,			/* dma_attr_granular */
17788447a05SGarrett D'Amore 	0			/* dma_attr_flags */
17888447a05SGarrett D'Amore };
17988447a05SGarrett D'Amore 
18088447a05SGarrett D'Amore #define	GET8(dev, offset)	\
18188447a05SGarrett D'Amore 	ddi_get8(dev->acch, (uint8_t *)(dev->regs + (offset)))
18288447a05SGarrett D'Amore #define	GET16(dev, offset)	\
18388447a05SGarrett D'Amore 	ddi_get16(dev->acch, (uint16_t *)(void *)(dev->regs + (offset)))
18488447a05SGarrett D'Amore #define	GET32(dev, offset)	\
18588447a05SGarrett D'Amore 	ddi_get32(dev->acch, (uint32_t *)(void *)(dev->regs + (offset)))
18688447a05SGarrett D'Amore #define	PUT8(dev, offset, v)	\
18788447a05SGarrett D'Amore 	ddi_put8(dev->acch, (uint8_t *)(dev->regs + (offset)), v)
18888447a05SGarrett D'Amore #define	PUT16(dev, offset, v)	\
18988447a05SGarrett D'Amore 	ddi_put16(dev->acch, (uint16_t *)(void *)(dev->regs + (offset)), v)
19088447a05SGarrett D'Amore #define	PUT32(dev, offset, v)	\
19188447a05SGarrett D'Amore 	ddi_put32(dev->acch, (uint32_t *)(void *)(dev->regs + (offset)), v)
19288447a05SGarrett D'Amore 
19388447a05SGarrett D'Amore #define	CLR8(dev, offset, v)	PUT8(dev, offset, GET8(dev, offset) & ~(v))
19488447a05SGarrett D'Amore #define	SET8(dev, offset, v)	PUT8(dev, offset, GET8(dev, offset) | (v))
19588447a05SGarrett D'Amore #define	CLR16(dev, offset, v)	PUT16(dev, offset, GET16(dev, offset) & ~(v))
19688447a05SGarrett D'Amore #define	SET16(dev, offset, v)	PUT16(dev, offset, GET16(dev, offset) | (v))
19788447a05SGarrett D'Amore 
19888447a05SGarrett D'Amore static void audiopci_init_hw(audiopci_dev_t *);
19988447a05SGarrett D'Amore static void audiopci_init_port(audiopci_port_t *);
20088447a05SGarrett D'Amore static uint16_t audiopci_dac_rate(int);
20188447a05SGarrett D'Amore static int audiopci_add_controls(audiopci_dev_t *);
20288447a05SGarrett D'Amore static void audiopci_del_controls(audiopci_dev_t *);
20388447a05SGarrett D'Amore static void audiopci_ak_write(audiopci_dev_t *, uint16_t, uint8_t);
20488447a05SGarrett D'Amore 
20588447a05SGarrett D'Amore static int
audiopci_ak_wait(audiopci_dev_t * dev,uint8_t wstat)20688447a05SGarrett D'Amore audiopci_ak_wait(audiopci_dev_t *dev, uint8_t wstat)
20788447a05SGarrett D'Amore {
20888447a05SGarrett D'Amore 	for (int i = 4000; i; i--) {
20988447a05SGarrett D'Amore 		if (!(GET8(dev, CONC_bCODECSTAT_OFF) & wstat))
21088447a05SGarrett D'Amore 			return (DDI_SUCCESS);
21188447a05SGarrett D'Amore 		drv_usecwait(10);
21288447a05SGarrett D'Amore 	}
21388447a05SGarrett D'Amore 	return (DDI_FAILURE);
21488447a05SGarrett D'Amore }
21588447a05SGarrett D'Amore 
21688447a05SGarrett D'Amore static void
audiopci_ak_idle(audiopci_dev_t * dev)21788447a05SGarrett D'Amore audiopci_ak_idle(audiopci_dev_t *dev)
21888447a05SGarrett D'Amore {
21988447a05SGarrett D'Amore 	for (int i = 0; i < 5; i++) {
22088447a05SGarrett D'Amore 		if (audiopci_ak_wait(dev, CONC_CSTAT_CSTAT) == DDI_SUCCESS)
22188447a05SGarrett D'Amore 			return;
22288447a05SGarrett D'Amore 	}
22388447a05SGarrett D'Amore 	audio_dev_warn(dev->adev, "timed out waiting for codec to idle");
22488447a05SGarrett D'Amore }
22588447a05SGarrett D'Amore 
22688447a05SGarrett D'Amore static void
audiopci_ak_write(audiopci_dev_t * dev,uint16_t addr,uint8_t data)22788447a05SGarrett D'Amore audiopci_ak_write(audiopci_dev_t *dev, uint16_t addr, uint8_t data)
22888447a05SGarrett D'Amore {
22988447a05SGarrett D'Amore 	uint8_t	wstat;
23088447a05SGarrett D'Amore 
23188447a05SGarrett D'Amore 	/* shadow the value */
23288447a05SGarrett D'Amore 	dev->ak_regs[addr] = data;
23388447a05SGarrett D'Amore 
23488447a05SGarrett D'Amore 	wstat = addr == CODEC_RESET_PWRD ? CONC_CSTAT_CWRIP : CONC_CSTAT_CSTAT;
23588447a05SGarrett D'Amore 
23688447a05SGarrett D'Amore 	/* wait for codec to be available */
23788447a05SGarrett D'Amore 	if (audiopci_ak_wait(dev, wstat) != DDI_SUCCESS) {
23888447a05SGarrett D'Amore 		audio_dev_warn(dev->adev, "timeout waiting for codec");
23988447a05SGarrett D'Amore 	}
24088447a05SGarrett D'Amore 
24188447a05SGarrett D'Amore 	PUT16(dev, CONC_wCODECCTL_OFF, (addr << 8) | data);
24288447a05SGarrett D'Amore }
24388447a05SGarrett D'Amore 
24488447a05SGarrett D'Amore static void
audiopci_writemem(audiopci_dev_t * dev,uint32_t page,uint32_t offs,uint32_t data)24588447a05SGarrett D'Amore audiopci_writemem(audiopci_dev_t *dev, uint32_t page, uint32_t offs,
24688447a05SGarrett D'Amore     uint32_t data)
24788447a05SGarrett D'Amore {
24888447a05SGarrett D'Amore 	/* Select memory page */
24988447a05SGarrett D'Amore 	PUT32(dev, CONC_bMEMPAGE_OFF, page);
25088447a05SGarrett D'Amore 	PUT32(dev, offs, data);
25188447a05SGarrett D'Amore }
25288447a05SGarrett D'Amore 
25388447a05SGarrett D'Amore static uint32_t
audiopci_readmem(audiopci_dev_t * dev,uint32_t page,uint32_t offs)25488447a05SGarrett D'Amore audiopci_readmem(audiopci_dev_t *dev, uint32_t page, uint32_t offs)
25588447a05SGarrett D'Amore {
25688447a05SGarrett D'Amore 	PUT32(dev, CONC_bMEMPAGE_OFF, page);	/* Select memory page */
25788447a05SGarrett D'Amore 	return (GET32(dev, offs));
25888447a05SGarrett D'Amore }
25988447a05SGarrett D'Amore 
26088447a05SGarrett D'Amore /*
26188447a05SGarrett D'Amore  * Audio routines
26288447a05SGarrett D'Amore  */
26388447a05SGarrett D'Amore 
26488447a05SGarrett D'Amore static int
audiopci_format(void * arg)26588447a05SGarrett D'Amore audiopci_format(void *arg)
26688447a05SGarrett D'Amore {
26788447a05SGarrett D'Amore 	_NOTE(ARGUNUSED(arg));
26888447a05SGarrett D'Amore 	return (AUDIO_FORMAT_S16_LE);
26988447a05SGarrett D'Amore }
27088447a05SGarrett D'Amore 
27188447a05SGarrett D'Amore static int
audiopci_channels(void * arg)27288447a05SGarrett D'Amore audiopci_channels(void *arg)
27388447a05SGarrett D'Amore {
27488447a05SGarrett D'Amore 	_NOTE(ARGUNUSED(arg));
27588447a05SGarrett D'Amore 	return (2);
27688447a05SGarrett D'Amore }
27788447a05SGarrett D'Amore 
27888447a05SGarrett D'Amore static int
audiopci_rate(void * arg)27988447a05SGarrett D'Amore audiopci_rate(void *arg)
28088447a05SGarrett D'Amore {
28188447a05SGarrett D'Amore 	audiopci_port_t	*port = arg;
28288447a05SGarrett D'Amore 
28388447a05SGarrett D'Amore 	return (port->speed);
28488447a05SGarrett D'Amore }
28588447a05SGarrett D'Amore 
28688447a05SGarrett D'Amore static void
audiopci_init_port(audiopci_port_t * port)28788447a05SGarrett D'Amore audiopci_init_port(audiopci_port_t *port)
28888447a05SGarrett D'Amore {
28988447a05SGarrett D'Amore 	audiopci_dev_t	*dev = port->dev;
29088447a05SGarrett D'Amore 	unsigned tmp;
29188447a05SGarrett D'Amore 
29288447a05SGarrett D'Amore 	switch (port->num) {
29388447a05SGarrett D'Amore 	case PORT_DAC:
29488447a05SGarrett D'Amore 
29588447a05SGarrett D'Amore 		/* Set physical address of the DMA buffer */
29688447a05SGarrett D'Amore 		audiopci_writemem(dev, CONC_DACCTL_PAGE, CONC_dDACPADDR_OFF,
29788447a05SGarrett D'Amore 		    port->paddr);
29888447a05SGarrett D'Amore 
29988447a05SGarrett D'Amore 		/* Set DAC rate */
30088447a05SGarrett D'Amore 		PUT16(dev, CONC_wDACRATE_OFF, audiopci_dac_rate(48000));
30188447a05SGarrett D'Amore 
30288447a05SGarrett D'Amore 		/* Set format */
30388447a05SGarrett D'Amore 		tmp = GET8(dev, CONC_bSERFMT_OFF);
30488447a05SGarrett D'Amore 		tmp |= CONC_PCM_DAC_16BIT;
30588447a05SGarrett D'Amore 		tmp |= CONC_PCM_DAC_STEREO;
30688447a05SGarrett D'Amore 
30788447a05SGarrett D'Amore 		PUT8(dev, CONC_bSKIPC_OFF, 0x10);
30888447a05SGarrett D'Amore 		PUT8(dev, CONC_bSERFMT_OFF, tmp);
30988447a05SGarrett D'Amore 
31088447a05SGarrett D'Amore 		/* Set the frame count */
31188447a05SGarrett D'Amore 		audiopci_writemem(dev, CONC_DACCTL_PAGE, CONC_wDACFC_OFF,
31288447a05SGarrett D'Amore 		    port->nframes - 1);
31388447a05SGarrett D'Amore 
31488447a05SGarrett D'Amore 		/* Set # of frames between interrupts */
315*68c47f65SGarrett D'Amore 		PUT16(dev, CONC_wDACIC_OFF, port->nframes - 1);
31688447a05SGarrett D'Amore 
31788447a05SGarrett D'Amore 		break;
31888447a05SGarrett D'Amore 
31988447a05SGarrett D'Amore 	case PORT_SYN:
32088447a05SGarrett D'Amore 
32188447a05SGarrett D'Amore 		/* Set physical address of the DMA buffer */
32288447a05SGarrett D'Amore 		audiopci_writemem(dev, CONC_SYNCTL_PAGE, CONC_dSYNPADDR_OFF,
32388447a05SGarrett D'Amore 		    port->paddr);
32488447a05SGarrett D'Amore 
32588447a05SGarrett D'Amore 		/* Set rate - we force to 44.1 kHz */
32688447a05SGarrett D'Amore 		SET8(dev, CONC_bMISCCTL_OFF, CONC_MISCCTL_SYN_44KHZ);
32788447a05SGarrett D'Amore 
32888447a05SGarrett D'Amore 		/* Set format */
32988447a05SGarrett D'Amore 		tmp = GET8(dev, CONC_bSERFMT_OFF);
33088447a05SGarrett D'Amore 		tmp |= CONC_PCM_SYN_16BIT;
33188447a05SGarrett D'Amore 		tmp |= CONC_PCM_SYN_STEREO;
33288447a05SGarrett D'Amore 
33388447a05SGarrett D'Amore 		PUT8(dev, CONC_bSERFMT_OFF, tmp);
33488447a05SGarrett D'Amore 
33588447a05SGarrett D'Amore 		/* Set the frame count */
33688447a05SGarrett D'Amore 		audiopci_writemem(dev, CONC_SYNCTL_PAGE, CONC_wSYNFC_OFF,
33788447a05SGarrett D'Amore 		    port->nframes - 1);
33888447a05SGarrett D'Amore 
33988447a05SGarrett D'Amore 		/* Set # of frames between interrupts */
340*68c47f65SGarrett D'Amore 		PUT16(dev, CONC_wSYNIC_OFF, port->nframes - 1);
34188447a05SGarrett D'Amore 
34288447a05SGarrett D'Amore 		break;
34388447a05SGarrett D'Amore 
34488447a05SGarrett D'Amore 	case PORT_ADC:
34588447a05SGarrett D'Amore 		/* Set physical address of the DMA buffer */
34688447a05SGarrett D'Amore 		audiopci_writemem(dev, CONC_ADCCTL_PAGE, CONC_dADCPADDR_OFF,
34788447a05SGarrett D'Amore 		    port->paddr);
34888447a05SGarrett D'Amore 
34988447a05SGarrett D'Amore 		/* Set ADC rate */
35088447a05SGarrett D'Amore 		PUT16(dev, CONC_wDACRATE_OFF, audiopci_dac_rate(48000));
35188447a05SGarrett D'Amore 
35288447a05SGarrett D'Amore 		/* Set format - for input we only support 16 bit input */
35388447a05SGarrett D'Amore 		tmp = GET8(dev, CONC_bSERFMT_OFF);
35488447a05SGarrett D'Amore 		tmp |= CONC_PCM_ADC_16BIT;
35588447a05SGarrett D'Amore 		tmp |= CONC_PCM_ADC_STEREO;
35688447a05SGarrett D'Amore 
35788447a05SGarrett D'Amore 		PUT8(dev, CONC_bSKIPC_OFF, 0x10);
35888447a05SGarrett D'Amore 
35988447a05SGarrett D'Amore 		PUT8(dev, CONC_bSERFMT_OFF, tmp);
36088447a05SGarrett D'Amore 
36188447a05SGarrett D'Amore 		/* Set the frame count */
36288447a05SGarrett D'Amore 		audiopci_writemem(dev, CONC_ADCCTL_PAGE, CONC_wADCFC_OFF,
36388447a05SGarrett D'Amore 		    port->nframes - 1);
36488447a05SGarrett D'Amore 
36588447a05SGarrett D'Amore 		/* Set # of frames between interrupts */
366*68c47f65SGarrett D'Amore 		PUT16(dev, CONC_wADCIC_OFF, port->nframes - 1);
36788447a05SGarrett D'Amore 
36888447a05SGarrett D'Amore 		break;
36988447a05SGarrett D'Amore 	}
37088447a05SGarrett D'Amore 
37188447a05SGarrett D'Amore 	port->frameno = 0;
37288447a05SGarrett D'Amore }
37388447a05SGarrett D'Amore 
37488447a05SGarrett D'Amore static int
audiopci_open(void * arg,int flag,unsigned * nframes,caddr_t * bufp)375*68c47f65SGarrett D'Amore audiopci_open(void *arg, int flag, unsigned *nframes, caddr_t *bufp)
37688447a05SGarrett D'Amore {
37788447a05SGarrett D'Amore 	audiopci_port_t	*port = arg;
37888447a05SGarrett D'Amore 
37988447a05SGarrett D'Amore 	_NOTE(ARGUNUSED(flag));
38088447a05SGarrett D'Amore 
38188447a05SGarrett D'Amore 	/* NB: frame size = 4 (16-bit stereo) */
382*68c47f65SGarrett D'Amore 	port->nframes = AUDIOPCI_BUF_LEN / 4;
38388447a05SGarrett D'Amore 	port->count = 0;
38488447a05SGarrett D'Amore 
385*68c47f65SGarrett D'Amore 	*nframes = port->nframes;
38688447a05SGarrett D'Amore 	*bufp = port->kaddr;
38788447a05SGarrett D'Amore 
38888447a05SGarrett D'Amore 	return (0);
38988447a05SGarrett D'Amore }
39088447a05SGarrett D'Amore 
39188447a05SGarrett D'Amore static int
audiopci_start(void * arg)39288447a05SGarrett D'Amore audiopci_start(void *arg)
39388447a05SGarrett D'Amore {
39488447a05SGarrett D'Amore 	audiopci_port_t *port = arg;
39588447a05SGarrett D'Amore 	audiopci_dev_t *dev = port->dev;
39688447a05SGarrett D'Amore 
39788447a05SGarrett D'Amore 	mutex_enter(&dev->mutex);
398*68c47f65SGarrett D'Amore 
399*68c47f65SGarrett D'Amore 	audiopci_init_port(port);
400*68c47f65SGarrett D'Amore 
401*68c47f65SGarrett D'Amore 	switch (port->num) {
402*68c47f65SGarrett D'Amore 	case PORT_DAC:
403*68c47f65SGarrett D'Amore 		SET8(dev, CONC_bDEVCTL_OFF, CONC_DEVCTL_DAC_EN);
404*68c47f65SGarrett D'Amore 		break;
405*68c47f65SGarrett D'Amore 	case PORT_SYN:
406*68c47f65SGarrett D'Amore 		SET8(dev, CONC_bDEVCTL_OFF, CONC_DEVCTL_SYN_EN);
407*68c47f65SGarrett D'Amore 		break;
408*68c47f65SGarrett D'Amore 	case PORT_ADC:
409*68c47f65SGarrett D'Amore 		SET8(dev, CONC_bDEVCTL_OFF, CONC_DEVCTL_ADC_EN);
410*68c47f65SGarrett D'Amore 		break;
41188447a05SGarrett D'Amore 	}
41288447a05SGarrett D'Amore 	mutex_exit(&dev->mutex);
41388447a05SGarrett D'Amore 
41488447a05SGarrett D'Amore 	return (0);
41588447a05SGarrett D'Amore }
41688447a05SGarrett D'Amore 
41788447a05SGarrett D'Amore static void
audiopci_stop(void * arg)41888447a05SGarrett D'Amore audiopci_stop(void *arg)
41988447a05SGarrett D'Amore {
42088447a05SGarrett D'Amore 	audiopci_port_t *port = arg;
42188447a05SGarrett D'Amore 	audiopci_dev_t *dev = port->dev;
42288447a05SGarrett D'Amore 
42388447a05SGarrett D'Amore 	mutex_enter(&dev->mutex);
424*68c47f65SGarrett D'Amore 	switch (port->num) {
425*68c47f65SGarrett D'Amore 	case PORT_DAC:
426*68c47f65SGarrett D'Amore 		CLR8(dev, CONC_bDEVCTL_OFF, CONC_DEVCTL_DAC_EN);
427*68c47f65SGarrett D'Amore 		break;
428*68c47f65SGarrett D'Amore 	case PORT_SYN:
429*68c47f65SGarrett D'Amore 		CLR8(dev, CONC_bDEVCTL_OFF, CONC_DEVCTL_SYN_EN);
430*68c47f65SGarrett D'Amore 		break;
431*68c47f65SGarrett D'Amore 	case PORT_ADC:
432*68c47f65SGarrett D'Amore 		CLR8(dev, CONC_bDEVCTL_OFF, CONC_DEVCTL_ADC_EN);
433*68c47f65SGarrett D'Amore 		break;
43488447a05SGarrett D'Amore 	}
43588447a05SGarrett D'Amore 	mutex_exit(&dev->mutex);
43688447a05SGarrett D'Amore }
43788447a05SGarrett D'Amore 
438*68c47f65SGarrett D'Amore static uint64_t
audiopci_count(void * arg)439*68c47f65SGarrett D'Amore audiopci_count(void *arg)
44088447a05SGarrett D'Amore {
441*68c47f65SGarrett D'Amore 	audiopci_port_t *port = arg;
442*68c47f65SGarrett D'Amore 	audiopci_dev_t *dev = port->dev;
443*68c47f65SGarrett D'Amore 	uint64_t val;
44488447a05SGarrett D'Amore 	uint32_t page, offs;
44588447a05SGarrett D'Amore 	int frameno, n;
44688447a05SGarrett D'Amore 
44788447a05SGarrett D'Amore 	switch (port->num) {
44888447a05SGarrett D'Amore 	case PORT_DAC:
44988447a05SGarrett D'Amore 		page = CONC_DACCTL_PAGE;
45088447a05SGarrett D'Amore 		offs = CONC_wDACFC_OFF;
45188447a05SGarrett D'Amore 		break;
45288447a05SGarrett D'Amore 
45388447a05SGarrett D'Amore 	case PORT_SYN:
45488447a05SGarrett D'Amore 		page = CONC_SYNCTL_PAGE;
45588447a05SGarrett D'Amore 		offs = CONC_wSYNFC_OFF;
45688447a05SGarrett D'Amore 		break;
45788447a05SGarrett D'Amore 
45888447a05SGarrett D'Amore 	case PORT_ADC:
45988447a05SGarrett D'Amore 		page = CONC_ADCCTL_PAGE;
46088447a05SGarrett D'Amore 		offs = CONC_wADCFC_OFF;
46188447a05SGarrett D'Amore 		break;
46288447a05SGarrett D'Amore 	}
46388447a05SGarrett D'Amore 
46488447a05SGarrett D'Amore 	/*
46588447a05SGarrett D'Amore 	 * Note that the current frame counter is in the high nybble.
46688447a05SGarrett D'Amore 	 */
467*68c47f65SGarrett D'Amore 	mutex_enter(&dev->mutex);
46888447a05SGarrett D'Amore 	frameno = audiopci_readmem(port->dev, page, offs) >> 16;
469*68c47f65SGarrett D'Amore 	mutex_exit(&dev->mutex);
470*68c47f65SGarrett D'Amore 
47188447a05SGarrett D'Amore 	n = frameno >= port->frameno ?
47288447a05SGarrett D'Amore 	    frameno - port->frameno :
47388447a05SGarrett D'Amore 	    frameno + port->nframes - port->frameno;
47488447a05SGarrett D'Amore 	port->frameno = frameno;
47588447a05SGarrett D'Amore 	port->count += n;
47688447a05SGarrett D'Amore 
47788447a05SGarrett D'Amore 	val = port->count;
47888447a05SGarrett D'Amore 	return (val);
47988447a05SGarrett D'Amore }
48088447a05SGarrett D'Amore 
48188447a05SGarrett D'Amore static void
audiopci_close(void * arg)48288447a05SGarrett D'Amore audiopci_close(void *arg)
48388447a05SGarrett D'Amore {
484*68c47f65SGarrett D'Amore 	_NOTE(ARGUNUSED(arg));
48588447a05SGarrett D'Amore }
48688447a05SGarrett D'Amore 
48788447a05SGarrett D'Amore static void
audiopci_sync(void * arg,unsigned nframes)48888447a05SGarrett D'Amore audiopci_sync(void *arg, unsigned nframes)
48988447a05SGarrett D'Amore {
49088447a05SGarrett D'Amore 	audiopci_port_t *port = arg;
49188447a05SGarrett D'Amore 
49288447a05SGarrett D'Amore 	_NOTE(ARGUNUSED(nframes));
49388447a05SGarrett D'Amore 
49488447a05SGarrett D'Amore 	if (port->num == PORT_ADC) {
49588447a05SGarrett D'Amore 		(void) ddi_dma_sync(port->dmah, 0, 0, DDI_DMA_SYNC_FORCPU);
49688447a05SGarrett D'Amore 	} else {
49788447a05SGarrett D'Amore 		(void) ddi_dma_sync(port->dmah, 0, 0, DDI_DMA_SYNC_FORDEV);
49888447a05SGarrett D'Amore 	}
49988447a05SGarrett D'Amore }
50088447a05SGarrett D'Amore 
50188447a05SGarrett D'Amore audio_engine_ops_t audiopci_engine_ops = {
50288447a05SGarrett D'Amore 	AUDIO_ENGINE_VERSION,		/* version number */
50388447a05SGarrett D'Amore 	audiopci_open,
50488447a05SGarrett D'Amore 	audiopci_close,
50588447a05SGarrett D'Amore 	audiopci_start,
50688447a05SGarrett D'Amore 	audiopci_stop,
50788447a05SGarrett D'Amore 	audiopci_count,
50888447a05SGarrett D'Amore 	audiopci_format,
50988447a05SGarrett D'Amore 	audiopci_channels,
51088447a05SGarrett D'Amore 	audiopci_rate,
51188447a05SGarrett D'Amore 	audiopci_sync,
512f9ead4a5SGarrett D'Amore 	NULL,
513f9ead4a5SGarrett D'Amore 	NULL,
51488447a05SGarrett D'Amore 	NULL,
51588447a05SGarrett D'Amore };
51688447a05SGarrett D'Amore 
51788447a05SGarrett D'Amore static uint16_t
audiopci_dac_rate(int samPerSec)51888447a05SGarrett D'Amore audiopci_dac_rate(int samPerSec)
51988447a05SGarrett D'Amore {
52088447a05SGarrett D'Amore 	unsigned short usTemp;
52188447a05SGarrett D'Amore 
52288447a05SGarrett D'Amore 	/* samPerSec /= 2; */
52388447a05SGarrett D'Amore 
52488447a05SGarrett D'Amore 	usTemp = (unsigned short) ((DAC_CLOCK_DIVIDE / 8) / samPerSec);
52588447a05SGarrett D'Amore 
52688447a05SGarrett D'Amore 	if (usTemp & 0x00000001) {
52788447a05SGarrett D'Amore 		usTemp >>= 1;
52888447a05SGarrett D'Amore 		usTemp -= 1;
52988447a05SGarrett D'Amore 	} else {
53088447a05SGarrett D'Amore 		usTemp >>= 1;
53188447a05SGarrett D'Amore 		usTemp -= 2;
53288447a05SGarrett D'Amore 	}
53388447a05SGarrett D'Amore 	return (usTemp);
53488447a05SGarrett D'Amore }
53588447a05SGarrett D'Amore 
53688447a05SGarrett D'Amore void
audiopci_init_hw(audiopci_dev_t * dev)53788447a05SGarrett D'Amore audiopci_init_hw(audiopci_dev_t *dev)
53888447a05SGarrett D'Amore {
53988447a05SGarrett D'Amore 	int tmp;
54088447a05SGarrett D'Amore 
54188447a05SGarrett D'Amore 	/* setup DAC frequency */
54288447a05SGarrett D'Amore 	PUT16(dev, CONC_wDACRATE_OFF, audiopci_dac_rate(48000));
54388447a05SGarrett D'Amore 
54488447a05SGarrett D'Amore 	CLR8(dev, CONC_bMISCCTL_OFF, CONC_MISCCTL_CCB_INTRM);
54588447a05SGarrett D'Amore 	SET8(dev, CONC_bMISCCTL_OFF, CONC_MISCCTL_SYN_44KHZ);
54688447a05SGarrett D'Amore 
54788447a05SGarrett D'Amore 	/* Turn on CODEC (UART and joystick left disabled) */
54888447a05SGarrett D'Amore 	tmp = GET8(dev, CONC_bDEVCTL_OFF);
54988447a05SGarrett D'Amore 	tmp |= CONC_DEVCTL_SERR_DIS;
55088447a05SGarrett D'Amore 	tmp |= CONC_DEVCTL_CODEC_EN;
55188447a05SGarrett D'Amore 	PUT8(dev, CONC_bDEVCTL_OFF, tmp);
55288447a05SGarrett D'Amore 
55388447a05SGarrett D'Amore 	/* Reset the UART */
55488447a05SGarrett D'Amore 	PUT8(dev, CONC_bUARTCSTAT_OFF, 0x00);
55588447a05SGarrett D'Amore 
55688447a05SGarrett D'Amore 	/* Disable NMI */
55788447a05SGarrett D'Amore 	PUT8(dev, CONC_bNMIENA_OFF, 0);
55888447a05SGarrett D'Amore 	PUT16(dev, CONC_wNMISTAT_OFF, 0);
55988447a05SGarrett D'Amore 
56088447a05SGarrett D'Amore 	/* Initialize serial interface */
56188447a05SGarrett D'Amore 	PUT8(dev, CONC_bSERCTL_OFF, 0);
56288447a05SGarrett D'Amore 	PUT8(dev, CONC_bSERFMT_OFF,
56388447a05SGarrett D'Amore 	    CONC_PCM_SYN_STEREO | CONC_PCM_SYN_16BIT);
56488447a05SGarrett D'Amore 
56588447a05SGarrett D'Amore 	/* Unmute codec */
56688447a05SGarrett D'Amore 	CLR8(dev, CONC_bMISCCTL_OFF, CONC_MISCCTL_MUTE);
56788447a05SGarrett D'Amore 
56888447a05SGarrett D'Amore 	/* mixer initialization */
56988447a05SGarrett D'Amore 	audiopci_ak_idle(dev);
57088447a05SGarrett D'Amore 
57188447a05SGarrett D'Amore 	/* power/reset down the codec */
57288447a05SGarrett D'Amore 	audiopci_ak_write(dev, CODEC_RESET_PWRD, 0);
57388447a05SGarrett D'Amore 	drv_usecwait(10);
57488447a05SGarrett D'Amore 
57588447a05SGarrett D'Amore 	/* now powerup and bring out of reset */
57688447a05SGarrett D'Amore 	audiopci_ak_write(dev, CODEC_RESET_PWRD, 0x3);
57788447a05SGarrett D'Amore 	audiopci_ak_idle(dev);
57888447a05SGarrett D'Amore 
57988447a05SGarrett D'Amore 	/* enable PLL for DAC2 */
58088447a05SGarrett D'Amore 	audiopci_ak_write(dev, CODEC_CLKSELECT, 0);
58188447a05SGarrett D'Amore 
58288447a05SGarrett D'Amore 	/* select input mixer */
58388447a05SGarrett D'Amore 	audiopci_ak_write(dev, CODEC_ADSELECT, 0);
58488447a05SGarrett D'Amore 
58588447a05SGarrett D'Amore 	/* mark FM for output mixer */
58688447a05SGarrett D'Amore 	audiopci_ak_write(dev, CODEC_OUT_SW1, CODEC_OUT_ENABLE_SYNTH);
58788447a05SGarrett D'Amore 	audiopci_ak_write(dev, CODEC_OUT_SW2, CODEC_OUT_ENABLE_WAVE);
58888447a05SGarrett D'Amore 
58988447a05SGarrett D'Amore 	/* initialize some reasonable values for the WAVE and SYNTH inputs */
59088447a05SGarrett D'Amore 	audiopci_ak_write(dev, CODEC_VOL_WAVE_L, 6);
59188447a05SGarrett D'Amore 	audiopci_ak_write(dev, CODEC_VOL_WAVE_R, 6);
59288447a05SGarrett D'Amore 	audiopci_ak_write(dev, CODEC_VOL_SYNTH_L, 6);
59388447a05SGarrett D'Amore 	audiopci_ak_write(dev, CODEC_VOL_SYNTH_R, 6);
59488447a05SGarrett D'Amore 
59588447a05SGarrett D'Amore 	/* enable microphone phantom power */
59688447a05SGarrett D'Amore 	if (dev->micbias) {
59788447a05SGarrett D'Amore 		SET16(dev, 2, CONC_DEVCTL_MICBIAS);
59888447a05SGarrett D'Amore 	}
59988447a05SGarrett D'Amore }
60088447a05SGarrett D'Amore 
60188447a05SGarrett D'Amore static int
audiopci_init(audiopci_dev_t * dev)60288447a05SGarrett D'Amore audiopci_init(audiopci_dev_t *dev)
60388447a05SGarrett D'Amore {
60488447a05SGarrett D'Amore 	dev->micbias = 1;
60588447a05SGarrett D'Amore 
60688447a05SGarrett D'Amore 	audiopci_init_hw(dev);
60788447a05SGarrett D'Amore 
60888447a05SGarrett D'Amore 	for (int i = 0; i <= PORT_MAX; i++) {
60988447a05SGarrett D'Amore 		audiopci_port_t *port;
61088447a05SGarrett D'Amore 		unsigned caps;
61188447a05SGarrett D'Amore 		unsigned dmaflags;
61288447a05SGarrett D'Amore 		size_t rlen;
61388447a05SGarrett D'Amore 		ddi_dma_cookie_t c;
61488447a05SGarrett D'Amore 		unsigned ccnt;
61588447a05SGarrett D'Amore 
61688447a05SGarrett D'Amore 		port = &dev->port[i];
61788447a05SGarrett D'Amore 		port->dev = dev;
61888447a05SGarrett D'Amore 
61988447a05SGarrett D'Amore 		switch (i) {
62088447a05SGarrett D'Amore 		case PORT_SYN:
62188447a05SGarrett D'Amore 			caps = ENGINE_OUTPUT_CAP;
62288447a05SGarrett D'Amore 			dmaflags = DDI_DMA_WRITE | DDI_DMA_CONSISTENT;
62388447a05SGarrett D'Amore 			port->speed = 44100;
62488447a05SGarrett D'Amore 			break;
62588447a05SGarrett D'Amore 
62688447a05SGarrett D'Amore 		case PORT_DAC:
62788447a05SGarrett D'Amore 			caps = ENGINE_OUTPUT_CAP;
62888447a05SGarrett D'Amore 			dmaflags = DDI_DMA_WRITE | DDI_DMA_CONSISTENT;
62988447a05SGarrett D'Amore 			port->speed = 48000;
63088447a05SGarrett D'Amore 			break;
63188447a05SGarrett D'Amore 
63288447a05SGarrett D'Amore 		case PORT_ADC:
63388447a05SGarrett D'Amore 			caps = ENGINE_INPUT_CAP;
63488447a05SGarrett D'Amore 			dmaflags = DDI_DMA_READ | DDI_DMA_CONSISTENT;
63588447a05SGarrett D'Amore 			port->speed = 48000;
63688447a05SGarrett D'Amore 			break;
63788447a05SGarrett D'Amore 		}
63888447a05SGarrett D'Amore 
63988447a05SGarrett D'Amore 		port->num = i;
64088447a05SGarrett D'Amore 
64188447a05SGarrett D'Amore 		/*
64288447a05SGarrett D'Amore 		 * Allocate DMA resources.
64388447a05SGarrett D'Amore 		 */
64488447a05SGarrett D'Amore 
64588447a05SGarrett D'Amore 		if (ddi_dma_alloc_handle(dev->dip, &dma_attr, DDI_DMA_SLEEP,
64688447a05SGarrett D'Amore 		    NULL, &port->dmah) != DDI_SUCCESS) {
64788447a05SGarrett D'Amore 			audio_dev_warn(dev->adev,
64888447a05SGarrett D'Amore 			    "port %d: dma handle allocation failed", i);
64988447a05SGarrett D'Amore 			return (DDI_FAILURE);
65088447a05SGarrett D'Amore 		}
65188447a05SGarrett D'Amore 		if (ddi_dma_mem_alloc(port->dmah, AUDIOPCI_BUF_LEN, &buf_attr,
65288447a05SGarrett D'Amore 		    DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL, &port->kaddr,
65388447a05SGarrett D'Amore 		    &rlen, &port->acch) != DDI_SUCCESS) {
65488447a05SGarrett D'Amore 			audio_dev_warn(dev->adev,
65588447a05SGarrett D'Amore 			    "port %d: dma memory allocation failed", i);
65688447a05SGarrett D'Amore 			return (DDI_FAILURE);
65788447a05SGarrett D'Amore 		}
65888447a05SGarrett D'Amore 		/* ensure that the buffer is zeroed out properly */
65988447a05SGarrett D'Amore 		bzero(port->kaddr, rlen);
66088447a05SGarrett D'Amore 		if (ddi_dma_addr_bind_handle(port->dmah, NULL, port->kaddr,
66188447a05SGarrett D'Amore 		    AUDIOPCI_BUF_LEN, dmaflags, DDI_DMA_SLEEP, NULL,
66288447a05SGarrett D'Amore 		    &c, &ccnt) != DDI_DMA_MAPPED) {
66388447a05SGarrett D'Amore 			audio_dev_warn(dev->adev,
66488447a05SGarrett D'Amore 			    "port %d: dma binding failed", i);
66588447a05SGarrett D'Amore 			return (DDI_FAILURE);
66688447a05SGarrett D'Amore 		}
66788447a05SGarrett D'Amore 		port->paddr = c.dmac_address;
66888447a05SGarrett D'Amore 
66988447a05SGarrett D'Amore 		/*
67088447a05SGarrett D'Amore 		 * Allocate and configure audio engine.
67188447a05SGarrett D'Amore 		 */
67288447a05SGarrett D'Amore 		port->engine = audio_engine_alloc(&audiopci_engine_ops, caps);
67388447a05SGarrett D'Amore 		if (port->engine == NULL) {
67488447a05SGarrett D'Amore 			audio_dev_warn(dev->adev,
67588447a05SGarrett D'Amore 			    "port %d: audio_engine_alloc failed", i);
67688447a05SGarrett D'Amore 			return (DDI_FAILURE);
67788447a05SGarrett D'Amore 		}
67888447a05SGarrett D'Amore 
67988447a05SGarrett D'Amore 		audio_engine_set_private(port->engine, port);
68088447a05SGarrett D'Amore 		audio_dev_add_engine(dev->adev, port->engine);
68188447a05SGarrett D'Amore 	}
68288447a05SGarrett D'Amore 
68388447a05SGarrett D'Amore 	/*
68488447a05SGarrett D'Amore 	 * Register audio controls.
68588447a05SGarrett D'Amore 	 */
68688447a05SGarrett D'Amore 	if (audiopci_add_controls(dev) == DDI_FAILURE) {
68788447a05SGarrett D'Amore 		return (DDI_FAILURE);
68888447a05SGarrett D'Amore 	}
68988447a05SGarrett D'Amore 
69088447a05SGarrett D'Amore 
69188447a05SGarrett D'Amore 	if (audio_dev_register(dev->adev) != DDI_SUCCESS) {
69288447a05SGarrett D'Amore 		audio_dev_warn(dev->adev,
69388447a05SGarrett D'Amore 		    "unable to register with audio framework");
69488447a05SGarrett D'Amore 		return (DDI_FAILURE);
69588447a05SGarrett D'Amore 	}
69688447a05SGarrett D'Amore 
69788447a05SGarrett D'Amore 	return (DDI_SUCCESS);
69888447a05SGarrett D'Amore }
69988447a05SGarrett D'Amore 
700*68c47f65SGarrett D'Amore static void
audiopci_destroy(audiopci_dev_t * dev)70188447a05SGarrett D'Amore audiopci_destroy(audiopci_dev_t *dev)
70288447a05SGarrett D'Amore {
70388447a05SGarrett D'Amore 	int	i;
70488447a05SGarrett D'Amore 
705*68c47f65SGarrett D'Amore 	mutex_destroy(&dev->mutex);
70688447a05SGarrett D'Amore 
70788447a05SGarrett D'Amore 	/* free up ports, including DMA resources for ports */
70888447a05SGarrett D'Amore 	for (i = 0; i <= PORT_MAX; i++) {
70988447a05SGarrett D'Amore 		audiopci_port_t	*port = &dev->port[i];
71088447a05SGarrett D'Amore 
71188447a05SGarrett D'Amore 		if (port->paddr != 0)
71288447a05SGarrett D'Amore 			(void) ddi_dma_unbind_handle(port->dmah);
71388447a05SGarrett D'Amore 		if (port->acch != NULL)
71488447a05SGarrett D'Amore 			ddi_dma_mem_free(&port->acch);
71588447a05SGarrett D'Amore 		if (port->dmah != NULL)
71688447a05SGarrett D'Amore 			ddi_dma_free_handle(&port->dmah);
71788447a05SGarrett D'Amore 
71888447a05SGarrett D'Amore 		if (port->engine != NULL) {
71988447a05SGarrett D'Amore 			audio_dev_remove_engine(dev->adev, port->engine);
72088447a05SGarrett D'Amore 			audio_engine_free(port->engine);
72188447a05SGarrett D'Amore 		}
72288447a05SGarrett D'Amore 	}
72388447a05SGarrett D'Amore 
72488447a05SGarrett D'Amore 	if (dev->acch != NULL) {
72588447a05SGarrett D'Amore 		ddi_regs_map_free(&dev->acch);
72688447a05SGarrett D'Amore 	}
72788447a05SGarrett D'Amore 
72888447a05SGarrett D'Amore 	audiopci_del_controls(dev);
72988447a05SGarrett D'Amore 
73088447a05SGarrett D'Amore 	if (dev->adev != NULL) {
73188447a05SGarrett D'Amore 		audio_dev_free(dev->adev);
73288447a05SGarrett D'Amore 	}
73388447a05SGarrett D'Amore 
73488447a05SGarrett D'Amore 	kmem_free(dev, sizeof (*dev));
73588447a05SGarrett D'Amore }
73688447a05SGarrett D'Amore 
73788447a05SGarrett D'Amore static void
audiopci_stereo(audiopci_dev_t * dev,audiopci_ctrl_num_t num,uint8_t lreg)73888447a05SGarrett D'Amore audiopci_stereo(audiopci_dev_t *dev, audiopci_ctrl_num_t num, uint8_t lreg)
73988447a05SGarrett D'Amore {
74088447a05SGarrett D'Amore 	uint8_t		lval, rval;
74188447a05SGarrett D'Amore 	uint8_t		lmute, rmute;
74288447a05SGarrett D'Amore 	uint64_t	val;
74388447a05SGarrett D'Amore 	uint8_t		rreg;
74488447a05SGarrett D'Amore 
74588447a05SGarrett D'Amore 	rreg = lreg + 1;
74688447a05SGarrett D'Amore 	val = dev->controls[num].val;
74788447a05SGarrett D'Amore 	lval = (val & 0xff00) >> 8;
74888447a05SGarrett D'Amore 	rval = val & 0xff;
74988447a05SGarrett D'Amore 
75088447a05SGarrett D'Amore 	lmute = lval ? 0 : CODEC_ATT_MUTE;
75188447a05SGarrett D'Amore 	rmute = rval ? 0 : CODEC_ATT_MUTE;
75288447a05SGarrett D'Amore 
75388447a05SGarrett D'Amore 	/* convert to attenuation & apply mute if appropriate */
75488447a05SGarrett D'Amore 	lval = ((((100U - lval) * CODEC_ATT_MAX) / 100) & 0xff) | lmute;
75588447a05SGarrett D'Amore 	rval = ((((100U - rval) * CODEC_ATT_MAX) / 100) & 0xff) | rmute;
75688447a05SGarrett D'Amore 
75788447a05SGarrett D'Amore 	audiopci_ak_write(dev, lreg, lval);
75888447a05SGarrett D'Amore 	audiopci_ak_write(dev, rreg, rval);
75988447a05SGarrett D'Amore }
76088447a05SGarrett D'Amore 
76188447a05SGarrett D'Amore static void
audiopci_mono(audiopci_dev_t * dev,audiopci_ctrl_num_t num,uint8_t reg)76288447a05SGarrett D'Amore audiopci_mono(audiopci_dev_t *dev, audiopci_ctrl_num_t num, uint8_t reg)
76388447a05SGarrett D'Amore {
76488447a05SGarrett D'Amore 	uint64_t val = (dev->controls[num].val & 0xff);
76588447a05SGarrett D'Amore 	uint8_t mute;
76688447a05SGarrett D'Amore 
76788447a05SGarrett D'Amore 	mute = val ? 0 : CODEC_ATT_MUTE;
76888447a05SGarrett D'Amore 	val = ((((100U - val) * CODEC_ATT_MAX) / 100) & 0xff) | mute;
76988447a05SGarrett D'Amore 
77088447a05SGarrett D'Amore 	audiopci_ak_write(dev, reg, val);
77188447a05SGarrett D'Amore }
77288447a05SGarrett D'Amore 
77388447a05SGarrett D'Amore static void
audiopci_mono8(audiopci_dev_t * dev,audiopci_ctrl_num_t num,uint8_t reg)77488447a05SGarrett D'Amore audiopci_mono8(audiopci_dev_t *dev, audiopci_ctrl_num_t num, uint8_t reg)
77588447a05SGarrett D'Amore {
77688447a05SGarrett D'Amore 	uint64_t val = (dev->controls[num].val & 0xff);
77788447a05SGarrett D'Amore 	uint8_t mute;
77888447a05SGarrett D'Amore 
77988447a05SGarrett D'Amore 	mute = val ? 0 : CODEC_ATT_MUTE;
78088447a05SGarrett D'Amore 	val = ((((100U - val) * CODEC_ATT_MONO) / 100) & 0xff) | mute;
78188447a05SGarrett D'Amore 
78288447a05SGarrett D'Amore 	audiopci_ak_write(dev, reg, val);
78388447a05SGarrett D'Amore }
78488447a05SGarrett D'Amore 
78588447a05SGarrett D'Amore static int
audiopci_get_value(void * arg,uint64_t * val)78688447a05SGarrett D'Amore audiopci_get_value(void *arg, uint64_t *val)
78788447a05SGarrett D'Amore {
78888447a05SGarrett D'Amore 	audiopci_ctrl_t	*pc = arg;
78988447a05SGarrett D'Amore 
79088447a05SGarrett D'Amore 	*val = pc->val;
791*68c47f65SGarrett D'Amore 
79288447a05SGarrett D'Amore 	return (0);
79388447a05SGarrett D'Amore }
79488447a05SGarrett D'Amore 
79588447a05SGarrett D'Amore static void
audiopci_configure_output(audiopci_dev_t * dev)79688447a05SGarrett D'Amore audiopci_configure_output(audiopci_dev_t *dev)
79788447a05SGarrett D'Amore {
79888447a05SGarrett D'Amore 	uint64_t val;
79988447a05SGarrett D'Amore 	uint8_t	tmp;
80088447a05SGarrett D'Amore 
80188447a05SGarrett D'Amore 	/* PCM/Wave level */
80288447a05SGarrett D'Amore 	audiopci_mono(dev, CTL_VOLUME, CODEC_VOL_WAVE_L);
80388447a05SGarrett D'Amore 	audiopci_mono(dev, CTL_VOLUME, CODEC_VOL_WAVE_R);
80488447a05SGarrett D'Amore 	audiopci_mono(dev, CTL_VOLUME, CODEC_VOL_SYNTH_L);
80588447a05SGarrett D'Amore 	audiopci_mono(dev, CTL_VOLUME, CODEC_VOL_SYNTH_R);
80688447a05SGarrett D'Amore 
80788447a05SGarrett D'Amore 	/* front & mono outputs */
80888447a05SGarrett D'Amore 	audiopci_stereo(dev, CTL_FRONT, CODEC_VOL_MASTER_L);
80988447a05SGarrett D'Amore 	audiopci_mono8(dev, CTL_MONO, CODEC_VOL_MONO);
81088447a05SGarrett D'Amore 
81188447a05SGarrett D'Amore 	val = dev->controls[CTL_MONSRC].val;
81288447a05SGarrett D'Amore 
81388447a05SGarrett D'Amore 	/* setup output monitoring as well */
81488447a05SGarrett D'Amore 	tmp = CODEC_OUT_ENABLE_SYNTH;
81588447a05SGarrett D'Amore 	if (val & (1U << INPUT_MIC))
81688447a05SGarrett D'Amore 		tmp |= CODEC_OUT_ENABLE_MIC;
81788447a05SGarrett D'Amore 	if (val & (1U << INPUT_CD))
81888447a05SGarrett D'Amore 		tmp |= CODEC_OUT_ENABLE_CD;
81988447a05SGarrett D'Amore 	if (val & (1U << INPUT_LINEIN))
82088447a05SGarrett D'Amore 		tmp |= CODEC_OUT_ENABLE_AUX;
82188447a05SGarrett D'Amore 	audiopci_ak_write(dev, CODEC_OUT_SW1, tmp);
82288447a05SGarrett D'Amore 
82388447a05SGarrett D'Amore 	tmp = CODEC_OUT_ENABLE_WAVE;
82488447a05SGarrett D'Amore 	if (val & (1U << INPUT_VIDEO))
82588447a05SGarrett D'Amore 		tmp |= CODEC_OUT_ENABLE_TV;
82688447a05SGarrett D'Amore 	if (val & (1U << INPUT_PHONE))
82788447a05SGarrett D'Amore 		tmp |= CODEC_OUT_ENABLE_TAD;
82888447a05SGarrett D'Amore 	audiopci_ak_write(dev, CODEC_OUT_SW2, tmp);
82988447a05SGarrett D'Amore }
83088447a05SGarrett D'Amore 
83188447a05SGarrett D'Amore static void
audiopci_configure_input(audiopci_dev_t * dev)83288447a05SGarrett D'Amore audiopci_configure_input(audiopci_dev_t *dev)
83388447a05SGarrett D'Amore {
83488447a05SGarrett D'Amore 	uint64_t	val = dev->controls[CTL_RECSRC].val;
83588447a05SGarrett D'Amore 	uint8_t		tmp;
83688447a05SGarrett D'Amore 
83788447a05SGarrett D'Amore 	tmp = 0;
83888447a05SGarrett D'Amore 	if (val & (1U << INPUT_LINEIN))
83988447a05SGarrett D'Amore 		tmp |= CODEC_IN_ENABLE_AUX_L;
84088447a05SGarrett D'Amore 	if (val & (1U << INPUT_CD))
84188447a05SGarrett D'Amore 		tmp |= CODEC_IN_ENABLE_CD_L;
84288447a05SGarrett D'Amore 	if (val & (1U << INPUT_MIC))
84388447a05SGarrett D'Amore 		tmp |= CODEC_IN_ENABLE_MIC;
84488447a05SGarrett D'Amore 	if (val & (1U << INPUT_PHONE))
84588447a05SGarrett D'Amore 		tmp |= CODEC_IN_ENABLE_TAD;
84688447a05SGarrett D'Amore 	audiopci_ak_write(dev, CODEC_LIN_SW1, tmp);
84788447a05SGarrett D'Amore 
84888447a05SGarrett D'Amore 	tmp = 0;
84988447a05SGarrett D'Amore 	if (val & (1U << INPUT_LINEIN))
85088447a05SGarrett D'Amore 		tmp |= CODEC_IN_ENABLE_AUX_R;
85188447a05SGarrett D'Amore 	if (val & (1U << INPUT_CD))
85288447a05SGarrett D'Amore 		tmp |= CODEC_IN_ENABLE_CD_R;
85388447a05SGarrett D'Amore 	if (val & (1U << INPUT_PHONE))
85488447a05SGarrett D'Amore 		tmp |= CODEC_IN_ENABLE_TAD;
85588447a05SGarrett D'Amore 	if (val & (1U << INPUT_MIC))
85688447a05SGarrett D'Amore 		tmp |= CODEC_IN_ENABLE_MIC;
85788447a05SGarrett D'Amore 	audiopci_ak_write(dev, CODEC_RIN_SW1, tmp);
85888447a05SGarrett D'Amore 
85988447a05SGarrett D'Amore 	tmp = 0;
86088447a05SGarrett D'Amore 	if (val & (1U << INPUT_VIDEO))
86188447a05SGarrett D'Amore 		tmp |= CODEC_IN_ENABLE_TV_L;
86288447a05SGarrett D'Amore 	if (val & (1U << INPUT_MIC))
86388447a05SGarrett D'Amore 		tmp |= CODEC_IN_ENABLE_TMIC;
86488447a05SGarrett D'Amore 	audiopci_ak_write(dev, CODEC_LIN_SW2, tmp);
86588447a05SGarrett D'Amore 
86688447a05SGarrett D'Amore 	tmp = 0;
86788447a05SGarrett D'Amore 	if (val & (1U << INPUT_VIDEO))
86888447a05SGarrett D'Amore 		tmp |= CODEC_IN_ENABLE_TV_R;
86988447a05SGarrett D'Amore 	if (val & (1U << INPUT_MIC))
87088447a05SGarrett D'Amore 		tmp |= CODEC_IN_ENABLE_TMIC;
87188447a05SGarrett D'Amore 	audiopci_ak_write(dev, CODEC_RIN_SW2, tmp);
87288447a05SGarrett D'Amore 
87388447a05SGarrett D'Amore 	/* configure volumes */
87488447a05SGarrett D'Amore 	audiopci_mono(dev, CTL_MIC, CODEC_VOL_MIC);
87588447a05SGarrett D'Amore 	audiopci_mono(dev, CTL_PHONE, CODEC_VOL_TAD);
87688447a05SGarrett D'Amore 	audiopci_stereo(dev, CTL_LINE, CODEC_VOL_AUX_L);
87788447a05SGarrett D'Amore 	audiopci_stereo(dev, CTL_CD, CODEC_VOL_CD_L);
87888447a05SGarrett D'Amore 	audiopci_stereo(dev, CTL_VID, CODEC_VOL_TV_L);
87988447a05SGarrett D'Amore 
88088447a05SGarrett D'Amore 	/* activate 30dB mic boost */
88188447a05SGarrett D'Amore 	audiopci_ak_write(dev, CODEC_MICBOOST,
88288447a05SGarrett D'Amore 	    dev->controls[CTL_MICBOOST].val ? 1 : 0);
88388447a05SGarrett D'Amore }
88488447a05SGarrett D'Amore 
88588447a05SGarrett D'Amore static int
audiopci_set_reclevel(void * arg,uint64_t val)88688447a05SGarrett D'Amore audiopci_set_reclevel(void *arg, uint64_t val)
88788447a05SGarrett D'Amore {
88888447a05SGarrett D'Amore 	audiopci_ctrl_t	*pc = arg;
88988447a05SGarrett D'Amore 	audiopci_dev_t	*dev = pc->dev;
89088447a05SGarrett D'Amore 	uint8_t		l;
89188447a05SGarrett D'Amore 	uint8_t		r;
89288447a05SGarrett D'Amore 
89388447a05SGarrett D'Amore 	l = (val & 0xff00) >> 8;
89488447a05SGarrett D'Amore 	r = val & 0xff;
89588447a05SGarrett D'Amore 
89688447a05SGarrett D'Amore 	if ((l > 100) || (r > 100))
89788447a05SGarrett D'Amore 		return (EINVAL);
89888447a05SGarrett D'Amore 
89988447a05SGarrett D'Amore 	mutex_enter(&dev->mutex);
90088447a05SGarrett D'Amore 	pc->val = val;
90188447a05SGarrett D'Amore 	audiopci_configure_input(dev);
90288447a05SGarrett D'Amore 
90388447a05SGarrett D'Amore 	mutex_exit(&dev->mutex);
90488447a05SGarrett D'Amore 	return (0);
90588447a05SGarrett D'Amore }
90688447a05SGarrett D'Amore 
90788447a05SGarrett D'Amore static int
audiopci_set_micboost(void * arg,uint64_t val)90888447a05SGarrett D'Amore audiopci_set_micboost(void *arg, uint64_t val)
90988447a05SGarrett D'Amore {
91088447a05SGarrett D'Amore 	audiopci_ctrl_t	*pc = arg;
91188447a05SGarrett D'Amore 	audiopci_dev_t	*dev = pc->dev;
91288447a05SGarrett D'Amore 
91388447a05SGarrett D'Amore 	mutex_enter(&dev->mutex);
91488447a05SGarrett D'Amore 	pc->val = val;
91588447a05SGarrett D'Amore 	audiopci_configure_input(dev);
91688447a05SGarrett D'Amore 	mutex_exit(&dev->mutex);
91788447a05SGarrett D'Amore 	return (0);
91888447a05SGarrett D'Amore }
91988447a05SGarrett D'Amore 
92088447a05SGarrett D'Amore static int
audiopci_set_monsrc(void * arg,uint64_t val)92188447a05SGarrett D'Amore audiopci_set_monsrc(void *arg, uint64_t val)
92288447a05SGarrett D'Amore {
92388447a05SGarrett D'Amore 	audiopci_ctrl_t	*pc = arg;
92488447a05SGarrett D'Amore 	audiopci_dev_t	*dev = pc->dev;
92588447a05SGarrett D'Amore 
92688447a05SGarrett D'Amore 	if ((val & ~INSRCS) != 0)
92788447a05SGarrett D'Amore 		return (EINVAL);
92888447a05SGarrett D'Amore 
92988447a05SGarrett D'Amore 	mutex_enter(&dev->mutex);
93088447a05SGarrett D'Amore 	pc->val = val;
93188447a05SGarrett D'Amore 	audiopci_configure_output(dev);
93288447a05SGarrett D'Amore 	mutex_exit(&dev->mutex);
93388447a05SGarrett D'Amore 	return (0);
93488447a05SGarrett D'Amore }
93588447a05SGarrett D'Amore 
93688447a05SGarrett D'Amore static int
audiopci_set_recsrc(void * arg,uint64_t val)93788447a05SGarrett D'Amore audiopci_set_recsrc(void *arg, uint64_t val)
93888447a05SGarrett D'Amore {
93988447a05SGarrett D'Amore 	audiopci_ctrl_t	*pc = arg;
94088447a05SGarrett D'Amore 	audiopci_dev_t	*dev = pc->dev;
94188447a05SGarrett D'Amore 
94288447a05SGarrett D'Amore 	if ((val & ~INSRCS) != 0)
94388447a05SGarrett D'Amore 		return (EINVAL);
94488447a05SGarrett D'Amore 
94588447a05SGarrett D'Amore 	mutex_enter(&dev->mutex);
94688447a05SGarrett D'Amore 	pc->val = val;
94788447a05SGarrett D'Amore 	audiopci_configure_input(dev);
94888447a05SGarrett D'Amore 	mutex_exit(&dev->mutex);
94988447a05SGarrett D'Amore 	return (0);
95088447a05SGarrett D'Amore }
95188447a05SGarrett D'Amore 
95288447a05SGarrett D'Amore static int
audiopci_set_volume(void * arg,uint64_t val)95388447a05SGarrett D'Amore audiopci_set_volume(void *arg, uint64_t val)
95488447a05SGarrett D'Amore {
95588447a05SGarrett D'Amore 	audiopci_ctrl_t	*pc = arg;
95688447a05SGarrett D'Amore 	audiopci_dev_t	*dev = pc->dev;
95788447a05SGarrett D'Amore 
95888447a05SGarrett D'Amore 	val &= 0xff;
95988447a05SGarrett D'Amore 	if (val > 100)
96088447a05SGarrett D'Amore 		return (EINVAL);
96188447a05SGarrett D'Amore 
96288447a05SGarrett D'Amore 	val = (val & 0xff) | ((val & 0xff) << 8);
96388447a05SGarrett D'Amore 
96488447a05SGarrett D'Amore 	mutex_enter(&dev->mutex);
96588447a05SGarrett D'Amore 	pc->val = val;
96688447a05SGarrett D'Amore 	audiopci_configure_output(dev);
96788447a05SGarrett D'Amore 	mutex_exit(&dev->mutex);
96888447a05SGarrett D'Amore 
96988447a05SGarrett D'Amore 	return (0);
97088447a05SGarrett D'Amore }
97188447a05SGarrett D'Amore 
97288447a05SGarrett D'Amore static int
audiopci_set_front(void * arg,uint64_t val)97388447a05SGarrett D'Amore audiopci_set_front(void *arg, uint64_t val)
97488447a05SGarrett D'Amore {
97588447a05SGarrett D'Amore 	audiopci_ctrl_t	*pc = arg;
97688447a05SGarrett D'Amore 	audiopci_dev_t	*dev = pc->dev;
97788447a05SGarrett D'Amore 	uint8_t		l;
97888447a05SGarrett D'Amore 	uint8_t		r;
97988447a05SGarrett D'Amore 
98088447a05SGarrett D'Amore 	l = (val & 0xff00) >> 8;
98188447a05SGarrett D'Amore 	r = val & 0xff;
98288447a05SGarrett D'Amore 
98388447a05SGarrett D'Amore 	if ((l > 100) || (r > 100))
98488447a05SGarrett D'Amore 		return (EINVAL);
98588447a05SGarrett D'Amore 
98688447a05SGarrett D'Amore 	mutex_enter(&dev->mutex);
98788447a05SGarrett D'Amore 	pc->val = val;
98888447a05SGarrett D'Amore 	audiopci_configure_output(dev);
98988447a05SGarrett D'Amore 
99088447a05SGarrett D'Amore 	mutex_exit(&dev->mutex);
99188447a05SGarrett D'Amore 	return (0);
99288447a05SGarrett D'Amore }
99388447a05SGarrett D'Amore 
99488447a05SGarrett D'Amore static int
audiopci_set_speaker(void * arg,uint64_t val)99588447a05SGarrett D'Amore audiopci_set_speaker(void *arg, uint64_t val)
99688447a05SGarrett D'Amore {
99788447a05SGarrett D'Amore 	audiopci_ctrl_t	*pc = arg;
99888447a05SGarrett D'Amore 	audiopci_dev_t	*dev = pc->dev;
99988447a05SGarrett D'Amore 
100088447a05SGarrett D'Amore 	val &= 0xff;
100188447a05SGarrett D'Amore 
100288447a05SGarrett D'Amore 	if (val > 100)
100388447a05SGarrett D'Amore 		return (EINVAL);
100488447a05SGarrett D'Amore 
100588447a05SGarrett D'Amore 	mutex_enter(&dev->mutex);
100688447a05SGarrett D'Amore 	pc->val = val;
100788447a05SGarrett D'Amore 	audiopci_configure_output(dev);
100888447a05SGarrett D'Amore 
100988447a05SGarrett D'Amore 	mutex_exit(&dev->mutex);
101088447a05SGarrett D'Amore 	return (0);
101188447a05SGarrett D'Amore }
101288447a05SGarrett D'Amore 
101388447a05SGarrett D'Amore #define	PLAYCTL	(AUDIO_CTRL_FLAG_RW | AUDIO_CTRL_FLAG_PLAY)
101488447a05SGarrett D'Amore #define	RECCTL	(AUDIO_CTRL_FLAG_RW | AUDIO_CTRL_FLAG_REC)
101588447a05SGarrett D'Amore #define	MONCTL	(AUDIO_CTRL_FLAG_RW | AUDIO_CTRL_FLAG_MONITOR)
101688447a05SGarrett D'Amore #define	PCMVOL	(PLAYCTL | AUDIO_CTRL_FLAG_PCMVOL)
101788447a05SGarrett D'Amore #define	MAINVOL	(PLAYCTL | AUDIO_CTRL_FLAG_MAINVOL)
101888447a05SGarrett D'Amore #define	RECVOL	(RECCTL | AUDIO_CTRL_FLAG_RECVOL)
101988447a05SGarrett D'Amore 
102088447a05SGarrett D'Amore static void
audiopci_alloc_ctrl(audiopci_dev_t * dev,uint32_t num,uint64_t val)102188447a05SGarrett D'Amore audiopci_alloc_ctrl(audiopci_dev_t *dev, uint32_t num, uint64_t val)
102288447a05SGarrett D'Amore {
102388447a05SGarrett D'Amore 	audio_ctrl_desc_t	desc;
102488447a05SGarrett D'Amore 	audio_ctrl_wr_t		fn;
102588447a05SGarrett D'Amore 	audiopci_ctrl_t		*pc;
102688447a05SGarrett D'Amore 
102788447a05SGarrett D'Amore 	bzero(&desc, sizeof (desc));
102888447a05SGarrett D'Amore 
102988447a05SGarrett D'Amore 	pc = &dev->controls[num];
103088447a05SGarrett D'Amore 	pc->num = num;
103188447a05SGarrett D'Amore 	pc->dev = dev;
103288447a05SGarrett D'Amore 
103388447a05SGarrett D'Amore 	switch (num) {
103488447a05SGarrett D'Amore 	case CTL_VOLUME:
103588447a05SGarrett D'Amore 		desc.acd_name = AUDIO_CTRL_ID_VOLUME;
103688447a05SGarrett D'Amore 		desc.acd_type = AUDIO_CTRL_TYPE_MONO;
103788447a05SGarrett D'Amore 		desc.acd_minvalue = 0;
103888447a05SGarrett D'Amore 		desc.acd_maxvalue = 100;
103988447a05SGarrett D'Amore 		desc.acd_flags = PCMVOL;
104088447a05SGarrett D'Amore 		fn = audiopci_set_volume;
104188447a05SGarrett D'Amore 		break;
104288447a05SGarrett D'Amore 
104388447a05SGarrett D'Amore 	case CTL_FRONT:
104488447a05SGarrett D'Amore 		desc.acd_name = AUDIO_CTRL_ID_LINEOUT;
104588447a05SGarrett D'Amore 		desc.acd_type = AUDIO_CTRL_TYPE_STEREO;
104688447a05SGarrett D'Amore 		desc.acd_minvalue = 0;
104788447a05SGarrett D'Amore 		desc.acd_maxvalue = 100;
104888447a05SGarrett D'Amore 		desc.acd_flags = MAINVOL;
104988447a05SGarrett D'Amore 		fn = audiopci_set_front;
105088447a05SGarrett D'Amore 		break;
105188447a05SGarrett D'Amore 
105288447a05SGarrett D'Amore 	case CTL_MONO:
105388447a05SGarrett D'Amore 		desc.acd_name = AUDIO_CTRL_ID_SPEAKER;
105488447a05SGarrett D'Amore 		desc.acd_type = AUDIO_CTRL_TYPE_MONO;
105588447a05SGarrett D'Amore 		desc.acd_minvalue = 0;
105688447a05SGarrett D'Amore 		desc.acd_maxvalue = 100;
105788447a05SGarrett D'Amore 		desc.acd_flags = MAINVOL;
105888447a05SGarrett D'Amore 		fn = audiopci_set_speaker;
105988447a05SGarrett D'Amore 		break;
106088447a05SGarrett D'Amore 
106188447a05SGarrett D'Amore 	case CTL_MIC:
106288447a05SGarrett D'Amore 		desc.acd_name = AUDIO_CTRL_ID_MIC;
106388447a05SGarrett D'Amore 		desc.acd_type = AUDIO_CTRL_TYPE_MONO;
106488447a05SGarrett D'Amore 		desc.acd_minvalue = 0;
106588447a05SGarrett D'Amore 		desc.acd_maxvalue = 100;
106688447a05SGarrett D'Amore 		desc.acd_flags = RECVOL;
106788447a05SGarrett D'Amore 		fn = audiopci_set_reclevel;
106888447a05SGarrett D'Amore 		break;
106988447a05SGarrett D'Amore 
107088447a05SGarrett D'Amore 	case CTL_LINE:
107188447a05SGarrett D'Amore 		desc.acd_name = AUDIO_CTRL_ID_LINEIN;
107288447a05SGarrett D'Amore 		desc.acd_type = AUDIO_CTRL_TYPE_STEREO;
107388447a05SGarrett D'Amore 		desc.acd_minvalue = 0;
107488447a05SGarrett D'Amore 		desc.acd_maxvalue = 100;
107588447a05SGarrett D'Amore 		desc.acd_flags = RECVOL;
107688447a05SGarrett D'Amore 		fn = audiopci_set_reclevel;
107788447a05SGarrett D'Amore 		break;
107888447a05SGarrett D'Amore 
107988447a05SGarrett D'Amore 	case CTL_CD:
108088447a05SGarrett D'Amore 		desc.acd_name = AUDIO_CTRL_ID_CD;
108188447a05SGarrett D'Amore 		desc.acd_type = AUDIO_CTRL_TYPE_STEREO;
108288447a05SGarrett D'Amore 		desc.acd_minvalue = 0;
108388447a05SGarrett D'Amore 		desc.acd_maxvalue = 100;
108488447a05SGarrett D'Amore 		desc.acd_flags = RECVOL;
108588447a05SGarrett D'Amore 		fn = audiopci_set_reclevel;
108688447a05SGarrett D'Amore 		break;
108788447a05SGarrett D'Amore 
108888447a05SGarrett D'Amore 	case CTL_VID:
108988447a05SGarrett D'Amore 		desc.acd_name = AUDIO_CTRL_ID_VIDEO;
109088447a05SGarrett D'Amore 		desc.acd_type = AUDIO_CTRL_TYPE_STEREO;
109188447a05SGarrett D'Amore 		desc.acd_minvalue = 0;
109288447a05SGarrett D'Amore 		desc.acd_maxvalue = 100;
109388447a05SGarrett D'Amore 		desc.acd_flags = RECVOL;
109488447a05SGarrett D'Amore 		fn = audiopci_set_reclevel;
109588447a05SGarrett D'Amore 		break;
109688447a05SGarrett D'Amore 
109788447a05SGarrett D'Amore 	case CTL_PHONE:
109888447a05SGarrett D'Amore 		desc.acd_name = AUDIO_CTRL_ID_PHONE;
109988447a05SGarrett D'Amore 		desc.acd_type = AUDIO_CTRL_TYPE_MONO;
110088447a05SGarrett D'Amore 		desc.acd_minvalue = 0;
110188447a05SGarrett D'Amore 		desc.acd_maxvalue = 100;
110288447a05SGarrett D'Amore 		desc.acd_flags = RECVOL;
110388447a05SGarrett D'Amore 		fn = audiopci_set_reclevel;
110488447a05SGarrett D'Amore 		break;
110588447a05SGarrett D'Amore 
110688447a05SGarrett D'Amore 	case CTL_RECSRC:
110788447a05SGarrett D'Amore 		desc.acd_name = AUDIO_CTRL_ID_RECSRC;
110888447a05SGarrett D'Amore 		desc.acd_type = AUDIO_CTRL_TYPE_ENUM;
110988447a05SGarrett D'Amore 		desc.acd_minvalue = INSRCS;
111088447a05SGarrett D'Amore 		desc.acd_maxvalue = INSRCS;
111188447a05SGarrett D'Amore 		desc.acd_flags = RECCTL | AUDIO_CTRL_FLAG_MULTI;
111288447a05SGarrett D'Amore 		for (int i = 0; audiopci_insrcs[i]; i++) {
111388447a05SGarrett D'Amore 			desc.acd_enum[i] = audiopci_insrcs[i];
111488447a05SGarrett D'Amore 		}
111588447a05SGarrett D'Amore 		fn = audiopci_set_recsrc;
111688447a05SGarrett D'Amore 		break;
111788447a05SGarrett D'Amore 
111888447a05SGarrett D'Amore 	case CTL_MONSRC:
111988447a05SGarrett D'Amore 		desc.acd_name = AUDIO_CTRL_ID_MONSRC;
112088447a05SGarrett D'Amore 		desc.acd_type = AUDIO_CTRL_TYPE_ENUM;
112188447a05SGarrett D'Amore 		desc.acd_minvalue = INSRCS;
112288447a05SGarrett D'Amore 		desc.acd_maxvalue = INSRCS;
112388447a05SGarrett D'Amore 		desc.acd_flags = MONCTL | AUDIO_CTRL_FLAG_MULTI;
112488447a05SGarrett D'Amore 		for (int i = 0; audiopci_insrcs[i]; i++) {
112588447a05SGarrett D'Amore 			desc.acd_enum[i] = audiopci_insrcs[i];
112688447a05SGarrett D'Amore 		}
112788447a05SGarrett D'Amore 		fn = audiopci_set_monsrc;
112888447a05SGarrett D'Amore 		break;
112988447a05SGarrett D'Amore 
113088447a05SGarrett D'Amore 	case CTL_MICBOOST:
113188447a05SGarrett D'Amore 		desc.acd_name = AUDIO_CTRL_ID_MICBOOST;
113288447a05SGarrett D'Amore 		desc.acd_type = AUDIO_CTRL_TYPE_BOOLEAN;
113388447a05SGarrett D'Amore 		desc.acd_minvalue = 0;
113488447a05SGarrett D'Amore 		desc.acd_maxvalue = 100;
113588447a05SGarrett D'Amore 		desc.acd_flags = RECCTL;
113688447a05SGarrett D'Amore 		fn = audiopci_set_micboost;
113788447a05SGarrett D'Amore 		break;
113888447a05SGarrett D'Amore 	}
113988447a05SGarrett D'Amore 
114088447a05SGarrett D'Amore 	pc->val = val;
114188447a05SGarrett D'Amore 	pc->ctrl = audio_dev_add_control(dev->adev, &desc,
114288447a05SGarrett D'Amore 	    audiopci_get_value, fn, pc);
114388447a05SGarrett D'Amore }
114488447a05SGarrett D'Amore 
114588447a05SGarrett D'Amore static int
audiopci_add_controls(audiopci_dev_t * dev)114688447a05SGarrett D'Amore audiopci_add_controls(audiopci_dev_t *dev)
114788447a05SGarrett D'Amore {
114888447a05SGarrett D'Amore 	audiopci_alloc_ctrl(dev, CTL_VOLUME, 75);
114988447a05SGarrett D'Amore 	audiopci_alloc_ctrl(dev, CTL_FRONT, ((75) | (75 << 8)));
115088447a05SGarrett D'Amore 	audiopci_alloc_ctrl(dev, CTL_MONO, 75);
115188447a05SGarrett D'Amore 	audiopci_alloc_ctrl(dev, CTL_MIC, 50);
115288447a05SGarrett D'Amore 	audiopci_alloc_ctrl(dev, CTL_LINE, 0);
115388447a05SGarrett D'Amore 	audiopci_alloc_ctrl(dev, CTL_CD, 0);
115488447a05SGarrett D'Amore 	audiopci_alloc_ctrl(dev, CTL_VID, 0);
115588447a05SGarrett D'Amore 	audiopci_alloc_ctrl(dev, CTL_PHONE, 0);
115688447a05SGarrett D'Amore 	audiopci_alloc_ctrl(dev, CTL_RECSRC, (1U << INPUT_MIC));
115788447a05SGarrett D'Amore 	audiopci_alloc_ctrl(dev, CTL_MONSRC, 0);
115888447a05SGarrett D'Amore 	audiopci_alloc_ctrl(dev, CTL_MICBOOST, 1);
115988447a05SGarrett D'Amore 
116088447a05SGarrett D'Amore 	audiopci_configure_output(dev);
116188447a05SGarrett D'Amore 	audiopci_configure_input(dev);
116288447a05SGarrett D'Amore 
116388447a05SGarrett D'Amore 	return (DDI_SUCCESS);
116488447a05SGarrett D'Amore }
116588447a05SGarrett D'Amore 
1166*68c47f65SGarrett D'Amore static void
audiopci_del_controls(audiopci_dev_t * dev)116788447a05SGarrett D'Amore audiopci_del_controls(audiopci_dev_t *dev)
116888447a05SGarrett D'Amore {
116988447a05SGarrett D'Amore 	for (int i = 0; i < CTL_NUM; i++) {
117088447a05SGarrett D'Amore 		if (dev->controls[i].ctrl) {
117188447a05SGarrett D'Amore 			audio_dev_del_control(dev->controls[i].ctrl);
117288447a05SGarrett D'Amore 		}
117388447a05SGarrett D'Amore 	}
117488447a05SGarrett D'Amore }
117588447a05SGarrett D'Amore 
1176*68c47f65SGarrett D'Amore static int
audiopci_attach(dev_info_t * dip)117788447a05SGarrett D'Amore audiopci_attach(dev_info_t *dip)
117888447a05SGarrett D'Amore {
117988447a05SGarrett D'Amore 	uint16_t pci_command, vendor, device;
118088447a05SGarrett D'Amore 	audiopci_dev_t *dev;
118188447a05SGarrett D'Amore 	ddi_acc_handle_t pcih;
118288447a05SGarrett D'Amore 
118388447a05SGarrett D'Amore 	dev = kmem_zalloc(sizeof (*dev), KM_SLEEP);
118488447a05SGarrett D'Amore 	dev->dip = dip;
118588447a05SGarrett D'Amore 	ddi_set_driver_private(dip, dev);
118688447a05SGarrett D'Amore 
1187*68c47f65SGarrett D'Amore 	mutex_init(&dev->mutex, NULL, MUTEX_DRIVER, NULL);
1188*68c47f65SGarrett D'Amore 
118988447a05SGarrett D'Amore 	if (pci_config_setup(dip, &pcih) != DDI_SUCCESS) {
119088447a05SGarrett D'Amore 		audio_dev_warn(dev->adev, "pci_config_setup failed");
1191*68c47f65SGarrett D'Amore 		mutex_destroy(&dev->mutex);
119288447a05SGarrett D'Amore 		kmem_free(dev, sizeof (*dev));
119388447a05SGarrett D'Amore 		return (DDI_FAILURE);
119488447a05SGarrett D'Amore 	}
119588447a05SGarrett D'Amore 
119688447a05SGarrett D'Amore 	vendor = pci_config_get16(pcih, PCI_CONF_VENID);
119788447a05SGarrett D'Amore 	device = pci_config_get16(pcih, PCI_CONF_DEVID);
119888447a05SGarrett D'Amore 
119988447a05SGarrett D'Amore 	if ((vendor != ENSONIQ_VENDOR_ID && vendor != CREATIVE_VENDOR_ID) ||
120088447a05SGarrett D'Amore 	    (device != ENSONIQ_ES1370))
120188447a05SGarrett D'Amore 		goto err_exit;
120288447a05SGarrett D'Amore 
120388447a05SGarrett D'Amore 	dev->devid = device;
120488447a05SGarrett D'Amore 
120588447a05SGarrett D'Amore 	dev->adev = audio_dev_alloc(dip, 0);
120688447a05SGarrett D'Amore 	if (dev->adev == NULL) {
120788447a05SGarrett D'Amore 		goto err_exit;
120888447a05SGarrett D'Amore 	}
120988447a05SGarrett D'Amore 
121088447a05SGarrett D'Amore 	audio_dev_set_description(dev->adev, "AudioPCI");
121188447a05SGarrett D'Amore 	audio_dev_set_version(dev->adev, "ES1370");
121288447a05SGarrett D'Amore 	audio_dev_add_info(dev->adev, "Legacy codec: Asahi Kasei AK4531");
121388447a05SGarrett D'Amore 
121488447a05SGarrett D'Amore 	/* activate the device */
121588447a05SGarrett D'Amore 	pci_command = pci_config_get16(pcih, PCI_CONF_COMM);
121688447a05SGarrett D'Amore 	pci_command |= PCI_COMM_ME | PCI_COMM_IO;
121788447a05SGarrett D'Amore 	pci_config_put16(pcih, PCI_CONF_COMM, pci_command);
121888447a05SGarrett D'Amore 
121988447a05SGarrett D'Amore 	/* map registers */
122088447a05SGarrett D'Amore 	if (ddi_regs_map_setup(dip, 1, &dev->regs, 0, 0, &acc_attr,
122188447a05SGarrett D'Amore 	    &dev->acch) != DDI_SUCCESS) {
122288447a05SGarrett D'Amore 		audio_dev_warn(dev->adev, "can't map registers");
122388447a05SGarrett D'Amore 		goto err_exit;
122488447a05SGarrett D'Amore 	}
122588447a05SGarrett D'Amore 
122688447a05SGarrett D'Amore 
122788447a05SGarrett D'Amore 	/* This allocates and configures the engines */
122888447a05SGarrett D'Amore 	if (audiopci_init(dev) != DDI_SUCCESS) {
122988447a05SGarrett D'Amore 		audio_dev_warn(dev->adev, "can't init device");
123088447a05SGarrett D'Amore 		goto err_exit;
123188447a05SGarrett D'Amore 	}
123288447a05SGarrett D'Amore 
123388447a05SGarrett D'Amore 	pci_config_teardown(&pcih);
123488447a05SGarrett D'Amore 
123588447a05SGarrett D'Amore 	ddi_report_dev(dip);
123688447a05SGarrett D'Amore 
123788447a05SGarrett D'Amore 	return (DDI_SUCCESS);
123888447a05SGarrett D'Amore 
123988447a05SGarrett D'Amore err_exit:
1240*68c47f65SGarrett D'Amore 	mutex_destroy(&dev->mutex);
124188447a05SGarrett D'Amore 	pci_config_teardown(&pcih);
124288447a05SGarrett D'Amore 
124388447a05SGarrett D'Amore 	audiopci_destroy(dev);
124488447a05SGarrett D'Amore 
124588447a05SGarrett D'Amore 	return (DDI_FAILURE);
124688447a05SGarrett D'Amore }
124788447a05SGarrett D'Amore 
1248*68c47f65SGarrett D'Amore static int
audiopci_detach(audiopci_dev_t * dev)124988447a05SGarrett D'Amore audiopci_detach(audiopci_dev_t *dev)
125088447a05SGarrett D'Amore {
125188447a05SGarrett D'Amore 	int tmp;
125288447a05SGarrett D'Amore 
125388447a05SGarrett D'Amore 	/* first unregister us from the DDI framework, might be busy */
125488447a05SGarrett D'Amore 	if (audio_dev_unregister(dev->adev) != DDI_SUCCESS)
125588447a05SGarrett D'Amore 		return (DDI_FAILURE);
125688447a05SGarrett D'Amore 
125788447a05SGarrett D'Amore 	mutex_enter(&dev->mutex);
125888447a05SGarrett D'Amore 
125988447a05SGarrett D'Amore 	tmp = GET8(dev, CONC_bSERCTL_OFF) &
126088447a05SGarrett D'Amore 	    ~(CONC_SERCTL_DACIE | CONC_SERCTL_SYNIE | CONC_SERCTL_ADCIE);
126188447a05SGarrett D'Amore 	PUT8(dev, CONC_bSERCTL_OFF, tmp);
126288447a05SGarrett D'Amore 	PUT8(dev, CONC_bSERCTL_OFF, tmp);
126388447a05SGarrett D'Amore 	PUT8(dev, CONC_bSERCTL_OFF, tmp);
126488447a05SGarrett D'Amore 	PUT8(dev, CONC_bSERCTL_OFF, tmp);
126588447a05SGarrett D'Amore 
126688447a05SGarrett D'Amore 	tmp = GET8(dev, CONC_bDEVCTL_OFF) &
126788447a05SGarrett D'Amore 	    ~(CONC_DEVCTL_DAC_EN | CONC_DEVCTL_ADC_EN | CONC_DEVCTL_SYN_EN);
126888447a05SGarrett D'Amore 	PUT8(dev, CONC_bDEVCTL_OFF, tmp);
126988447a05SGarrett D'Amore 	PUT8(dev, CONC_bDEVCTL_OFF, tmp);
127088447a05SGarrett D'Amore 	PUT8(dev, CONC_bDEVCTL_OFF, tmp);
127188447a05SGarrett D'Amore 	PUT8(dev, CONC_bDEVCTL_OFF, tmp);
127288447a05SGarrett D'Amore 
127388447a05SGarrett D'Amore 	mutex_exit(&dev->mutex);
127488447a05SGarrett D'Amore 
127588447a05SGarrett D'Amore 	audiopci_destroy(dev);
127688447a05SGarrett D'Amore 
127788447a05SGarrett D'Amore 	return (DDI_SUCCESS);
127888447a05SGarrett D'Amore }
127988447a05SGarrett D'Amore 
128088447a05SGarrett D'Amore static int
audiopci_resume(audiopci_dev_t * dev)128188447a05SGarrett D'Amore audiopci_resume(audiopci_dev_t *dev)
128288447a05SGarrett D'Amore {
128388447a05SGarrett D'Amore 	/* reinitialize hardware */
128488447a05SGarrett D'Amore 	audiopci_init_hw(dev);
128588447a05SGarrett D'Amore 
1286*68c47f65SGarrett D'Amore 	audio_dev_resume(dev->adev);
128788447a05SGarrett D'Amore 	return (DDI_SUCCESS);
128888447a05SGarrett D'Amore }
128988447a05SGarrett D'Amore 
129088447a05SGarrett D'Amore static int
audiopci_suspend(audiopci_dev_t * dev)129188447a05SGarrett D'Amore audiopci_suspend(audiopci_dev_t *dev)
129288447a05SGarrett D'Amore {
1293*68c47f65SGarrett D'Amore 	audio_dev_suspend(dev->adev);
129488447a05SGarrett D'Amore 
129588447a05SGarrett D'Amore 	return (DDI_SUCCESS);
129688447a05SGarrett D'Amore }
129788447a05SGarrett D'Amore 
129888447a05SGarrett D'Amore static int
audiopci_quiesce(dev_info_t * dip)129988447a05SGarrett D'Amore audiopci_quiesce(dev_info_t *dip)
130088447a05SGarrett D'Amore {
130188447a05SGarrett D'Amore 	audiopci_dev_t	*dev;
130288447a05SGarrett D'Amore 	uint8_t		tmp;
130388447a05SGarrett D'Amore 
130488447a05SGarrett D'Amore 	if ((dev = ddi_get_driver_private(dip)) == NULL) {
130588447a05SGarrett D'Amore 		return (DDI_FAILURE);
130688447a05SGarrett D'Amore 	}
130788447a05SGarrett D'Amore 
130888447a05SGarrett D'Amore 	/* This disables all DMA engines and interrupts */
130988447a05SGarrett D'Amore 	tmp = GET8(dev, CONC_bSERCTL_OFF) &
131088447a05SGarrett D'Amore 	    ~(CONC_SERCTL_DACIE | CONC_SERCTL_SYNIE | CONC_SERCTL_ADCIE);
131188447a05SGarrett D'Amore 	PUT8(dev, CONC_bSERCTL_OFF, tmp);
131288447a05SGarrett D'Amore 	PUT8(dev, CONC_bSERCTL_OFF, tmp);
131388447a05SGarrett D'Amore 	PUT8(dev, CONC_bSERCTL_OFF, tmp);
131488447a05SGarrett D'Amore 	PUT8(dev, CONC_bSERCTL_OFF, tmp);
131588447a05SGarrett D'Amore 
131688447a05SGarrett D'Amore 	tmp = GET8(dev, CONC_bDEVCTL_OFF) &
131788447a05SGarrett D'Amore 	    ~(CONC_DEVCTL_DAC_EN | CONC_DEVCTL_ADC_EN | CONC_DEVCTL_SYN_EN);
131888447a05SGarrett D'Amore 	PUT8(dev, CONC_bDEVCTL_OFF, tmp);
131988447a05SGarrett D'Amore 	PUT8(dev, CONC_bDEVCTL_OFF, tmp);
132088447a05SGarrett D'Amore 	PUT8(dev, CONC_bDEVCTL_OFF, tmp);
132188447a05SGarrett D'Amore 	PUT8(dev, CONC_bDEVCTL_OFF, tmp);
132288447a05SGarrett D'Amore 
132388447a05SGarrett D'Amore 	return (DDI_SUCCESS);
132488447a05SGarrett D'Amore }
132588447a05SGarrett D'Amore 
132688447a05SGarrett D'Amore 
132788447a05SGarrett D'Amore static int
audiopci_ddi_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)132888447a05SGarrett D'Amore audiopci_ddi_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
132988447a05SGarrett D'Amore {
133088447a05SGarrett D'Amore 	audiopci_dev_t *dev;
133188447a05SGarrett D'Amore 
133288447a05SGarrett D'Amore 	switch (cmd) {
133388447a05SGarrett D'Amore 	case DDI_ATTACH:
133488447a05SGarrett D'Amore 		return (audiopci_attach(dip));
133588447a05SGarrett D'Amore 
133688447a05SGarrett D'Amore 	case DDI_RESUME:
133788447a05SGarrett D'Amore 		if ((dev = ddi_get_driver_private(dip)) == NULL) {
133888447a05SGarrett D'Amore 			return (DDI_FAILURE);
133988447a05SGarrett D'Amore 		}
134088447a05SGarrett D'Amore 		return (audiopci_resume(dev));
134188447a05SGarrett D'Amore 
134288447a05SGarrett D'Amore 	default:
134388447a05SGarrett D'Amore 		return (DDI_FAILURE);
134488447a05SGarrett D'Amore 	}
134588447a05SGarrett D'Amore }
134688447a05SGarrett D'Amore 
134788447a05SGarrett D'Amore static int
audiopci_ddi_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)134888447a05SGarrett D'Amore audiopci_ddi_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
134988447a05SGarrett D'Amore {
135088447a05SGarrett D'Amore 	audiopci_dev_t *dev;
135188447a05SGarrett D'Amore 
135288447a05SGarrett D'Amore 	if ((dev = ddi_get_driver_private(dip)) == NULL) {
135388447a05SGarrett D'Amore 		return (DDI_FAILURE);
135488447a05SGarrett D'Amore 	}
135588447a05SGarrett D'Amore 
135688447a05SGarrett D'Amore 	switch (cmd) {
135788447a05SGarrett D'Amore 	case DDI_DETACH:
135888447a05SGarrett D'Amore 		return (audiopci_detach(dev));
135988447a05SGarrett D'Amore 
136088447a05SGarrett D'Amore 	case DDI_SUSPEND:
136188447a05SGarrett D'Amore 		return (audiopci_suspend(dev));
136288447a05SGarrett D'Amore 	default:
136388447a05SGarrett D'Amore 		return (DDI_FAILURE);
136488447a05SGarrett D'Amore 	}
136588447a05SGarrett D'Amore }
136688447a05SGarrett D'Amore 
136788447a05SGarrett D'Amore static struct dev_ops audiopci_dev_ops = {
136888447a05SGarrett D'Amore 	DEVO_REV,		/* rev */
136988447a05SGarrett D'Amore 	0,			/* refcnt */
137088447a05SGarrett D'Amore 	NULL,			/* getinfo */
137188447a05SGarrett D'Amore 	nulldev,		/* identify */
137288447a05SGarrett D'Amore 	nulldev,		/* probe */
137388447a05SGarrett D'Amore 	audiopci_ddi_attach,	/* attach */
137488447a05SGarrett D'Amore 	audiopci_ddi_detach,	/* detach */
137588447a05SGarrett D'Amore 	nodev,			/* reset */
137688447a05SGarrett D'Amore 	NULL,			/* cb_ops */
137788447a05SGarrett D'Amore 	NULL,			/* bus_ops */
137888447a05SGarrett D'Amore 	NULL,			/* power */
137988447a05SGarrett D'Amore 	audiopci_quiesce,	/* quiesce */
138088447a05SGarrett D'Amore };
138188447a05SGarrett D'Amore 
138288447a05SGarrett D'Amore static struct modldrv audiopci_modldrv = {
138388447a05SGarrett D'Amore 	&mod_driverops,			/* drv_modops */
138488447a05SGarrett D'Amore 	"Ensoniq 1370 Audio",		/* linkinfo */
138588447a05SGarrett D'Amore 	&audiopci_dev_ops,		/* dev_ops */
138688447a05SGarrett D'Amore };
138788447a05SGarrett D'Amore 
138888447a05SGarrett D'Amore static struct modlinkage modlinkage = {
138988447a05SGarrett D'Amore 	MODREV_1,
139088447a05SGarrett D'Amore 	{ &audiopci_modldrv, NULL }
139188447a05SGarrett D'Amore };
139288447a05SGarrett D'Amore 
139388447a05SGarrett D'Amore int
_init(void)139488447a05SGarrett D'Amore _init(void)
139588447a05SGarrett D'Amore {
139688447a05SGarrett D'Amore 	int	rv;
139788447a05SGarrett D'Amore 
139888447a05SGarrett D'Amore 	audio_init_ops(&audiopci_dev_ops, DRVNAME);
139988447a05SGarrett D'Amore 	if ((rv = mod_install(&modlinkage)) != 0) {
140088447a05SGarrett D'Amore 		audio_fini_ops(&audiopci_dev_ops);
140188447a05SGarrett D'Amore 	}
140288447a05SGarrett D'Amore 	return (rv);
140388447a05SGarrett D'Amore }
140488447a05SGarrett D'Amore 
140588447a05SGarrett D'Amore int
_fini(void)140688447a05SGarrett D'Amore _fini(void)
140788447a05SGarrett D'Amore {
140888447a05SGarrett D'Amore 	int	rv;
140988447a05SGarrett D'Amore 
141088447a05SGarrett D'Amore 	if ((rv = mod_remove(&modlinkage)) == 0) {
141188447a05SGarrett D'Amore 		audio_fini_ops(&audiopci_dev_ops);
141288447a05SGarrett D'Amore 	}
141388447a05SGarrett D'Amore 	return (rv);
141488447a05SGarrett D'Amore }
141588447a05SGarrett D'Amore 
141688447a05SGarrett D'Amore int
_info(struct modinfo * modinfop)141788447a05SGarrett D'Amore _info(struct modinfo *modinfop)
141888447a05SGarrett D'Amore {
141988447a05SGarrett D'Amore 	return (mod_info(&modlinkage, modinfop));
142088447a05SGarrett D'Amore }
1421